From d807c1ad9eb8b176e36300c6ea41744431a05bf0 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 6 Jun 2026 10:01:58 +0300 Subject: test(aio): add io_getevents and io_cancel enter coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close the integration-test gaps for two classic-AIO syscalls that the existing scenario never exercised. The AIO workload only drove io_setup/io_submit/io_destroy, so io_getevents (nr 208) and io_cancel (nr 210) had no end-to-end coverage despite their tracer classification (FamilyAIO, KindNull enter, UNCLASSIFIED ret) being correct by inspection. cmd/ioworkload/scenario_aio.go: - Factor the submit scaffolding (temp dir, target file, AIO context) into withAioTarget so each scenario stays short. - ioSubmitWrite now returns the submitted iocb pointer (io_cancel needs it). - aio-getevents: submit, then reap the completion with a blocking io_getevents (min_nr=1, NULL timeout); asserts the return is a count. - aio-cancel: submit, then best-effort io_cancel (return ignored: it races the I/O completion and often yields -EINVAL/-EAGAIN, but enter still fires), then drain the ring non-blockingly (min_nr=0) so io_destroy has nothing in flight and we never hang when the cancel left no completion. integrationtests/aio_test.go: - TestAioGetevents asserts enter_io_getevents (MinCount 1), mirroring TestAioSubmit, with io_getevents added to the trace-arg set. - TestAioCancel asserts ONLY enter_io_cancel (MinCount 1) — never success — because io_cancel's return is non-deterministic. Co-Authored-By: Claude Opus 4.8 --- integrationtests/aio_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'integrationtests') diff --git a/integrationtests/aio_test.go b/integrationtests/aio_test.go index b38e20b..9fba4b1 100644 --- a/integrationtests/aio_test.go +++ b/integrationtests/aio_test.go @@ -51,3 +51,45 @@ func TestAioSubmit(t *testing.T) { }, }, aioSubmitTraceArgs) } + +// aioGeteventsTraceArgs additionally traces io_getevents so the getevents +// scenario can assert on its enter tracepoint. io_getevents is KindNull +// (FamilyAIO): the enter event captures no fd/path (ctx_id is an AIO context +// handle, events/timeout are opaque userspace pointers) and the exit event +// carries the raw return value (a count of events reaped, UNCLASSIFIED). +var aioGeteventsTraceArgs = []string{"-trace-syscalls", "io_setup,io_submit,io_getevents,io_destroy,close"} + +// TestAioGetevents exercises io_getevents(2) end-to-end: the workload sets up an +// AIO context, submits one real iocb (a positional write), then reaps its +// completion with io_getevents. ior must record the enter_io_getevents +// tracepoint for the AIO family workload. +func TestAioGetevents(t *testing.T) { + runScenarioResultWithIorArgs(t, "aio-getevents", []ExpectedEvent{ + { + Tracepoint: "enter_io_getevents", + Comm: "ioworkload", + MinCount: 1, + }, + }, aioGeteventsTraceArgs) +} + +// aioCancelTraceArgs additionally traces io_cancel and io_getevents so the +// cancel scenario can assert on its enter tracepoint and still drain the +// completion ring. io_cancel is KindNull (FamilyAIO): the enter event captures +// no fd/path (ctx_id is an AIO context handle, iocb/result are opaque userspace +// pointers) and the exit event carries the raw return value (UNCLASSIFIED). +var aioCancelTraceArgs = []string{"-trace-syscalls", "io_setup,io_submit,io_cancel,io_getevents,io_destroy,close"} + +// TestAioCancel exercises io_cancel(2): the workload submits one real iocb and +// then attempts to cancel it. io_cancel races the I/O completion and frequently +// returns -EINVAL/-EAGAIN, so the test asserts ONLY on the enter_io_cancel +// tracepoint (which fires regardless of the return value), never on success. +func TestAioCancel(t *testing.T) { + runScenarioResultWithIorArgs(t, "aio-cancel", []ExpectedEvent{ + { + Tracepoint: "enter_io_cancel", + Comm: "ioworkload", + MinCount: 1, + }, + }, aioCancelTraceArgs) +} -- cgit v1.2.3