Liburing 2026.10.29
static
YoSTEALTH/Liburing

Ring and setup

Create, configure and tear down a Ring; probe what the kernel supports.

class Probe()#

Result of io_uring_get_probe / io_uring_get_probe_ring: which io_uring operations the kernel supports.

Example

>>> probe = io_uring_get_probe()
>>> probe.last_op, probe.ops_len
(64, 65)
>>> io_uring_opcode_supported(probe, io_uring_op.IORING_OP_NOP)
True
>>> io_uring_free_probe(probe)

Note

  • Can not be created directly (NotImplementedError), the io_uring_get_probe* functions make it.
  • Reading last_op / ops_len after io_uring_free_probe raises MemoryError.
  • Needs Linux 5.6+ (IORING_REGISTER_PROBE): io_uring_get_probe* give None before. An unsupported opcode is not refused at the submit, its completion fails with OSError: EINVAL; probe() gives the whole table as a dict.

property last_op: int#

The highest IORING_OP_* number the kernel knows (not every one below it need be supported: ask io_uring_opcode_supported).

property ops_len: int#

How many opcodes the probe reports on (last_op + 1: 0 to last_op).

class Param()#

io_uring setup parameters (struct io_uring_params): set flags and the sizes before io_uring_queue_init_params / io_uring_setup, read back what the kernel gave after.

Example

>>> ring = Ring()
>>> param = Param()
>>> param.flags = IORING_SETUP_CQSIZE
>>> param.cq_entries = 256
>>> io_uring_queue_init_params(8, ring, param)
0
>>> param.sq_entries, param.cq_entries
(8, 256)
>>> io_uring_queue_exit(ring)

Note

  • Takes no arguments (TypeError for any). features is read only.
  • Sizes: the ring gets at least entries submission entries, rounded up to a power of two (at most IORING_MAX_ENTRIES, 32768), and twice as many completion entries; IORING_SETUP_CQSIZE uses cq_entries instead (more than entries, rounded up to a power of two, at most 65536). Larger values fail with OSError: EINVAL, or are clamped with IORING_SETUP_CLAMP. The kernel writes the final sizes back.
  • sq_thread_cpu, sq_thread_idle and wq_fd only matter with IORING_SETUP_SQPOLL / IORING_SETUP_SQ_AFF / IORING_SETUP_ATTACH_WQ in flags.

property sq_entries: int#

Submission queue entries (filled in by the kernel: entries rounded up to a power of two).

property cq_entries: int#

Completion queue entries (set it with IORING_SETUP_CQSIZE, else the kernel fills it in: twice sq_entries). More room here lets many requests complete before you reap them without an overflow.

property flags: int#

IORING_SETUP_* flags: yours before the setup; after it also what liburing added (IORING_SETUP_NO_SQARRAY), as Ring.flags. A flag this kernel lacks: UnsupportedError (EINVAL).

property sq_thread_cpu: int#

CPU the IORING_SETUP_SQPOLL thread is bound to (with IORING_SETUP_SQ_AFF; IORING_SETUP_SQ_AFF without IORING_SETUP_SQPOLL is OSError: EINVAL). A cgroup cpuset change may move it.

property sq_thread_idle: int#

Milliseconds the IORING_SETUP_SQPOLL thread polls an empty queue before it sleeps (0: never sleeps, one CPU busy). Asleep, it sets IORING_SQ_NEED_WAKEUP and the next io_uring_submit wakes it with a system call; kept busy, it never sleeps and submits cost no system call.

property features: int#

IORING_FEAT_* flags the kernel reports (read only), the same as Ring.features after the setup (its doc lists them).

property wq_fd: int#

ring_fd of an existing ring whose async worker pool this ring shares instead of making its own (with IORING_SETUP_ATTACH_WQ; both with IORING_SETUP_SQPOLL: its polling thread too). Not a live ring: OSError: ENXIO.

class Ring(auto_submit=None)#

I/O URing

Example

>>> ring = Ring()
>>> io_uring_queue_init(8, ring)
0
>>> io_uring_queue_exit(ring)

