Repository: Thomasdezeeuw/a10
Pull request #108 (2024-04-04 08:22:35)

Add Config::disable and Ring::enable
Allows the ring to be started in a disable mode, and to be enabled again.


Patch:
diff --git a/src/config.rs b/src/config.rs
index 8c70893..9913a1c 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -17,6 +17,7 @@ use crate::{libc, AtomicBitMap, CompletionQueue, Ring, SharedSubmissionQueue, Su
 pub struct Config<'r> {
     submission_entries: u32,
     completion_entries: Option<u32>,
+    disabled: bool,
     clamp: bool,
     kernel_thread: bool,
     cpu_affinity: Option<u32>,
@@ -56,6 +57,7 @@ impl<'r> Config<'r> {
         Config {
             submission_entries: entries,
             completion_entries: None,
+            disabled: false,
             clamp: false,
             kernel_thread: true,
             cpu_affinity: None,
@@ -64,6 +66,16 @@ impl<'r> Config<'r> {
         }
     }
 
+    /// Start the ring in a disabled state.
+    ///
+    /// While the ring is disabled submissions are not allowed. To enable the
+    /// ring use [`Ring::enable`].
+    #[doc(alias = "IORING_SETUP_R_DISABLED")]
+    pub const fn disable(mut self) -> Config<'r> {
+        self.disabled = true;
+        self
+    }
+
     /// Set the size of the completion queue.
     ///
     /// By default the kernel will use a completion queue twice as large as the
@@ -151,6 +163,7 @@ impl<'r> Config<'r> {
     }
 
     /// Same as [`Config::attach`], but accepts a [`SubmissionQueue`].
+    #[doc(alias = "IORING_SETUP_ATTACH_WQ")]
     pub const fn attach_queue(mut self, other_ring: &'r SubmissionQueue) -> Self {
         self.attach = Some(other_ring);
         self
@@ -170,6 +183,9 @@ impl<'r> Config<'r> {
             // Don't interrupt userspace, the user must call `Ring::poll` any way.
             parameters.flags |= libc::IORING_SETUP_COOP_TASKRUN;
         }
+        if self.disabled {
+            parameters.flags |= libc::IORING_SETUP_R_DISABLED;
+        }
         if let Some(completion_entries) = self.completion_entries {
             parameters.cq_entries = completion_entries;
             parameters.flags |= libc::IORING_SETUP_CQSIZE;
diff --git a/src/lib.rs b/src/lib.rs
index 5454c2d..e97fb74 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -226,6 +226,20 @@ impl Ring {
         &self.sq
     }
 
+    /// Enable the ring.
+    ///
+    /// This only required when starting the ring in disabled mode, see
+    /// [`Config::disable`].
+    pub fn enable(&mut self) -> io::Result<()> {
+        libc::syscall!(io_uring_register(
+            self.sq.shared.ring_fd.as_raw_fd(),
+            libc::IORING_REGISTER_ENABLE_RINGS,
+            ptr::null(),
+            0,
+        ))?;
+        Ok(())
+    }
+
     /// Poll the ring for completions.
     ///
     /// This will wake all completed [`Future`]s with the result of their


Test patch:
diff --git a/tests/ring.rs b/tests/ring.rs
index da3e953..ee4d0a6 100644
--- a/tests/ring.rs
+++ b/tests/ring.rs
@@ -149,6 +149,20 @@ fn submission_queue_full_is_handle_internally() {
     }
 }
 
+#[test]
+fn config_disabled() {
+    init();
+    let mut ring = Ring::config(1).disable().build().unwrap();
+
+    // In a disabled state, so we expect an error.
+    let err = ring.poll(None).unwrap_err();
+    assert_eq!(err.raw_os_error(), Some(libc::EBADFD));
+
+    // Enabling it should allow us to poll.
+    ring.enable().unwrap();
+    ring.poll(Some(Duration::from_millis(1))).unwrap();
+}
+
 #[test]
 fn wake_ring() {
     init();