registered-ring-fd.dhover×71all
#!/usr/bin/env dub
/+ dub.sdl:
    name "io_uring_registered_ring_fd"
    dependency "during" version="~>0.5.0"
    platforms "linux"
    targetPath "build"
+/
/**
 * `io_uring` — registered ring fd (`IORING_REGISTER_RING_FDS`, Linux 5.18).
 *
 * Every `io_uring_enter(2)` normally takes the ring's file descriptor as its
 * first argument, so the kernel must do an `fdget()` to translate that integer
 * into the backing `struct file` and a matching `fdput()` afterwards — once per
 * syscall. For submit-heavy, syscall-per-batch workloads that fdget/fdput pair
 * is pure overhead. `IORING_REGISTER_RING_FDS` lets userspace register the ring
 * fd once and thereafter pass a small *registered index* plus the
 * `IORING_ENTER_REGISTERED_RING` flag, so the kernel skips the fd lookup
 * entirely on the hot path.
 *
 * `during` wires this up transparently: after `registerRingFd()` succeeds it
 * passes the registered index + `ENTER_REGISTERED_RING` on every subsequent
 * `submit`/`wait`. We prove the registered ring is functional by running a full
 * NOP submit/complete cycle through it, then revert with `unregisterRingFd()`.
 *
 * Companion to the io_uring chronology:
 * see docs/research/async-io/io-uring/timeline.md
 *   § "5.18 — Ring-fd registration, msg-ring, linked-file".
 *
 * Run with: `dub run --single registered-ring-fd.d`
 *
 * Portability: if io_uring is unavailable, or the kernel predates 5.18 (the
 * register call returns `-EINVAL`/`-EOPNOTSUPP`/`-ENOSYS`), the program prints a
 * `SKIP:` line and exits 0 so it stays green regardless of the host kernel.
 */
module 
(module) io_uring_registered_ring_fd

io_uring — registered ring fd (IORING_REGISTER_RING_FDS, Linux 5.18).

Every io_uring_enter(2) normally takes the ring's file descriptor as its first argument, so the kernel must do an fdget() to translate that integer into the backing struct file and a matching fdput() afterwards — once per syscall. For submit-heavy, syscall-per-batch workloads that fdget/fdput pair is pure overhead. IORING_REGISTER_RING_FDS lets userspace register the ring fd once and thereafter pass a small registered index plus the IORING_ENTER_REGISTERED_RING flag, so the kernel skips the fd lookup entirely on the hot path.

during wires this up transparently: after registerRingFd() succeeds it passes the registered index + ENTER_REGISTERED_RING on every subsequent submit/wait. We prove the registered ring is functional by running a full NOP submit/complete cycle through it, then revert with unregisterRingFd().

Companion to the io_uring chronology: see docs/research/async-io/io-uring/timeline.md § "5.18 — Ring-fd registration, msg-ring, linked-file".

Run with: dub run --single registered-ring-fd.d

Portability

if io_uring is unavailable, or the kernel predates 5.18 (the register call returns -EINVAL/-EOPNOTSUPP/-ENOSYS), the program prints a SKIP: line and exits 0 so it stays green regardless of the host kernel.

io_uring_registered_ring_fd
;
import
(module) during

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

during
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.errno

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html, errno.h

Source

core/stdc/errno.d

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly, Alex Rønne Petersen@standardsISO/IEC 9899:1999 (E)
errno
:
(alias constant) io_uring_registered_ring_fd.EINVAL = int core.stdc.errno.EINVAL = 22
EINVAL
,
(alias constant) io_uring_registered_ring_fd.EOPNOTSUPP = int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
,
(alias constant) io_uring_registered_ring_fd.ENOSYS = int core.stdc.errno.ENOSYS = 38
ENOSYS
;
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_registered_ring_fd.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
()
{ enum ulong
(constant) ulong io_uring_registered_ring_fd.main.cookie = 1367404557LU
cookie
= 0x5180_F00D; // 5.18 marker, just a recognizable user_data
(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; } // Register the ring's own fd. On success `during` swaps in the registered // index + ENTER_REGISTERED_RING for all later io_uring_enter(2) calls. const
(local variable) const(int) regRet
regRet
=
(local variable) during.Uring io
io
.
int during.Uring.registerRingFd() nothrow @nogc @trusted

Registers the ring's own file descriptor with the kernel (IORING_REGISTER_RING_FDS). Once registered, during transparently passes the registered index plus EnterFlags.ENTER_REGISTERED_RING to every io_uring_enter(2), avoiding the fdget/fdput cost on each call. Mirrors liburing's io_uring_register_ring_fd.

@returnsOn success, returns 0. On error, -errno (-EEXIST if already registered).
registerRingFd
();
if (
(local variable) const(int) regRet
regRet
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
||
(local variable) const(int) regRet
regRet
== -
(constant) int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
||
(local variable) const(int) regRet
regRet
== -
(constant) int core.stdc.errno.ENOSYS = 38
ENOSYS
)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

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

writefln
("SKIP: IORING_REGISTER_RING_FDS unsupported (errno %d) — needs Linux 5.18+", -
(local variable) const(int) regRet
regRet
);
return 0; } if (
(local variable) const(int) regRet
regRet
< 0)
{ // Any other negative result is a genuine, unexpected failure. stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("registerRingFd failed: errno %d", -
(local variable) const(int) regRet
regRet
);
return 1; } // From here on, every submit/wait reaches the kernel via the *registered* // ring index — no per-syscall fdget/fdput. Drive a NOP through it to prove // the registered ring still submits and completes correctly.
(local variable) during.Uring io
io
.putWith!((ref SubmissionEntry e) {
e.prepNop(); e.user_data = cookie; })(); 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
(1); // uses the registered ring fd under the hood
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 (via registered ring) failed: errno %d", -
(local variable) const(int) submitted
submitted
);
return 1; }
(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
();
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
("NOP via registered ring completed with error: errno %d", -
(local variable) const(int) res
res
);
return 1; } if (
(local variable) const(ulong) echoed
echoed
!=
(constant) ulong io_uring_registered_ring_fd.main.cookie = 1367404557LU
cookie
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("user_data mismatch: expected 0x%X, got 0x%X",
(constant) ulong io_uring_registered_ring_fd.main.cookie = 1367404557LU
cookie
,
(local variable) const(ulong) echoed
echoed
);
return 1; } // Revert to using the real ring fd again. Mirrors liburing's // io_uring_unregister_ring_fd; -EINVAL here would mean "wasn't registered". const
(local variable) const(int) unregRet
unregRet
=
(local variable) during.Uring io
io
.
int during.Uring.unregisterRingFd() nothrow @nogc @trusted

Reverts registerRingFd — subsequent io_uring_enter(2) calls use the real ring fd again. Mirrors liburing's io_uring_unregister_ring_fd.

@returnsOn success, returns 0. On error, -errno (-EINVAL if not registered).
unregisterRingFd
();
if (
(local variable) const(int) unregRet
unregRet
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("unregisterRingFd failed: errno %d", -
(local variable) const(int) unregRet
unregRet
);
return 1; }
void std.stdio.writefln!(char, const(int), const(ulong))(in char[] fmt, const(int) __param_1, const(ulong) __param_2) @safe

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

writefln
("ok: registered ring fd, ran a NOP through it (res=%d, user_data 0x%X), then unregistered — io_uring_enter skips per-call fdget/fdput",
(local variable) const(int) res
res
,
(local variable) const(ulong) echoed
echoed
);
return 0; }