Note

  • One ring per thread: a Ring belongs to the thread that initialized it (io_uring_queue_init*), using it (or a CQE / SQE from it) from another thread raises RuntimeError. Threads talk to each other's rings with io_uring_prep_msg_ring. A Cqe or CqeBatch belongs to the thread of its first wait or peek, an Sqe template to the thread of its first prep or put_sqe (another thread: RuntimeError): make one per thread.
  • The waits (io_uring_wait_cqe*, io_uring_submit_and_wait*) let other threads run while the kernel sleeps.
  • user_data can be any 64-bit value, 0 and duplicates included: at the submit the ring keeps it in a table of in-flight requests and gives the kernel the entry's index (an SQE read after the submit shows that index). cqe.user_data gives your value back, until the completion is consumed (io_uring_cqe_seen, io_uring_cq_advance). Cancels, timeout and poll removes / updates and io_uring_register_sync_cancel take your value too: the first in-flight request with it, also with IORING_ASYNC_CANCEL_ALL (each request has its own entry; to cancel a group use IORING_ASYNC_CANCEL_FD | IORING_ASYNC_CANCEL_ALL, or one cancel per request).
  • A message from io_uring_prep_msg_ring* reports the sender's data as user_data: it is sent with bit 63 set (no request's entry has it), so data must be below 2**63. A message from code that is not Liburing (data without bit 63) would read as one of the ring's requests.
  • IOSQE_CQE_SKIP_SUCCESS (no completion on success): fine in a link chain (IOSQE_IO_LINK / IOSQE_IO_HARDLINK), whose end's completion releases what the skipped requests hold, but not on the chain's last request (the last SQE of a submit ends a chain): ValueError at the submit, nothing sent. Alone, a request with it may hold nothing (no buffer, path, ...: ValueError) and a failed one reports user_data 0.

property auto_submit: bool#

True: taking an entry from a full submission queue (a prep on Sqe(ring), io_uring_get_sqe, put_sqe) first submits what is queued, as io_uring_submit, and takes the freed slot; except when the last queued request links to the next one (IOSQE_IO_LINK / IOSQE_IO_HARDLINK): a submit would end its chain there, so that call finds the queue full as with False. False (the default): the prep returns False (io_uring_get_sqe: None), you submit and try again.

property flags: int#

IORING_SETUP_* flags the ring was set up with.

property ring_fd: int#

The ring's file descriptor; -1 when it has none: not set up (before io_uring_queue_init*, after a failed one, after io_uring_queue_exit) or after io_uring_close_ring_fd, which leaves only the registered one (enter_ring_fd).

property features: int#

IORING_FEAT_* flags the kernel reported at setup: what this kernel can do (the version each came in).

Flags

Note

property int_flags: int#

liburing's internal flags (IORING_INT_FLAG_*).

property enter_ring_fd: int#

The file descriptor used for io_uring_enter (differs from ring_fd with a registered ring fd); -1 when the ring is not set up.

probe() -> dict[str, bool]#

Probe your system to find out which io_uring operations are available: a dict of every IORING_OP_* name (up to, not including, IORING_OP_LAST) and whether the running kernel supports it.

Type

return dict[str, bool]

Example #1

>>> probe()
{'IORING_OP_NOP': True, 'IORING_OP_READV': True, 'IORING_OP_WRITEV': True, ...}

Example #2

# or: one operation
>>> probe()['IORING_OP_READ']
True

Note

  • Needs Linux 5.6+ (IORING_REGISTER_PROBE), else RuntimeError. Opcodes after the kernel's Probe.last_op are False. Submitting an unsupported opcode is not refused: its completion fails with OSError: EINVAL.

io_uring_get_probe_ring(ring: Ring) -> Probe#

Probe which io_uring operations ring's kernel supports; use with io_uring_opcode_supported.

Type

return Probe

Example

