Skip to content

Commit c712a5b

Browse files
authored
feat(driver): force OpCode support (#690)
1 parent 88846e5 commit c712a5b

6 files changed

Lines changed: 131 additions & 21 deletions

File tree

compio-driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pin-project-lite = { workspace = true }
2828
thin-cell = { version = "0.1.1" }
2929
smallvec = { workspace = true, optional = true, features = ["union"] }
3030
synchrony = { workspace = true, features = ["waker_slot"] }
31+
bitflags = { version = "2.11.0" }
3132

3233
# Windows specific dependencies
3334
[target.'cfg(windows)'.dependencies]

compio-driver/src/driver_type.rs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,17 @@ pub enum DriverType {
1111
}
1212

1313
impl DriverType {
14-
/// Get the underlying driver type
14+
/// Suggest the driver type base on OpCode availability.
15+
///
16+
/// This is used when the user doesn't specify a driver type, and the driver
17+
/// will choose the best one based on the supported OpCodes.
1518
#[cfg(fusion)]
16-
pub(crate) fn suggest() -> DriverType {
17-
use io_uring::opcode::*;
19+
pub(crate) fn suggest(additional: crate::op::OpCodeFlag) -> DriverType {
20+
use crate::op::OpCodeFlag;
1821

19-
// Add more opcodes here if used
20-
const USED_OP: &[u8] = &[
21-
Read::CODE,
22-
Readv::CODE,
23-
Write::CODE,
24-
Writev::CODE,
25-
Fsync::CODE,
26-
Accept::CODE,
27-
Connect::CODE,
28-
Recv::CODE,
29-
Send::CODE,
30-
RecvMsg::CODE,
31-
SendMsg::CODE,
32-
PollAdd::CODE,
33-
];
22+
let flags = additional | OpCodeFlag::basic();
3423

35-
if USED_OP.iter().all(|op| crate::sys::is_op_supported(*op)) {
24+
if flags.get_codes().all(crate::sys::is_op_supported) {
3625
DriverType::IoUring
3726
} else {
3827
DriverType::Poll

compio-driver/src/lib.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub use sys::*;
4747
mod cancel;
4848
pub use cancel::*;
4949

50-
use crate::key::ErasedKey;
50+
use crate::{key::ErasedKey, op::OpCodeFlag};
5151

5252
mod sys_slice;
5353

@@ -450,6 +450,7 @@ pub struct ProactorBuilder {
450450
taskrun_flag: bool,
451451
eventfd: Option<RawFd>,
452452
driver_type: Option<DriverType>,
453+
op_flags: OpCodeFlag,
453454
}
454455

455456
// SAFETY: `RawFd` is thread safe.
@@ -473,6 +474,7 @@ impl ProactorBuilder {
473474
taskrun_flag: false,
474475
eventfd: None,
475476
driver_type: None,
477+
op_flags: OpCodeFlag::empty(),
476478
}
477479
}
478480

@@ -580,6 +582,27 @@ impl ProactorBuilder {
580582
self
581583
}
582584

585+
/// Set which io-uring [`OpCode`] must be supported by the driver.
586+
///
587+
/// Support for io-uring opcodes varies by kernel version. Setting this
588+
/// will force the driver to check for support of the specified opcodes, and
589+
/// when any of them are not supported:
590+
/// - Fallback to `polling` driver if `fusion` driver is enabled,
591+
/// - Return an [`Unsupported`] error when building the proactor otherwise.
592+
///
593+
/// # Notes
594+
///
595+
/// - Only effective when the `io-uring` feature is enabled
596+
/// - [`OpCodeFlag`] is a bitflag struct, you can combine multiple opcodes
597+
/// with bitwise OR or use [`OpCodeFlag::all`] to require all opcodes to
598+
/// be supported.
599+
///
600+
/// [`Unsupported`]: std::io::ErrorKind::Unsupported
601+
pub fn detect_opcode_support(&mut self, flags: OpCodeFlag) -> &mut Self {
602+
self.op_flags = flags;
603+
self
604+
}
605+
583606
/// Force a driver type to use.
584607
///
585608
/// It is ignored if the fusion driver is disabled.

compio-driver/src/op.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,91 @@ pub(crate) mod managed {
572572

573573
#[cfg(not(io_uring))]
574574
pub use managed::*;
575+
576+
bitflags::bitflags! {
577+
/// Flags for operations.
578+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
579+
pub struct OpCodeFlag: u32 {
580+
/// Detect `Read` OpCode
581+
const Read = 1 << 0;
582+
/// Detect `Readv` OpCode
583+
const Readv = 1 << 1;
584+
/// Detect `Write` OpCode
585+
const Write = 1 << 2;
586+
/// Detect `Writev` OpCode
587+
const Writev = 1 << 3;
588+
/// Detect `Fsync` OpCode
589+
const Fsync = 1 << 4;
590+
/// Detect `Accept` OpCode
591+
const Accept = 1 << 5;
592+
/// Detect `Connect` OpCode
593+
const Connect = 1 << 6;
594+
/// Detect `Recv` OpCode
595+
const Recv = 1 << 7;
596+
/// Detect `Send` OpCode
597+
const Send = 1 << 8;
598+
/// Detect `RecvMsg` OpCode
599+
const RecvMsg = 1 << 9;
600+
/// Detect `SendMsg` OpCode
601+
const SendMsg = 1 << 10;
602+
/// Detect `AsyncCancel` OpCode
603+
const AsyncCancel = 1 << 11;
604+
/// Detect `OpenAt` OpCode
605+
const OpenAt = 1 << 12;
606+
/// Detect `Close` OpCode
607+
const Close = 1 << 13;
608+
/// Detect `Splice` OpCode
609+
const Splice = 1 << 14;
610+
/// Detect `Shutdown` OpCode
611+
const Shutdown = 1 << 15;
612+
/// Detect `PollAdd` OpCode
613+
const PollAdd = 1 << 16;
614+
}
615+
}
616+
617+
impl OpCodeFlag {
618+
/// Get the [`OpCodeFlag`] corresponds to basic OpCodes that are commonly
619+
/// used.
620+
pub fn basic() -> Self {
621+
OpCodeFlag::Read
622+
| OpCodeFlag::Readv
623+
| OpCodeFlag::Write
624+
| OpCodeFlag::Writev
625+
| OpCodeFlag::Fsync
626+
| OpCodeFlag::Accept
627+
| OpCodeFlag::Connect
628+
| OpCodeFlag::Recv
629+
| OpCodeFlag::Send
630+
| OpCodeFlag::RecvMsg
631+
| OpCodeFlag::SendMsg
632+
| OpCodeFlag::PollAdd
633+
}
634+
}
635+
636+
#[cfg(io_uring)]
637+
impl OpCodeFlag {
638+
pub(crate) fn get_codes(self) -> impl Iterator<Item = u8> {
639+
use io_uring::opcode::*;
640+
641+
self.iter().map(|flag| match flag {
642+
OpCodeFlag::Read => Read::CODE,
643+
OpCodeFlag::Readv => Readv::CODE,
644+
OpCodeFlag::Write => Write::CODE,
645+
OpCodeFlag::Writev => Writev::CODE,
646+
OpCodeFlag::Fsync => Fsync::CODE,
647+
OpCodeFlag::Accept => Accept::CODE,
648+
OpCodeFlag::Connect => Connect::CODE,
649+
OpCodeFlag::Recv => Recv::CODE,
650+
OpCodeFlag::Send => Send::CODE,
651+
OpCodeFlag::RecvMsg => RecvMsg::CODE,
652+
OpCodeFlag::SendMsg => SendMsg::CODE,
653+
OpCodeFlag::AsyncCancel => AsyncCancel::CODE,
654+
OpCodeFlag::OpenAt => OpenAt::CODE,
655+
OpCodeFlag::Close => Close::CODE,
656+
OpCodeFlag::Splice => Splice::CODE,
657+
OpCodeFlag::Shutdown => Shutdown::CODE,
658+
OpCodeFlag::PollAdd => PollAdd::CODE,
659+
unknown => unreachable!("Unknown OpCodeFlag specified: {unknown:?}"),
660+
})
661+
}
662+
}

compio-driver/src/sys/fusion/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl Driver {
9696
pub fn new(builder: &ProactorBuilder) -> io::Result<Self> {
9797
let (ty, fallback) = match &builder.driver_type {
9898
Some(t) => (*t, false),
99-
None => (DriverType::suggest(), true),
99+
None => (DriverType::suggest(builder.op_flags), true),
100100
};
101101
match ty {
102102
DriverType::Poll => Ok(Self {

compio-driver/src/sys/iour/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@ impl Driver {
154154
pub fn new(builder: &ProactorBuilder) -> io::Result<Self> {
155155
instrument!(compio_log::Level::TRACE, "new", ?builder);
156156
trace!("new iour driver");
157+
// if op_flags is empty, this loop will not run
158+
for code in builder.op_flags.get_codes() {
159+
if !is_op_supported(code) {
160+
return Err(io::Error::new(
161+
io::ErrorKind::Unsupported,
162+
format!("io-uring does not support opcode {code:?}({code})"),
163+
));
164+
}
165+
}
157166
let notifier = Notifier::new()?;
158167
let mut io_uring_builder = IoUring::builder();
159168
if let Some(sqpoll_idle) = builder.sqpoll_idle {

0 commit comments

Comments
 (0)