summaryrefslogtreecommitdiff
path: root/cmd/ioworkload/scenario_misc.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/ioworkload/scenario_misc.go')
-rw-r--r--cmd/ioworkload/scenario_misc.go141
1 files changed, 141 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_misc.go b/cmd/ioworkload/scenario_misc.go
new file mode 100644
index 0000000..4a79c6d
--- /dev/null
+++ b/cmd/ioworkload/scenario_misc.go
@@ -0,0 +1,141 @@
+package main
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+// miscVmspliceLen is the size of the tiny buffer vmsplice gathers into the
+// pipe. It is kept far below the default pipe capacity (64 KiB) so vmsplice
+// never blocks waiting for room, and we drain the read end afterwards anyway.
+const miscVmspliceLen = 16
+
+// miscBasic exercises the SAFE, UNPRIVILEGED members of the Misc syscall
+// family so the enter_/exit_ tracepoints fire end-to-end. Every call here is
+// read-only or self-contained: none mutate global host state, none require
+// elevated capabilities, and none can block.
+//
+// - getcpu reports the CPU/NUMA node the caller runs on (raw syscall; the
+// unix package has no portable wrapper).
+// - newuname (via unix.Uname) reads the kernel/host name strings.
+// - sysinfo (via unix.Sysinfo) reads memory/load/uptime counters.
+// - vmsplice gathers a tiny in-memory buffer into a self-created pipe; we
+// drain (and close) the pipe so it can never fill up or block.
+// - alarm(0) cancels any pending SIGALRM and returns the previous value;
+// passing 0 is harmless and arms no new timer (raw syscall; no wrapper).
+//
+// INTENTIONALLY EXCLUDED from this scenario (and documented here so the reasons
+// travel with the code):
+// - acct, sethostname, setdomainname, syslog, fanotify_init, fanotify_mark:
+// require CAP_SYS_ADMIN and/or mutate GLOBAL host state (hostname, kernel
+// log, process accounting) — unsafe to invoke from a test workload.
+// - ioperm, iopl, modify_ldt: require CAP_SYS_RAWIO and are x86-only port/LDT
+// manipulation — privileged and non-portable.
+// - file_getattr, file_setattr: only exist on Linux 6.13+, so may be absent
+// on the kernels the integration suite runs against.
+// - rseq, get_robust_list, set_robust_list: auto-managed by the Go/C runtime
+// for restartable sequences and robust futexes; re-invoking them by hand
+// would corrupt the runtime's own registration.
+// - uprobe, uretprobe: probe-attach mechanisms, not user-callable syscalls.
+func miscBasic() error {
+ if err := miscGetcpu(); err != nil {
+ return err
+ }
+ if err := miscUname(); err != nil {
+ return err
+ }
+ if err := miscSysinfo(); err != nil {
+ return err
+ }
+ if err := miscVmsplice(); err != nil {
+ return err
+ }
+ return miscAlarmCancel()
+}
+
+// miscGetcpu issues getcpu(2) via a raw syscall (golang.org/x/sys/unix ships
+// no portable wrapper). It writes the current CPU and NUMA node into two output
+// words; the third argument (the obsolete tcache) is NULL.
+func miscGetcpu() error {
+ var cpu, node uint32
+ if _, _, errno := syscall.RawSyscall(
+ unix.SYS_GETCPU,
+ uintptr(unsafe.Pointer(&cpu)),
+ uintptr(unsafe.Pointer(&node)),
+ 0,
+ ); errno != 0 {
+ return fmt.Errorf("getcpu: %w", errno)
+ }
+ return nil
+}
+
+// miscUname issues sys_newuname (the modern uname(2)) via unix.Uname, reading
+// the kernel/host identification strings into a caller-owned Utsname buffer.
+func miscUname() error {
+ var uts unix.Utsname
+ if err := unix.Uname(&uts); err != nil {
+ return fmt.Errorf("uname: %w", err)
+ }
+ return nil
+}
+
+// miscSysinfo issues sysinfo(2) via unix.Sysinfo, reading uptime/load/memory
+// counters into a caller-owned Sysinfo_t buffer. Purely a read.
+func miscSysinfo() error {
+ var info unix.Sysinfo_t
+ if err := unix.Sysinfo(&info); err != nil {
+ return fmt.Errorf("sysinfo: %w", err)
+ }
+ return nil
+}
+
+// miscVmsplice gathers a tiny fixed buffer into a freshly created pipe via
+// vmsplice(2), then drains and closes the pipe. The buffer (miscVmspliceLen
+// bytes) is far smaller than the pipe capacity, so vmsplice cannot block, and
+// draining the read end leaves no descriptors or data behind.
+func miscVmsplice() error {
+ var fds [2]int
+ if err := unix.Pipe(fds[:]); err != nil {
+ return fmt.Errorf("pipe for vmsplice: %w", err)
+ }
+ readEnd, writeEnd := fds[0], fds[1]
+ defer unix.Close(readEnd)
+ defer unix.Close(writeEnd)
+
+ buf := make([]byte, miscVmspliceLen)
+ iov := unix.Iovec{Base: &buf[0], Len: uint64(len(buf))}
+ n, _, errno := syscall.Syscall6(
+ unix.SYS_VMSPLICE,
+ uintptr(writeEnd),
+ uintptr(unsafe.Pointer(&iov)),
+ 1, // one iovec
+ 0, // no SPLICE_F_* flags needed for this tiny, non-blocking write
+ 0, 0,
+ )
+ if errno != 0 {
+ return fmt.Errorf("vmsplice: %w", errno)
+ }
+
+ // Drain whatever vmsplice placed into the pipe so nothing lingers and the
+ // pipe never approaches its capacity. A short read is fine.
+ drain := make([]byte, int(n))
+ if int(n) > 0 {
+ if _, err := unix.Read(readEnd, drain); err != nil {
+ return fmt.Errorf("drain vmsplice pipe: %w", err)
+ }
+ }
+ return nil
+}
+
+// miscAlarmCancel issues alarm(0) via a raw syscall (no unix wrapper). Passing 0
+// CANCELS any pending SIGALRM and returns the seconds remaining on the previous
+// timer; it arms no new alarm, so it is entirely harmless. The previous value
+// is ignored — we only care that the syscall is issued and traced.
+func miscAlarmCancel() error {
+ // alarm never fails; RawSyscall's errno is always 0 here.
+ _, _, _ = syscall.RawSyscall(unix.SYS_ALARM, 0, 0, 0)
+ return nil
+}