Setup used to run this example
    'io_uring_get_probe_ring', 'io_uring_ring_dontfork', 'io_uring_queue_exit', 'io_uring_register',
    'io_uring_set_iowait', 'io_uring_sq_ready', 'io_uring_sq_space_left', 'io_uring_sqring_wait', 'io_uring_get_sqe',
    'io_uring_get_events', 'io_uring_close_ring_fd', 'io_uring_register_ring_fd', 'io_uring_unregister_ring_fd',
    'io_uring_register_personality', 'io_uring_unregister_personality', 'io_uring_unregister_files',
    'io_uring_register_eventfd', 'io_uring_unregister_eventfd', 'io_uring_register_buffers', 'io_uring_buf_ring_head',
    'io_uring_register_iowq_aff', 'io_uring_register_iowq_max_workers', 'io_uring_cq_has_overflow',
    'io_uring_cq_eventfd_enabled', 'io_uring_cq_eventfd_toggle', 'BufReg', 'Napi', 'ClockRegister', 'Bpf', 'SQE',
    'SiginfoT', 'io_uring_prep_waitid', 'io_uring_prep_cancel', 'io_uring_prep_cancel64', 'ZcrxIfqReg',
)  # fmt: skip
def ring(ns):
    make_ring(ns)

Note

io_uring_get_probe() -> Probe#

Like io_uring_get_probe_ring, without a ring (a temporary one is made for it).

Type

return Probe

Example

Note

io_uring_free_probe(probe: Probe) -> None#

Free probe (it is freed on garbage collection too). Calling it again is harmless.

Example

>>> probe = io_uring_get_probe()
>>> io_uring_free_probe(probe)
>>> io_uring_free_probe(probe)  # again: nothing happens

io_uring_opcode_supported(probe: Probe, op: int) -> bool#

Whether the kernel supports the IORING_OP_* opcode op (from probe).

Example

io_uring_queue_init_mem(entries: int, ring: Ring, p: Param, buf: Buffer | None = None) -> int#

Setup Ring in memory you supply (IORING_SETUP_NO_MMAP). Returns the bytes of buf it used. RuntimeError if ring is already initialized.

Example

>>> import mmap
>>> ring = Ring()
>>> buf = mmap.mmap(-1, 2 * 1024 * 1024)  # page aligned and zeroed
>>> try:
...     io_uring_queue_init_mem(8, ring, Param(), buf)
...     # do stuff ...
... finally:
...     io_uring_queue_exit(ring)
...
8192

Note

io_uring_queue_init_params(entries: int, ring: Ring, param: Param) -> int#

Setup Ring like io_uring_queue_init but with a Param, which lets the caller pass setup flags in and read back what the kernel actually gave.

Note

Example

>>> ring = Ring()
>>> param = Param()
>>> param.flags = IORING_SETUP_SINGLE_ISSUER
>>> try:
...     io_uring_queue_init_params(1024, ring, param)
...     param.sq_entries
...     # do stuff ...
... finally:
...     io_uring_queue_exit(ring)
...
0
1024

io_uring_queue_init(entries: int, ring: Ring, flags: int | None = None) -> int#

Setup Ring Submission & Completion Queues. flags are IORING_SETUP_* (default None: none). Returns 0; RuntimeError if ring is already initialized.

entries is the submission queue size (rounded up to a power of 2, max 32768): it caps one submit batch, not the requests in flight. The completion queue gets 2x as many (io_uring_queue_init_params to pick).

Flags

Example

>>> ring = Ring()
>>> try:
...     io_uring_queue_init(1024, ring)
...     # do stuff ...
... finally:
...     io_uring_queue_exit(ring)
...
0

Note

  • A flag the kernel lacks raises UnsupportedError (on kernels with IORING_REGISTER_QUERY, else OSError: EINVAL). OSError: EPERM: io_uring is disabled (/proc/sys/kernel/io_uring_disabled), or IORING_SETUP_SQPOLL without privileges before Linux 5.13; OSError: ENOMEM: out of locked memory (RLIMIT_MEMLOCK, kernels before 5.12).
  • IORING_SETUP_IOPOLL allows few opcodes (NOP, READ*/WRITE*, URING_CMD, ...) and needs the device set up for polling (NVMe: poll_queues).
  • IORING_SETUP_DEFER_TASKRUN rings must enter the kernel regularly (a submit or wait, or io_uring_get_events) or completions stall; io_uring_resize_rings needs it.

