Stabilize single-copy tfork clone path#2
Conversation
Review: stabilize single-copy tfork clone pathRead the full diff (1,326 lines across vendored CRIU, crun, and podman). The PID-namespace work looks like the real fix here — nested pidns leaf-id assignment at dump time, hierarchy truncation at restore, and the My main concern is a pattern running through the rest of the diff: under 🔴 Threads are restored with fresh TIDsTwo changes combine. Thread creation drops if (args->tfork_active) {
pr_info("tfork: restore thread pid=%d with fresh tid, set_tid_size %d -> 0 ...");
c_args.set_tid = 0;
c_args.set_tid_size = 0;
}and if (args->ta && args->ta->tfork_active) {
pr_info("tfork: accepting fresh thread tid %d instead of dumped tid %d\n", ...);
args->pid = my_pid;CRIU restores exact TIDs specifically because userspace caches them. After this, the restored TCB still holds the dumped tid while the kernel tid is fresh. That breaks:
The clone comes up fine and then misbehaves later, far from the cause. If this is a deliberate tradeoff to get forks working at all, could it get a comment at the 🔴 The restore barrier abort is suppressed, then papered over with a timeoutIn if (tfork_active_local && siginfo->si_code == CLD_EXITED && siginfo->si_status == 0) {
pr_info("tfork: ignoring clean child exit during restore: task %d\n", siginfo->si_pid);
return;
}Previously this called And in for (waited = 0; waited < 100; waited++) {
if ((int)futex_get(np) <= participants) break;
usleep(100000);
}
if ((int)futex_get(np) > participants) { ...; return -ETIMEDOUT; }These two are coupled: suppressing the abort is what makes the barrier hang, and the 10s poll is what stops the hang. That works, but it converts "restore failed for reason X" into "restore timed out for no stated reason." Concerns with the timeout specifically:
🟠 ~20 new
|
Re-review:
|
Re-review:
|
Re-review:
|
Re-review:
|
Pre-merge checklist — os4agent #2Consolidating what's still open. Nothing here is a known-broken finding; it's one verification gap plus three cheap cleanups. Ordered by what I'd do first. 1. Verify the thread-TID change end to end (blocking)
if (args->tfork_active) {
/*
* Preserve the TID visible in the clone's innermost PID namespace.
* Outer namespace TIDs are allocated by the kernel so concurrent
* copy helpers cannot collide with each other on the host.
*/
c_args.set_tid_size = 1;
}This reads correctly against the clone3 contract — Suggested verification:
Small hardening while you're in there: the tfork branch forces if (args->tfork_active && thread_args[i].ns_level > 0) {2. Confirm
|
Summary
This PR stabilizes the single-copy tfork/live-clone path and aligns it with the n-copy restore machinery. It fixes nested PID-namespace restoration, preserves application-visible thread IDs, hardens pstree construction and unwind behavior, improves restore synchronization and diagnostics, and restores pycriu compatibility with both PSTREE image formats.
The final diff touches 18 files across Podman, crun, CRIU, and pycriu.
Single-copy clone routing
PODMAN_TFORK_SINGLE_COPY_CONMON=1selects the legacy conmon bootstrap.PODMAN_TFORK_SINGLE_COPY_DIRECT=1bypasses the n-copy restore helper.--tfork-copies=1as a real n-copy-helper request throughout Podman, crun, and CRIU.PODMAN_TFORK_CLONE_READY_TIMEOUT_SECS.PID namespaces and process restoration
nsidstays aligned with the innermostlocalpid.CLONE_NEWPIDstate for nested PID namespace init tasks.set_tid[0]; outer namespace TIDs remain kernel-assigned. Thens_level > 0guard keeps the zero-level case from reading an uninitialized TID slot.PSTREE correctness and compatibility
TASK_UNDEFfor diagnostic-safe reuse checks.pstree_entryper framed record;pstree_file_entry.test_pstree_compat.pyand run it before starting the pycriu service tests, including under parallel make.Restore synchronization and failure handling
Operational behavior and diagnostics
CRUN_TFORK_EXT_UNIX_SK.Inotify behavior
The temporary tfork-specific inotify bypass was reverted. This is independent of the thread-TID fix: clones now use CRIU's normal watch-mark replay path. If mark replay cannot be completed, restore fails instead of silently producing a clone with dead watchers. Because the final tree matches the normal CRIU implementation,
fsnotify.cdoes not appear in the net PR diff.Validation
Completed:
git diff --check: pass.Still required before merge on the Ubuntu x86_64 tfork host:
gettid()inside the clone matches its pre-fork value.These checks require root, btrfs-backed Podman storage, and the tfork kernel modules and cannot be run on the macOS/ARM development machine.