Liburing 2026.10.29
static
YoSTEALTH/Liburing

Register and buffers

Register files, buffers, rings and restrictions; provided buffer rings.

class FileIndex(list)#

Example

>>> ids = FileIndex([-1, -1, 3])
>>> ids[1]
-1
>>> ids.update([4, 5, 6])
>>> ids[1]
5
>>> list(ids)
[4, 5, 6]
>>> for id in ids:
...     id  # do something e.g.`if id != -1: os.close(id)`
...
4
5
6

Note

  • Makes copy of the list provided & shares it with io_uring, so modifying python list will not effect FileIndex already submitting.
  • For io_uring_register_files / _files_update: each slot is a file descriptor to register, -1 an empty (sparse) slot a later update or a *_direct request (IORING_FILE_INDEX_ALLOC) may fill. A request uses a registered file by its slot index as fd with IOSQE_FIXED_FILE in sqe.flags.

method update(list: Sequence[int]) -> None#

Replace the file descriptors with list, which must have the same length (ValueError otherwise).

Example

>>> ids = FileIndex([-1, -1, -1])
>>> ids.update([4, 5, 6])
>>> list(ids)
[4, 5, 6]

class FileIndexIter#

Iterator over a FileIndex, from iter(ids) (a for loop): its own position, so loops over one FileIndex don't interfere.

class RegWait()#

One wait argument (struct io_uring_reg_wait), for io_uring_submit_and_wait_reg: it is copied into a memory region registered with IORING_MEM_REGION_REG_WAIT_ARG (see MemRegionReg), at index * RegWait.SIZE.

Example

>>> mem = mmap.mmap(-1, 4096)
>>> region = MemRegionReg()
>>> region.flags = IORING_MEM_REGION_REG_WAIT_ARG
>>> region.region = mem
>>> io_uring_register_region(ring, region)  # `ring` set up with `IORING_SETUP_R_DISABLED`
0
>>> io_uring_enable_rings(ring)
0

>>> wait = RegWait()
>>> wait.timeout = 0.5            # seconds, `None` for none
>>> wait.flags = IORING_REG_WAIT_TS
>>> mem[0 : RegWait.SIZE] = wait.to_bytes()
>>> io_uring_prep_nop(io_uring_get_sqe(ring))
True
>>> io_uring_submit_and_wait_reg(ring, cqe, 1, 0)   # the wait argument at index 0
1
Setup used to run this example
def ring_disabled(ns):
    make_ring(ns, entries=8, flags=IORING_SETUP_R_DISABLED)
    ns['cqe'] = Cqe()

Note

method to_bytes() -> bytes#

The entry as bytes (RegWait.SIZE of them) to copy into the registered memory region.

property timeout: float | None#

The wait time in seconds (None for none); needs IORING_REG_WAIT_TS in flags.

property min_wait_usec: int#

The minimum wait in microseconds (min_wait_usec).

property flags: int#

IORING_REG_WAIT_* flags.

value SIZE = 64#

class Restriction()#

What a ring set up with IORING_SETUP_R_DISABLED may do once io_uring_enable_rings is called.

Example

>>> res = Restriction()
>>> res.add(IORING_RESTRICTION_SQE_OP, io_uring_op.IORING_OP_NOP)
>>> res.add(IORING_RESTRICTION_SQE_FLAGS_ALLOWED, IOSQE_IO_LINK)
>>> io_uring_register_restrictions(ring, res)  # `ring` set up with `IORING_SETUP_R_DISABLED`
0
>>> io_uring_enable_rings(ring)
0
Setup used to run this example
def ring_disabled(ns):
    make_ring(ns, entries=8, flags=IORING_SETUP_R_DISABLED)
    ns['cqe'] = Cqe()

method add(opcode: int, value: int) -> None#

Allows value for opcode (IORING_RESTRICTION_*): an sqe opcode, register opcode or sqe flags.

class BufReg()#

Registration of a buffer ring you set up yourself (io_uring_register_buf_ring); io_uring_setup_buf_ring does this for you.

Example

>>> reg = BufReg()
>>> reg.ring = mmap.mmap(-1, 8 * 16)   # 16 bytes per entry, page aligned
>>> reg.ring_entries = 8
>>> reg.bgid = 1
>>> io_uring_register_buf_ring(ring, reg)
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

  • ring is the memory itself, not its address: the ring keeps it locked from the register until io_uring_unregister_buf_ring or its exit (it can not be resized, closed or freed meanwhile).

