Skip to content

CLONE dump: process live-migration  #3047

Description

@asafpamzn

Summary

We'd like to upstream a new CRIU mode that clones a running process from one machine to another with minimal downtime on the source, no intermediate disk image required.

A high level design is at: https://asafpamzn.github.io/criu/clone-dump-design.html.
This issue gives the short version.

See PR: #3049

Goals

  1. Reduce downtime at the source. The source process must be responsive almost the entire migration.
  2. Minimize total migration time. The wall-clock from "start migrating" to "process running on target" should be dominated by physical resources, that is, network bandwidth or CPU, not by design limitations.
  3. No disk needed. Pages stream directly from source memory to target memory; no intermediate image file is written for pages.

How it works

CLONE dump introduce a way to clone a process from one machine to the other with a three-phase flow that uses userfaultfd write-protect and PAGEMAP_SCAN to track writes while the process keeps running. Conceptually similar to the existing pre-dump + dump split, but the running phase tracks dirty pages via UFFD_FEATURE_WP_ASYNC rather than via soft-dirty/pagemap snapshots.

Note: pre-dump did not meet the requirements as it required going over all of the dirty pages during the freeze, which caused a long freeze time for our use case.

Phase 1 — short freeze, set up tracking

Seize the process, enumerate VMAs, register them with userfaultfd in UFFDIO_REGISTER_MODE_WP | UFFD_FEATURE_WP_ASYNC mode, apply UFFDIO_WRITEPROTECT to mark the address space write-protected (async), then unfreeze. After Phase 1, every page the process writes leaves a soft-dirty trail in pagemap; reads run normally.

Phase 2 — process runs; pages stream

Multiple scanner threads call PAGEMAP_SCAN to find newly-dirty regions. They publish dirty regions to a shared MPMC queue. P3 sender threads pull from the queue, process_vm_readv the pages out of the running process, LZ4-compress, and send them over a TLS-protected socket to the target. The target's clone-receive daemon receives, decompresses, and buffers the pages in a per-chunk hash table keyed by virtual address. The scanners iterate until the dirty-page count converges below a threshold.

Phase 3 — final short freeze

Re-seize, do one last PAGEMAP_SCAN for any pages dirtied between the last Phase 2 scan and the freeze, send those, write the regular CRIU "skeleton" image (everything except the page contents), and unfreeze or kill. Total of two brief freezes — the wall-clock between them is governed by your write rate and link bandwidth, not by CRIU.

On the target, the buffered pages are applied via UFFDIO_COPY from the userfaultfd handler that takes over once criu restore runs the restorer. From the restored process's point of view the migration is indistinguishable from a normal CRIU restore — userfaultfd serves any page that hasn't been applied yet, and the drain threads finish applying buffered pages in the background.

Multithreading

Both sides are heavily multithreaded:

  • Source: N scanner threads (default 20) + N P3 sender threads (default 15) + an event-reader thread for UFFD UNMAP/REMOVE/REMAP. Scanners share a lock-free MPMC dirty-region queue; senders share a shared work queue for the initial bulk transfer (atomic fetch-and-add for work-stealing).
  • Target: N P3 receiver threads (one per source sender) + N drain threads that apply buffered pages via UFFDIO_COPY chunk-by-chunk.

All thread counts are runtime-tunable via CLI options (--clone-p3-threads, --clone-scanners, --clone-drain-threads).

In our benchmark we migrated a 300GB process under heavy writes in less than 30 seconds.

Footprint in CRIU's code base

We tried hard to keep the change isolated:

  • New code lives in criu/clone/ and criu/include/clone/ - all behind --clone-dump / criu clone-receive entry points.
  • Touch-points to existing CRIU code are small as much as we could. Existing code paths are untouched when --clone-dump is not set.

Requirements

  • Linux 6.7+ on both source and target — needs both UFFD_FEATURE_WP_ASYNC (5.7+) and PAGEMAP_SCAN (6.7+).
  • vm.unprivileged_userfaultfd=1 on both hosts.
  • TCP reachability source → target.

Usage

# target
sudo criu clone-receive --images-dir /tmp/img \
    --address <SRC_IP> --port 27 -v4

# source
sudo criu dump -t <PID> --images-dir /tmp/img \
    --clone-dump \
    --page-server --address <TGT_IP> --port 27 -v4

clone-receive performs the restore itself once all pages have arrived.

High level design doc (HTML, with diagrams): https://asafpamzn.github.io/criu/clone-dump-design.html

Happy to chunk the patch series however you'd prefer (one giant series vs. a stack of smaller ones; we lean toward the smaller-stack approach, starting with the parts that touch existing files).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions