summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-31 09:55:02 +0300
committerPaul Buetow <paul@buetow.org>2026-05-31 09:55:02 +0300
commite8948e9f52235cbbbd8af098bc624bc20e173e6c (patch)
tree75fac81f5121cfdaac5234cca371a126be52cdb4 /cmd
parent75d00f479d333476990ff18f7427905bb09d49f0 (diff)
test(aio): add io_setup end-to-end integration coverage
The classic Linux AIO family (io_setup/io_submit/io_getevents/io_cancel/ io_destroy) had no integration coverage: family_test.go exercises only FS/Memory/IPC/Network/Process/Sched/Time, and iouring_test.go covers only the distinct io_uring_* family. io_setup is classified KindNull/FamilyAIO, which is correct by inspection against man 2 io_setup (nr_events is a count, ctx_idp an output pointer, so no fd/path is captured), so the tracer itself needed no change. Add an ioworkload AIO scenario that drives io_setup(2)/io_destroy(2) raw (no privileges, no libaio) plus an EINVAL variant, and integration tests that assert ior records the enter_io_setup tracepoint end-to-end, mirroring the existing iouring scenario/test pattern. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/ioworkload/scenario_aio.go80
-rw-r--r--cmd/ioworkload/scenarios.go2
2 files changed, 82 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_aio.go b/cmd/ioworkload/scenario_aio.go
new file mode 100644
index 0000000..389e734
--- /dev/null
+++ b/cmd/ioworkload/scenario_aio.go
@@ -0,0 +1,80 @@
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "syscall"
+ "unsafe"
+)
+
+// Linux AIO (io_setup family) syscall numbers on x86_64. These are the classic
+// kernel AIO interface (io_setup/io_submit/io_getevents/io_cancel/io_destroy),
+// distinct from the io_uring_* family. We invoke them raw via Syscall because
+// the Go standard library does not wrap them.
+const (
+ sysIoSetup = 206
+ sysIoDestroy = 207
+
+ // aioMaxEvents is the nr_events count requested from io_setup(2). It is a
+ // plain count (NOT an fd), so the tracer must classify the enter event as
+ // KindNull and capture no fd/path argument.
+ aioMaxEvents = 32
+)
+
+// aioSetup exercises io_setup(2): it creates an AIO context (writing the
+// context id into a userspace pointer) and then tears it down with
+// io_destroy(2). io_setup needs no special privileges, so this runs end-to-end
+// in the integration harness and validates that ior records the
+// enter_io_setup/exit_io_setup tracepoints for the AIO family.
+func aioSetup() error {
+ ctx, err := ioSetupContext(aioMaxEvents)
+ if err != nil {
+ return err
+ }
+ return ioDestroyContext(ctx)
+}
+
+// aioSetupEinval calls io_setup(2) with nr_events = 0, which the kernel rejects
+// with EINVAL. The syscall fails, but ior still captures the enter_io_setup
+// tracepoint and an exit_io_setup return event carrying the negative errno.
+func aioSetupEinval() error {
+ for i := 0; i < 5; i++ {
+ var ctx uint64
+ _, _, errno := syscall.Syscall(
+ sysIoSetup,
+ 0, // nr_events = 0 -> EINVAL
+ uintptr(unsafe.Pointer(&ctx)),
+ 0,
+ )
+ runtime.KeepAlive(ctx)
+ if errno == 0 {
+ return fmt.Errorf("expected EINVAL, but io_setup(0) succeeded")
+ }
+ }
+ return nil
+}
+
+// ioSetupContext calls io_setup(2) and returns the opaque aio_context_t id.
+func ioSetupContext(nrEvents uint32) (uint64, error) {
+ var ctx uint64
+ _, _, errno := syscall.Syscall(
+ sysIoSetup,
+ uintptr(nrEvents),
+ uintptr(unsafe.Pointer(&ctx)),
+ 0,
+ )
+ runtime.KeepAlive(ctx)
+ if errno != 0 {
+ return 0, fmt.Errorf("io_setup: %w", errno)
+ }
+ return ctx, nil
+}
+
+// ioDestroyContext tears down an AIO context created by io_setup(2).
+func ioDestroyContext(ctx uint64) error {
+ _, _, errno := syscall.Syscall(sysIoDestroy, uintptr(ctx), 0, 0)
+ if errno != 0 {
+ return fmt.Errorf("io_destroy: %w", errno)
+ }
+ return nil
+}
diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go
index b37f272..2c4737a 100644
--- a/cmd/ioworkload/scenarios.go
+++ b/cmd/ioworkload/scenarios.go
@@ -123,6 +123,8 @@ var scenarios = map[string]func() error{
"iouring-register": iouringRegister,
"iouring-enter-ebadf": iouringEnterEbadf,
"iouring-register-ebadf": iouringRegisterEbadf,
+ "aio-setup": aioSetup,
+ "aio-setup-einval": aioSetupEinval,
}
func makeTempDir(prefix string) (string, func(), error) {