diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-20 15:06:02 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-20 15:06:02 +0300 |
| commit | 271af607921ceabc640271c475a66e45b9460d3f (patch) | |
| tree | 6ae443fd372dbeea947cba7bd5851f7936f354b5 /cmd | |
| parent | 63184df8d5e30f70011a97d862103fa38d797bb3 (diff) | |
feat: add mount/fs management syscall tracing for c7
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/ioworkload/scenario_mountfs.go | 91 | ||||
| -rw-r--r-- | cmd/ioworkload/scenario_mountfs_test.go | 30 | ||||
| -rw-r--r-- | cmd/ioworkload/scenarios.go | 1 |
3 files changed, 122 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_mountfs.go b/cmd/ioworkload/scenario_mountfs.go new file mode 100644 index 0000000..4c48ded --- /dev/null +++ b/cmd/ioworkload/scenario_mountfs.go @@ -0,0 +1,91 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +type mountIDReq struct { + Size uint32 + Pad uint32 + MntID uint64 + Param uint64 +} + +func mountfsManagement() error { + dir, cleanup, err := makeTempDir("mountfs-management") + if err != nil { + return err + } + defer cleanup() + + mountPoint := filepath.Join(dir, "mnt") + if err := os.Mkdir(mountPoint, 0o755); err != nil { + return fmt.Errorf("mkdir mountpoint: %w", err) + } + + swapFile := filepath.Join(dir, "swapfile") + if err := os.WriteFile(swapFile, []byte("swap"), 0o600); err != nil { + return fmt.Errorf("write swap file: %w", err) + } + + mountPath := mustCStringPtr(mountPoint) + swapPath := mustCStringPtr(swapFile) + newRoot := mustCStringPtr(mountPoint) + putOld := mustCStringPtr(dir) + tmpfs := mustCStringPtr("tmpfs") + none := mustCStringPtr("none") + atFDCWDInt := int64(unix.AT_FDCWD) + atFDCWD := uintptr(atFDCWDInt) + + // Best-effort coverage: these calls are expected to fail on most hosts + // without CAP_SYS_ADMIN, but still exercise syscall tracing paths. + _, _, _ = syscall.RawSyscall6(unix.SYS_MOUNT, uintptr(unsafe.Pointer(none)), uintptr(unsafe.Pointer(mountPath)), uintptr(unsafe.Pointer(tmpfs)), 0, 0, 0) + _, _, _ = syscall.RawSyscall(unix.SYS_UMOUNT2, uintptr(unsafe.Pointer(mountPath)), 0, 0) + _, _, _ = syscall.RawSyscall(unix.SYS_UMOUNT2, uintptr(unsafe.Pointer(mountPath)), uintptr(unix.MNT_DETACH), 0) + _, _, _ = syscall.RawSyscall6(unix.SYS_MOVE_MOUNT, atFDCWD, uintptr(unsafe.Pointer(mountPath)), atFDCWD, uintptr(unsafe.Pointer(mountPath)), 0, 0) + _, _, _ = syscall.RawSyscall(unix.SYS_FSMOUNT, ^uintptr(0), 0, 0) + _, _, _ = syscall.RawSyscall(unix.SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(newRoot)), uintptr(unsafe.Pointer(putOld)), 0) + _, _, _ = syscall.RawSyscall6(unix.SYS_QUOTACTL, 0, uintptr(unsafe.Pointer(mountPath)), 0, 0, 0, 0) + _, _, _ = syscall.RawSyscall(unix.SYS_SWAPON, uintptr(unsafe.Pointer(swapPath)), 0, 0) + _, _, _ = syscall.RawSyscall(unix.SYS_SWAPOFF, uintptr(unsafe.Pointer(swapPath)), 0, 0) + + req := mountIDReq{Size: uint32(unsafe.Sizeof(mountIDReq{}))} + var statBuf [256]byte + _, _, _ = syscall.RawSyscall6(unix.SYS_STATMOUNT, uintptr(unsafe.Pointer(&req)), uintptr(unsafe.Pointer(&statBuf[0])), uintptr(len(statBuf)), 0, 0, 0) + + var mountIDs [8]uint64 + _, _, _ = syscall.RawSyscall6(unix.SYS_LISTMOUNT, uintptr(unsafe.Pointer(&req)), uintptr(unsafe.Pointer(&mountIDs[0])), uintptr(len(mountIDs)), 0, 0, 0) + + if nr, err := listnsSyscallNr(); err == nil { + var nsIDs [8]uint64 + _, _, _ = syscall.RawSyscall6(nr, uintptr(unsafe.Pointer(&req)), uintptr(unsafe.Pointer(&nsIDs[0])), uintptr(len(nsIDs)), 0, 0, 0) + } + + return nil +} + +func listnsSyscallNr() (uintptr, error) { + return listnsSyscallNrForArch(runtime.GOARCH) +} + +func listnsSyscallNrForArch(arch string) (uintptr, error) { + // __NR_listns was introduced from asm-generic numbering where amd64/arm64 use 470. + switch arch { + case "amd64", "arm64": + return 470, nil + default: + return 0, fmt.Errorf("listns syscall number not defined for GOARCH=%s", arch) + } +} + +func mustCStringPtr(s string) *byte { + p, _ := unix.BytePtrFromString(s) + return p +} diff --git a/cmd/ioworkload/scenario_mountfs_test.go b/cmd/ioworkload/scenario_mountfs_test.go new file mode 100644 index 0000000..c1c317a --- /dev/null +++ b/cmd/ioworkload/scenario_mountfs_test.go @@ -0,0 +1,30 @@ +package main + +import "testing" + +func TestListnsSyscallNrForArch(t *testing.T) { + for _, tc := range []struct { + name string + arch string + want uintptr + wantErr bool + }{ + {name: "amd64", arch: "amd64", want: 470}, + {name: "arm64", arch: "arm64", want: 470}, + {name: "unsupported", arch: "riscv64", wantErr: true}, + } { + got, err := listnsSyscallNrForArch(tc.arch) + if tc.wantErr { + if err == nil { + t.Fatalf("%s: expected error", tc.name) + } + continue + } + if err != nil { + t.Fatalf("%s: unexpected error: %v", tc.name, err) + } + if got != tc.want { + t.Fatalf("%s: got %d, want %d", tc.name, got, tc.want) + } + } +} diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go index e0827a5..79d4e6d 100644 --- a/cmd/ioworkload/scenarios.go +++ b/cmd/ioworkload/scenarios.go @@ -34,6 +34,7 @@ var scenarios = map[string]func() error{ "pipe2-basic": pipe2Basic, "eventfd-basic": eventfdBasic, "eventfd2-basic": eventfd2Basic, + "mountfs-management": mountfsManagement, "polling-epoll": pollingEpoll, "sleep-syscalls": sleepSyscalls, "family-mixed": familyMixed, |
