Conversation
vDSO clock_gettime drops from 1256 ns SVC trap to 2.5ns via CNTVCT-based fast path (493x speedup, 20x under the sub-50 ns design target). The trampoline emits a 28-instruction A64 sequence that reads CNTVCT_EL0, LDAR-acquires the vvar initialized flag, and interpolates wall clock from the anchor as delta * 125 / 3 (Apple Silicon CNTFRQ = 24 MHz), falling back to SVC on first call or CNTVCT regression. The first SVC seeds the vvar via a three-state CAS (0 -> 2 -> 1) so concurrent first calls cannot tear the anchor fields. The seed is gated on ELR_EL1 matching the trampoline's svc_fallback PC so an unrelated raw clock_gettime syscall cannot poison the anchor from arbitrary X9. /dev/urandom 1-byte reads drop from 5688 ns uncached to 2054 ns (2.77x) via a new per-fd entropy cache: an arc4random_buf-refilled 4 KiB buffer per FD_URANDOM slot. The cache is zeroed on close via a type-to-cleanup registry that also closes pre-existing dup and fork-state race windows for every synthetic fd type. eventfd dup shares state across aliases per the Linux contract (refcounted slot plus eventfd_owner[FD_TABLE_SIZE] table). The dup path holds fd_lock and sfd_lock together for the bind commit so racing close cannot leak the refcount; the source identity is pinned via snapshotted host fd so a racing close-and-rebind of the source cannot bind to the wrong slot. tests/test-eventfd-dup pins the shared-state contract. fork_ipc_send_fd_table filters eventfd, signalfd, timerfd, inotify, netlink, pidfd, and epoll out of the SCM_RIGHTS payload. macOS rejects kqueue fds across SCM_RIGHTS and per-class side-table state is not transferable, so a clean drop is the only honest contract. tests/test-fork-synthetic-fd pins it. Startup decomposition: ELFUSE_STARTUP_TRACE=1 emits per-step wall time for VM bring-up (17 steps on test-hello, dominated by hv_vcpu_create and guest_init at roughly 0.9 ms each). Zero overhead when unset.
Collaborator
|
I re-ran the exact same Python syscall-density script from #34 against the same
|
This introduces an EL1-only shim_data block holding a host-published cache: identity slots (pid/ppid/uid/euid/gid/egid/tid), urandom-eligible fd bitmap, a 4 KiB urandom ring with head/tail/lock, and a 32-bit attention bitmask. The EL1 shim assembly serves identity and urandom 1-byte reads inline without trapping to the host; the existing HVC #5 forwarder is taken only when attention is raised, when a non-urandom fd is consulted, or when the ring needs a host-side refill. Measured at 1 M iterations under the new tests/bench-hot-syscalls.c : getpid/getppid/getuid/geteuid/getgid/getegid/gettid : 47 ns/op clock_gettime via __kernel_clock_gettime vDSO : 3.7 ns/op read(/dev/urandom, 1 byte) : 134 ns/op clock_gettime via SVC fallback : 2056 ns/op The vDSO clock_gettime trampoline now seeds CLOCK_{MONOTONIC,REALTIME} anchors back-to-back from a single SVC fallback, so the fast path serves either clockid after one warm-up call. The X9/ELR_EL1 gate runs before the host wall-clock samples so the anchor inherits no positive bias from the seeding round trip. Integrity boundary around the new cache: - The shim_data block is mapped MEM_PERM_RW_EL1_ONLY (AP[2:1]=00) by both bootstrap and execve so EL0 cannot read or store the bytes directly. /proc/self/maps reports PROT_NONE for [shim-data] to match what guest dereferences would observe. - gva_translate_perm refuses MEM_PERM_EL1_ONLY descriptors on guest-behalf access in both the L2 block and L3 page walk paths. read(fd, shim_data_gva, n) now returns EFAULT instead of letting the host spoof the cache. - elf_map_segments takes an explicit infra reserve range and rejects PT_PHDR copies or PT_LOAD segments whose page-aligned write extent intersects it, closing a host-side overwrite path through the ELF loader that bypassed page-table permissions. - A new EL1 data-abort recover handler in shim.S catches strb faults inside named urandom write ranges (caused by a racing EL0 munmap or mprotect), drops the inner exception frame, releases the ring lock, and returns EFAULT to EL0. Cred publish is bracketed so concurrent fast-path readers see a consistent snapshot. The attention word splits into ATTN_BIT_SIGTIMER (0x1), ATTN_BIT_CRED (0x2), and ATTN_BIT_TRACE (0x4). CRED_BRACKETED ORs the CRED bit, runs the setuid/setgid mutator, publishes the four cred slots, then ANDs the CRED bit off. shim_globals_attn_or uses __ATOMIC_SEQ_CST so the mutator's publish stores cannot become globally visible before the attention bit on weakly-ordered ARM64; the AND clear stays __ATOMIC_RELEASE because release pairs with the shim LDAR for the publish-then-clear order. vdso_attention_or mirrors the same ordering. Signal and itimer path support the lane discipline: - attention_guest is now _Atomic so signal_init's NULL clear during the execve reset window pairs with attention_raise's acquire load on any sibling thread. - signal_set_itimer writes expiry and interval before the release store of .active, matching the field order already used by the virt and prof setters. Consumers that ACQUIRE-load .active without holding sig_lock now never observe armed=true with stale fields. - New signal_attention_needed() OR-reads the three guest itimer .active fields plus an unblocked-deliverable signal hint so the HVC epilogue's recompute decides accurately whether the next call may stay on the fast path. The fd-table publication paths that feed the urandom bitmap are serialized so a pathological sibling close+reopen on the same guest fd cannot make the EL1 fast path consult a stale bit: - fd_refresh_urandom_bitmap snapshots (type, linux_flags) AND publishes the bitmap bit inside the same fd_lock critical section. - fd_alloc_opened_host and duplicate_guest_fd install linux_flags, dir, seals, and the urandom bit only after re-acquiring fd_lock and confirming the slot's (type, host_fd) tuple still matches the just- allocated values. On mismatch (the slot was reallocated by a sibling) the install is skipped and any cloned DIR* is closed to avoid a leak. - The host-side urandom cache replaces its single global mutex with a per-fd lock embedded in urandom_cache_t, initialized by io_init() from syscall_init. Concurrent urandom reads on different fds no longer serialize on one mutex. - sys_readv on /dev/urandom now triggers shim_globals_refill_urandom_ring on the slow path, matching sys_read so readv consumers do not leave the shim ring drained.
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
vDSO clock_gettime drops from 1256 ns SVC trap to 2.5ns via CNTVCT-based fast path (493x speedup, 20x under the sub-50 ns design target). The trampoline emits a 28-instruction A64 sequence that reads CNTVCT_EL0, LDAR-acquires the vvar initialized flag, and interpolates wall clock from the anchor as delta * 125 / 3 (Apple Silicon CNTFRQ = 24 MHz), falling back to SVC on first call or CNTVCT regression. The first SVC seeds the vvar via a three-state CAS (0 -> 2 -> 1) so concurrent first calls cannot tear the anchor fields. The seed is gated on ELR_EL1 matching the trampoline's svc_fallback PC so an unrelated raw clock_gettime syscall cannot poison the anchor from arbitrary X9.
/dev/urandom 1-byte reads drop from 5688 ns uncached to 2054 ns (2.77x) via a new per-fd entropy cache: an arc4random_buf-refilled 4 KiB buffer per FD_URANDOM slot. The cache is zeroed on close via a type-to-cleanup registry that also closes pre-existing dup and fork-state race windows for every synthetic fd type.
eventfd dup shares state across aliases per the Linux contract (refcounted slot plus eventfd_owner[FD_TABLE_SIZE] table). The dup path holds fd_lock and sfd_lock together for the bind commit so racing close cannot leak the refcount; the source identity is pinned via snapshotted host fd so a racing close-and-rebind of the source cannot bind to the wrong slot. tests/test-eventfd-dup pins the shared-state contract.
fork_ipc_send_fd_table filters eventfd, signalfd, timerfd, inotify, netlink, pidfd, and epoll out of the SCM_RIGHTS payload. macOS rejects kqueue fds across SCM_RIGHTS and per-class side-table state is not transferable, so a clean drop is the only honest contract. tests/test-fork-synthetic-fd pins it.
Startup decomposition: ELFUSE_STARTUP_TRACE=1 emits per-step wall time for VM bring-up (17 steps on test-hello, dominated by hv_vcpu_create and guest_init at roughly 0.9 ms each). Zero overhead when unset.
Summary by cubic
Adds a CNTVCT-based vDSO fast path for clock_gettime and EL1 shim fast paths for identity syscalls and 1‑byte /dev/urandom reads. Delivers major speedups (clock_gettime ~500x; urandom 1‑byte ~2.7x; identity ~47 ns) and hardens isolation with an EL1‑only shim_data block and stricter infra guards.
New Features
Bug Fixes
Written for commit 7642bee. Summary will update on new commits.
Review in cubic