#!/usr/bin/env dub
/+ dub.sdl:
name "io_uring_probe"
dependency "during" version="~>0.5.0"
platforms "linux"
targetPath "build"
+/
/**
* `io_uring` — capability detection via `IORING_REGISTER_PROBE` (Linux 5.6).
*
* Before 5.6, the only portable way to learn whether the kernel implemented a
* given `io_uring` opcode was to submit it and inspect the completion for
* `-EINVAL`. `IORING_REGISTER_PROBE` replaced that guesswork with a proper
* query: the kernel fills an `io_uring_probe` table reporting, per opcode,
* whether it is supported. This is THE mechanism a portable program uses to
* decide at runtime which fast paths it may take on the host kernel.
*
* Here we set up a ring, fetch the probe table (`Uring.probe`, which wraps the
* register call), and print a compact support report for a curated list of
* historically interesting opcodes — from the original 5.1 `NOP`/`READ`/`WRITE`
* through `SEND_ZC` (5.19), `FUTEX_WAIT` (6.7), `RECV_ZC`/`PIPE` (6.x), and the
* 128-byte `NOP128`. Because it only *probes*, this example runs and succeeds
* on every kernel that has `io_uring` at all — newer opcodes simply report
* `no` rather than failing.
*
* Companion to the io_uring chronology:
* see docs/research/async-io/io-uring/timeline.md
* § "5.6 — The filesystem/syscall expansion (March 2020)".
*
* Run with: `dub run --single probe.d`
*
* Portability: if the host has no `io_uring`, or the kernel is older than 5.6
* (so `IORING_REGISTER_PROBE` itself is unavailable and the probe fetch fails),
* the program prints a `SKIP:` line and exits 0 so it stays green in CI.
*/
module (module) io_uring_probeio_uring — capability detection via IORING_REGISTER_PROBE (Linux 5.6).
Before 5.6, the only portable way to learn whether the kernel implemented a
given io_uring opcode was to submit it and inspect the completion for
-EINVAL. IORING_REGISTER_PROBE replaced that guesswork with a proper
query: the kernel fills an io_uring_probe table reporting, per opcode,
whether it is supported. This is THE mechanism a portable program uses to
decide at runtime which fast paths it may take on the host kernel.
Here we set up a ring, fetch the probe table (Uring.probe, which wraps the
register call), and print a compact support report for a curated list of
historically interesting opcodes — from the original 5.1 NOP/READ/WRITE
through SEND_ZC (5.19), FUTEX_WAIT (6.7), RECV_ZC/PIPE (6.x), and the
128-byte NOP128. Because it only probes, this example runs and succeeds
on every kernel that has io_uring at all — newer opcodes simply report
no rather than failing.
Companion to the io_uring chronology:
see docs/research/async-io/io-uring/timeline.md
§ "5.6 — The filesystem/syscall expansion (March 2020)".
Run with: dub run --single probe.d
Portability
if the host has no io_uring, or the kernel is older than 5.6
(so IORING_REGISTER_PROBE itself is unavailable and the probe fetch fails),
the program prints a SKIP: line and exits 0 so it stays green in CI.
io_uring_probe;
import (module) duringSimple idiomatic dlang wrapper around linux io_uring
(see: https://kernel.dk/io_uring.pdf) asynchronous API.
during;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) io_uring_probe.writef = std.stdio.writef(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Writes formatted data to standard output (without a trailing newline).
Note
In older versions of Phobos, it used to be possible to write:
writef(stderr, "%s", "message");
to print a message to stderr. This syntax is no longer supported, and has
been superceded by:
stderr.writef("%s", "message");
writef, (alias template) io_uring_probe.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, stderr;
int int D main()main()
{
(struct) during.UringMain entry point to work with io_uring.
It hides SubmissionQueue and CompletionQueue behind standard range interface.
We put in SubmissionEntry entries and take out CompletionEntry entries.
Use predefined prepXX methods to fill required fields of SubmissionEntry before put or during putWith.
Note
prepXX functions doesn't touch previous entry state, just fills in operation properties. This is because for
less error prone interface it is cleared automatically when prepared using putWith. So when using on own SubmissionEntry
(outside submission queue), that would be added to the submission queue using put, be sure its cleared if it's
reused for multiple operations.
Uring (local variable) during.Uring ioio;
const (local variable) const(int) setupRetsetupRet = (local variable) during.Uring ioio.int during.setup(ref during.Uring uring, uint entries = 128u, during.io_uring.SetupFlags flags = SetupFlags.NONE) nothrow @nogc @safeSetup new instance of io_uring into provided Uring structure.
setup(8);
if ((local variable) const(int) setupRetsetupRet < 0)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: io_uring_setup failed (errno %d) — io_uring unavailable on this host", -(local variable) const(int) setupRetsetupRet);
return 0;
}
// `IORING_REGISTER_PROBE`: ask the kernel for its per-opcode support table.
// `cast(bool)probe` is false (and `probe.error` holds -errno) when the
// register call is unavailable — e.g. a pre-5.6 kernel that has io_uring
// but not the PROBE register op.
(struct) during.ProbeSimplified wrapper around io_uring_probe that is used to check what io_uring operations current
kernel is actually supporting.
Probe (local variable) during.Probe probeprobe = (local variable) during.Uring ioio.during.Probe during.Uring.probe() nothrow @nogc @safeProbes supported operations
probe();
if (!cast(bool) (local variable) during.Probe probeprobe)
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: IORING_REGISTER_PROBE unavailable (errno %d) — needs Linux 5.6+",
-(local variable) during.Probe probeprobe.int during.Probe.error() const pure nothrow @nogc @property @safeError code when we fail to get Probe.
error);
return 0;
}
// A curated tour of the opcode timeline. The label is just for the report;
// the kernel's verdict comes from `probe.isSupported`.
static struct (struct) io_uring_probe.main.OpOp { (enum) during.io_uring.OperationDescribes the operation to be performed
Operation (field) during.io_uring.Operation io_uring_probe.main.Op.opop; (alias) object.string = stringstring (field) string io_uring_probe.main.Op.namename; }
static immutable (struct) io_uring_probe.main.OpOp[] (immutable global) immutable(io_uring_probe.main.Op[]) io_uring_probe.main.tourtour = [
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.NOP = cast(ubyte)0uIORING_OP_NOP
NOP, "NOP"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.READ = cast(ubyte)22uIORING_OP_READ
READ, "READ"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.WRITE = cast(ubyte)23uIORING_OP_WRITE
WRITE, "WRITE"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.ACCEPT = cast(ubyte)13uIORING_OP_ACCEPT
ACCEPT, "ACCEPT"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.CONNECT = cast(ubyte)16uIORING_OP_CONNECT
CONNECT, "CONNECT"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.SEND = cast(ubyte)26uIORING_OP_SEND
SEND, "SEND"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.RECV = cast(ubyte)27uIORING_OP_RECV
RECV, "RECV"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.OPENAT = cast(ubyte)18uIORING_OP_OPENAT
OPENAT, "OPENAT"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.TIMEOUT = cast(ubyte)11uIORING_OP_TIMEOUT
TIMEOUT, "TIMEOUT"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.POLL_ADD = cast(ubyte)6uIORING_OP_POLL_ADD
POLL_ADD, "POLL_ADD"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.SEND_ZC = cast(ubyte)47uIORING_OP_SEND_ZC - zero-copy send
SEND_ZC, "SEND_ZC"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.FUTEX_WAIT = cast(ubyte)51uIORING_OP_FUTEX_WAIT - async futex(2) FUTEX_WAIT
FUTEX_WAIT, "FUTEX_WAIT"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.RECV_ZC = cast(ubyte)58uIORING_OP_RECV_ZC - zero-copy receive (requires REGISTER_ZCRX_IFQ)
RECV_ZC, "RECV_ZC"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.PIPE = cast(ubyte)62uIORING_OP_PIPE - async pipe(2)/pipe2(2)
PIPE, "PIPE"),
(struct) io_uring_probe.main.OpOp((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.NOP128 = cast(ubyte)63uIORING_OP_NOP128 - 128-byte NOP for testing SQE128 rings
NOP128, "NOP128"),
];
int (local variable) int supportedsupported;
foreach ((parameter) immutable(io_uring_probe.main.Op) entryentry; (immutable global) immutable(io_uring_probe.main.Op[]) io_uring_probe.main.tourtour)
{
const (local variable) const(bool) okok = (local variable) during.Probe probeprobe.bool during.Probe.isSupported(during.io_uring.Operation op) const pure nothrow @nogc @safeIs operation supported?
isSupported((local variable) immutable(io_uring_probe.main.Op) entryentry.(field) during.io_uring.Operation io_uring_probe.main.Op.opop);
if ((local variable) const(bool) okok) ++(local variable) int supportedsupported;
void std.stdio.writef!(char, string, string)(in char[] fmt, string __param_1, string __param_2) @safeWrites formatted data to standard output (without a trailing newline).
Note
In older versions of Phobos, it used to be possible to write:
writef(stderr, "%s", "message");
to print a message to stderr. This syntax is no longer supported, and has
been superceded by:
stderr.writef("%s", "message");
writef(" %-11s %s\n", (local variable) immutable(io_uring_probe.main.Op) entryentry.(field) string io_uring_probe.main.Op.namename, (local variable) const(bool) okok ? "yes" : "no");
}
// One summary line, as required: how many of the curated opcodes this
// kernel actually implements.
void std.stdio.writefln!(char, int, int)(in char[] fmt, int __param_1, int __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("ok: IORING_REGISTER_PROBE succeeded — %d/%d curated opcodes supported on this kernel",
(local variable) int supportedsupported, cast(int) (immutable global) immutable(io_uring_probe.main.Op[]) io_uring_probe.main.tourtour.(field) ulong immutable(io_uring_probe.main.Op[]).lengthlength);
return 0;
}