probe.dhover×93all
#!/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_probe

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.

io_uring_probe
;
import
(module) during

Simple idiomatic dlang wrapper around linux io_uring (see: https://kernel.dk/io_uring.pdf) asynchronous API.

during
;
import
(package) std
std
.
(module) std.stdio
Category 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:

  1. The lowest layer is the operating system layer. The two main schemes are Windows and Posix.

  2. C's stdio.h which unifies the two operating system schemes.

  3. std.stdio, this module, unifies the various stdio.h implementations into a high level package for D programs.

Source

std/stdio.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Alex Rønne Petersen
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");
@paramfmt The format string. When passed as a compile-time argument, the string will be statically checked against the argument types passed.@paramargs Items to write.
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.Uring

Main 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 io
io
;
const
(local variable) const(int) setupRet
setupRet
=
(local variable) during.Uring io
io
.
int during.setup(ref during.Uring uring, uint entries = 128u, during.io_uring.SetupFlags flags = SetupFlags.NONE) nothrow @nogc @safe

Setup new instance of io_uring into provided Uring structure.

@paramuring Uring structure to be initialized (must not be already initialized)@paramentries Number of entries to initialize uring with@paramflags SetupFlags to use to initialize uring.@returnsOn succes it returns 0, -errno otherwise.
setup
(8);
if (
(local variable) const(int) setupRet
setupRet
< 0)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: io_uring_setup failed (errno %d) — io_uring unavailable on this host", -
(local variable) const(int) setupRet
setupRet
);
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.Probe

Simplified wrapper around io_uring_probe that is used to check what io_uring operations current kernel is actually supporting.

Probe
(local variable) during.Probe probe
probe
=
(local variable) during.Uring io
io
.
during.Probe during.Uring.probe() nothrow @nogc @safe

Probes supported operations

probe
();
if (!cast(bool)
(local variable) during.Probe probe
probe
)
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: IORING_REGISTER_PROBE unavailable (errno %d) — needs Linux 5.6+",
-
(local variable) during.Probe probe
probe
.
int during.Probe.error() const pure nothrow @nogc @property @safe

Error 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.Op
Op
{
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
(field) during.io_uring.Operation io_uring_probe.main.Op.op
op
;
(alias) object.string = string
string
(field) string io_uring_probe.main.Op.name
name
; }
static immutable
(struct) io_uring_probe.main.Op
Op
[]
(immutable global) immutable(io_uring_probe.main.Op[]) io_uring_probe.main.tour
tour
= [
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.NOP = cast(ubyte)0u

IORING_OP_NOP

NOP
, "NOP"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.READ = cast(ubyte)22u

IORING_OP_READ

READ
, "READ"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.WRITE = cast(ubyte)23u

IORING_OP_WRITE

WRITE
, "WRITE"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.ACCEPT = cast(ubyte)13u

IORING_OP_ACCEPT

ACCEPT
, "ACCEPT"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.CONNECT = cast(ubyte)16u

IORING_OP_CONNECT

CONNECT
, "CONNECT"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.SEND = cast(ubyte)26u

IORING_OP_SEND

SEND
, "SEND"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.RECV = cast(ubyte)27u

IORING_OP_RECV

RECV
, "RECV"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.OPENAT = cast(ubyte)18u

IORING_OP_OPENAT

OPENAT
, "OPENAT"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.TIMEOUT = cast(ubyte)11u

IORING_OP_TIMEOUT

TIMEOUT
, "TIMEOUT"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.POLL_ADD = cast(ubyte)6u

IORING_OP_POLL_ADD

POLL_ADD
, "POLL_ADD"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.SEND_ZC = cast(ubyte)47u

IORING_OP_SEND_ZC - zero-copy send

SEND_ZC
, "SEND_ZC"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.FUTEX_WAIT = cast(ubyte)51u

IORING_OP_FUTEX_WAIT - async futex(2) FUTEX_WAIT

FUTEX_WAIT
, "FUTEX_WAIT"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.RECV_ZC = cast(ubyte)58u

IORING_OP_RECV_ZC - zero-copy receive (requires REGISTER_ZCRX_IFQ)

RECV_ZC
, "RECV_ZC"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.PIPE = cast(ubyte)62u

IORING_OP_PIPE - async pipe(2)/pipe2(2)

PIPE
, "PIPE"),
(struct) io_uring_probe.main.Op
Op
(
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.NOP128 = cast(ubyte)63u

IORING_OP_NOP128 - 128-byte NOP for testing SQE128 rings

NOP128
, "NOP128"),
]; int
(local variable) int supported
supported
;
foreach (
(parameter) immutable(io_uring_probe.main.Op) entry
entry
;
(immutable global) immutable(io_uring_probe.main.Op[]) io_uring_probe.main.tour
tour
)
{ const
(local variable) const(bool) ok
ok
=
(local variable) during.Probe probe
probe
.
bool during.Probe.isSupported(during.io_uring.Operation op) const pure nothrow @nogc @safe

Is operation supported?

isSupported
(
(local variable) immutable(io_uring_probe.main.Op) entry
entry
.
(field) during.io_uring.Operation io_uring_probe.main.Op.op
op
);
if (
(local variable) const(bool) ok
ok
) ++
(local variable) int supported
supported
;
void std.stdio.writef!(char, string, string)(in char[] fmt, string __param_1, string __param_2) @safe

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");
@paramfmt The format string. When passed as a compile-time argument, the string will be statically checked against the argument types passed.@paramargs Items to write.
writef
(" %-11s %s\n",
(local variable) immutable(io_uring_probe.main.Op) entry
entry
.
(field) string io_uring_probe.main.Op.name
name
,
(local variable) const(bool) ok
ok
? "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) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("ok: IORING_REGISTER_PROBE succeeded — %d/%d curated opcodes supported on this kernel",
(local variable) int supported
supported
, cast(int)
(immutable global) immutable(io_uring_probe.main.Op[]) io_uring_probe.main.tour
tour
.
(field) ulong immutable(io_uring_probe.main.Op[]).length
length
);
return 0; }