Skip to content

hypervisor: skip the fd scan for stale sockets - #564

Closed
chruffins wants to merge 1 commit into
mainfrom
hypeship/jailed-socket-lookup-fast-path
Closed

chruffins wants to merge 1 commit into
mainfrom
hypeship/jailed-socket-lookup-fast-path

Conversation

@chruffins

@chruffins chruffins commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Since #560, instances.list on a staging host with many dead VMMs takes ~2.5–3s at p50 (was ~130ms). Almost all of it is hydrate_ms. The jailer is off on every host, so this is the new jailed-socket fallback in ResolveProcessPID running for unjailed VMs.

A VMM that dies can leave its control socket file behind. List hydration calls refreshHypervisorPID for those instances, which calls ResolveProcessPID. Before #560 that returned ErrNoOwningProcess after one read of /proc/net/unix. #560 added a fallback for jailed VMMs that runs whenever no listener is bound to the socket path, so every stale socket now scans the fds of every process on the host.

On the affected host: 38 of 72 VMM control sockets have no listener, the host runs ~1,450 processes, and each stale lookup costs ~100ms. The slowest instance in every slow list was one of those stale sockets.

This takes the fallback only when it can apply:

  • the socket path is a symlink (a jailed VM's host socket path always is), and
  • some listener in /proc/net/unix is bound to the link target's name at the root (/api.sock for a jailed firecracker).

Otherwise it returns ErrNoOwningProcess from /proc/net/unix alone, as before #560. When the fallback does run, it only checks listeners bound to that name instead of every listener with a path.

Existing and new flows

Before #560:   ResolveProcessPID(path) -> /proc/net/unix has path? -> no -> ErrNoOwningProcess

#560:          ResolveProcessPID(path) -> /proc/net/unix has path? -> no
                 -> path is a socket file -> scan fds of every process, stat each listener via /proc/<pid>/root
                 -> ErrNoOwningProcess (stale) or the jailed owner

This PR:       ResolveProcessPID(path) -> /proc/net/unix has path? -> no
                 -> path not a symlink                      -> ErrNoOwningProcess   (stale unjailed: no scan)
                 -> no listener bound to /<target name>     -> ErrNoOwningProcess   (no live jailed VMM: no scan)
                 -> scan fds, match only listeners bound to /<target name> -> jailed owner or ErrNoOwningProcess

Exact path matches (every live unjailed VMM) are unchanged.

Testing

  • TestResolveProcessPIDSkipsFDScanForStaleSocket: a stale plain socket file and a stale jailed link both return ErrNoOwningProcess, with a fake /proc that turns any fd scan into an error. It fails on main (inspect process fds: ... not a directory) and passes here.
  • Benchmark of ResolveProcessPID on a stale socket on a shared dev host with ~3,000 processes, 20 iterations each: 306ms/op on main, 5ms/op with this change.
  • Jailer integration tests on that host as root with real guests: TestStandbySnapshotRestore (jailed and unjailed), TestJailedSocketOwners (two concurrent jailed VMs, each resolves to its own pid), and the jailed snapshot, restore and fork tests all pass.
  • go test ./lib/hypervisor/: the resolver tests pass. TestSocketCacheKeyChangesWhenSocketIsRecreated fails on my machine because the filesystem reuses inodes, and TestResolveProcessPIDDuringProcessChurn is flaky under parallel package runs (passes alone). Both fail the same way on main.

Note

Medium Risk
Changes Linux socket PID resolution used during instance list hydration; live unjailed exact-path matches are unchanged, but jailed fallback preconditions are stricter.

Overview
Limits the jailed-VM socket owner fallback so stale control sockets no longer trigger a full /proc fd scan.

socketMatcherForPath now returns ErrNoOwningProcess immediately when the path is not a symlink, or when no listener in /proc/net/unix is bound to /<link-target basename>. Only that symlink + live jailed-listener case keeps the fd scan, and matching uses listenersBoundTo (replacing a scan of all path-backed listeners) to narrow candidates.

Adds TestResolveProcessPIDSkipsFDScanForStaleSocket so dead unjailed sockets and stale jailed symlinks still report ErrNoOwningProcess without touching process fds.

Reviewed by Cursor Bugbot for commit 365c27c. Configure here.

The jailed-socket fallback in ResolveProcessPID ran whenever no listener
was bound to the socket path, including a plain socket file left behind
by a dead VMM. Each such lookup then scanned every process's fds, and
listing instances resolves one per stale socket, so a host with many
dead VMMs spent seconds per list.

Only take the fallback when the socket path is a link and a listener is
bound to the link target's name at the root. Anything else reports
ErrNoOwningProcess straight from /proc/net/unix, as before the jailer.
@chruffins chruffins closed this Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant