diff --git a/io-uring-test/src/tests/net.rs b/io-uring-test/src/tests/net.rs index cb61c449..987ca4a6 100644 --- a/io-uring-test/src/tests/net.rs +++ b/io-uring-test/src/tests/net.rs @@ -941,7 +941,7 @@ pub fn test_tcp_buffer_select( match bid { 0 => assert_eq!(&buf0[..256], &input[1024..]), 1 => assert_eq!(&buf1[..256], &input[1024..]), - _ => panic!("{}", cqe.flags()), + _ => panic!("{}", cqe.flags().bits()), } // remove one remaining buf @@ -1496,7 +1496,7 @@ pub fn test_socket( assert!(cqes[0].result() >= 0); let io_uring_socket = unsafe { Socket::from_raw_fd(cqes[0].result()) }; assert!(io_uring_socket.as_raw_fd() != plain_fd); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); // Try a setsockopt. { @@ -1534,7 +1534,7 @@ pub fn test_socket( assert_eq!(cqes.len(), 1); assert_eq!(cqes[0].user_data(), 1234); assert_eq!(cqes[0].result(), 0); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); // Check value actually set. optval = 0; @@ -1582,7 +1582,7 @@ pub fn test_socket( assert_eq!(cqes.len(), 1); assert_eq!(cqes[0].user_data(), 55); assert_eq!(cqes[0].result(), 0); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); // If the fixed-socket operation worked properly, this must not fail. ring.submitter().unregister_files().unwrap(); @@ -1628,7 +1628,7 @@ pub fn test_socket_bind_listen( assert!(cqes[0].result() >= 0); let io_uring_socket = unsafe { Socket::from_raw_fd(cqes[0].result()) }; assert!(io_uring_socket.as_raw_fd() != plain_fd); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); // Try to bind. { @@ -1649,7 +1649,7 @@ pub fn test_socket_bind_listen( assert_eq!(cqes.len(), 1); assert_eq!(cqes[0].user_data(), 2345); assert_eq!(cqes[0].result(), 0); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); assert_eq!( io_uring_socket @@ -1676,7 +1676,7 @@ pub fn test_socket_bind_listen( assert_eq!(cqes.len(), 1); assert_eq!(cqes[0].user_data(), 3456); assert_eq!(cqes[0].result(), 0); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); // Ensure the socket is actually in the listening state. _ = TcpStream::connect( @@ -1719,7 +1719,7 @@ pub fn test_socket_bind_listen( assert_eq!(cqes.len(), 1); assert_eq!(cqes[0].user_data(), 55); assert_eq!(cqes[0].result(), 0); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); // If the fixed-socket operation worked properly, this must not fail. ring.submitter().unregister_files().unwrap(); @@ -1768,7 +1768,7 @@ pub fn test_udp_recvmsg_multishot assert_eq!(cqes.len(), 1); assert_eq!(cqes[0].user_data(), 11); assert_eq!(cqes[0].result(), 0); - assert_eq!(cqes[0].flags(), 0); + assert_eq!(cqes[0].flags(), cqueue::CompletionFlags::empty()); } let recvmsg_e = opcode::RecvMulti::new(Fd(server_socket.as_raw_fd()), BUF_GROUP) diff --git a/io-uring-test/src/tests/queue.rs b/io-uring-test/src/tests/queue.rs index 68dddad3..13d6a73c 100644 --- a/io-uring-test/src/tests/queue.rs +++ b/io-uring-test/src/tests/queue.rs @@ -187,13 +187,13 @@ pub fn test_msg_ring_data( assert_eq!(source_cqes.len(), 1); assert_eq!(source_cqes[0].user_data(), 0); assert_eq!(source_cqes[0].result(), 0); - assert_eq!(source_cqes[0].flags(), 0); + assert_eq!(source_cqes[0].flags(), cqueue::CompletionFlags::empty()); let dest_cqes: Vec = dest_ring.completion().map(Into::into).collect(); assert_eq!(dest_cqes.len(), 1); assert_eq!(dest_cqes[0].user_data(), user_data); assert_eq!(dest_cqes[0].result(), result); - assert_eq!(dest_cqes[0].flags(), 0); + assert_eq!(dest_cqes[0].flags(), cqueue::CompletionFlags::empty()); Ok(()) } @@ -254,13 +254,13 @@ pub fn test_msg_ring_send_fd( assert_eq!(source_cqes.len(), 1); assert_eq!(source_cqes[0].user_data(), 0); assert_eq!(source_cqes[0].result(), 0); - assert_eq!(source_cqes[0].flags(), 0); + assert_eq!(source_cqes[0].flags(), cqueue::CompletionFlags::empty()); let dest_cqes: Vec = temp_ring.completion().map(Into::into).collect(); assert_eq!(dest_cqes.len(), 1); assert_eq!(dest_cqes[0].user_data(), 22); assert_eq!(dest_cqes[0].result(), 0); - assert_eq!(dest_cqes[0].flags(), 0); + assert_eq!(dest_cqes[0].flags(), cqueue::CompletionFlags::empty()); } // Unregister the fixed files from the source ring, then reserve some empty slots @@ -285,13 +285,13 @@ pub fn test_msg_ring_send_fd( assert_eq!(source_cqes.len(), 1); assert_eq!(source_cqes[0].user_data(), 0); assert_eq!(source_cqes[0].result(), 0); - assert_eq!(source_cqes[0].flags(), 0); + assert_eq!(source_cqes[0].flags(), cqueue::CompletionFlags::empty()); let dest_cqes: Vec = ring.completion().map(Into::into).collect(); assert_eq!(dest_cqes.len(), 1); assert_eq!(dest_cqes[0].user_data(), 44); assert_eq!(dest_cqes[0].result(), 0); - assert_eq!(dest_cqes[0].flags(), 0); + assert_eq!(dest_cqes[0].flags(), cqueue::CompletionFlags::empty()); } // Unregister the fixed files from both rings, then repeat again to diff --git a/io-uring-test/src/tests/register_buf_ring.rs b/io-uring-test/src/tests/register_buf_ring.rs index e336bb46..2a610dfa 100644 --- a/io-uring-test/src/tests/register_buf_ring.rs +++ b/io-uring-test/src/tests/register_buf_ring.rs @@ -264,7 +264,12 @@ impl InnerBufRing { // Returns the buffer the uring interface picked from the buf_ring for the completion result // represented by the res and flags. - fn get_buf(&self, buf_ring: FixedSizeBufRing, res: u32, flags: u32) -> io::Result { + fn get_buf( + &self, + buf_ring: FixedSizeBufRing, + res: u32, + flags: cqueue::CompletionFlags, + ) -> io::Result { // This fn does the odd thing of having self as the BufRing and taking an argument that is // the same BufRing but wrapped in Rc<_> so the wrapped buf_ring can be passed to the // outgoing GBuf. @@ -279,7 +284,12 @@ impl InnerBufRing { } // Returns vector of buffers for completion results that can return a bundle - pub(crate) fn get_bufs(&self, buf_ring: &FixedSizeBufRing, res: u32, flags: u32) -> Vec { + pub(crate) fn get_bufs( + &self, + buf_ring: &FixedSizeBufRing, + res: u32, + flags: cqueue::CompletionFlags, + ) -> Vec { let mut bid = io_uring::cqueue::buffer_select(flags).unwrap(); let mut len = res as usize; let mut output = Vec::with_capacity(len / self.buf_len); diff --git a/src/cqueue.rs b/src/cqueue.rs index 45921086..0b7ea222 100644 --- a/src/cqueue.rs +++ b/src/cqueue.rs @@ -1,5 +1,6 @@ //! Completion Queue +use bitflags::bitflags; use std::fmt::{self, Debug}; use std::mem; use std::mem::MaybeUninit; @@ -212,8 +213,10 @@ impl Entry { /// - Storing the selected buffer ID, if one was selected. See /// [`BUFFER_SELECT`](crate::squeue::Flags::BUFFER_SELECT) for more info. #[inline] - pub fn flags(&self) -> u32 { - self.0.flags + pub fn flags(&self) -> CompletionFlags { + // Use retain as the upper 16 bits store buffer ID if BUFFER_SELECT opt + // is enabled. + CompletionFlags::from_bits_retain(self.0.flags) } } @@ -261,8 +264,10 @@ impl Entry32 { /// - Storing the selected buffer ID, if one was selected. See /// [`BUFFER_SELECT`](crate::squeue::Flags::BUFFER_SELECT) for more info. #[inline] - pub fn flags(&self) -> u32 { - self.0 .0.flags + pub fn flags(&self) -> CompletionFlags { + // Use retain as the upper 16 bits store buffer ID if BUFFER_SELECT opt + // is enabled. + CompletionFlags::from_bits_retain(self.0 .0.flags) } /// Additional data available in 32-byte completion queue entries (CQEs). @@ -295,14 +300,56 @@ impl Debug for Entry32 { } } +bitflags!( + /// Request specific information carried in CQE flags field. + /// See man page for complete description: + /// https://man7.org/linux/man-pages/man7/io_uring.7.html + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub struct CompletionFlags: u32 { + /// If set, the upper 16 bits of the flags field carries the + /// buffer ID that was chosen for this request. The request + /// must have been issued with IOSQE_BUFFER_SELECT set, and + /// used with a request type that supports buffer selection. + /// Additionally, buffers must have been provided upfront + /// either via the IORING_OP_PROVIDE_BUFFERS or the + /// IORING_REGISTER_PBUF_RING methods. + const BUFFER = sys::IORING_CQE_F_BUFFER; + + /// If set, the application should expect more completions from + /// the request. This is used for requests that can generate + /// multiple completions, such as multi-shot requests, receive, + /// or accept. + const MORE = sys::IORING_CQE_F_MORE; + + /// If set, upon receiving the data from the socket in the + /// current request, the socket still had data left on + /// completion of this request. + const SOCK_NONEMPTY = sys::IORING_CQE_F_SOCK_NONEMPTY; + + /// Set for notification CQEs, as seen with the zero-copy + /// networking send and receive support. + const NOTIF = sys::IORING_CQE_F_NOTIF; + + /// If set, the buffer ID set in the completion will get more + /// completions. This means that the provided buffer has been + /// partially consumed and there's more buffer space left, and + /// hence the application should expect more completions with + /// this buffer ID. Each completion will continue where the + /// previous one left off. This can only happen if the provided + /// buffer ring has been setup with IOU_PBUF_RING_INC to allow + /// for incremental / partial consumption of buffers. + const BUF_MORE = sys::IORING_CQE_F_BUF_MORE; + } +); + /// Return whether the buffer will be reused by future CQE completions /// /// This corresponds to the `IORING_CQE_BUF_MORE` flag, and it signals to /// the consumer that it should expect further completions involging the /// related buffer ID when the registered buffer ring was setup with /// the `IOU_PBUF_RING_INC` flag. -pub fn buffer_more(flags: u32) -> bool { - flags & sys::IORING_CQE_F_BUF_MORE != 0 +pub fn buffer_more(flags: CompletionFlags) -> bool { + flags.contains(CompletionFlags::BUF_MORE) } /// Return which dynamic buffer was used by this operation. @@ -310,9 +357,9 @@ pub fn buffer_more(flags: u32) -> bool { /// This corresponds to the `IORING_CQE_F_BUFFER` flag (and related bit-shifting), /// and it signals to the consumer which provided contains the result of this /// operation. -pub fn buffer_select(flags: u32) -> Option { - if flags & sys::IORING_CQE_F_BUFFER != 0 { - let id = flags >> sys::IORING_CQE_BUFFER_SHIFT; +pub fn buffer_select(flags: CompletionFlags) -> Option { + if flags.contains(CompletionFlags::BUFFER) { + let id = flags.bits() >> sys::IORING_CQE_BUFFER_SHIFT; // FIXME // @@ -329,8 +376,8 @@ pub fn buffer_select(flags: u32) -> Option { /// This corresponds to the `IORING_CQE_F_MORE` flag, and it signals to /// the consumer that it should expect further CQE entries after this one, /// still from the same original SQE request (e.g. for multishot operations). -pub fn more(flags: u32) -> bool { - flags & sys::IORING_CQE_F_MORE != 0 +pub fn more(flags: CompletionFlags) -> bool { + flags.contains(CompletionFlags::MORE) } /// Return whether socket has more data ready to read. @@ -340,14 +387,14 @@ pub fn more(flags: u32) -> bool { /// /// The io_uring documentation says recv, recv-multishot, recvmsg, and recvmsg-multishot /// can provide this bit in their respective CQE. -pub fn sock_nonempty(flags: u32) -> bool { - flags & sys::IORING_CQE_F_SOCK_NONEMPTY != 0 +pub fn sock_nonempty(flags: CompletionFlags) -> bool { + flags.contains(CompletionFlags::SOCK_NONEMPTY) } /// Returns whether this completion event is a notification. /// /// This corresponds to the `IORING_CQE_F_NOTIF` flag, /// currently used by the [SendZc](crate::opcode::SendZc) operation. -pub fn notif(flags: u32) -> bool { - flags & sys::IORING_CQE_F_NOTIF != 0 +pub fn notif(flags: CompletionFlags) -> bool { + flags.contains(CompletionFlags::NOTIF) }