property ring: Buffer | None#

The buffer ring's memory: a writable, page-aligned buffer (mmap.mmap) of at least ring_entries * 16 bytes, or None (with IOU_PBUF_RING_MMAP the kernel allocates it).

Type

return Buffer | None

property ring_entries: int#

The number of entries in the ring (a power of 2).

property bgid: int#

The buffer group id.

property flags: int#

IOU_PBUF_RING_* flags.

property min_left: int#

min_left of struct io_uring_buf_reg.

class SyncCancelReg()#

What io_uring_register_sync_cancel cancels: by addr (user_data), fd, or both with flags.

Example

>>> reg = SyncCancelReg()
>>> reg.addr = 123                      # the user_data of the request (a pending read here)
>>> reg.timeout = 1.0                   # seconds to wait (default: until done)
>>> io_uring_register_sync_cancel(ring, reg)
0
Setup used to run this example
def pending_read(ns):
    '''A ring with a read (user_data 123) waiting on an empty pipe.'''
    ring = make_ring(ns)
    r, _w = os.pipe()
    sqe = io_uring_get_sqe(ring)
    io_uring_prep_read(sqe, r, bytearray(1))
    sqe.user_data = 123
    io_uring_submit(ring)

property addr: int#

The user_data of the request to cancel (with IORING_ASYNC_CANCEL_USERDATA, or by default).

property fd: int#

The file descriptor to cancel by (with IORING_ASYNC_CANCEL_FD); -1 by default.

property flags: int#

IORING_ASYNC_CANCEL_* flags.

property opcode: int#

The opcode to cancel by (with IORING_ASYNC_CANCEL_OP).

property timeout: float | None#

Seconds to wait for the cancellation (None for no limit).

class Napi()#

Busy polling of the network device for a ring (io_uring_register_napi; the old settings are written back).

Example

>>> napi = Napi()
>>> napi.busy_poll_to = 50      # microseconds
>>> napi.prefer_busy_poll = True
>>> io_uring_register_napi(ring, napi)
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)

property busy_poll_to: int#

The busy poll timeout in microseconds.

property prefer_busy_poll: bool#

Prefer busy polling over interrupts.

class Query(op=None)#

Asks the kernel what io_uring supports (io_uring_register_query; needs no ring).

Example

>>> query = Query()                   # IO_URING_QUERY_OPCODES
>>> io_uring_register_query(query)
0
>>> query.data["nr_request_opcodes"]  # the kernel's count
65
>>> query = Query(IO_URING_QUERY_SCQ)
>>> io_uring_register_query(query)
0
>>> query.data
{'hdr_size': 64, 'hdr_alignment': 64}

Note

property op: int#

The query this was made for (IO_URING_QUERY_*).

property result: int#

The error the kernel put in the header (0 on success).

property data: dict[str, int]#

The fields of the C struct for op by name (all 0 until it is registered).

Type

return dict[str, int]

class ZcrxCtrl(zcrx_id, op)#

Control of a zero-copy receive instance (io_uring_register_zcrx_ctrl).

Example

>>> ctrl = ZcrxCtrl(zcrx_id, ZCRX_CTRL_FLUSH_RQ)
>>> io_uring_register_zcrx_ctrl(ring, ctrl)
# or export it to share with another ring
>>> ctrl = ZcrxCtrl(zcrx_id, ZCRX_CTRL_EXPORT)
>>> io_uring_register_zcrx_ctrl(ring, ctrl)
>>> ctrl.zcrx_fd
Setup used to run this example
def zcrx(ns):
    make_ring(ns)
    ns['zcrx_id'] = 0  # what `io_uring_register_ifq` gave (`ZcrxIfqReg.zcrx_id`)

Note

  • zcrx_id is the ZcrxIfqReg.zcrx_id of io_uring_register_ifq; needs a registered instance.
  • Not tested with a real device (needs a NIC whose driver supports zero-copy receive, like mlx5, bnxt, gve): only the fields and the error without a device are.

property zcrx_id: int#

The zero-copy receive instance the control is for.

property op: int#

The control operation (ZCRX_CTRL_*).

property zcrx_fd: int#

The file descriptor ZCRX_CTRL_EXPORT wrote.

class ZcrxIfqReg()#

Zero-copy receive queue registration (io_uring_register_ifq); needs a network device that supports it.

Example

>>> reg = ZcrxIfqReg()
>>> reg.if_idx = socket.if_nametoindex("eth0")
>>> reg.if_rxq = 0
>>> reg.rq_entries = 4096
>>> reg.area = mmap.mmap(-1, 64 * 4096)    # also `area_len`
>>> reg.region = mmap.mmap(-1, 4 * 4096)   # the refill queue; also `region_size`, `region_flags`
>>> io_uring_register_ifq(ring, reg)
0
>>> reg.zcrx_id
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

  • area and region are the memory itself, not addresses: the ring keeps them locked from the register until its exit (the device writes into area). region = None: the kernel allocates the refill queue.
  • Not tested with a real device (needs a NIC whose driver supports zero-copy receive, like mlx5, bnxt, gve): only the fields and the error without a device are.

property area: Buffer | None#

The receive area: a writable, page-aligned buffer (mmap.mmap) the device writes into. Setting it sets area_len to its length (it may be lowered); None for none (a dma-buf: area_dmabuf_fd).

Type

return Buffer | None

property region: Buffer | None#

The refill queue's memory: a writable, page-aligned buffer (mmap.mmap). Setting it sets region_size to its length (it may be lowered) and IORING_MEM_REGION_TYPE_USER in region_flags; None clears both (the kernel allocates it).

Type

return Buffer | None

property if_idx: int#

The network interface index (socket.if_nametoindex).

property if_rxq: int#

The receive queue of the interface.

property rq_entries: int#

The number of entries in the refill queue.

property flags: int#

ZCRX_REG_* flags.

property rx_buf_len: int#

The receive buffer length (0: the default).

property zcrx_id: int#

The id of the instance, set by io_uring_register_ifq.

property area_len: int#

The length of area in bytes.

property area_flags: int#

IORING_ZCRX_AREA_* flags.

property area_dmabuf_fd: int#

The dma-buf file descriptor for an area of None.

property area_rq_area_token: int#

The token the kernel gave for the area, set by io_uring_register_ifq.

property region_size: int#

The size of region in bytes.

property region_flags: int#

IORING_MEM_REGION_* flags of region.

class ClockRegister()#

The clock a ring uses for waiting with timeouts (io_uring_register_clock).

Example

>>> reg = ClockRegister()
>>> reg.clockid = time.CLOCK_BOOTTIME
>>> io_uring_register_clock(ring, reg)
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)

property clockid: int#

The clock id (time.CLOCK_*).

class Bpf()#

A BPF filter for the requests of a ring or task (io_uring_register_bpf_filter, io_uring_register_bpf_filter_task).

Example

>>> bpf = Bpf()
>>> bpf.opcode = io_uring_op.IORING_OP_OPENAT
>>> program = struct.pack("HBBI", 0x06, 0, 0, 0)  # `BPF_RET | BPF_K` 0: deny every `openat`
>>> bpf.filter = program          # `struct sock_filter` instructions (8 bytes each), hold on to it
>>> io_uring_register_bpf_filter(ring, bpf)
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)

property cmd_type: int#

The command type (IO_URING_BPF_CMD_*); IO_URING_BPF_CMD_FILTER by default.

property cmd_flags: int#

The command flags.

property opcode: int#

The opcode of the requests to filter (IORING_OP_*).

property flags: int#

The filter flags.

property pdu_size: int#

The size of the per-request data the filter sees.

property filter_len: int#

The number of BPF instructions.

property filter: Any | None#

The program set by filter, or None.

class MemRegionReg()#

Registers memory the ring uses itself (io_uring_register_region): flags for the registration and the region's description (region_*).

Example

>>> mem = mmap.mmap(-1, 4096)
>>> reg = MemRegionReg()
>>> reg.region = mem      # also `region_size = len(mem)` and `IORING_MEM_REGION_TYPE_USER` in `region_flags`
>>> reg.flags = IORING_MEM_REGION_REG_WAIT_ARG
>>> io_uring_register_region(ring, reg)  # `ring` set up with `IORING_SETUP_R_DISABLED`
0
Setup used to run this example
def ring_disabled(ns):
    make_ring(ns, entries=8, flags=IORING_SETUP_R_DISABLED)
    ns['cqe'] = Cqe()

Note

  • region is the memory itself, not its address: the ring keeps it locked from the register until its exit (a region can not be unregistered).

property flags: int#

IORING_MEM_REGION_REG_* flags of the registration.

property region: Buffer | None#

The region's memory: a writable, page-aligned buffer (mmap.mmap). Setting it sets region_size to its length (it may be lowered) and IORING_MEM_REGION_TYPE_USER in region_flags; None clears both (the kernel allocates the region).

Type

return Buffer | None

property region_size: int#

The size of region in bytes.

property region_flags: int#

IORING_MEM_REGION_TYPE_* flags of region.

property region_id: int#

The region id.

property region_mmap_offset: int#

The offset to mmap the region at, when the kernel allocated it.

class BufRing#

A buffer ring made by io_uring_setup_buf_ring (not instantiable): add buffers with io_uring_buf_ring_add, then io_uring_buf_ring_advance.

io_uring_register(fd: int, opcode: int, nr_args: int, arg: int | None = None) -> int#

The raw io_uring_register(2) system call on fd: opcode is an IORING_REGISTER_*, nr_args the number of arguments at address arg (default None: none). Returns the call's result; UnsupportedError if the kernel does not know opcode.

Example

>>> io_uring_register(ring.ring_fd, 9, 0, None)  # IORING_REGISTER_PERSONALITY
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

  • arg is a raw address: the caller keeps what it points to alive until the call returns (and for as long as opcode says the kernel reads it later).

io_uring_register_wait_reg(ring: Ring, reg: RegWait, nr: int) -> int#

Always EINVAL: C liburing no longer supports it (see RegWait, io_uring_register_region). nr must be 1 (ValueError otherwise).

io_uring_register_buffers(ring: Ring, bufs: Sequence[Buffer], tags: Sequence[int] | None = None) -> int#

Register the buffers bufs with the ring, with an optional tags for each.

Example #1

>>> bufs = [bytearray(4096), bytearray(4096)]
>>> io_uring_register_buffers(ring, bufs)
0
>>> io_uring_unregister_buffers(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)

Example #2

# with a tag for each buffer
>>> bufs = [bytearray(4096), bytearray(4096)]
>>> io_uring_register_buffers(ring, bufs, [1, 2])
0

Note

  • io_uring_register_buffers includes io_uring_register_buffers_tags
  • Linux 5.13+ (IORING_REGISTER_BUFFERS2). Returns 0. Use the buffers with the *_fixed requests (io_uring_prep_read_fixed, ...) by their index in bufs; a request may cover any part of its buffer.
  • bufs: writable buffers; the kernel pins them until io_uring_unregister_buffers (or the ring's exit), so the ring holds them (locked: no resize) until then.
  • bufs must be anonymous memory (bytearray, mmap.mmap(-1, ...); file-backed memory fails with EOPNOTSUPP or EFAULT), each at most 1 GiB (EFAULT), at most 1024 of them (EINVAL). A buffer on a huge page pins the whole page. Before Linux 5.12 they count against RLIMIT_MEMLOCK (ENOMEM).
  • OSError: EBUSY when buffers are already registered: unregister them first (registering again is how to change the set). Before Linux 5.13 the registration waits for the ring to go idle.
  • tags needs one item for each buffer in bufs. A tag of 0 means none; for any other, once the buffer is unregistered (or replaced) and no request uses it any more, a CQE is posted with user_data set to the tag and every other field 0.

io_uring_register_buffers_sparse(ring: Ring, nr: int) -> int#

Register nr empty buffer slots, to fill with io_uring_register_buffers_update_tag.

Note

io_uring_register_buffers_update_tag(ring: Ring, bufs: Sequence[Buffer], tags: Sequence[int], offset: int | None = None) -> int#

Update the registered buffers from slot offset (default 0) with bufs and tags.

Note

  • Function arguments position has changed for C origin for better usability.
  • Linux 5.13+. Returns the number of buffers updated. A slot may be empty (from io_uring_register_buffers_sparse) or already in use: a replaced buffer stays valid until the requests using it complete, then its tag's CQE (see io_uring_register_buffers) is posted.
  • The ring holds bufs until io_uring_unregister_buffers (or its exit); the buffers they replace too.

io_uring_unregister_buffers(ring: Ring) -> int#

Unregister all buffers.

Note

  • Returns 0; OSError: ENXIO when none are registered. Not needed before io_uring_queue_exit, which unregisters them too (the pages may be unpinned a little later then).
  • A CQE is posted for each tagged buffer (io_uring_register_buffers) once no request uses it.
  • Releases the buffers the ring held for the registrations (io_uring_register_buffers / _update_tag).

io_uring_register_files(ring: Ring, files: FileIndex) -> int#

Register the file descriptors of files.

Example

>>> ids = FileIndex([fd, -1, -1])  # `-1`: an empty slot
>>> io_uring_register_files(ring, ids)
0
>>> io_uring_unregister_files(ring)
0
Setup used to run this example
    'Sqe', 'put_sqe', 'io_uring_prep_open', 'io_uring_prep_open_direct', 'io_uring_prep_read', 'io_uring_prep_write',
    'io_uring_prep_statx', 'Statx', 'io_uring_register_files', 'io_uring_register_files_tags',
)  # fmt: skip
def file_ring(ns):
    '''A ring (2 entries for `put_sqe`, to show a full one), an empty `cqe` and `fd` of `./file.txt`
    (`hi... bye!`).'''
    make_ring(ns, entries=2 if ns['_name'] == 'put_sqe' else 1024)
    ns['cqe'] = Cqe()
    make_file(ns)

Note

  • Returns 0. Use a registered file with IOSQE_FIXED_FILE in the sqe's flags and its index in files as the sqe's fd. A file table is also what direct descriptors (*_direct requests) live in.
  • The kernel copies the descriptors at registration: ids can be freed, and the fds closed, afterwards (the ring holds its own reference to each file).
  • OSError: EBUSY when files are already registered (unregister first, or use io_uring_register_files_update), EMFILE over the fixed file table maximum or, for an unprivileged user, over RLIMIT_NOFILE (a per user limit), EBADF for a descriptor that is not open. Before Linux 5.13 the registration waits for the ring to go idle.
  • "Registered files have less overhead per operation than normal files. This is due to the kernel grabbing a reference count on a file when an operation begins, and dropping it when it's done. When the process file table is shared, for example if the process has ever created any threads, then this cost goes up even more. Using registered files reduces the overhead of file reference management across requests that operate on a file."

io_uring_register_files_tags(ring: Ring, files: FileIndex, tags: Sequence[int]) -> int#

Register the file descriptors of files, with a tag for each.

Example

>>> io_uring_register_files_tags(ring, FileIndex([fd, -1]), [1, 0])  # an empty slot takes no tag
0
Setup used to run this example
    'Sqe', 'put_sqe', 'io_uring_prep_open', 'io_uring_prep_open_direct', 'io_uring_prep_read', 'io_uring_prep_write',
    'io_uring_prep_statx', 'Statx', 'io_uring_register_files', 'io_uring_register_files_tags',
)  # fmt: skip
def file_ring(ns):
    '''A ring (2 entries for `put_sqe`, to show a full one), an empty `cqe` and `fd` of `./file.txt`
    (`hi... bye!`).'''
    make_ring(ns, entries=2 if ns['_name'] == 'put_sqe' else 1024)
    ns['cqe'] = Cqe()
    make_file(ns)

Note

  • Linux 5.13+ (IORING_REGISTER_FILES2). Returns 0; the errors of io_uring_register_files.
  • tags needs one item for each file in files. A tag of 0 means none; for any other, once the file is unregistered (or replaced) and no request uses it any more, a CQE is posted with user_data set to the tag and every other field 0.

io_uring_register_files_sparse(ring: Ring, nr: int) -> int#

Register nr empty file slots (-1), to fill with io_uring_register_files_update or direct descriptors.

Note

  • Linux 5.19+. Returns 0; OSError: EBUSY when files are already registered.
  • If nr is over RLIMIT_NOFILE, C liburing raises the limit (setrlimit) and retries before failing.

io_uring_register_files_update_tag(ring: Ring, files: FileIndex, tags: Sequence[int], offset: int | None = None) -> int#

Update the registered files from slot offset (default 0) with files and tags.

Note

  • Function arguments position has changed for C origin for better usability.
  • Linux 5.13+. Returns the number of files updated. In files, -1 empties a slot and IORING_REGISTER_FILES_SKIP leaves one as it is; a replaced file stays open for the requests using it, then its tag's CQE (see io_uring_register_files_tags) is posted.

io_uring_unregister_files(ring: Ring) -> int#

Unregister all files.

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_register_files_update(ring: Ring, fds: FileIndex, offset: int | None = None) -> int#

Update the registered files from slot offset (default 0) with fds.