io_uring_queue_mmap(fd: int, ring: Ring, param: Param) -> int#

Map the rings of an io_uring file descriptor fd (from io_uring_setup, with the same param) into ring. Returns 0; OSError on failure. io_uring_queue_init* does both steps: this is for a ring set up by hand.

Example

>>> param = Param()
>>> fd = io_uring_setup(8, param)
>>> ring = Ring()
>>> io_uring_queue_mmap(fd, ring, param)
0

io_uring_ring_dontfork(ring: Ring) -> int#

Apply madvise(MADV_DONTFORK) to ring's SQE array, SQ ring and CQ ring, so they are not mapped into a child process after fork().

By default the rings are MAP_SHARED, so a forked child shares them with the parent and both could submit/reap on the same queue. After this call the child simply has no such memory: touching ring in the child (e.g. io_uring_get_sqe) crashes it with SIGSEGV. The parent is not affected and keeps using ring as normal.

Note

  • ring must be initialized, else raises RuntimeError.
  • Call it before the fork (os.fork, subprocess, multiprocessing with the fork start method); it does nothing for children already forked. Returns 0; OSError on failure.

Example

>>> io_uring_ring_dontfork(ring)
0
Setup used to run this example
    'io_uring_get_probe_ring', 'io_uring_ring_dontfork', 'io_uring_queue_exit', 'io_uring_register',
    'io_uring_set_iowait', 'io_uring_sq_ready', 'io_uring_sq_space_left', 'io_uring_sqring_wait', 'io_uring_get_sqe',
    'io_uring_get_events', 'io_uring_close_ring_fd', 'io_uring_register_ring_fd', 'io_uring_unregister_ring_fd',
    'io_uring_register_personality', 'io_uring_unregister_personality', 'io_uring_unregister_files',
    'io_uring_register_eventfd', 'io_uring_unregister_eventfd', 'io_uring_register_buffers', 'io_uring_buf_ring_head',
    'io_uring_register_iowq_aff', 'io_uring_register_iowq_max_workers', 'io_uring_cq_has_overflow',
    'io_uring_cq_eventfd_enabled', 'io_uring_cq_eventfd_toggle', 'BufReg', 'Napi', 'ClockRegister', 'Bpf', 'SQE',
    'SiginfoT', 'io_uring_prep_waitid', 'io_uring_prep_cancel', 'io_uring_prep_cancel64', 'ZcrxIfqReg',
)  # fmt: skip
def ring(ns):
    make_ring(ns)

io_uring_queue_exit(ring: Ring) -> None#

Tear down ring: unmap its Submission & Completion Queues and close its fd.

Note

  • Raises RuntimeError if ring is not initialized or already exited.
  • Requests still in flight are cancelled and waited for first.
  • The kernel frees the ring later, from a workqueue: what it locked stays locked a few ms after this returns. Registered buffers count against RLIMIT_MEMLOCK (a buffer inside a huge page pins all of it, 2 MiB), as the rings did before Linux 5.12, so setting up rings with them in a tight loop can hit OSError: ENOMEM.

Example

