macOS: a VM's phys_footprint counts guest pages written by the VMM twice
I maintainer di solito rispondono entro 3 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 35/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Ambito
- performance
Direzione di ricerca
Start with the supplied hvf-double.c reproducer and the reported measurements for hv_vm_map() on macOS; compare process phys_footprint with system-wide vm_stat as the guest touches mapped pages. The report says the double charge appears to be macOS accounting behavior and notes no known libkrun workaround. Done looks like confirming whether a mapping avoids the double count, or recording Apple's response; the reporter says they will add that response here.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
While measuring free page reporting (#931, #941) through Colima, I found that krunkit's physical footprint, the number Activity Monitor shows, overstates what a VM costs the host. It isn't a libkrun bug, but it affects how anyone measures libkrun's memory use on macOS, so I'm writing it down here.
What happens: when the VMM writes a page of guest RAM through its own mapping and the guest then uses that page, macOS charges the page to the process twice. The page exists only once.
Reproducer without libkrun (hvf-double.c below, ~80 lines): it maps anonymous memory into a VM with hv_vm_map() and runs a vCPU with a load or store loop over it.
| 512 MiB of guest RAM | phys_footprint |
|---|---|
| the VMM writes every page | +512 MB |
| the guest writes every page | +512 MB |
| the VMM writes every page, then the guest reads it | +1025 MB |
It's the same with MAP_SHARED. With 2 GiB, phys_footprint grows by 4098 MB while system-wide used memory (vm_stat) grows by 1948 MB, so only the accounting is doubled.
With libkrun (krunkit through Colima, #941 with --free-page-reporting replace, 4 vCPU, 2 GiB; guest memory in use counted from /proc/kpageflags):
| guest memory in use: +602 MiB | krunkit phys_footprint |
|---|---|
| written by the guest (tmpfs) | +616 MB |
| read from the disk through virtio-blk | +1180 MB |
Both go away when the guest frees the pages.
What it means in practice:
- Device I/O inflates the footprint: disk reads, received packets, virtio-fs reads, and the kernel and initramfs loaded at boot. On a freshly booted guest here, the footprint is ~210 MB + ~17 MB per vCPU above what the guest can account for, which probably is mostly this.
- Comparing footprints between configurations that do different amounts of I/O is misleading. System-wide
vm_stat(or Memory Pressure) shows the real cost. - The free page reporting numbers in #931/#941 aren't affected: those tests write guest memory from inside the guest.
I'm reporting it to Apple through Feedback Assistant and will add the number here. I don't see a way for libkrun to avoid it, but if someone knows of a mapping that isn't counted twice, I'd be happy to test it. I've also opened a short note for krunkit's docs: libkrun/krunkit#123.
Tested on macOS 26.5.2 (25F84), Apple M5.
hvf-double.c
// Is guest RAM that the VMM writes through its own mapping charged twice once
// the guest touches it? No libkrun: map N MiB of anonymous memory into a VM,
// then per mode:
// vmm-then-guest the VMM writes every page, then a vCPU reads every page
// guest-only a vCPU writes every page
// vmm-only the VMM writes every page, the guest never runs
// and print phys_footprint. A 1x charge is N MiB; 2x means the VMM's pages and
// the guest's are not the same pages any more.
// hvf-double <mode> <mem_mib> [private|shared]
//
// Build: clang -O2 -o hvf-double hvf-double.c -framework Hypervisor
// codesign --entitlements ent.plist -s - hvf-double
// (ent.plist grants com.apple.security.hypervisor)
#include <Hypervisor/Hypervisor.h>
#include <libproc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#define CODE_IPA 0x10000000ULL // one 16 KiB page of guest code
#define RAM_IPA 0x40000000ULL
static long fp(void) {
struct rusage_info_v4 ri;
proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ri);
return (long)(ri.ri_phys_footprint >> 20);
}
#define CHECK(x) do { hv_return_t r = (x); if (r != HV_SUCCESS) { fprintf(stderr, "%s: %d\n", #x, r); exit(1); } } while (0)
// Guest code, MMU off: x0 = start, x1 = end, x2 = 1 to store, 0 to load.
// loop: if (x2) str x3,[x0] else ldr x3,[x0]; x0 += 4096; if x0 < x1 loop; hvc #0
static const uint32_t code[] = {
0xb4000062, // cbz x2, +12 -> load
0xf9000003, // str x3, [x0]
0x14000002, // b +8
0xf9400003, // ldr x3, [x0]
0x91400400, // add x0, x0, #4096
0xeb01001f, // cmp x0, x1
0x54ffff43, // b.lo -24 -> loop
0xd4000002, // hvc #0
};
static void run_guest(uint64_t start, uint64_t end, int store) {
hv_vcpu_t v; hv_vcpu_exit_t *e;
CHECK(hv_vcpu_create(&v, &e, NULL));
CHECK(hv_vcpu_set_reg(v, HV_REG_PC, CODE_IPA));
CHECK(hv_vcpu_set_reg(v, HV_REG_CPSR, 0x3c5)); // EL1h, interrupts masked
CHECK(hv_vcpu_set_reg(v, HV_REG_X0, start));
CHECK(hv_vcpu_set_reg(v, HV_REG_X1, end));
CHECK(hv_vcpu_set_reg(v, HV_REG_X2, store));
CHECK(hv_vcpu_set_reg(v, HV_REG_X3, 0x5a5a5a5a5a5a5a5aULL));
for (;;) {
CHECK(hv_vcpu_run(v));
if (e->reason == HV_EXIT_REASON_EXCEPTION && (e->exception.syndrome >> 26) == 0x16) break; // hvc
if (e->reason != HV_EXIT_REASON_EXCEPTION) continue;
fprintf(stderr, "guest exception, syndrome %llx va %llx\n", e->exception.syndrome, e->exception.virtual_address);
exit(1);
}
hv_vcpu_destroy(v);
}
int main(int argc, char **argv) {
const char *mode = argv[1]; size_t mem = (size_t)atol(argv[2]) << 20;
int shared = argc > 3 && !strcmp(argv[3], "shared");
CHECK(hv_vm_create(NULL));
void *cpage = mmap(NULL, 16384, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
memcpy(cpage, code, sizeof code);
CHECK(hv_vm_map(cpage, CODE_IPA, 16384, HV_MEMORY_READ | HV_MEMORY_EXEC));
uint8_t *ram = mmap(NULL, mem, PROT_READ | PROT_WRITE,
(shared ? MAP_SHARED : MAP_PRIVATE) | MAP_ANON | MAP_NORESERVE, -1, 0);
CHECK(hv_vm_map(ram, RAM_IPA, mem, HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC));
long base = fp();
if (!strcmp(mode, "guest-only")) {
run_guest(RAM_IPA, RAM_IPA + mem, 1);
} else {
for (size_t o = 0; o < mem; o += 4096) ram[o] = 0xa5;
if (!strcmp(mode, "vmm-then-guest")) run_guest(RAM_IPA, RAM_IPA + mem, 0);
}
printf("%-15s %-7s %4zu MiB: +%ld MB\n", mode, shared ? "shared" : "private", mem >> 20, fp() - base);
fflush(stdout);
// Stay alive so that the system-wide numbers can be read with the memory held.
if (getenv("HOLD")) pause();
return 0;
}
- Lingua principale
- Rust
- Stelle
- 2.8k
- Fork
- 279
- Merge medio
- 5g 12h
- PR unite (30g)
- 27
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di libkrun/libkrun
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
I maintainer di solito rispondono entro 3 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
libkrun/libkrun#895 · 2 commenti ·
I maintainer di solito rispondono entro 3 giorni
-
virtio-fs: any open() drops the guest page cache of the file, including pages other processes have mapped (CachePolicy::Auto)Forse già presa @jgoyvaerts l’ha presa 4 giorni fa. Aperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
I maintainer di solito rispondono entro 3 giorni
-
macOS/HVF: anonymous guest RAM is counted twice in phys_footprint after host and guest accessAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
I maintainer di solito rispondono entro 3 giorni
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
I maintainer di solito rispondono entro 3 giorni
Tutte le issue di libkrun/libkrun
Issue simili
-
desktop documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
vfarcic/dot-agent-deck#2008 ·
I maintainer di solito rispondono entro 1 giorno
-
needs triage
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
pbakaus/impeccable#1210 ·
I maintainer di solito rispondono entro 1 giorno
-
area: ci priority: P4 ready-for-agent
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
shep-pm/shep#1249 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
L: javascript
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
dependabot/dependabot-core#16744 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
nautechsystems/nautilus_trader#5486 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno