sqe-mixed.dhover×85all
#!/usr/bin/env dub
/+ dub.sdl:
    name "io_uring_sqe_mixed"
    dependency "during" version="~>0.5.0"
    platforms "linux"
    targetPath "build"
+/
/**
 * `io_uring` — mixed-size SQEs and 128-byte opcodes
 * (`IORING_SETUP_SQE_MIXED` + `IORING_OP_NOP128`, Linux 6.19).
 *
 * Historically a ring's submission queue entries (SQEs) were a fixed stride:
 * either all 64 bytes, or — with `IORING_SETUP_SQE128` (6.16) — all 128 bytes.
 * `IORING_SETUP_SQE_MIXED` (6.19) lets a single ring carry *both*: ordinary
 * 64-byte SQEs sit alongside 128-byte SQEs, with the kernel and the userspace
 * library tracking the per-slot stride. `IORING_OP_NOP128` is the simplest op
 * that *requires* the wide layout — a `NOP` that occupies a 128-byte slot —
 * making it the canonical smoke test for the feature.
 *
 * This example sets up a mixed ring, emits one plain 64-byte `NOP` and one
 * 128-byte `NOP128` (reserved via `next128()`, which claims the two contiguous
 * SQ slots a 128-byte entry needs), submits them together, and reaps both CQEs.
 *
 * Companion to the io_uring chronology:
 * see docs/research/async-io/io-uring/timeline.md
 *     § "6.19 — Mixed-size SQE and 128-byte opcodes".
 *
 * Run with: `dub run --single sqe-mixed.d`
 *
 * Portability: `SQE_MIXED`/`NOP128` land in Linux 6.19. On an older kernel
 * `io_uring_setup` rejects the flag with `-EINVAL` (or the op completes with
 * `-EINVAL`/`-EOPNOTSUPP`); the program then prints a `SKIP:` line and exits 0
 * so it stays green on hosts predating the feature. It is also green when the
 * host has no `io_uring` at all.
 */
module 
(module) io_uring_sqe_mixed

io_uring — mixed-size SQEs and 128-byte opcodes (IORING_SETUP_SQE_MIXED + IORING_OP_NOP128, Linux 6.19).

Historically a ring's submission queue entries (SQEs) were a fixed stride: either all 64 bytes, or — with IORING_SETUP_SQE128 (6.16) — all 128 bytes. IORING_SETUP_SQE_MIXED (6.19) lets a single ring carry both: ordinary 64-byte SQEs sit alongside 128-byte SQEs, with the kernel and the userspace library tracking the per-slot stride. IORING_OP_NOP128 is the simplest op that requires the wide layout — a NOP that occupies a 128-byte slot — making it the canonical smoke test for the feature.

This example sets up a mixed ring, emits one plain 64-byte NOP and one 128-byte NOP128 (reserved via next128(), which claims the two contiguous SQ slots a 128-byte entry needs), submits them together, and reaps both CQEs.

Companion to the io_uring chronology: see docs/research/async-io/io-uring/timeline.md § "6.19 — Mixed-size SQE and 128-byte opcodes".

Run with: dub run --single sqe-mixed.d

Portability

SQE_MIXED/NOP128 land in Linux 6.19. On an older kernel io_uring_setup rejects the flag with -EINVAL (or the op completes with -EINVAL/-EOPNOTSUPP); the program then prints a SKIP: line and exits 0 so it stays green on hosts predating the feature. It is also green when the host has no io_uring at all.

io_uring_sqe_mixed
;
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_sqe_mixed.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

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

writefln
, stderr;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.linux
linux
.
(module) core.sys.linux.errno

D header file for GNU/Linux

glibc stdlib/errno.h

errno
:
(alias constant) io_uring_sqe_mixed.EINVAL = int core.stdc.errno.EINVAL = 22
EINVAL
,
(alias constant) io_uring_sqe_mixed.EOPNOTSUPP = int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
;
int
int D main()
main
()
{ // Cookies that ride through the kernel and come back on the CQEs, letting us // confirm the 64-byte and 128-byte completions are correlated correctly. enum ulong
(constant) ulong io_uring_sqe_mixed.main.cookie64 = 100LU
cookie64
= 0x64;
enum ulong
(constant) ulong io_uring_sqe_mixed.main.cookie128 = 296LU
cookie128
= 0x128;
// `SQE_MIXED` is requested as a setup flag. On a pre-6.19 kernel this is the // gate that fails first (-EINVAL), giving us a clean SKIP without ever // touching the wide-SQE code path.
(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,
(enum) during.io_uring.SetupFlags

io_uring_setup() flags

SetupFlags
.
(enum value) during.io_uring.SetupFlags.SQE_MIXED = 524288u

IORING_SETUP_SQE_MIXED (from Linux 6.18)

Allow a mix of 64- and 128-byte SQEs in the same ring.

SQE_MIXED
);
if (
(local variable) const(int) setupRet
setupRet
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
||
(local variable) const(int) setupRet
setupRet
== -
(constant) int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

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

writefln
("SKIP: IORING_SETUP_SQE_MIXED unsupported (errno %d) — needs Linux 6.19+",
-
(local variable) const(int) setupRet
setupRet
);
return 0; } 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; } // A normal 64-byte SQE: `putWith` reserves one slot and runs the prep callback.
(local variable) during.Uring io
io
.putWith!((ref SubmissionEntry e) {
e.prepNop(); e.user_data = cookie64; })(); // A 128-byte SQE: `next128()` reserves the two contiguous SQ slots a wide // entry needs (on an SQE_MIXED ring) and returns a reference to fill in. // `prepNop128()` is the 128-byte NOP opcode (IORING_OP_NOP128). auto
(local variable) during.io_uring.SubmissionEntry* sqe128
sqe128
= &
(local variable) during.Uring io
io
.
during.io_uring.SubmissionEntry during.Uring.next128!()() pure nothrow @nogc ref @safe

