Repository navigation
imagecache: skip /proc/self/mountinfo scan via STATX_MNT_ID fast path - #2963
M.Besher Massri (besher-massri) wants to merge 1 commit into
Conversation
|
+1, wanted to link the issue I opened which has my test data #2950 |
| if name == "upper" || name == "work" { | ||
| continue | ||
| } | ||
| sx, err := statxAt(dirfd, name) | ||
| if err != nil { | ||
| if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.ENOENT) { | ||
| continue | ||
| } | ||
| _ = f.Close() | ||
| return err | ||
| } | ||
| if sx.Mode&unix.S_IFMT == unix.S_IFLNK { | ||
| continue | ||
| } | ||
| if isMountRoot(sx, dirMntID) { | ||
| *points = append(*points, filepath.Join(curDir, name)) | ||
| // SetupBundleRootfs mounts <bundle>/rootfs and | ||
| // <bundle>/volumes/<name>; neither contains nested submounts, | ||
| // so never descend into a mounted directory. | ||
| continue | ||
| } | ||
| // Never descend into an unmounted rootfs or volume directory. | ||
| isDir := sx.Mode&unix.S_IFMT == unix.S_IFDIR | ||
| if !isDir || name == "rootfs" || inVolumesDir { | ||
| continue | ||
| } | ||
| subdirs = append(subdirs, subdir{ | ||
| path: filepath.Join(curDir, name), | ||
| mntID: sx.Mnt_id, | ||
| inVol: name == "volumes", | ||
| }) |
There was a problem hiding this comment.
🤖 should-fix 🟡 – These name checks run at every directory level rather than only inside a container bundle directory. Calling UnmountAllUnder(actorDirs.GetOciBundleDir()) starts at <actor>/bundles, where subdirectories are container names (k8s-short-name), and image volumes live at <bundle>/volumes/<name>. A container named upper, work, rootfs, or volumes, or an image volume named upper or work, will be skipped before its mounts are reached, leaving mounts attached in the worker namespace.
Gate the upper/work skip, the unmounted rootfs skip, and inVol: name == "volumes" on curDir being a bundle directory (for example when names contains OverlaySpecFileName or "config.json"), so container directories under bundles/ and volume entries under volumes/ are always checked with statxAt and isMountRoot.
| var ( | ||
| statxUnmount = true | ||
| mountinfoPath = "/proc/self/mountinfo" | ||
| ) |
There was a problem hiding this comment.
🤖 nit 🟢 – AGENTS.md asks not to add package globals just so tests can swap behavior. TestMountPointsUnderStatx_NonExistentAndMultiBundle already exercises the fallback against /proc/self/mountinfo on Linux without overriding mountinfoPath, and BenchmarkUnmountAllUnder (which has no active mounts to detach) can call mountPointsUnderStatx and mountPointsIn directly without statxUnmount or mountinfoPath.
9e18f99 to
1872468
Compare
Benjamin Elder (BenTheElder)
left a comment
There was a problem hiding this comment.
so some of the comments in imagecache about kernel versions are wrong ... filing a fix for that to avoid wasting time next time. couple vetted comments.
| points, err := mountPointsUnder(dir) | ||
| points, err := mountPointsUnderStatx(dir) | ||
| if err != nil { | ||
| points, err = mountPointsUnder(dir) |
There was a problem hiding this comment.
🤖 should-fix 🟡 – Every kernel that can mount the rootfs here reports both statx fields (see the thread below), so this fallback only runs when the walk hits an error. It does that silently, pays the mountinfo scan this PR is removing, and loses the statx error if mountinfo also fails.
I think we should just return the walk error, then mountPointsUnder and the fallback test can go. If we keep the fallback, it should at least log the statx error.
There was a problem hiding this comment.
Removed the fallback
| return sx, err | ||
| } | ||
| if sx.Mask&unix.STATX_MNT_ID == 0 { | ||
| return sx, unix.ENOSYS |
There was a problem hiding this comment.
🤖 should-fix 🟡 – statx has set STATX_MNT_ID and STATX_ATTR_MOUNT_ROOT since Linux 5.8:
https://github.com/torvalds/linux/blob/v5.8/fs/stat.c#L207-L211
The overlay mount already needs a newer kernel than that, since it appends each layer with lowerdir+:
substrate/internal/imagecache/bundle_linux.go
Line 175 in 4d98981
which landed in 6.7, and in 6.6.23 on the 6.6 stable branch:
https://github.com/torvalds/linux/blob/v6.7/fs/overlayfs/params.c#L148
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/fs/overlayfs/params.c?h=v6.6.23#n148
So this ENOSYS check, the Mnt_id comparison in isMountRoot and the parent statxAt in mountPointsUnderStatx can't be reached. Let's drop them and check STATX_ATTR_MOUNT_ROOT directly, then the mntID plumbing through the walk can go too.
| } | ||
| } | ||
|
|
||
| func TestUnmountAllUnder_DetachesRegularFileBindMount(t *testing.T) { |
There was a problem hiding this comment.
🤖 nit 🟢 – This mostly duplicates TestResetRunscStateAndPidFileDirs_DetachesMounts, which already detaches a null-netns file bind through UnmountAllUnder:
substrate/cmd/ateworker-gvisor/actorpath_test.go
Lines 47 to 71 in 1872468
Can we drop it, or keep only the mountPointsUnderStatx(target) case?
There was a problem hiding this comment.
Done. Went with trimming it to only mountPointsUnderStatx
…gent-substrate#3128) follow-up to agent-substrate#2963 (review) The imagecache comment and README say the minimum kernel is 6.5, but overlayfs's `lowerdir+` landed in 6.7 (24e16e385f22, "ovl: add support for appending lowerdirs one by one"), and was backported to 6.6.y in v6.6.23: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/overlayfs/params.c?h=v6.7#n148 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/fs/overlayfs/params.c?h=v6.6.23#n148 6.5 had a different append syntax, `lowerdir=:dir`, but it was disabled before 6.6 shipped (beae836e9c61, "ovl: temporarily disable appending lowedirs") and we don't use it: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/overlayfs/params.c?h=v6.6#n375 AI use: AI-assisted, I reviewed it. - [x] Tests pass - [x] Docs updated
Discover mount points in UnmountAllUnder using dirfd-relative statx with AT_SYMLINK_NOFOLLOW, AT_STATX_DONT_SYNC, and STATX_ATTR_MOUNT_ROOT instead of scanning /proc/self/mountinfo. Gate bundle-specific upper, work, rootfs, and volumes pruning on curDir containing OverlaySpecFileName or config.json so container directories and image volumes with those names are still checked.
1872468 to
c12486d
Compare
|
is this still active ? I ran into this at fleet scale, and I have before/after numbers in case they help with prioritizing it. Setup
One difference from #2950: on my nodes, the mounts don't come from the sandboxes. The worker pod mounts Reading mountinfo gets slow as mounts grow. Time for one read of
The worker reads this file 5 times per wake + suspend: 2 times before restore and 3 times during teardown. The bigger problem is queueing. Teardown runs while the checkpoint is holding one of the 2 Results with a fix. I tested a local change that uses the same approach as this PR (a statx mount-root walk). It isn't the PR itself. Two neighbouring nodes in the same run, v1.0.0 vs. patched, p50:
Across the fleet, the best 5 minutes went from 2,775 to 3,681 starts/s. |
|
+1, would love for this to be active again as the numbers in #2950 are still relevant for this |
Why is this needed?
Fixes #2950
When a worker pod hosts many concurrent sandboxes, each sandbox adds multiple mount points (rootfs overlays, image volumes, and shared directories) to the worker mount namespace, causing /proc/self/mountinfo to grow to thousands of lines (each containing long overlay lowerdir chains). Calling imagecache.UnmountAllUnder on every checkpoint and restore previously opened and scanned the entire /proc/self/mountinfo table, so unmount CPU cost scaled linearly with the total number of active mounts on the worker rather than with the single actor being torn down.
On Linux 5.8+, unix.Statx sets unix.STATX_ATTR_MOUNT_ROOT in sx.Attributes whenever a path sits at the root of a mount point without reading /proc/self/mountinfo. Checking each bundle directory entry with dirfd-relative statx identifies active mounts in O(bundle entries) syscalls while skipping recursion into overlay scratch directories (upper and work) and mounted rootfs or volume directories inside each container bundle.
Changes
Benchmark Results
AI use: AI-assisted, I reviewed and tested the changes.