>>> io_uring_queue_exit(ring)
Setup used to run this example
    'io_uring_get_probe_ring', 'io_uring_ring_dontfork', 'io_uring_queue_exit', 'io_uring_register',
    'io_uring_set_iowait', 'io_uring_sq_ready', 'io_uring_sq_space_left', 'io_uring_sqring_wait', 'io_uring_get_sqe',
    'io_uring_get_events', 'io_uring_close_ring_fd', 'io_uring_register_ring_fd', 'io_uring_unregister_ring_fd',
    'io_uring_register_personality', 'io_uring_unregister_personality', 'io_uring_unregister_files',
    'io_uring_register_eventfd', 'io_uring_unregister_eventfd', 'io_uring_register_buffers', 'io_uring_buf_ring_head',
    'io_uring_register_iowq_aff', 'io_uring_register_iowq_max_workers', 'io_uring_cq_has_overflow',
    'io_uring_cq_eventfd_enabled', 'io_uring_cq_eventfd_toggle', 'BufReg', 'Napi', 'ClockRegister', 'Bpf', 'SQE',
    'SiginfoT', 'io_uring_prep_waitid', 'io_uring_prep_cancel', 'io_uring_prep_cancel64', 'ZcrxIfqReg',
)  # fmt: skip
def ring(ns):
    make_ring(ns)

io_uring_resize_rings(ring: Ring, p: Param) -> int#

Resize ring's submission and completion queues to what p says (p.sq_entries, p.cq_entries with IORING_SETUP_CQSIZE). Returns 0; UnsupportedError before Linux 6.13.

Note

Example

>>> p = Param()
>>> p.sq_entries = 64
>>> p.cq_entries = 128
>>> p.flags = IORING_SETUP_CQSIZE
>>> io_uring_resize_rings(ring, p)
0
Setup used to run this example
def ring_defer_taskrun(ns):
    make_ring(ns, flags=IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN)

io_uring_enter(fd: int, to_submit: int, min_complete: int, flags: int, sig: SigsetT | None = None) -> int#

The raw io_uring_enter(2) system call on fd: submit to_submit entries and wait for min_complete completions, flags are IORING_ENTER_*, sig an optional SigsetT. Returns the number of entries consumed (0 when to_submit is 0 or the queue is empty; on an IORING_SETUP_SQPOLL ring just to_submit, the kernel thread does the submitting).

Flags

Example

>>> io_uring_enter(ring.ring_fd, 0, 1, IORING_ENTER_GETEVENTS, None)
0
Setup used to run this example
def ring_with_completion(ns):
    ring = make_ring(ns)
    queue_nops(ring, 1)
    io_uring_submit_and_wait(ring, 1)

Note

io_uring_enter2(fd: int, to_submit: int, min_complete: int, flags: int, sz: int, arg: int | None = None) -> int#

io_uring_enter with the size sz of the argument at address arg (IORING_ENTER_EXT_ARG: a struct io_uring_getevents_arg, or with IORING_ENTER_EXT_ARG_REG an offset into a registered region); arg default None: no argument.

Example

>>> io_uring_enter2(ring.ring_fd, 0, 1, IORING_ENTER_GETEVENTS, 0)
0
Setup used to run this example
def ring_with_completion(ns):
    ring = make_ring(ns)
    queue_nops(ring, 1)
    io_uring_submit_and_wait(ring, 1)

Note

io_uring_setup(entries: int, p: Param) -> int#

The raw io_uring_setup(2) system call: makes a ring of entries as p says, p is filled in with what the kernel gave. Returns the file descriptor (use it with io_uring_queue_mmap).

Example

>>> p = Param()
>>> fd = io_uring_setup(8, p)

Note

io_uring_set_iowait(ring: Ring, enable_iowait: bool) -> int#

Turn the iowait accounting of a task waiting for completions on (True, what a new ring does) or off (False). Returns 0; UnsupportedError without IORING_FEAT_NO_IOWAIT (Linux 6.15+).

Example

>>> io_uring_set_iowait(ring, False)
0
Setup used to run this example
    'io_uring_get_probe_ring', 'io_uring_ring_dontfork', 'io_uring_queue_exit', 'io_uring_register',
    'io_uring_set_iowait', 'io_uring_sq_ready', 'io_uring_sq_space_left', 'io_uring_sqring_wait', 'io_uring_get_sqe',
    'io_uring_get_events', 'io_uring_close_ring_fd', 'io_uring_register_ring_fd', 'io_uring_unregister_ring_fd',
    'io_uring_register_personality', 'io_uring_unregister_personality', 'io_uring_unregister_files',
    'io_uring_register_eventfd', 'io_uring_unregister_eventfd', 'io_uring_register_buffers', 'io_uring_buf_ring_head',
    'io_uring_register_iowq_aff', 'io_uring_register_iowq_max_workers', 'io_uring_cq_has_overflow',
    'io_uring_cq_eventfd_enabled', 'io_uring_cq_eventfd_toggle', 'BufReg', 'Napi', 'ClockRegister', 'Bpf', 'SQE',
    'SiginfoT', 'io_uring_prep_waitid', 'io_uring_prep_cancel', 'io_uring_prep_cancel64', 'ZcrxIfqReg',
)  # fmt: skip
def ring(ns):
    make_ring(ns)

