diff options
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/ioworkload/scenario_mmap.go | 59 | ||||
| -rw-r--r-- | cmd/ioworkload/scenarios.go | 1 |
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, |