Advances the userspace submission queue and returns a slot suitable for a 128-byte SQE (Operation.NOP128 / Operation.URING_CMD128). On SetupFlags.SQE128 rings this is equivalent to next; on SetupFlags.SQE_MIXED rings it reserves two contiguous 64-byte slots, inserting a skipped NOP if needed to avoid wrapping a 128-byte SQE over the ring end.

next128
();
(*
(local variable) during.io_uring.SubmissionEntry* sqe128
sqe128
).
during.io_uring.SubmissionEntry during.prepNop128(return ref during.io_uring.SubmissionEntry entry) nothrow @nogc ref @safe

Prepares a 128-byte nop (IORING_OP_NOP128). Only meaningful on rings created with SetupFlags.SQE128 — exposes the second-half SQE payload for kernels/code that need to exercise the SQE128 layout.

Note

Available from Linux 6.16

prepNop128
();
(local variable) during.io_uring.SubmissionEntry* sqe128
sqe128
.
(field) ulong during.io_uring.SubmissionEntry.user_data

data to be passed back at completion time

user_data
=
(constant) ulong io_uring_sqe_mixed.main.cookie128 = 296LU
cookie128
;
// We asked for 2 logical ops; the wide one occupies an extra SQ slot, so the // kernel sees 3 submission slots. We only assert success, not the slot count. const
(local variable) const(int) submitted
submitted
=
(local variable) during.Uring io
io
.
int during.Uring.submit(uint want) nothrow @nogc @safe

Submits qued SubmissionEntry to be processed by kernel.

@paramwant number of CompletionEntries to wait for. If 0, this just submits queued entries and returns. If > 0, it blocks until at least wanted number of entries were completed.@paramsig See io_uring_enter(2) man page@returnsNumber of submitted entries on success, -errno on error
submit
(2);
if (
(local variable) const(int) submitted
submitted
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("submit failed: errno %d", -
(local variable) const(int) submitted
submitted
);
return 1; } // Reap both completions. Bound the loop so a misbehaving kernel can't hang us. bool
(local variable) bool sawNop
sawNop
;
bool
(local variable) bool sawNop128
sawNop128
;
foreach (
(local variable) int i
i
; 0 .. 2)
{
(local variable) during.Uring io
io
.
int during.Uring.wait(uint want = 1u) nothrow @nogc

Simmilar to submit but with this method we just wait for required number of CompletionEntries.

@returns0 on success, -errno on error
wait
(1);
const
(local variable) const(int) res
res
=
(local variable) during.Uring io
io
.
during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safe

Get first CompletionEntry from cq ring

front
.
(field) int during.io_uring.CompletionEntry.res

result code for this event

res
;
const
(local variable) const(ulong) echoed
echoed
=
(local variable) during.Uring io
io
.
during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safe

Get first CompletionEntry from cq ring

front
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
;
(local variable) during.Uring io
io
.
void during.Uring.popFront() pure nothrow @nogc @safe

Move to next CompletionEntry

popFront
();
// A kernel that knows SQE_MIXED setup but not the NOP128 op (or rejects // the wide layout) reports it per-op here — still an expected SKIP. if (
(local variable) const(int) res
res
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
||
(local variable) const(int) res
res
== -
(constant) int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

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

writefln
("SKIP: NOP128/SQE_MIXED op unsupported (errno %d) — needs Linux 6.19+",
-
(local variable) const(int) res
res
);
return 0; } if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("op completed with error: errno %d", -
(local variable) const(int) res
res
);
return 1; } if (
(local variable) const(ulong) echoed
echoed
==
(constant) ulong io_uring_sqe_mixed.main.cookie64 = 100LU
cookie64
)
(local variable) bool sawNop
sawNop
= true;
else if (
(local variable) const(ulong) echoed
echoed
==
(constant) ulong io_uring_sqe_mixed.main.cookie128 = 296LU
cookie128
)
(local variable) bool sawNop128
sawNop128
= true;
else { stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("unexpected user_data: 0x%X",
(local variable) const(ulong) echoed
echoed
);
return 1; } } if (!
(local variable) bool sawNop
sawNop
|| !
(local variable) bool sawNop128
sawNop128
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("missing completion (nop=%s nop128=%s)",
(local variable) bool sawNop
sawNop
,
(local variable) bool sawNop128
sawNop128
);
return 1; }
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safe

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

writefln
("ok: SQE_MIXED ring completed a 64-byte NOP (0x%X) and a 128-byte NOP128 (0x%X)",
(constant) ulong io_uring_sqe_mixed.main.cookie64 = 100LU
cookie64
,
(constant) ulong io_uring_sqe_mixed.main.cookie128 = 296LU
cookie128
);
return 0; }