Repository: Thomasdezeeuw/a10
Pull request #106 (2024-03-30 22:52:10)

Add AsyncFd::(set_)socket_option


Patch:
diff --git a/src/net.rs b/src/net.rs
index dc67579..a4e2798 100644
--- a/src/net.rs
+++ b/src/net.rs
@@ -344,6 +344,44 @@ impl AsyncFd {
     pub const fn multishot_accept4<'fd>(&'fd self, flags: libc::c_int) -> MultishotAccept<'fd> {
         MultishotAccept::new(self, flags)
     }
+
+    /// Get socket option.
+    ///
+    /// At the time of writing this limited to the `SOL_SOCKET` level.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `T` is the valid type for the option.
+    #[doc(alias = "getsockopt")]
+    #[allow(clippy::cast_sign_loss)] // No valid negative level or optnames.
+    pub fn socket_option<'fd, T>(
+        &'fd self,
+        level: libc::c_int,
+        optname: libc::c_int,
+    ) -> SocketOption<'fd, T> {
+        // TODO: replace with `Box::new_uninit` once `new_uninit` is stable.
+        let value = Box::new(MaybeUninit::uninit());
+        SocketOption::new(self, value, (level as libc::__u32, optname as libc::__u32))
+    }
+
+    /// Set socket option.
+    ///
+    /// At the time of writing this limited to the `SOL_SOCKET` level.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `T` is the valid type for the option.
+    #[doc(alias = "setsockopt")]
+    #[allow(clippy::cast_sign_loss)] // No valid negative level or optnames.
+    pub fn set_socket_option<'fd, T>(
+        &'fd self,
+        level: libc::c_int,
+        optname: libc::c_int,
+        optvalue: T,
+    ) -> SetSocketOption<'fd, T> {
+        let value = Box::new(optvalue);
+        SetSocketOption::new(self, value, (level as libc::__u32, optname as libc::__u32))
+    }
 }
 
 /// [`Future`] to create a new [`socket`] asynchronously.
@@ -1003,6 +1041,49 @@ op_async_iter! {
     },
 }
 
+// SocketOption.
+op_future! {
+    fn AsyncFd::socket_option -> T,
+    struct SocketOption<'fd, T> {
+        /// Value for the socket option, needs to stay in memory so the kernel
+        /// can access it safely.
+        value: Box<MaybeUninit<T>>,
+    },
+    setup_state: flags: (libc::__u32, libc::__u32),
+    setup: |submission, fd, (value,), (level, optname)| unsafe {
+        let optvalue = ptr::addr_of_mut!(**value).cast();
+        let optlen = size_of::<T>() as u32;
+        submission.uring_command(libc::SOCKET_URING_OP_GETSOCKOPT, fd.fd(), level, optname, optvalue, optlen);
+    },
+    map_result: |this, (value,), optlen| {
+        debug_assert!(optlen == (size_of::<T>() as i32));
+        // SAFETY: the kernel initialised the value for us as part of the
+        // getsockopt call.
+        Ok(unsafe { MaybeUninit::assume_init(*value) })
+    },
+}
+
+// SetSocketOption.
+op_future! {
+    fn AsyncFd::set_socket_option -> (),
+    struct SetSocketOption<'fd, T> {
+        /// Value for the socket option, needs to stay in memory so the kernel
+        /// can access it safely.
+        value: Box<T>,
+    },
+    setup_state: flags: (libc::__u32, libc::__u32),
+    setup: |submission, fd, (value,), (level, optname)| unsafe {
+        let optvalue = ptr::addr_of_mut!(**value).cast();
+        let optlen = size_of::<T>() as u32;
+        submission.uring_command(libc::SOCKET_URING_OP_SETSOCKOPT, fd.fd(), level, optname, optvalue, optlen);
+    },
+    map_result: |result| Ok(debug_assert!(result == 0)),
+    extract: |this, (value,), res| -> Box<T> {
+        debug_assert!(res == 0);
+        Ok(value)
+    },
+}
+
 /// Trait that defines the behaviour of socket addresses.
 ///
 /// Linux (Unix) uses different address types for different sockets, to support
diff --git a/src/op.rs b/src/op.rs
index ba0555d..ec22647 100644
--- a/src/op.rs
+++ b/src/op.rs
@@ -1,6 +1,6 @@
 //! Code related to executing an asynchronous operations.
 
