Catch up to liburing 2.9 / Linux 7.1-rc4 - #16
Merged
Conversation
Set `targetPath "build"` in each dub manifest so binaries and intermediate artifacts land in a per-package `build/` directory, and ignore `build/` globally. Replaces the per-example `.gitignore` pattern, which required adding a new entry for every new example binary.
- Extend `Operation` with `SEND_ZC=47`, `SENDMSG_ZC=48`, `READ_MULTISHOT=49`, `WAITID=50`. - Add `prepSendZc`, `prepSendZcFixed`, `prepSendmsgZc`, `prepReadMultishot`, `prepWaitid` in `during.package`. - Extend `CQEFlags` with `NOTIF`, `BUF_MORE`, `SKIP` and add the `IORING_NOTIF_USAGE_ZC_COPIED` constant for the zero-copy notification CQE's `res` field. - Extend the `SubmissionEntry` rw_flags union with `waitid_flags`. Tests (kernel-gated via `checkKernelVersion`): - `tests.zerocopy`: TCP loopback `SEND_ZC` asserting the two-CQE pattern (send result + CQE_F_NOTIF notification). - `tests.waitid`: fork/exit reaping via async `WAITID`. - `tests.rw`: pipe-based `READ_MULTISHOT` with two-buffer provided ring.
…ge register ops - Extend `Operation` with `FUTEX_WAIT=51`, `FUTEX_WAKE=52`, `FUTEX_WAITV=53`. - Extend `RegisterOpCode` with `REGISTER_SYNC_CANCEL=24` and `REGISTER_FILE_ALLOC_RANGE=25` plus the backing structs `io_uring_sync_cancel_reg` and `io_uring_file_index_range`. - Extend the `SubmissionEntry` rw_flags union with `futex_flags`. - Add `futex_waitv` struct and the `FUTEX2_*` flag constants from `<linux/futex.h>` (size + PRIVATE/NUMA). - Add prep helpers `prepFutexWait`, `prepFutexWake`, `prepFutexWaitv` in `during.package`. - Add `Uring.registerSyncCancel` and `Uring.registerFileAllocRange`. Tests (kernel-gated): `tests.futex` covers each new SQE op via the safe mismatch/empty-waiter paths (the wait-vs-wake race in a single ring would deadlock), plus a no-match `registerSyncCancel` and a sparse-files `registerFileAllocRange` round-trip.
- New `futex_wait woken by helper thread` exercises a genuine wait/wake round-trip: main submits an io_uring FUTEX_WAIT, a pthread helper issues legacy `futex(SYS_futex, FUTEX_WAKE_PRIVATE, …)` to wake it. The helper retries every 5ms (bounded to ~1s) to defeat the inherent race between SQE submission and the kernel actually parking the waiter. - New `registerSyncCancel cancels matching read` submits a blocked pipe read with `IOSQE_ASYNC` and a known `user_data`, then issues `register_sync_cancel` matching on that data and confirms both the cancel-count (>=1) and the cancelled CQE (`-ECANCELED` or `-EINTR`).
…D_INSTALL, FTRUNCATE - Extend `Operation` with `FIXED_FD_INSTALL=54`, `FTRUNCATE=55` (Linux 6.7) and `BIND=56`, `LISTEN=57` (Linux 6.11). - Add `IORING_FIXED_FD_NO_CLOEXEC` flag and extend the `SubmissionEntry` rw_flags union with `install_fd_flags`. - Add prep helpers `prepFtruncate`, `prepFixedFdInstall`, `prepBind`, `prepListen` in `during.package`. Tests: - `tests.socket`: full BIND+LISTEN round-trip on TCP/127.0.0.1, verified by a regular connect()/accept() against the listening socket. - `tests.fixed_fd`: ftruncate a regular file and verify via fstat; promote an OPENAT_DIRECT slot to a real fd via FIXED_FD_INSTALL and verify it is readable through a regular read(2).
…s register ops - Extend `Operation` with `RECV_ZC=58`, `EPOLL_WAIT=59`. - Extend `RegisterOpCode` with `REGISTER_PBUF_STATUS=26`, `REGISTER_NAPI=27`, `UNREGISTER_NAPI=28` plus backing structs `io_uring_buf_status` and `io_uring_napi`. - Add `SubmissionEntry.zcrx_ifq_idx` union member for addressing the zerocopy-RX ifq table on RECV_ZC submissions. - Add prep helpers `prepEpollWait`, `prepRecvZc` in `during.package`. - Add `Uring.bufRingStatus`, `Uring.registerNapi`, `Uring.unregisterNapi`. Tests: - `tests.epoll_wait`: pipe + epoll fd, write to make the pipe readable, submit EPOLL_WAIT and assert it reports the pipe fd as ready. - `tests.register`: `bufRingStatus` against a missing group reports an error (validates the io_uring_register plumbing); a `registerNapi` + `unregisterNapi` round-trip; static assertion of `RECV_ZC` opcode value (functional test requires a configured NIC, deferred to user code).
…bundles, uring_cmd helpers - Extend `Operation` with `READV_FIXED=60`, `WRITEV_FIXED=61` (Linux 6.13), `PIPE=62` (Linux 6.14), `NOP128=63`, `URING_CMD128=64` (Linux 6.16). - Extend `SubmissionEntry` unions to cover `uring_cmd_flags`, `nop_flags`, `pipe_flags`, `optlen`, `addr_len`, `optval`, and the `level`/`optname` overlay needed by socket `uring_cmd` ops. - Add the `IORING_RECVSEND_BUNDLE` flag (Linux 6.10), the `IORING_MSG_RING_FLAGS_PASS` flag (Linux 6.3), the `SOCKET_URING_OP_*` subcommand enum, and `BLOCK_URING_CMD_DISCARD`. - Add prep helpers in `during.package`: `prepReadvFixed`, `prepWritevFixed`, `prepPipe`, `prepPipeDirect`, `prepNop128`, `prepUringCmd`, `prepUringCmd128`, `prepCmdSock`, `prepCmdGetsockname`, `prepCmdDiscard`, `prepMsgRingCqeFlags`, `prepSendBundle`, `prepSendSetAddr`. - Bump `Probe` capacity from 64 to 128 to accommodate the new opcodes. SQE128 caveat: - The existing SQE region mmaps `entries * SubmissionEntry.sizeof` (=64 B), which is wrong for rings created with `SetupFlags.SQE128` (kernel expects 128 B/SQE). Fixing the storage requires a separate change to use a runtime stride; until then NOP128/URING_CMD128 work for users who maintain their own SQE storage. `tests/sqe128.d` documents this and only asserts opcode values. Tests: - `tests.pipe`: IORING_OP_PIPE creates a usable read/write pair, validated via write+read round-trip. - `tests.rw`: `readv_fixed` / `writev_fixed` round-trip against a registered buffer, with two-iovec vectored payload. - `tests.sqe128`: documented static-only assertion (see caveat above).
Previously the SQE region was mmaped at `entries * SubmissionEntry.sizeof` (=64 B) regardless of `SetupFlags.SQE128`. With SQE128 the kernel expects `entries * 128` and the trailing 64 B per slot is where URING_CMD128 reads its `cmd[]` payload — so any access into that region from the D side fell off the mapped pages. Internals: - `SubmissionQueue.sqes` (typed `SubmissionEntry[]`) becomes `sqesPtr` (raw `void*`) + `entries` + `stride`. `stride` is set to 64 or 128 in `mapRings()` based on `params.flags & SetupFlags.SQE128`. - New `ref SubmissionEntry slot(uint)` accessor does the stride math. The `next` / `put` / `putWith` hot path keeps its call-site shape. - Destructor unmaps `entries * stride` bytes. Tests in `tests.sqe128`: - `nop128 round-trip on SQE128 ring`: instantiates the ring with `SetupFlags.SQE128`, submits NOP128, validates the CQE. - `SQE128 cmd payload is addressable`: writes 64 B into the trailing `cmd[]` region of a slot and confirms NOP128 still completes cleanly — proves the second half of each slot is mapped and writable.
…appers - Extend `RegisterOpCode` with the kernel 6.10+ register ops: `REGISTER_CLOCK=29`, `REGISTER_CLONE_BUFFERS=30`, `REGISTER_SEND_MSG_RING=31`, `REGISTER_ZCRX_IFQ=32`, `REGISTER_RESIZE_RINGS=33`, `REGISTER_MEM_REGION=34`, `REGISTER_QUERY=35`, `REGISTER_BPF_FILTER=37`. - Add backing structs `io_uring_clock_register`, `io_uring_clone_buffers`, `io_uring_region_desc`, `io_uring_mem_region_reg`, `io_uring_zcrx_offsets`, `io_uring_zcrx_ifq_reg`, `io_uring_reg_wait`, plus the related flag constants (SRC_REGISTERED, DST_REPLACE, MEM_REGION_TYPE_USER, MEM_REGION_REG_WAIT_ARG). - Add `EnterFlags.EXT_ARG_REG` (Linux 6.13) for the registered-wait-arg path. - Add `Uring` methods: `registerClock`, `cloneBuffers[_offset]`, `resizeRings`, `registerMemRegion`, `registerWaitReg`, `submitAndWaitReg`, `submitAndWaitMinTimeout`, `registerIfq`, `registerBpfFilter`, and a static `sendMsgRingSync` (no ring required). - `resizeRings` unmaps and re-maps SQ/CQ + SQE region using the kernel's new layout — works on top of the SQE128-stride fix. Tests: - `tests.api`: `resizeRings grows and shrinks` — grow from 8 to 32 SQEs, then shrink to 4, with NOP round-trips before, between and after to prove the cached pointers and the new kernel layout stay in sync. - `tests.register`: `cloneBuffers` register a buffer in src, clone into dst, then read through dst with `prepReadFixed` against the cloned slot — proves the buffer table is actually shared. - `tests.wait_reg`: clock-registration accepts MONOTONIC/BOOTTIME, registerWaitReg surface check (kernel may stub-reject, both branches asserted), and registerBpfFilter rejects an invalid prog fd.
- Extend `SetupFlags` with `SINGLE_ISSUER` (6.0), `DEFER_TASKRUN` (6.1), `NO_MMAP` (6.5), `REGISTERED_FD_ONLY` (6.5), `NO_SQARRAY` (6.6), `HYBRID_IOPOLL` (6.13), `CQE_MIXED` / `SQE_MIXED` / `SQ_REWIND` (6.18). - Extend `SetupFeatures` with `REG_REG_RING` (6.3), `RECVSEND_BUNDLE` (6.10), `MIN_TIMEOUT` (6.13), `RW_ATTR` (6.13), `NO_IOWAIT` (6.16). Tests: - `tests.api single_issuer + defer_taskrun NOP round-trip`: instantiates a ring with `SINGLE_ISSUER | DEFER_TASKRUN` and runs a NOP through it. Kernel-gated on 6.1 (the version DEFER_TASKRUN requires SINGLE_ISSUER becomes meaningful), accepts `-EINVAL` from older kernels and bails.
…RAY, SQ_REWIND, SQE_MIXED The previous commit only added the SetupFlags enum values for these new 6.5–6.18 setup flags. This commit implements the runtime behaviour so callers can actually use them. setup(): - Reject `NO_MMAP` and `REGISTERED_FD_ONLY` with -EINVAL up-front. Both expect the caller to supply the ring memory, which this wrapper does not expose. Rejecting at the wrapper boundary gives a clear error instead of letting the kernel return -EINVAL after partial setup. UringDesc.mapRings(): - Skip the SQ-array index initialization when `NO_SQARRAY` is set; the kernel no longer reads an indirection table in that mode (head and tail index the SQE array directly). - Decode and cache the new `rewind` and `sqeMixed` flags on the SubmissionQueue for fast-path use during put / submit. SQ_REWIND (Linux 6.18): - `head()` returns 0 unconditionally — the kernel always starts reading from slot 0 in rewind mode. - `flushTail()` becomes a no-op — the kernel doesn't consume a user-published tail in rewind mode. - New `submitted(n)` callback resets `localTail` to 0 after the kernel acknowledges the batch. Threaded into all three submission paths (`submit`, `submit-and-wait`, the registered-ring variant) so the bookkeeping stays in sync regardless of which entry point is used. SQE_MIXED (Linux 6.18): - New `Uring.next128()` / `SubmissionQueue.next128()` return a slot suitable for a 128-byte SQE. On pure `SQE128` rings the slot stride is already 128 bytes, so next128 short-circuits to `next()`; on `SQE_MIXED` rings the stride is 64 bytes and a 128-byte SQE has to occupy two consecutive slots. - New private `reserve128Slot()` handles the two-slot reservation. If the would-be second slot crosses the ring-wrap boundary, it inserts a `CQE_SKIP_SUCCESS` NOP padder so the 128-byte SQE never straddles the wrap (the kernel reads SQEs by slot index and would otherwise read the extension half from a different ring iteration). - `put(SubmissionEntry)` auto-detects `NOP128` / `URING_CMD128` opcodes and routes them through the two-slot path. Tests: - `tests.api no_mmap setup is rejected by wrapper` — covers both `NO_MMAP` alone and `NO_MMAP | REGISTERED_FD_ONLY`. - `tests.api no_sqarray NOP round-trip` — full NOP submit/wait on a `NO_SQARRAY` ring. - `tests.api sq_rewind submits fresh entries from slot zero` — two consecutive NOP round-trips through a `NO_SQARRAY | SQ_REWIND` ring, verifying the second iteration reuses slot 0. - `tests.sqe128 sqe_mixed supports 64-byte and 128-byte SQEs` — one 64-byte NOP plus one 128-byte NOP128 through an `SQE_MIXED` ring, asserting submit consumes 3 SQ slots. - `tests.sqe128 sqe_mixed pads a 128-byte SQE that would wrap the ring end` — drains the ring head past the wrap boundary, fills to the last slot, then submits a NOP128 to exercise the `reserve128Slot` wrap-pad path and assert the padder is suppressed from the CQ stream.
- New `examples/zerocopy_echo`: loopback TCP echo over `IORING_OP_SEND_ZC`, asserts the two-CQE pattern (send result with `F_MORE`, then the `F_NOTIF` zero-copy completion). - New `examples/futex_pingpong`: io_uring `FUTEX_WAIT` on a 32-bit private futex woken by a helper pthread issuing legacy `futex(SYS_futex, FUTEX_WAKE_PRIVATE, …)`. Bounded retry on the wake side defeats the inherent race between SQE submission and the kernel parking the waiter. - README: update the "Linux 5.14" line to "Linux 6.x / liburing 2.9", bump the version recommendation to `~>0.5.0`, fix the json snippet's missing closing quote. - CHANGELOG: cut `[0.5.0]` from the accumulated Unreleased entries. Bug fix surfaced while writing the futex example: - `prepFutexWait` / `prepFutexWake` were being called with `mask = ~0UL`. The kernel rejects masks whose bits exceed the futex's declared size (`FUTEX2_SIZE_U32` → mask must fit in 32 bits) with `-EINVAL`. The Phase 2 wait/wake test silently bailed on `-EINVAL`, masking the bug. Added `FUTEX_BITSET_MATCH_ANY = 0xFFFFFFFF` and `FUTEX2_MPOL` (Linux 6.18), updated tests/example to use the correct mask, and tightened the wait test so the silent-bail only fires on `-EOPNOTSUPP`.
Picks up the kernel additions that landed between liburing 2.9 and the
current Linux 7.1-rc4 tree. Most are flag-bit / struct-field deltas the
previous commits couldn't surface yet because they're newer than
liburing's last cut.
io_uring.d:
- `SubmissionEntry.write_stream` byte field (6.18 — stream id for
WRITE / WRITEV / SEND on file-stream-aware backends).
- `CQEFlags.F_32` and `F_TSTAMP_HW` (6.18), plus
`IORING_TIMESTAMP_HW_SHIFT` / `TYPE_SHIFT`.
- `TimeoutFlags.MULTISHOT` (6.4) and `IMMEDIATE_ARG` (6.18).
- `PollFlags.ADD_LEVEL` (6.0 — level-triggered poll).
- `CancelFlags.CANCEL_FD_FIXED` (6.1), `CANCEL_USERDATA` /
`CANCEL_OP` (6.6).
- `IORING_ACCEPT_DONTWAIT` and `IORING_ACCEPT_POLL_FIRST` (6.6),
`IORING_SEND_VECTORIZED` (7.0), `IORING_MSG_RING_CQE_SKIP` (6.5).
- All six `IORING_NOP_*` flags (6.13/6.18), `IORING_URING_CMD_FIXED`
(6.0) / `_MULTISHOT` (6.18) / `_MASK`, `IORING_RW_ATTR_FLAG_PI` (6.13).
- `EnterFlags.ABS_TIMER` (6.10) and `NO_IOWAIT` (6.16).
- `RegisterOpCode.REGISTER_ZCRX_CTRL` (6.16) and
`SetupParameters.PROVIDED_BUFFER_RING_OFFSET` / `_SHIFT` / `MMAP_MASK`.
- ZCRX scaffolding: `io_uring_zcrx_rqe`, `io_uring_zcrx_cqe`,
`io_uring_zcrx_area_reg`, `IORING_ZCRX_AREA_DMABUF`,
`IORING_ZCRX_AREA_SHIFT` / `_MASK`, plus the `io_timespec` struct used
by the new timestamp paths.
- `io_uring_napi` extended with `opcode` and `op_param` to match the
6.18 kernel layout. Backward-compatible — zero-initialised callers
still get the original register/unregister behaviour. New
`IO_URING_NAPI_REGISTER_OP` / `STATIC_ADD_ID` / `STATIC_DEL_ID`
opcode constants and `TRACKING_DYNAMIC` / `STATIC` / `INACTIVE`
strategy constants.
Tests (each kernel-gated via `checkKernelVersion`):
- `tests.api nop inject_result drives custom cqe.res` — sets
`IORING_NOP_INJECT_RESULT` plus `sqe.len`, asserts the value surfaces
in `cqe.res`. Gated on 6.13.
- `tests.timeout timeout multishot fires repeatedly` — counts three
CQEs from a single MULTISHOT timeout, asserts only the last lacks
`CQEFlags.MORE`. Gated on 6.4.
- `tests.timeout timeout immediate_arg accepts inline nanoseconds` —
builds a TIMEOUT SQE by hand with `IMMEDIATE_ARG`, places the
duration in `sqe.addr`, asserts the expected `-ETIME`. Gated on 6.18.
- `tests.poll poll add_level on pre-readable eventfd` — arms a
level-triggered poll against an already-readable eventfd and asserts
the SQE is accepted and completes. Gated on 6.0.
Note: The `CQE32` / `CQE_MIXED` storage-path support that the new
`CQEFlags.F_32` marker drives is implemented in the follow-up commit
("wire up runtime support for CQE32 / CQE_MIXED completion rings").
…mpletion rings The previous commit added the `SetupFlags.CQE32` / `CQE_MIXED` enum values and the `CQEFlags.F_32` per-CQE marker but didn't extend the CompletionQueue to walk variable-stride completion entries. This commit makes those flags actually usable. UringDesc.mapRings(): - Cache a `shift` of 1 for `CQE32` rings, 0 otherwise. The `cqes` slice is sized as `entries << shift` so the second 16 bytes of each 32-byte CQE remain addressable. CompletionQueue.front() / popFront() / peekAt(): - `front()` indexes the cqes array as `(localHead & ringMask) << shift`, which collapses to the original behaviour on 16-byte rings and skips the trailing half on pure `CQE32` rings. - `popFront()` advances by `cqeSlots(front)` rather than a fixed 1. `cqeSlots` returns 2 when the CQE carries `CQEFlags.F_32`, the kernel marker for "this CQE is 32 bytes". This is what makes `CQE_MIXED` rings work: entries can be 16 or 32 bytes within the same ring, and only the per-CQE flag tells the consumer which is in front. - `peekAt(i)` walks i CQEs from the head, decoding each entry's stride on the way rather than assuming a fixed step. Tests: - `tests.api cqe_mixed advances over 32-byte CQEs` — submits a NOP with `IORING_NOP_CQE32` to a `CQE_MIXED` ring, verifies the CQE comes back with `F_32` set, then confirms a subsequent regular NOP completes cleanly (i.e. popFront correctly advanced over both ring slots of the 32-byte CQE rather than landing on its extension half).
Purely additive: a flake-parts-based `flake.nix` pins `ldc`, `dub`, and `dtools` from a known-good nixpkgs revision, and an `.envrc` lets `nix-direnv` users get the toolchain by stepping into the repo. Does not change how dub builds, what dub uses, or any existing build instructions — contributors who don't use Nix are unaffected. Drop this commit if upstream prefers not to carry Nix infrastructure in-tree.
Owner
|
thanks! |
tchaloupka
added a commit
that referenced
this pull request
May 19, 2026
Follow-up to PR #16: complete the io_uring.h uapi mirror to v7.1-rc4 (5200f5f4) incl. io_uring_buf_reg layout and msg/zcrx-ctrl constants, add liburing 2.9 helper parity (14 prep/register helpers) with smoke tests, and fix the two CI failures from PR #16 — the O_CLOEXEC import on pre-2.098 frontends and a betterC link error (_memset128) from a default-initialised slice array in tests/rw.d.
tchaloupka
added a commit
that referenced
this pull request
May 19, 2026
Bugs found reviewing PR #16, each with a regression test: - CQE32: cqes mmap was sized for 16-byte CQEs, so CQE32|NO_SQARRAY rings read past the mapping - io_uring_getevents_arg waits never set IORING_ENTER_EXT_ARG - submitAndWaitReg used the wrong EXT_ARG_REG ABI; registerWaitReg passed a non-page-aligned region size - CQE_MIXED: length() counted 16-byte slots instead of logical CQEs - prepSendBundle crashed on an empty slice and diverged from liburing's send_bundle — realigned (now takes a len argument) - put/putWith built 128-byte ops in a single slot on SQE_MIXED rings - SQ_REWIND: a partial submit dropped the unsubmitted SQEs
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings
duringup to liburing 2.9 / Linux 7.1-rc4 parity. The lastrelease (0.4.0) was synced to Linux 5.19, so the bulk of liburing 2.7–2.9
plus the post-2.9 kernel additions through 7.1-rc4 was missing. This PR
closes that gap.
Quantified: 18 new opcodes, 14 new register opcodes, 9 new
setup flags, 5 new feature bits, plus the structural changes to
the SQE / CQE storage paths that the new variable-stride rings
(
SQE128,SQE_MIXED,CQE32,CQE_MIXED) require. 38 of the 56unittest blocks are new and exercise the new prep helpers, register
wrappers, and ring-storage paths; kernel-gated tests bail cleanly on
older hosts via
checkKernelVersion().The branch is shaped as 15 atomic commits, one feature group per
commit, intended to be reviewed and merged in order.
What's in here
Opcodes (18 new):
SEND_ZCprepSendZc,prepSendZcFixedSENDMSG_ZCprepSendmsgZcREAD_MULTISHOTprepReadMultishotWAITIDprepWaitidFUTEX_WAITprepFutexWaitFUTEX_WAKEprepFutexWakeFUTEX_WAITVprepFutexWaitvFIXED_FD_INSTALLprepFixedFdInstallFTRUNCATEprepFtruncateBINDprepBindLISTENprepListenRECV_ZCprepRecvZcEPOLL_WAITprepEpollWaitREADV_FIXEDprepReadvFixedWRITEV_FIXEDprepWritevFixedPIPEprepPipe,prepPipeDirectNOP128prepNop128URING_CMD128prepUringCmd128Plus generic
URING_CMDhelpers (prepUringCmd,prepCmdSock,prepCmdGetsockname,prepCmdDiscard) layered over the pre-existingURING_CMDopcode.Register opcodes (14 new):
REGISTER_SYNC_CANCELUring.registerSyncCancelREGISTER_FILE_ALLOC_RANGEUring.registerFileAllocRangeREGISTER_PBUF_STATUSUring.bufRingStatusREGISTER_NAPIUring.registerNapiUNREGISTER_NAPIUring.unregisterNapiREGISTER_CLOCKUring.registerClockREGISTER_CLONE_BUFFERSUring.cloneBuffers,cloneBuffersOffsetREGISTER_SEND_MSG_RINGUring.sendMsgRingSyncREGISTER_ZCRX_IFQUring.registerIfqREGISTER_RESIZE_RINGSUring.resizeRingsREGISTER_MEM_REGIONUring.registerMemRegion,registerWaitRegREGISTER_QUERYREGISTER_ZCRX_CTRLREGISTER_BPF_FILTERUring.registerBpfFilterSetup flags (9 new):
SINGLE_ISSUERDEFER_TASKRUNDEFER_TASKRUNSINGLE_ISSUERNO_MMAPREGISTERED_FD_ONLYNO_MMAPpathNO_SQARRAYHYBRID_IOPOLLCQE_MIXEDSQE_MIXEDSQ_REWINDExisting
SQE128/CQE32rings now have the storage-path supportthey were missing.
Storage-path changes:
SubmissionQueuenow selects the SQE stride (64 B or 128 B) at setuptime based on
SetupFlags.SQE128. The previous code hardcodedentries * 64, leaving the trailingcmd[]payload of SQE128 ringsoutside the mapped region —
NOP128andURING_CMD128were unusable.SQE_MIXEDrings, a newnext128()accessor reserves twocontiguous 64-byte slots and inserts a
CQE_SKIP_SUCCESSNOP padderif a 128-byte SQE would straddle the ring wrap.
SQ_REWINDrings,head()returns 0 unconditionally,flushTail()becomes a no-op, andlocalTailresets after eachacknowledged batch.
CompletionQueuewalks variable-stride CQEs: a per-ringshiftdoubles the cqes slice for
CQE32rings, andpopFront/peekAtadvance by 1 or 2 ring slots depending on the per-CQE
F_32flag.Examples:
examples/zerocopy_echo— TCP echo overIORING_OP_SEND_ZC,asserts the kernel emits both the data CQE and the trailing
notification CQE.
examples/futex_pingpong— two threads, io_uringFUTEX_WAITwokenvia legacy
futex(2) FUTEX_WAKE_PRIVATE.Build / dev infra:
dub.sdlpackage files now settargetPath "build"so examplebinaries land in
build/instead of in the example root.build/is git-ignored.
dub). Happy to drop this commit if upstream prefers not to carry it.
Test plan
dub test -- --threads=1— all 56 unit tests pass serially.The default parallel
sillyrunner has transient-EMFILEflakeson
io_uring_setupwhen many tests open rings at once (a knownsilly-runner artefact, not a regression). Serial run is stable.dub build --config=betterC— green; the new APIs remainBetterC-compatible.
dub build --config=during-test-betterC— green.dub buildin each example —examples/echo_server,examples/zerocopy_echo,examples/futex_pingpongall build andland their binaries in their per-example
build/directory.tests.register proberuns and confirms the6.18 host enumerates all new opcodes.
Test environment
The branch was developed and tested on:
6.18.17x86_64flake.nixintroduced in this PR(
nixpkgsrev549bd84)Sources used for grounding
Every opcode number, register opcode number, struct field, flag bit,
and feature bit was cross-checked against:
torvalds/linuxat v7.1-rc4 (io_uring/,include/uapi/linux/io_uring.h,include/linux/io_uring*.h).axboe/liburingat v2.9(
src/include/liburing/io_uring.hplus thesrc/queue.c/src/register.creference implementations of the new helpers).Wrong opcode numbers are the single highest-risk class of bug in this
kind of catch-up work — they silently misroute requests on real
kernels — so each commit was reviewed against the kernel header
before it was finalised.
LLM disclosure
The bulk of this PR was drafted with Claude Opus 4.7 (high reasoning,
1M-token context), with a follow-up review pass by GPT-5.5 Codex (high
reasoning). Throughout, the assistants were grounded against the local kernel
and liburing git trees noted above rather than relying on training data. New
prep helpers and register wrappers have focused functional or ABI-surface tests
where the operation can be exercised on a stock test host; hardware- or
configuration-dependent paths are covered by compile-time layout checks and
structured-call smoke tests.