Repository navigation
Discard dirty guest memory at exit #415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -203,9 +203,10 @@ int fork_child_main(int ipc_fd, | |
| guest_t g; | ||
|
|
||
| if (hdr.has_shm) { | ||
| /* CoW fork: receive shm fd via SCM_RIGHTS, then map MAP_PRIVATE. This | ||
| * gives the child an instant copy-on-write snapshot of the parent's | ||
| * entire guest memory, with no region enumeration or byte copying. | ||
| /* CoW fork: receive shm fd via SCM_RIGHTS for guest_init_from_shm to | ||
| * map. This gives the child an instant copy-on-write snapshot of the | ||
| * parent's entire guest memory, with no region enumeration or byte | ||
| * copying. | ||
| */ | ||
| int shm_fd = -1, shm_count = 0; | ||
| if (fork_ipc_recv_fds(ipc_fd, &shm_fd, 1, &shm_count) < 0 || | ||
|
|
@@ -1769,8 +1770,8 @@ int64_t sys_clone(hv_vcpu_t vcpu, | |
| goto fail_snapshot; | ||
|
|
||
| /* CoW fast path: if shm_fd >= 0, send a snapshot of guest memory to the | ||
| * child instead of the per-region copy. The child maps that snapshot | ||
| * MAP_PRIVATE; subsequent writes on either side are private. | ||
| * child instead of the per-region copy; guest_init_from_shm maps it so the | ||
| * child's writes stay out of the parent's memory. | ||
| * | ||
| * The parent's own mapping cannot be flipped to MAP_PRIVATE here: hv_vm_map | ||
| * caches the host VA->PA mapping, and a MAP_FIXED remap invalidates it (the | ||
|
|
@@ -1833,8 +1834,15 @@ int64_t sys_clone(hv_vcpu_t vcpu, | |
| * fork (Redis BGSAVE, checkpointing runtimes). On failure the fallback | ||
| * differs per design above: Rosetta drops use_shm so the region-copy | ||
| * path runs; native guests keep use_shm and send the live g->shm_fd. | ||
| * Exercise the fallback on APFS hosts. | ||
| */ | ||
| snapshot_shm_fd = fork_snapshot_shm_via_clonefile(g->shm_fd); | ||
| const char *disable_clone = getenv("ELFUSE_DISABLE_FORK_CLONEFILE"); | ||
| if (disable_clone && strcmp(disable_clone, "1") == 0) { | ||
| snapshot_shm_fd = -1; | ||
| errno = ENOTSUP; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: This new branch routes a deliberately disabled clone through the error path, so on Rosetta the pre-existing handler logs "clone: rosetta CoW snapshot via fclonefileat failed (Operation not supported)" even though fclonefileat was never attempted, and native guests log a fabricated ENOTSUP. Emit the diagnostic before faking the failure, or use a distinct message for the disabled case, so the logs report the real cause. Prompt for AI agents |
||
| } else { | ||
| snapshot_shm_fd = fork_snapshot_shm_via_clonefile(g->shm_fd); | ||
| } | ||
| if (snapshot_shm_fd < 0) { | ||
| if (g->is_rosetta) { | ||
| log_warn( | ||
|
|
@@ -2010,6 +2018,12 @@ int64_t sys_clone(hv_vcpu_t vcpu, | |
| goto fail_snapshot; | ||
| } | ||
|
|
||
| /* Only an admitted child keeps the live slab mapped. The flag stays set | ||
| * after it exits, which costs only the write-back. | ||
| */ | ||
| if (use_shm && snapshot_shm_fd < 0) | ||
| g->shm_exported = true; | ||
|
|
||
| /* The child's inherited slave copies are live from the moment fork returns, | ||
| * so they go on the shared count here rather than in the child's own init, | ||
| * which the parent's close of its copy can beat. | ||
|
|
@@ -2033,8 +2047,7 @@ int64_t sys_clone(hv_vcpu_t vcpu, | |
| close(ipc_sock); | ||
|
|
||
| /* After CoW fork, parent stays on MAP_SHARED because no remap was done. The | ||
| * shm fd is kept open so subsequent forks can also use CoW. The child has | ||
| * its own MAP_PRIVATE view of the same file. | ||
| * shm fd is kept open so subsequent forks can also use CoW. | ||
| */ | ||
|
|
||
| /* CLONE_VFORK suspends the parent until the child exits or execs. The | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| /* | ||
| * Record the bytes a command wrote to disk, as the kernel charged them | ||
| * | ||
| * Copyright 2026 elfuse contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * Usage: probe-disk-writes OUT CMD [ARG...] | ||
| * | ||
| * Runs CMD, waits for it to exit without reaping it, and writes the | ||
| * ri_diskio_byteswritten count of the zombie to OUT. That count includes the | ||
| * write-back a process forces by closing the last descriptor of a file with | ||
| * dirty mapped pages, which is what test-slab-exit-writes measures; once | ||
| * reaped, the count is gone. OUT keeps the number clear of whatever CMD prints. | ||
| * Exits with CMD's status. | ||
| */ | ||
|
|
||
| #include <libproc.h> | ||
| #include <spawn.h> | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include <sys/resource.h> | ||
| #include <sys/wait.h> | ||
|
|
||
| extern char **environ; | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| if (argc < 3) { | ||
| fprintf(stderr, "usage: %s OUT CMD [ARG...]\n", argv[0]); | ||
| return 2; | ||
| } | ||
|
|
||
| pid_t pid; | ||
| int rc = posix_spawnp(&pid, argv[2], NULL, NULL, argv + 2, environ); | ||
| if (rc != 0) { | ||
| fprintf(stderr, "probe-disk-writes: %s: %s\n", argv[2], strerror(rc)); | ||
| return 2; | ||
| } | ||
|
|
||
| siginfo_t info; | ||
| if (waitid(P_PID, (id_t) pid, &info, WEXITED | WNOWAIT) < 0) { | ||
| perror("probe-disk-writes: waitid"); | ||
| return 2; | ||
| } | ||
| struct rusage_info_v4 usage; | ||
| if (proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t *) &usage) != 0) { | ||
| perror("probe-disk-writes: proc_pid_rusage"); | ||
| return 2; | ||
| } | ||
|
|
||
| int status; | ||
| if (waitpid(pid, &status, 0) < 0) { | ||
| perror("probe-disk-writes: waitpid"); | ||
| return 2; | ||
| } | ||
| FILE *out = fopen(argv[1], "w"); | ||
| if (!out || | ||
| fprintf(out, "%llu\n", | ||
| (unsigned long long) usage.ri_diskio_byteswritten) < 0 || | ||
| fclose(out) != 0) { | ||
| perror(argv[1]); | ||
| return 2; | ||
| } | ||
| return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.