-use std::mem::replace;
+use std::mem::{replace, ManuallyDrop};
 use std::os::fd::RawFd;
 use std::task::{self, Poll};
 use std::{fmt, io, ptr};
@@ -387,6 +387,29 @@ impl Submission {
         self.inner.__bindgen_anon_3 = libc::io_uring_sqe__bindgen_ty_3 { rw_flags: flags };
     }
 
+    pub(crate) unsafe fn uring_command(
+        &mut self,
+        cmd_op: libc::__u32,
+        fd: RawFd,
+        level: libc::__u32,
+        optname: libc::__u32,
+        optvalue: *mut libc::c_void,
+        optlen: libc::__u32,
+    ) {
+        self.inner.opcode = libc::IORING_OP_URING_CMD as u8;
+        self.inner.fd = fd;
+        self.inner.__bindgen_anon_1 = libc::io_uring_sqe__bindgen_ty_1 {
+            __bindgen_anon_1: libc::io_uring_sqe__bindgen_ty_1__bindgen_ty_1 { cmd_op, __pad1: 0 },
+        };
+        self.inner.__bindgen_anon_2 = libc::io_uring_sqe__bindgen_ty_2 {
+            __bindgen_anon_1: libc::io_uring_sqe__bindgen_ty_2__bindgen_ty_1 { level, optname },
+        };
+        self.inner.__bindgen_anon_5 = libc::io_uring_sqe__bindgen_ty_5 { optlen };
+        self.inner.__bindgen_anon_6 = libc::io_uring_sqe__bindgen_ty_6 {
+            optval: ManuallyDrop::new(optvalue as libc::__u64),
+        };
+    }
+
     pub(crate) unsafe fn connect(
         &mut self,
         fd: RawFd,
diff --git a/src/sys.rs b/src/sys.rs
index 3976d85..ab1be27 100644
--- a/src/sys.rs
+++ b/src/sys.rs
@@ -1,15 +1,6 @@
 //! Code that should be moved to libc once C libraries have a wrapper.
 
-#![allow(dead_code, non_camel_case_types, non_snake_case)]
-#![allow(
-    clippy::cast_lossless,
-    clippy::cast_possible_truncation,
-    clippy::cast_possible_wrap,
-    clippy::missing_const_for_fn,
-    clippy::missing_safety_doc,
-    clippy::ptr_as_ptr,
-    clippy::unreadable_literal
-)]
+#![allow(warnings, clippy::all, clippy::pedantic, clippy::nursery)]
 
 /// Helper macro to execute a system call that returns an `io::Result`.
 macro_rules! syscall {
@@ -73,6 +64,10 @@ pub const IOSQE_IO_HARDLINK: u8 = 1 << IOSQE_IO_HARDLINK_BIT as u8;
 pub const IOSQE_ASYNC: u8 = 1 << IOSQE_ASYNC_BIT as u8;
 pub const IOSQE_BUFFER_SELECT: u8 = 1 << IOSQE_BUFFER_SELECT_BIT as u8;
 pub const IOSQE_CQE_SKIP_SUCCESS: u8 = 1 << IOSQE_CQE_SKIP_SUCCESS_BIT as u8;
+pub const SOCKET_URING_OP_SIOCINQ: __u32 = 0;
+pub const SOCKET_URING_OP_SIOCOUTQ: __u32 = 1;
+pub const SOCKET_URING_OP_GETSOCKOPT: __u32 = 2;
+pub const SOCKET_URING_OP_SETSOCKOPT: __u32 = 3;
 
 pub type __kernel_time64_t = ::std::os::raw::c_longlong;
 pub type __u8 = ::std::os::raw::c_uchar;
@@ -81,12 +76,12 @@ pub type __s32 = ::std::os::raw::c_int;
 pub type __u32 = ::std::os::raw::c_uint;
 pub type __u64 = ::std::os::raw::c_ulonglong;
 pub type __kernel_rwf_t = ::std::os::raw::c_int;
-pub type _bindgen_ty_18 = ::std::os::raw::c_uint;
+pub type _bindgen_ty_13 = ::std::os::raw::c_uint;
 pub type io_uring_op = ::std::os::raw::c_uint;
+pub type _bindgen_ty_14 = ::std::os::raw::c_uint;
+pub type _bindgen_ty_15 = ::std::os::raw::c_uint;
+pub type _bindgen_ty_16 = ::std::os::raw::c_uint;
 pub type _bindgen_ty_19 = ::std::os::raw::c_uint;
-pub type _bindgen_ty_20 = ::std::os::raw::c_uint;
-pub type _bindgen_ty_21 = ::std::os::raw::c_uint;
-pub type _bindgen_ty_24 = ::std::os::raw::c_uint;
 #[repr(C)]
 #[derive(Default)]
 pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
@@ -271,6 +266,21 @@ pub struct io_uring_buf_reg {
 }
 #[repr(C)]
 #[derive(Copy, Clone)]
+pub struct io_uring_buf_status {
+    pub buf_group: __u32,
+    pub head: __u32,
+    pub resv: [__u32; 8usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct io_uring_napi {
+    pub busy_poll_to: __u32,
+    pub prefer_busy_poll: __u8,
+    pub pad: [__u8; 3usize],
+    pub resv: __u64,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
 pub struct io_uring_getevents_arg {
     pub sigmask: __u64,
     pub sigmask_sz: __u32,
@@ -430,13 +440,13 @@ pub const IORING_FEAT_LINKED_FILE: u32 = 4096;
 pub const IORING_FEAT_REG_REG_RING: u32 = 8192;
 pub const IORING_RSRC_REGISTER_SPARSE: u32 = 1;
 pub const IORING_REGISTER_FILES_SKIP: i32 = -2;
-pub const IOSQE_FIXED_FILE_BIT: _bindgen_ty_18 = 0;
-pub const IOSQE_IO_DRAIN_BIT: _bindgen_ty_18 = 1;
-pub const IOSQE_IO_LINK_BIT: _bindgen_ty_18 = 2;
-pub const IOSQE_IO_HARDLINK_BIT: _bindgen_ty_18 = 3;
-pub const IOSQE_ASYNC_BIT: _bindgen_ty_18 = 4;
-pub const IOSQE_BUFFER_SELECT_BIT: _bindgen_ty_18 = 5;
-pub const IOSQE_CQE_SKIP_SUCCESS_BIT: _bindgen_ty_18 = 6;
+pub const IOSQE_FIXED_FILE_BIT: _bindgen_ty_13 = 0;
+pub const IOSQE_IO_DRAIN_BIT: _bindgen_ty_13 = 1;
+pub const IOSQE_IO_LINK_BIT: _bindgen_ty_13 = 2;
+pub const IOSQE_IO_HARDLINK_BIT: _bindgen_ty_13 = 3;
+pub const IOSQE_ASYNC_BIT: _bindgen_ty_13 = 4;
+pub const IOSQE_BUFFER_SELECT_BIT: _bindgen_ty_13 = 5;
+pub const IOSQE_CQE_SKIP_SUCCESS_BIT: _bindgen_ty_13 = 6;
 pub const IORING_OP_NOP: io_uring_op = 0;
 pub const IORING_OP_READV: io_uring_op = 1;
 pub const IORING_OP_WRITEV: io_uring_op = 2;
@@ -492,43 +502,47 @@ pub const IORING_OP_FUTEX_WAIT: io_uring_op = 51;
 pub const IORING_OP_FUTEX_WAKE: io_uring_op = 52;
 pub const IORING_OP_FUTEX_WAITV: io_uring_op = 53;
 pub const IORING_OP_FIXED_FD_INSTALL: io_uring_op = 54;
-pub const IORING_OP_LAST: io_uring_op = 55;
-pub const IORING_MSG_DATA: _bindgen_ty_19 = 0;
-pub const IORING_MSG_SEND_FD: _bindgen_ty_19 = 1;
-pub const IORING_CQE_BUFFER_SHIFT: _bindgen_ty_20 = 16;
-pub const IORING_REGISTER_BUFFERS: _bindgen_ty_21 = 0;
-pub const IORING_UNREGISTER_BUFFERS: _bindgen_ty_21 = 1;
-pub const IORING_REGISTER_FILES: _bindgen_ty_21 = 2;
-pub const IORING_UNREGISTER_FILES: _bindgen_ty_21 = 3;
-pub const IORING_REGISTER_EVENTFD: _bindgen_ty_21 = 4;
-pub const IORING_UNREGISTER_EVENTFD: _bindgen_ty_21 = 5;
-pub const IORING_REGISTER_FILES_UPDATE: _bindgen_ty_21 = 6;
-pub const IORING_REGISTER_EVENTFD_ASYNC: _bindgen_ty_21 = 7;
-pub const IORING_REGISTER_PROBE: _bindgen_ty_21 = 8;
-pub const IORING_REGISTER_PERSONALITY: _bindgen_ty_21 = 9;
-pub const IORING_UNREGISTER_PERSONALITY: _bindgen_ty_21 = 10;
-pub const IORING_REGISTER_RESTRICTIONS: _bindgen_ty_21 = 11;
-pub const IORING_REGISTER_ENABLE_RINGS: _bindgen_ty_21 = 12;
-pub const IORING_REGISTER_FILES2: _bindgen_ty_21 = 13;
-pub const IORING_REGISTER_FILES_UPDATE2: _bindgen_ty_21 = 14;
-pub const IORING_REGISTER_BUFFERS2: _bindgen_ty_21 = 15;
-pub const IORING_REGISTER_BUFFERS_UPDATE: _bindgen_ty_21 = 16;
-pub const IORING_REGISTER_IOWQ_AFF: _bindgen_ty_21 = 17;
-pub const IORING_UNREGISTER_IOWQ_AFF: _bindgen_ty_21 = 18;
-pub const IORING_REGISTER_IOWQ_MAX_WORKERS: _bindgen_ty_21 = 19;
-pub const IORING_REGISTER_RING_FDS: _bindgen_ty_21 = 20;
-pub const IORING_UNREGISTER_RING_FDS: _bindgen_ty_21 = 21;
-pub const IORING_REGISTER_PBUF_RING: _bindgen_ty_21 = 22;
-pub const IORING_UNREGISTER_PBUF_RING: _bindgen_ty_21 = 23;
-pub const IORING_REGISTER_SYNC_CANCEL: _bindgen_ty_21 = 24;
-pub const IORING_REGISTER_FILE_ALLOC_RANGE: _bindgen_ty_21 = 25;
-pub const IORING_REGISTER_LAST: _bindgen_ty_21 = 26;
-pub const IORING_REGISTER_USE_REGISTERED_RING: _bindgen_ty_21 = 2147483648;
-pub const IORING_RESTRICTION_REGISTER_OP: _bindgen_ty_24 = 0;
-pub const IORING_RESTRICTION_SQE_OP: _bindgen_ty_24 = 1;
-pub const IORING_RESTRICTION_SQE_FLAGS_ALLOWED: _bindgen_ty_24 = 2;
-pub const IORING_RESTRICTION_SQE_FLAGS_REQUIRED: _bindgen_ty_24 = 3;
-pub const IORING_RESTRICTION_LAST: _bindgen_ty_24 = 4;
+pub const IORING_OP_FTRUNCATE: io_uring_op = 55;
+pub const IORING_OP_LAST: io_uring_op = 56;
+pub const IORING_MSG_DATA: _bindgen_ty_14 = 0;
+pub const IORING_MSG_SEND_FD: _bindgen_ty_14 = 1;
+pub const IORING_CQE_BUFFER_SHIFT: _bindgen_ty_15 = 16;
+pub const IORING_REGISTER_BUFFERS: _bindgen_ty_16 = 0;
+pub const IORING_UNREGISTER_BUFFERS: _bindgen_ty_16 = 1;
+pub const IORING_REGISTER_FILES: _bindgen_ty_16 = 2;
+pub const IORING_UNREGISTER_FILES: _bindgen_ty_16 = 3;
+pub const IORING_REGISTER_EVENTFD: _bindgen_ty_16 = 4;
+pub const IORING_UNREGISTER_EVENTFD: _bindgen_ty_16 = 5;
+pub const IORING_REGISTER_FILES_UPDATE: _bindgen_ty_16 = 6;
+pub const IORING_REGISTER_EVENTFD_ASYNC: _bindgen_ty_16 = 7;
+pub const IORING_REGISTER_PROBE: _bindgen_ty_16 = 8;
+pub const IORING_REGISTER_PERSONALITY: _bindgen_ty_16 = 9;
+pub const IORING_UNREGISTER_PERSONALITY: _bindgen_ty_16 = 10;
+pub const IORING_REGISTER_RESTRICTIONS: _bindgen_ty_16 = 11;
+pub const IORING_REGISTER_ENABLE_RINGS: _bindgen_ty_16 = 12;
+pub const IORING_REGISTER_FILES2: _bindgen_ty_16 = 13;
+pub const IORING_REGISTER_FILES_UPDATE2: _bindgen_ty_16 = 14;
+pub const IORING_REGISTER_BUFFERS2: _bindgen_ty_16 = 15;
+pub const IORING_REGISTER_BUFFERS_UPDATE: _bindgen_ty_16 = 16;
+pub const IORING_REGISTER_IOWQ_AFF: _bindgen_ty_16 = 17;
+pub const IORING_UNREGISTER_IOWQ_AFF: _bindgen_ty_16 = 18;
+pub const IORING_REGISTER_IOWQ_MAX_WORKERS: _bindgen_ty_16 = 19;
+pub const IORING_REGISTER_RING_FDS: _bindgen_ty_16 = 20;
+pub const IORING_UNREGISTER_RING_FDS: _bindgen_ty_16 = 21;
+pub const IORING_REGISTER_PBUF_RING: _bindgen_ty_16 = 22;
+pub const IORING_UNREGISTER_PBUF_RING: _bindgen_ty_16 = 23;
+pub const IORING_REGISTER_SYNC_CANCEL: _bindgen_ty_16 = 24;
+pub const IORING_REGISTER_FILE_ALLOC_RANGE: _bindgen_ty_16 = 25;
+pub const IORING_REGISTER_PBUF_STATUS: _bindgen_ty_16 = 26;
+pub const IORING_REGISTER_NAPI: _bindgen_ty_16 = 27;
+pub const IORING_UNREGISTER_NAPI: _bindgen_ty_16 = 28;
+pub const IORING_REGISTER_LAST: _bindgen_ty_16 = 29;
+pub const IORING_REGISTER_USE_REGISTERED_RING: _bindgen_ty_16 = 2147483648;
+pub const IORING_RESTRICTION_REGISTER_OP: _bindgen_ty_19 = 0;
+pub const IORING_RESTRICTION_SQE_OP: _bindgen_ty_19 = 1;
+pub const IORING_RESTRICTION_SQE_FLAGS_ALLOWED: _bindgen_ty_19 = 2;
+pub const IORING_RESTRICTION_SQE_FLAGS_REQUIRED: _bindgen_ty_19 = 3;
+pub const IORING_RESTRICTION_LAST: _bindgen_ty_19 = 4;
 #[test]
 fn bindgen_test_layout___kernel_timespec() {
     const UNINIT: ::std::mem::MaybeUninit<__kernel_timespec> = ::std::mem::MaybeUninit::uninit();
@@ -2292,6 +2306,106 @@ fn bindgen_test_layout_io_uring_buf_reg() {
     );
 }
 #[test]
+fn bindgen_test_layout_io_uring_buf_status() {
+    const UNINIT: ::std::mem::MaybeUninit<io_uring_buf_status> = ::std::mem::MaybeUninit::uninit();
+    let ptr = UNINIT.as_ptr();
+    assert_eq!(
+        ::std::mem::size_of::<io_uring_buf_status>(),
+        40usize,
+        concat!("Size of: ", stringify!(io_uring_buf_status))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<io_uring_buf_status>(),
+        4usize,
+        concat!("Alignment of ", stringify!(io_uring_buf_status))
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).buf_group) as usize - ptr as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(io_uring_buf_status),
+            "::",
+            stringify!(buf_group)
+        )
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).head) as usize - ptr as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(io_uring_buf_status),
+            "::",
+            stringify!(head)
+        )
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(io_uring_buf_status),
+            "::",
+            stringify!(resv)
+        )
+    );
+}
+#[test]
+fn bindgen_test_layout_io_uring_napi() {
+    const UNINIT: ::std::mem::MaybeUninit<io_uring_napi> = ::std::mem::MaybeUninit::uninit();
+    let ptr = UNINIT.as_ptr();
+    assert_eq!(
+        ::std::mem::size_of::<io_uring_napi>(),
+        16usize,
+        concat!("Size of: ", stringify!(io_uring_napi))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<io_uring_napi>(),
+        8usize,
+        concat!("Alignment of ", stringify!(io_uring_napi))
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).busy_poll_to) as usize - ptr as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(io_uring_napi),
+            "::",
+            stringify!(busy_poll_to)
+        )
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).prefer_busy_poll) as usize - ptr as usize },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(io_uring_napi),
+            "::",
+            stringify!(prefer_busy_poll)
+        )
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).pad) as usize - ptr as usize },
+        5usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(io_uring_napi),
+            "::",
+            stringify!(pad)
+        )
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(io_uring_napi),
+            "::",
+            stringify!(resv)
+        )
+    );
+}
+#[test]
 fn bindgen_test_layout_io_uring_getevents_arg() {
     const UNINIT: ::std::mem::MaybeUninit<io_uring_getevents_arg> =
         ::std::mem::MaybeUninit::uninit();
diff --git a/sys/build.rs b/sys/build.rs
index abd5bea..bcb2ab8 100644
--- a/sys/build.rs
+++ b/sys/build.rs
@@ -44,16 +44,7 @@ fn main() {
 const HEADER: &str = "
 //! Code that should be moved to libc once C libraries have a wrapper.
 
-#![allow(dead_code, non_camel_case_types, non_snake_case)]
-#![allow(
-    clippy::cast_lossless,
-    clippy::cast_possible_truncation,
-    clippy::cast_possible_wrap,
-    clippy::missing_const_for_fn,
-    clippy::missing_safety_doc,
-    clippy::ptr_as_ptr,
-    clippy::unreadable_literal,
-)]
+#![allow(warnings, clippy::all, clippy::pedantic, clippy::nursery)]
 
 /// Helper macro to execute a system call that returns an `io::Result`.
 macro_rules! syscall {
@@ -117,4 +108,8 @@ pub const IOSQE_IO_HARDLINK: u8 = 1 << IOSQE_IO_HARDLINK_BIT as u8;
 pub const IOSQE_ASYNC: u8 = 1 << IOSQE_ASYNC_BIT as u8;
 pub const IOSQE_BUFFER_SELECT: u8 = 1 << IOSQE_BUFFER_SELECT_BIT as u8;
 pub const IOSQE_CQE_SKIP_SUCCESS: u8 = 1 << IOSQE_CQE_SKIP_SUCCESS_BIT as u8;
+pub const SOCKET_URING_OP_SIOCINQ: __u32 = 0;
+pub const SOCKET_URING_OP_SIOCOUTQ: __u32 = 1;
+pub const SOCKET_URING_OP_GETSOCKOPT: __u32 = 2;
+pub const SOCKET_URING_OP_SETSOCKOPT: __u32 = 3;
 ";


Test patch:
diff --git a/tests/async_fd/net.rs b/tests/async_fd/net.rs
index 4164fc4..ae4148e 100644
--- a/tests/async_fd/net.rs
+++ b/tests/async_fd/net.rs
@@ -14,15 +14,15 @@ use a10::cancel::{Cancel, CancelResult};
 use a10::io::ReadBufPool;
 use a10::net::{
     Accept, MultishotAccept, MultishotRecv, NoAddress, Recv, RecvN, RecvNVectored, Send, SendAll,
-    SendAllVectored, SendTo, Socket,
+    SendAllVectored, SendTo, SetSocketOption, Socket, SocketOption,
 };
 use a10::{Extract, Ring};
 
 use crate::async_fd::io::{BadBuf, BadBufSlice, BadReadBuf, BadReadBufSlice};
 use crate::util::{
     bind_and_listen_ipv4, bind_ipv4, block_on, expect_io_errno, expect_io_error_kind, init,
-    is_send, is_sync, next, poll_nop, require_kernel, syscall, tcp_ipv4_socket, test_queue,
-    udp_ipv4_socket, Waker,
+    is_send, is_sync, new_socket, next, poll_nop, require_kernel, syscall, tcp_ipv4_socket,
+    test_queue, udp_ipv4_socket, Waker,
 };
 
 const DATA1: &[u8] = b"Hello, World!";
@@ -1676,6 +1676,92 @@ fn shutdown() {
     assert_eq!(n, 0);
 }
 
+#[test]
+fn socket_option() {
+    require_kernel!(6, 7);
+
+    let sq = test_queue();
+    let waker = Waker::new();
+
+    is_send::<SocketOption<libc::c_int>>();
+    is_sync::<SocketOption<libc::c_int>>();
+
+    let domain = libc::AF_INET;
+    let r#type = libc::SOCK_STREAM | libc::SOCK_CLOEXEC;
+    let protocol = 0;
+    let socket = waker.block_on(new_socket(sq, domain, r#type, protocol));
+
+    let got_domain = waker
+        .block_on(socket.socket_option(libc::SOL_SOCKET, libc::SO_DOMAIN))
+        .unwrap();
+    assert_eq!(libc::AF_INET, got_domain);
+
+    let got_type = waker
+        .block_on(socket.socket_option(libc::SOL_SOCKET, libc::SO_TYPE))
+        .unwrap();
+    assert_eq!(libc::SOCK_STREAM, got_type);
+
+    let got_protocol = waker
+        .block_on(socket.socket_option(libc::SOL_SOCKET, libc::SO_PROTOCOL))
+        .unwrap();
+    assert_eq!(libc::IPPROTO_TCP, got_protocol);
+
+    let got_linger = waker
+        .block_on(socket.socket_option::<libc::linger>(libc::SOL_SOCKET, libc::SO_LINGER))
+        .unwrap();
+    assert_eq!(0, got_linger.l_onoff);
+    assert_eq!(0, got_linger.l_linger);
+
+    let got_error = waker
+        .block_on(socket.socket_option::<libc::c_int>(libc::SOL_SOCKET, libc::SO_ERROR))
+        .unwrap();
+    assert_eq!(0, got_error);
+}
+
+#[test]
+fn set_socket_option() {
+    require_kernel!(6, 7);
+
+    let sq = test_queue();
+    let waker = Waker::new();
+
+    is_send::<SetSocketOption<libc::c_int>>();
+    is_sync::<SetSocketOption<libc::c_int>>();
+
+    let domain = libc::AF_INET;
+    let r#type = libc::SOCK_STREAM | libc::SOCK_CLOEXEC;
+    let protocol = 0;
+    let socket = waker.block_on(new_socket(sq, domain, r#type, protocol));
+
+    waker
+        .block_on(socket.set_socket_option::<libc::c_int>(
+            libc::SOL_SOCKET,
+            libc::SO_INCOMING_CPU,
+            0,
+        ))
+        .unwrap();
+
+    let linger = libc::linger {
+        l_onoff: 1,
+        l_linger: 100,
+    };
+    let got_linger = waker
+        .block_on(
+            socket
+                .set_socket_option(libc::SOL_SOCKET, libc::SO_LINGER, linger)
+                .extract(),
+        )
+        .unwrap();
+    assert_eq!(linger.l_onoff, got_linger.l_onoff);
+    assert_eq!(linger.l_linger, got_linger.l_linger);
+
+    let got_linger = waker
+        .block_on(socket.socket_option::<libc::linger>(libc::SOL_SOCKET, libc::SO_LINGER))
+        .unwrap();
+    assert_eq!(linger.l_onoff, got_linger.l_onoff);
+    assert_eq!(linger.l_linger, got_linger.l_linger);
+}
+
 fn addr_storage(address: &SocketAddrV4) -> libc::sockaddr_in {
     // SAFETY: a `sockaddr_in` of all zeros is valid.
     let mut storage: libc::sockaddr_in = unsafe { mem::zeroed() };
diff --git a/tests/util/mod.rs b/tests/util/mod.rs
index 12efe42..205f287 100644
--- a/tests/util/mod.rs
+++ b/tests/util/mod.rs
@@ -441,7 +441,7 @@ pub(crate) async fn udp_ipv4_socket(sq: SubmissionQueue) -> AsyncFd {
     new_socket(sq, domain, r#type, protocol).await
 }
 
-async fn new_socket(
+pub(crate) async fn new_socket(
     sq: SubmissionQueue,
     domain: libc::c_int,
     r#type: libc::c_int,