Note

  • With it on, a wait with requests pending counts as I/O wait (iowait in load and CPU statistics) and keeps the CPU out of deeper sleep states; off is for non-storage work and power. Off, the wait calls pass IORING_ENTER_NO_IOWAIT to io_uring_enter.

io_uring_memory_size(entries: int, flags: int) -> int#

Bytes the kernel needs to map a ring of entries with flags. OSError (EINVAL) for entries 0 or over the limit without IORING_SETUP_CLAMP.

Example

>>> io_uring_memory_size(8, 0)
8192

io_uring_memory_size_params(entries: int, p: Param) -> int#

Bytes the kernel needs to map a ring of entries set up as p says (p.cq_entries counts with IORING_SETUP_CQSIZE): what to give io_uring_queue_init_mem. Errors like io_uring_memory_size.

Example

>>> io_uring_memory_size_params(8, Param())
8192

io_uring_mlock_size(entries: int, flags: int) -> int#

Bytes of locked memory (RLIMIT_MEMLOCK) a ring of entries with flags needs.

Example

>>> io_uring_mlock_size(8, 0)
0

Note

  • 0 on kernels that no longer count rings against RLIMIT_MEMLOCK (Linux 5.12+).

io_uring_mlock_size_params(entries: int, p: Param) -> int#

Bytes of locked memory (RLIMIT_MEMLOCK) a ring of entries set up as p says needs (p.cq_entries counts with IORING_SETUP_CQSIZE).

Example

Note

  • 0 on kernels that no longer count rings against RLIMIT_MEMLOCK (Linux 5.12+).

liburing_version_major() -> int#

Example

>>> liburing_version_major()
2

Note

liburing_version_minor() -> int#

Example

>>> liburing_version_minor()
15

Note

liburing_version_check(major: int, minor: int) -> bool#

Liburing Version Check: True if the liburing in use is older than major.minor, else False.

Example

>>> liburing_version_check(99, 0)
True
>>> liburing_version_check(2, 0)
False

Note

io_uring_clone_buffers_offset(dst_ring: Ring, src_ring: Ring, dst_off: int, src_off: int, nr: int, flags: int | None = None) -> int#

Like io_uring_clone_buffers, but clones nr buffers from src_off of src_ring to dst_off of dst_ring.

Flags

Note

  • Linux 6.13+. Returns 0. dst_off, src_off and nr all 0: the whole table, as io_uring_clone_buffers.
  • OSError: EBUSY when dst_ring has buffers registered and IORING_REGISTER_DST_REPLACE is not set, ENXIO when src_ring has none.
  • dst_ring holds every buffer registered on src_ring (not only the cloned ones) until its own io_uring_unregister_buffers (or its exit), as io_uring_clone_buffers does.
  • Linux 7.2+ (kernel bug): cloning some of src_ring's buffers, when one shares a huge page with a buffer not cloned, miscounts locked memory. Unregistering (or exiting) src_ring first stops counting memory that dst_ring still pins; then dst_ring uncounts it again: the user's count goes below 0, and every small buffer registration fails with OSError: ENOMEM until a reboot. Clone the whole table (io_uring_clone_buffers), or keep each registered buffer off shared huge pages (e.g. its own mmap.mmap with madvise(mmap.MADV_NOHUGEPAGE)).

