Skip to content

Commit d028c72

Browse files
authored
Add io_uring flags available in linux-raw-sys (#1115)
* Add io_uring flags available in linux-raw-sys * Make IORING_REGISTER_USE_REGISTERED_RING const * Add io_uring_register_with and IoringRegisterFlags IORING_REGISTER_USE_REGISTERED_RING must be ored into `opcode` when calling `io_uring_register` with a registered ring fd. The value of this flag is 1u32 << 31, and does not fit into the `IoringRegisterOp` enum. To support this use case, add a new function `io_uring_register_with` that takes a bitflags flags argument. Also adds a minimal test that calls `io_uring_register` with a registered ring fd. This operation isn't supported on older kernels, so we ensure the feature is supported before completing the test.
1 parent e1f4d48 commit d028c72

File tree

5 files changed

+202
-3
lines changed

5 files changed

+202
-3
lines changed

src/backend/libc/io_uring/syscalls.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::backend::c;
44
use crate::backend::conv::{borrowed_fd, ret_owned_fd, ret_u32};
55
use crate::fd::{BorrowedFd, OwnedFd};
66
use crate::io;
7-
use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterOp};
7+
use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp};
88

99
#[inline]
1010
pub(crate) fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result<OwnedFd> {
@@ -40,6 +40,30 @@ pub(crate) unsafe fn io_uring_register(
4040
))
4141
}
4242

43+
#[inline]
44+
pub(crate) unsafe fn io_uring_register_with(
45+
fd: BorrowedFd<'_>,
46+
opcode: IoringRegisterOp,
47+
flags: IoringRegisterFlags,
48+
arg: *const c::c_void,
49+
nr_args: u32,
50+
) -> io::Result<u32> {
51+
syscall! {
52+
fn io_uring_register(
53+
fd: c::c_uint,
54+
opcode: c::c_uint,
55+
arg: *const c::c_void,
56+
nr_args: c::c_uint
57+
) via SYS_io_uring_register -> c::c_int
58+
}
59+
ret_u32(io_uring_register(
60+
borrowed_fd(fd) as _,
61+
(opcode as u32) | bitflags_bits!(flags),
62+
arg,
63+
nr_args,
64+
))
65+
}
66+
4367
#[inline]
4468
pub(crate) unsafe fn io_uring_enter(
4569
fd: BorrowedFd<'_>,

src/backend/linux_raw/io_uring/syscalls.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use crate::backend::conv::{by_mut, c_uint, pass_usize, ret_c_uint, ret_owned_fd};
99
use crate::fd::{BorrowedFd, OwnedFd};
1010
use crate::io;
11-
use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterOp};
11+
use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp};
1212
use core::ffi::c_void;
1313

1414
#[inline]
@@ -38,6 +38,23 @@ pub(crate) unsafe fn io_uring_register(
3838
))
3939
}
4040