Note

  • Function arguments position has changed for C origin for better usability.
  • Linux 5.5+. Returns the number of files updated. In fds, -1 empties a slot (closing a direct descriptor) and IORING_REGISTER_FILES_SKIP (Linux 5.12+) leaves one as it is. Unlike a registration, an update never waits for the ring to go idle (since Linux 5.13).

io_uring_register_eventfd(ring: Ring, fd: int) -> int#

Register an eventfd fd that is signalled for each completion.

Example

>>> efd = os.eventfd(0)
>>> io_uring_register_eventfd(ring, efd)
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

  • Returns 0. A ring has at most one eventfd.
  • A signal is only a hint to check the CQ ring: spurious ones happen, and a batch of completions may signal once.

io_uring_register_eventfd_async(ring: Ring, fd: int) -> int#

Like io_uring_register_eventfd, but only signalled for requests completed asynchronously.

Note

  • Linux 5.6+. A request that completes inline during io_uring_submit does not signal.

io_uring_unregister_eventfd(ring: Ring) -> int#

Unregister the eventfd.

Example

>>> io_uring_register_eventfd(ring, os.eventfd(0))
0
>>> io_uring_unregister_eventfd(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_register_personality(ring: Ring) -> int#

Register the credentials of the calling task and give their id (use as personality of an sqe).

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

  • Linux 5.6+. Returns a positive id. An sqe with it in personality runs with these credentials: a ring shared between users or processes keeps them separate this way.

io_uring_unregister_personality(ring: Ring, id: int) -> int#

Unregister the personality id.

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

  • Returns 0; OSError for an id not registered. id must not be used in an sqe afterwards.

io_uring_register_restrictions(ring: Ring, res: Restriction) -> int#

Register the restrictions added to res (ValueError if it is empty), for a ring set up with IORING_SETUP_R_DISABLED.

Note

  • Linux 5.10+. Returns 0. The restrictions are an allowlist (an opcode or flag not added is denied, OSError: EACCES for the request or register call), registered all in one call before io_uring_enable_rings.
  • OSError: EBADFD when the ring is not disabled, EBUSY when restrictions are already registered, EINVAL for an unknown restriction opcode or too many restrictions.

io_uring_register_ring_fd(ring: Ring) -> int#

Register the ring's own file descriptor, so io_uring_enter calls skip the fd lookup (1 on success).

Example

>>> io_uring_register_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

  • Linux 5.18+. Worth it in a threaded process (its file table is shared, so the lookup costs more).
  • The registration is per thread: a ring shared between threads (one submits, another reaps) can not use it, as each thread would have its own index. At most 16 ring fds per thread.
  • Not undone when the thread outlives the ring: io_uring_queue_exit unregisters it, else io_uring_unregister_ring_fd frees the slot.

io_uring_unregister_ring_fd(ring: Ring) -> int#

Unregister the ring's registered file descriptor (1 on success).

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_register_buf_ring(ring: Ring, reg: BufReg, flags: int | None = None) -> int#

Register the buffer ring reg (io_uring_setup_buf_ring does this for you).

Note

  • Linux 5.19+. Returns 0. reg.ring_entries must be a power of 2, at most 32768; reg.ring at least ring_entries * 16 bytes, page aligned. reg.flags: IOU_PBUF_RING_INC (Linux 6.12+) lets a buffer be consumed over several completions (IORING_CQE_F_BUF_MORE while more are to come), reg.min_left the least left for it to be handed out again (1 byte if 0).
  • A request with IOSQE_BUFFER_SELECT and buf_group = reg.bgid takes a buffer when its data arrives; its CQE has IORING_CQE_F_BUFFER set and the buffer id in flags >> IORING_CQE_BUFFER_SHIFT.
  • The ring keeps reg.ring locked until io_uring_unregister_buf_ring or its exit.

io_uring_unregister_buf_ring(ring: Ring, bgid: int) -> int#

Unregister the buffer ring of group bgid.

Note

  • Linux 5.19+. Returns 0; FileNotFoundError (ENOENT) when bgid has no buffer ring.
  • Drops the ring's BufRing of bgid (from io_uring_setup_buf_ring), with the buffers added to it.

io_uring_buf_ring_head(ring: Ring, buf_group: int) -> int#

The kernel's current head of the buffer ring buf_group (how many buffers it has used).

Example

>>> br = io_uring_setup_buf_ring(ring, 8, 1, 0)  # group 1
>>> io_uring_buf_ring_head(ring, 1)
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

  • Linux 6.8+ (IORING_REGISTER_PBUF_STATUS). FileNotFoundError (ENOENT) when buf_group has no buffers, OSError: EINVAL when it has legacy provided buffers (io_uring_prep_provide_buffers) and not a ring.

io_uring_register_sync_cancel(ring: Ring, reg: SyncCancelReg) -> int#

Cancel requests as described by reg and wait (up to reg.timeout) for it.

Note

  • Linux 6.0+. Like io_uring_prep_cancel, but the result comes back here instead of in a CQE: 0 when the request was found, or with a flag matching several (IORING_ASYNC_CANCEL_ALL, ...) how many. The cancelled requests still post their own CQEs (their res raises OSError: ECANCELED).
  • OSError: ENOENT (FileNotFoundError) when nothing matches (completed already, or a wrong addr), EALREADY when the request is too far along to cancel (it completes shortly on its own), ETIME when reg.timeout ran out first.
  • reg.addr is the user_data you gave the request (the kernel's is its in-flight entry, looked up here).

io_uring_register_sync_msg(sqe: Sqe) -> int#

Send the IORING_OP_MSG_RING request prepared in sqe to another ring at once, needs no ring of its own.

Note

io_uring_register_file_alloc_range(ring: Ring, off: int, len: int) -> int#

Limit the direct descriptor slots the kernel allocates (IORING_FILE_INDEX_ALLOC) to len slots from off.

Note

io_uring_register_napi(ring: Ring, napi: Napi) -> int#

Register the busy polling settings napi; the old settings are written back into it.

Note

  • Linux 6.9+. Returns 0; OSError on a kernel without CONFIG_NET_RX_BUSY_POLL. Busy polling the network device (as the SO_PREFER_BUSY_POLL socket option) lowers the round trip time.

io_uring_unregister_napi(ring: Ring, napi: Napi) -> int#

Unregister busy polling; the old settings are written back into napi.

Note

  • Linux 6.9+. Returns 0.

io_uring_register_ifq(ring: Ring, reg: ZcrxIfqReg) -> int#

Register a zero-copy receive queue as described by reg; its zcrx_id is set.

Note

io_uring_register_query(query: Query) -> int#

Fill query.data with what the kernel supports.

Example

Note

  • Returns 0 (the call worked); query.result is the kernel's answer for the query itself. UnsupportedError on a kernel without IORING_REGISTER_QUERY.
  • Needs no ring. Refer to help(Query).

io_uring_register_zcrx_ctrl(ring: Ring, ctrl: ZcrxCtrl) -> int#

Control a zero-copy receive instance as described by ctrl.

Note

  • Returns 0. ZCRX_CTRL_FLUSH_RQ flushes the buffers pending in the refill queue; ZCRX_CTRL_EXPORT sets ctrl.zcrx_fd, a file descriptor another ring can take the instance over with.
  • Not tested with a real device. Refer to help(ZcrxCtrl).

io_uring_register_clock(ring: Ring, arg: ClockRegister) -> int#

Set the clock the ring uses for waiting with timeouts.

Note

  • Linux 6.12+. Returns 0. arg.clockid: time.CLOCK_MONOTONIC (the default) or time.CLOCK_BOOTTIME (counts suspend too); OSError: EINVAL for any other.
  • Only waits with a timeout (io_uring_wait_cqe_timeout, ...) use it; with IORING_ENTER_ABS_TIMER the timeout is an absolute time of this clock.

io_uring_register_bpf_filter(ring: Ring, bpf: Bpf) -> int#

Add the BPF filter bpf to the ring.

Note

  • Returns 0. The filter runs for each request of bpf.opcode submitted through this ring: a non-zero result allows it, 0 denies it (the request fails with EACCES). Several filters for one opcode stack: all must allow. IO_URING_BPF_FILTER_DENY_REST in bpf.flags denies every opcode without a filter (an allowlist).
  • OSError: EACCES unless the caller has CAP_SYS_ADMIN or no_new_privs (prctl PR_SET_NO_NEW_PRIVS, as for seccomp); EMSGSIZE when bpf.pdu_size is over the kernel's size for the opcode, or differs from it with IO_URING_BPF_FILTER_SZ_STRICT (the kernel writes its size into bpf.pdu_size then: read it and retry); EINVAL for a bad filter, opcode or flags.

io_uring_register_bpf_filter_task(bpf: Bpf) -> int#

Add the BPF filter bpf to the calling task (and its children) instead of a ring.

Note

  • Returns 0; the filter, errors and privileges of io_uring_register_bpf_filter.
  • Applies to every ring the task creates afterwards (not to existing ones) and is inherited by fork children, who may add filters but not loosen these; it can not be removed.

io_uring_register_region(ring: Ring, reg: MemRegionReg) -> int#

Register the memory region reg with the ring.

Note

io_uring_setup_buf_ring(ring: Ring, nentries: int, bgid: int, flags: int) -> BufRing#

Set up and register a buffer ring of nentries for group bgid, and give its BufRing.

Type

return BufRing

Flags

Note

io_uring_free_buf_ring(ring: Ring, br: BufRing, nentries: int, bgid: int) -> int#

Unregister and free the buffer ring br (nentries and bgid as for io_uring_setup_buf_ring).

Note

  • Returns 0; FileNotFoundError (ENOENT) when bgid is not registered any more. The ring's exit does it too.

io_uring_register_iowq_aff(ring: Ring, mask: Buffer) -> int#

Limits the cpus the ring's async workers (io-wq) may run on to those set in mask (bytes / bytearray in the cpu_set_t layout, bit n of byte n // 8 for cpu n).

Example

>>> io_uring_register_iowq_aff(ring, bytes([0b11]))  # cpus 0 and 1
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

  • Linux 5.14+. Returns 0; OSError: EINVAL for an empty mask. By default the workers inherit the cpu mask of the task that made the ring. A worker already running moves at its next schedule point.

io_uring_unregister_iowq_aff(ring: Ring) -> int#

Remove the cpu limit set by io_uring_register_iowq_aff.

Note

  • Linux 5.14+. Returns 0.

io_uring_register_iowq_max_workers(ring: Ring, bounded: int, unbounded: int) -> tuple[int, int]#

Sets the most io-wq workers the ring may run: bounded (regular files) and unbounded (sockets, ...); 0 leaves one as is. Gives the previous (bounded, unbounded).

Example

>>> io_uring_register_iowq_max_workers(ring, 0, 0)  # (bounded, unbounded)
(96, 1048576)
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

  • Linux 5.15+. Defaults: bounded the ring's SQ entries or 4 times the online cpus, whichever is less; unbounded the task limit (RLIMIT_NPROC). The limits are per NUMA node, and maxima: idle workers are reaped. OSError: EINVAL for a value over the kernel's maximum.

io_uring_buf_ring_mask(ring_entries: int) -> int#

The mask for a buffer ring of ring_entries (for io_uring_buf_ring_add).

io_uring_buf_ring_init(br: BufRing) -> None#

Initialize the buffer ring br (zero its tail) before the first io_uring_buf_ring_add.

Note

io_uring_buf_ring_add(br: BufRing, addr: Buffer, bid: int, mask: int, buf_offset: int | None = None) -> None#

Add the buffer addr with id bid to br, buf_offset (default 0) entries after the tail; not visible to the kernel until io_uring_buf_ring_advance.

Note

  • mask is io_uring_buf_ring_mask(nentries). buf_offset is 0 for one buffer; adding several before one io_uring_buf_ring_advance, it goes up by 1 for each (0, 1, 2, ...).
  • bid comes back in the CQE (flags >> IORING_CQE_BUFFER_SHIFT), and may name a different buffer each time it is added: only you track which. A request writes at most INT_MAX bytes into a buffer.
  • addr must be writable (the kernel fills it); br holds it (locked: no resize) until bid is added again or the buffer ring is freed (the ring holds br until then).

io_uring_buf_ring_advance(br: BufRing, count: int) -> None#

Make count added buffers visible to the kernel.

Note

  • This hands the buffers over to the ring: do not write into them until a CQE gives them back.

io_uring_buf_ring_cq_advance(ring: Ring, br: BufRing, count: int) -> None#

Like io_uring_buf_ring_advance, and advance the completion queue by count too.

Note

io_uring_buf_ring_available(ring: Ring, br: BufRing, bgid: int) -> int#

The number of buffers in br the kernel has not used yet.

Note

  • Linux 6.8+ (asks the kernel for its head, io_uring_buf_ring_head). The count is of that moment: with requests in flight that may take buffers, it is already stale.
  • Returns a negative error number, not an exception: -ENOENT when bgid has no buffers, -EINVAL when it is not a buffer ring or the kernel lacks the call.
Generated by tools/setu_docs.py from the docstrings of Liburing 2026.10.29 (CPython 3.14+).