io_uring_clone_buffers(dst_ring: Ring, src_ring: Ring, flags: int | None = None) -> int#

Register the buffers registered on src_ring with dst_ring too (the kernel shares them).

Example

>>> bufs = [bytearray(4096)]
>>> io_uring_register_buffers(src, bufs)
0
>>> io_uring_clone_buffers(dst, src)
0
>>> io_uring_unregister_buffers(src)  # `dst` still uses them: `bufs` stay locked
0
>>> io_uring_unregister_buffers(dst)  # released
0
Setup used to run this example
def two_rings(ns):
    make_ring(ns, 'src', 8)
    make_ring(ns, 'dst', 8)

Note

  • Linux 6.12+. Returns 0; afterwards both rings have the same registered buffers (as if io_uring_register_buffers had been called on dst_ring with them).
  • OSError: EBUSY when dst_ring already has buffers registered (unregister them first, or clone with IORING_REGISTER_DST_REPLACE, see io_uring_clone_buffers_offset), ENXIO when src_ring has none.
  • flags defaults to 0 (C's __io_uring_clone_buffers): the rings are found by their real file descriptors, so these must still be open; IORING_REGISTER_SRC_REGISTERED uses src_ring's registered ring fd instead, which only the thread that registered it can.
  • Both rings must belong to the calling thread.
  • dst_ring holds src_ring's registered buffers until its own io_uring_unregister_buffers (or its exit): each ring keeps them locked (no resize) while the kernel can use them through it.

io_uring_enable_rings(ring: Ring) -> int#

Enable a ring set up with IORING_SETUP_R_DISABLED.

Example

>>> ring = Ring()
>>> param = Param()
>>> param.flags = IORING_SETUP_R_DISABLED
>>> io_uring_queue_init_params(8, ring, param)
0
>>> io_uring_enable_rings(ring)
0
>>> io_uring_queue_exit(ring)

Note

  • Linux 5.10+. Returns 0; OSError: EBADFD when the ring is not disabled. Nothing can be submitted before it; registrations before it are unrestricted, after it they (and submissions) are checked against the restrictions registered (io_uring_register_restrictions).

io_uring_close_ring_fd(ring: Ring) -> int#

Close the ring's real file descriptor, keeping only the registered one (io_uring_register_ring_fd first).

Example

>>> io_uring_register_ring_fd(ring)
1
>>> io_uring_close_ring_fd(ring)
1
Setup used to run this example
    'io_uring_get_probe_ring', 'io_uring_ring_dontfork', 'io_uring_queue_exit', 'io_uring_register',
    'io_uring_set_iowait', 'io_uring_sq_ready', 'io_uring_sq_space_left', 'io_uring_sqring_wait', 'io_uring_get_sqe',
    'io_uring_get_events', 'io_uring_close_ring_fd', 'io_uring_register_ring_fd', 'io_uring_unregister_ring_fd',
    'io_uring_register_personality', 'io_uring_unregister_personality', 'io_uring_unregister_files',
    'io_uring_register_eventfd', 'io_uring_unregister_eventfd', 'io_uring_register_buffers', 'io_uring_buf_ring_head',
    'io_uring_register_iowq_aff', 'io_uring_register_iowq_max_workers', 'io_uring_cq_has_overflow',
    'io_uring_cq_eventfd_enabled', 'io_uring_cq_eventfd_toggle', 'BufReg', 'Napi', 'ClockRegister', 'Bpf', 'SQE',
    'SiginfoT', 'io_uring_prep_waitid', 'io_uring_prep_cancel', 'io_uring_prep_cancel64', 'ZcrxIfqReg',
)  # fmt: skip
def ring(ns):
    make_ring(ns)

Note

  • Needs IORING_FEAT_REG_REG_RING (Linux 6.3+). Returns 1. The ring stays open, reachable only through the registered index: every liburing call keeps working, and nothing (os.close, close_range) can close it by mistake. Only the thread that registered the fd can use the ring then.
Generated by tools/setu_docs.py from the docstrings of Liburing 2026.10.29 (CPython 3.14+).