41+
#[inline]
42+
pub(crate) unsafe fn io_uring_register_with(
43+
fd: BorrowedFd<'_>,
44+
opcode: IoringRegisterOp,
45+
flags: IoringRegisterFlags,
46+
arg: *const c_void,
47+
nr_args: u32,
48+
) -> io::Result<u32> {
49+
ret_c_uint(syscall_readonly!(
50+
__NR_io_uring_register,
51+
fd,
52+
c_uint((opcode as u32) | bitflags_bits!(flags)),
53+
arg,
54+
c_uint(nr_args)
55+
))
56+
}
57+
4158
#[inline]
4259
pub(crate) unsafe fn io_uring_enter(
4360
fd: BorrowedFd<'_>,

src/io_uring.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ use linux_raw_sys::net;
3535
pub use crate::event::epoll::{
3636
Event as EpollEvent, EventData as EpollEventData, EventFlags as EpollEventFlags,
3737
};
38-
pub use crate::fs::{Advice, AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, Statx, StatxFlags};
38+
pub use crate::fs::{
39+
Advice, AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, Statx, StatxFlags, XattrFlags,
40+
};
3941
pub use crate::io::ReadWriteFlags;
4042
pub use crate::net::{RecvFlags, SendFlags, SocketFlags};
4143
pub use crate::timespec::Timespec;
@@ -115,6 +117,30 @@ pub unsafe fn io_uring_register<Fd: AsFd>(
115117
backend::io_uring::syscalls::io_uring_register(fd.as_fd(), opcode, arg, nr_args)
116118
}
117119

120+
/// `io_uring_register_with(fd, opcode, flags, arg, nr_args)`—Register files or
121+
/// user buffers for asynchronous I/O.
122+
///
123+
/// # Safety
124+
///
125+
/// io_uring operates on raw pointers and raw file descriptors. Users are
126+
/// responsible for ensuring that memory and resources are only accessed in
127+
/// valid ways.
128+
///
129+
/// # References
130+
/// - [Linux]
131+
///
132+
/// [Linux]: https://man.archlinux.org/man/io_uring_register.2.en
133+
#[inline]
134+
pub unsafe fn io_uring_register_with<Fd: AsFd>(
135+
fd: Fd,
136+
opcode: IoringRegisterOp,
137+
flags: IoringRegisterFlags,
138+
arg: *const c_void,
139+
nr_args: u32,
140+
) -> io::Result<u32> {
141+
backend::io_uring::syscalls::io_uring_register_with(fd.as_fd(), opcode, flags, arg, nr_args)
142+
}
143+
118144
/// `io_uring_enter(fd, to_submit, min_complete, flags, arg, size)`—Initiate
119145
/// and/or complete asynchronous I/O.
120146
///
@@ -260,6 +286,19 @@ pub enum IoringRegisterOp {
260286
RegisterFileAllocRange = sys::IORING_REGISTER_FILE_ALLOC_RANGE as _,
261287
}
262288

289+
bitflags::bitflags! {
290+
/// `IORING_REGISTER_*` flags for use with [`io_uring_register_with`].
291+
#[repr(transparent)]
292+
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
293+
pub struct IoringRegisterFlags: u32 {
294+
/// `IORING_REGISTER_USE_REGISTERED_RING`
295+
const USE_REGISTERED_RING = sys::IORING_REGISTER_USE_REGISTERED_RING as u32;
296+
297+
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
298+
const _ = !0;
299+
}
300+
}
301+
263302
/// `IORING_OP_*` constants for use with [`io_uring_sqe`].
264303
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
265304
#[repr(u8)]
@@ -511,6 +550,9 @@ bitflags::bitflags! {
511550
/// `IORING_SETUP_REGISTERED_FD_ONLY`
512551
const REGISTERED_FD_ONLY = sys::IORING_SETUP_REGISTERED_FD_ONLY;
513552

553+
/// `IORING_SETUP_NO_SQARRAY`
554+
const NO_SQARRAY = sys::IORING_SETUP_NO_SQARRAY;
555+
514556
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
515557
const _ = !0;
516558
}
@@ -712,6 +754,9 @@ bitflags::bitflags! {
712754
/// `IORING_FEAT_LINKED_FILE`
713755
const LINKED_FILE = sys::IORING_FEAT_LINKED_FILE;
714756

757+
/// `IORING_FEAT_REG_REG_RING`
758+
const REG_REG_RING = sys::IORING_FEAT_REG_REG_RING;
759+
715760
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
716761
const _ = !0;
717762
}
@@ -1128,6 +1173,7 @@ pub union op_flags_union {
11281173
pub rename_flags: RenameFlags,
11291174
pub unlink_flags: AtFlags,
11301175
pub hardlink_flags: AtFlags,
1176+
pub xattr_flags: XattrFlags,
11311177
pub msg_ring_flags: IoringMsgringFlags,
11321178
}
11331179

tests/io_uring/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[cfg(linux_kernel)]
2+
#[cfg(feature = "io_uring")]
3+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
4+
mod register;

tests/io_uring/register.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use libc::c_void;
2+
use rustix::{
3+
fd::{AsFd, AsRawFd, BorrowedFd},
4+
io::Result,
5+
io_uring::{
6+
io_uring_params, io_uring_register_with, io_uring_rsrc_update, io_uring_setup,
7+
IoringFeatureFlags, IoringRegisterFlags, IoringRegisterOp,
8+
},
9+
};
10+
11+
fn do_register<FD>(
12+
fd: FD,
13+
registered_fd: bool,
14+
opcode: IoringRegisterOp,
15+
arg: *const c_void,
16+
arg_nr: u32,
17+
) -> Result<()>
18+
where
19+
FD: AsFd,
20+
{
21+
let flags = if registered_fd {
22+
IoringRegisterFlags::USE_REGISTERED_RING
23+
} else {
24+
IoringRegisterFlags::default()
25+
};
26+
27+
unsafe {
28+
io_uring_register_with(fd, opcode, flags, arg, arg_nr)?;
29+
}
30+
31+
Ok(())
32+
}
33+
34+
fn register_ring<'a>(fd: BorrowedFd<'a>) -> Result<BorrowedFd<'a>> {
35+
let update = io_uring_rsrc_update {
36+
data: fd.as_raw_fd() as u64,
37+
offset: u32::MAX,
38+
resv: 0,
39+
};
40+
41+
do_register(
42+
fd,
43+
false,
44+
IoringRegisterOp::RegisterRingFds,
45+
(&update) as *const io_uring_rsrc_update as *const c_void,
46+
1,
47+
)?;
48+
49+
let registered_fd = unsafe { BorrowedFd::borrow_raw(update.offset as i32) };
50+
Ok(registered_fd)
51+
}
52+
53+
fn unregister_ring<FD>(fd: FD) -> Result<()>
54+
where
55+
FD: AsRawFd + AsFd,
56+
{
57+
let update = io_uring_rsrc_update {
58+
offset: fd.as_raw_fd() as u32,
59+
data: 0,
60+
resv: 0,
61+
};
62+
63+
do_register(
64+
fd,
65+
true,
66+
IoringRegisterOp::UnregisterRingFds,
67+
(&update) as *const io_uring_rsrc_update as *const c_void,
68+
1,
69+
)?;
70+
71+
Ok(())
72+
}
73+
74+
/// Set bounded and unbounded async kernel worker counts to 0, to test registering with registered
75+
/// ring fd.
76+
fn register_iowq_max_workers<FD>(fd: FD) -> Result<()>
77+
where
78+
FD: AsFd,
79+
{
80+
let iowq_max_workers = [0u32; 2];
81+
do_register(
82+
fd,
83+
true,
84+
IoringRegisterOp::RegisterIowqMaxWorkers,
85+
(&iowq_max_workers) as *const [u32; 2] as *const c_void,
86+
2,
87+
)?;
88+
89+
Ok(())
90+
}
91+
92+
#[test]
93+
fn test_io_uring_register_with() {
94+
let mut params = io_uring_params::default();
95+
let ring_fd = io_uring_setup(4, &mut params).unwrap();
96+
assert_eq!(params.sq_entries, 4);
97+
assert_eq!(params.cq_entries, 8);
98+
99+
if !params.features.contains(IoringFeatureFlags::REG_REG_RING) {
100+
// Kernel does not support `io_uring_register` with a registered ring fd
101+
return;
102+
}
103+
104+
let ring_fd = register_ring(ring_fd.as_fd()).unwrap();
105+
let register_result = register_iowq_max_workers(ring_fd);
106+
let _ = unregister_ring(ring_fd).unwrap();
107+
register_result.unwrap();
108+
}

0 commit comments

Comments
 (0)