summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-09 22:33:34 +0300
committerPaul Buetow <paul@buetow.org>2026-06-09 22:33:34 +0300
commitc61fb1f71a72d66960914877e8f0a24638c85324 (patch)
treee7306a3fa1d613dcbf5523afb4ed74cdcff8138c /cmd
parentd33d2ef1186096dddf2836f83e91b833866ef3bd (diff)
test(memory): add coverage for mlock/mlock2/munlock/mlockall/munlockall
The memory-locking cluster (FamilyMemory) had no end-to-end integration coverage; mmap_test.go only exercised mmap/msync/mremap/munmap. These syscalls return UNCLASSIFIED, so enter-tracepoint presence is the right end-to-end check (mlock/mlock2/munlock are KindMem, mlockall/munlockall are KindNull). Add a mmap-memory-lock scenario that, on a single anonymous page (small enough to stay under RLIMIT_MEMLOCK), issues raw syscalls for mlock, munlock, mlock2(addr,len,0), mlockall(MCL_CURRENT) and munlockall so the exact sys_enter_ tracepoints fire. mlock/mlock2/mlockall tolerate EPERM/ENOMEM best-effort since the enter tracepoint fires before the error; munlock/munlockall always succeed and clean up. TestMmapMemoryLock asserts enter_mlock, enter_munlock, enter_mlock2, enter_mlockall and enter_munlockall each fire (MinCount>=1, Comm ioworkload). All five captured under sudo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/ioworkload/scenario_mmap.go59
-rw-r--r--cmd/ioworkload/scenarios.go1
2 files changed, 60 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_mmap.go b/cmd/ioworkload/scenario_mmap.go
index 0d953d2..471788d 100644
--- a/cmd/ioworkload/scenario_mmap.go
+++ b/cmd/ioworkload/scenario_mmap.go
@@ -5,6 +5,8 @@ import (
"path/filepath"
"syscall"
"unsafe"
+
+ "golang.org/x/sys/unix"
)
const mremapMayMove = 1
@@ -136,3 +138,60 @@ func mmapMremapMunmap() error {
}
return nil
}
+
+// mmapMemoryLock exercises the memory-locking cluster (mlock, mlock2, munlock,
+// mlockall, munlockall — all FamilyMemory) so the integration suite captures
+// their sys_enter_ tracepoints end-to-end.
+//
+// All locking is done on a single anonymous page (one page = small enough to
+// stay well under RLIMIT_MEMLOCK for an unprivileged process). The raw syscall
+// path is used throughout so the exact tracepoints fire:
+// - mlock(addr, len) / munlock(addr, len) -> KindMem (captures addr+len)
+// - mlock2(addr, len, 0) -> KindMem (no stdlib wrapper)
+// - mlockall(MCL_CURRENT) / munlockall() -> KindNull
+//
+// Errors from mlock/mlock2/mlockall are tolerated best-effort: even when the
+// kernel rejects the call (e.g. EPERM/ENOMEM if RLIMIT_MEMLOCK is too low), the
+// sys_enter_ tracepoint has already fired, which is all this scenario needs.
+// munlock/munlockall always succeed, so we clean up unconditionally.
+func mmapMemoryLock() error {
+ const pageSize = 4096
+
+ mapped, err := syscall.Mmap(-1, 0, pageSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_PRIVATE|syscall.MAP_ANON)
+ if err != nil {
+ return fmt.Errorf("mmap: %w", err)
+ }
+ defer syscall.Munmap(mapped)
+
+ addr := uintptr(unsafe.Pointer(&mapped[0]))
+ length := uintptr(len(mapped))
+
+ // mlock then munlock the single page (KindMem). EPERM/ENOMEM are tolerated:
+ // the enter tracepoint fires regardless of the eventual errno.
+ if _, _, errno := unix.Syscall(unix.SYS_MLOCK, addr, length, 0); errno != 0 && errno != syscall.EPERM && errno != syscall.ENOMEM {
+ return fmt.Errorf("mlock: %w", errno)
+ }
+ if _, _, errno := unix.Syscall(unix.SYS_MUNLOCK, addr, length, 0); errno != 0 {
+ return fmt.Errorf("munlock: %w", errno)
+ }
+
+ // mlock2(addr, len, 0) has no stdlib/x-sys wrapper; issue the raw syscall.
+ if _, _, errno := unix.Syscall(unix.SYS_MLOCK2, addr, length, 0); errno != 0 && errno != syscall.EPERM && errno != syscall.ENOMEM {
+ return fmt.Errorf("mlock2: %w", errno)
+ }
+ // Drop any lock established by mlock2 before unmapping.
+ if _, _, errno := unix.Syscall(unix.SYS_MUNLOCK, addr, length, 0); errno != 0 {
+ return fmt.Errorf("munlock after mlock2: %w", errno)
+ }
+
+ // mlockall(MCL_CURRENT) then munlockall() (KindNull). mlockall may fail with
+ // EPERM/ENOMEM for unprivileged callers when RLIMIT_MEMLOCK is too low; its
+ // enter tracepoint still fires. munlockall always succeeds.
+ if _, _, errno := unix.Syscall(unix.SYS_MLOCKALL, uintptr(unix.MCL_CURRENT), 0, 0); errno != 0 && errno != syscall.EPERM && errno != syscall.ENOMEM {
+ return fmt.Errorf("mlockall: %w", errno)
+ }
+ if _, _, errno := unix.Syscall(unix.SYS_MUNLOCKALL, 0, 0, 0); errno != 0 {
+ return fmt.Errorf("munlockall: %w", errno)
+ }
+ return nil
+}
diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go
index 22f3e3c..8f2701e 100644
--- a/cmd/ioworkload/scenarios.go
+++ b/cmd/ioworkload/scenarios.go
@@ -144,6 +144,7 @@ var scenarios = map[string]func() error{
"mmap-msync-sync": mmapMsyncSync,
"mmap-msync-invalid-flags": mmapMsyncInvalidFlags,
"mmap-mremap-munmap": mmapMremapMunmap,
+ "mmap-memory-lock": mmapMemoryLock,
"copy-file-range-basic": copyFileRangeBasic,
"copy-file-range-bad-dst-fd": copyFileRangeBadDstFd,
"truncate-basic": truncateBasic,