Re: [PATCH RFC v3 4/6] rust: add virtio module
Stefano Garzarella <[email protected]> Fri, 7 Aug 2026 16:09:03 +0200
| Newsgroups | dev.linux.lists.virtualization,org.kernel.vger.linux-kernel,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <anXddihIKoU9ZxmT@sgarzare-redhat> |
On Sun, May 10, 2026 at 04:38:17PM +0300, Manos Pitsidianakis wrote: >Add module that exposes bindings for the virtio API. > >Signed-off-by: Manos Pitsidianakis <[email protected]> >--- > MAINTAINERS | 2 + > rust/kernel/lib.rs | 2 + > rust/kernel/virtio.rs | 423 ++++++++++++++++++++++++++++++++++++++++ > rust/kernel/virtio/utils.rs | 57 ++++++ > rust/kernel/virtio/virtqueue.rs | 314 +++++++++++++++++++++++++++++ > 5 files changed, 798 insertions(+) > >diff --git a/MAINTAINERS b/MAINTAINERS >index 48c9c666d90b5a256ab6fae1f42508b789a0ce50..e8012f708df5d4ee858c82aec3269e615fc8caad 100644 >--- a/MAINTAINERS >+++ b/MAINTAINERS >@@ -27935,6 +27935,8 @@ M: Manos Pitsidianakis <[email protected]> > L: [email protected] > S: Maintained > F: rust/helpers/virtio.c >+F: rust/kernel/virtio.rs >+F: rust/kernel/virtio/ > > VIRTIO CRYPTO DRIVER > M: Gonglei <[email protected]> >diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs >index d93292d47420f1f298a452ade5feefedce5ade86..061394f441dfa27f99939b5c4160e4161a7eaa1e 100644 >--- a/rust/kernel/lib.rs >+++ b/rust/kernel/lib.rs >@@ -161,6 +161,8 @@ > pub mod uaccess; > #[cfg(CONFIG_USB = "y")] > pub mod usb; >+#[cfg(CONFIG_VIRTIO = "y")] >+pub mod virtio; > pub mod workqueue; > pub mod xarray; > >diff --git a/rust/kernel/virtio.rs b/rust/kernel/virtio.rs >new file mode 100644 >index 0000000000000000000000000000000000000000..a5a4e2cfec55bc7cbca0d42b198fde6cd2b25f1c >--- /dev/null >+++ b/rust/kernel/virtio.rs >@@ -0,0 +1,423 @@ >+// SPDX-License-Identifier: GPL-2.0 >+ >+//! VIRTIO abstraction. >+//! >+//! To implement a VIRTIO driver: >+//! >+//! - Implement the [`Driver`] trait for your driver type (use [`virtio_device_table`] macro to >+//! declare the `ID_TABLE` associated item) >+//! - Use the [`module_virtio_driver`] macro to declare your module >+ >+use crate::{ >+ bindings, >+ device_id::RawDeviceId, >+ error::{ >+ from_result, >+ to_result, >+ Error, >+ Result, // >+ }, >+ ffi::c_uint, >+ prelude::*, >+ types::Opaque, // >+}; >+ >+use core::{ >+ marker::PhantomData, >+ pin::Pin, >+ ptr::NonNull, // >+}; >+ >+pub mod utils; >+pub mod virtqueue; >+ >+/// IdTable type for virtio drivers. >+pub type IdTable<T> = &'static dyn crate::device_id::IdTable<DeviceId, T>; >+ >+/// A VIRTIO device id. >+/// >+/// [`struct virtio_device_id`]: srctree/include/linux/mod_devicetable.h >+#[repr(transparent)] >+#[derive(Clone, Copy)] >+pub struct DeviceId(bindings::virtio_device_id); >+ >+// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct virtio_device_id` and >+// does not add additional invariants, so it's safe to transmute to `RawType`. >+unsafe impl RawDeviceId for DeviceId { >+ type RawType = bindings::virtio_device_id; >+} >+ >+impl DeviceId { >+ #[inline] >+ /// Create a new device id >+ pub const fn new(device: VirtioID) -> Self { >+ Self::new_with_vendor(device, VIRTIO_DEV_ANY_ID) >+ } >+ >+ #[inline] >+ /// Create a new device id with vendor >+ pub const fn new_with_vendor(device: VirtioID, vendor: u32) -> Self { >+ // Replace with `bindings::virtio_device_id::default()` once stabilized for `const`. >+ // SAFETY: FFI type is valid to be zero-initialized. >+ let mut ret: bindings::virtio_device_id = unsafe { core::mem::zeroed() }; >+ ret.device = device as u32; >+ ret.vendor = vendor; >+ Self(ret) >+ } >+} >+ >+/// Create a virtio `IdTable` with its alias for modpost. >+#[macro_export] >+macro_rules! virtio_device_table { >+ ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data:expr) => { >+ const $table_name: $crate::device_id::IdArray< >+ $crate::virtio::DeviceId, >+ $id_info_type, >+ { $table_data.len() }, >+ > = $crate::device_id::IdArray::new_without_index($table_data); >+ >+ $crate::module_device_table!("virtio", $module_table_name, $table_name); >+ }; >+} >+ >+/// Declares a kernel module that exposes a single virtio driver. >+#[macro_export] >+macro_rules! module_virtio_driver { >+($($f:tt)*) => { >+ $crate::module_driver!(<T>, $crate::virtio::Adapter<T>, { $($f)* }); >+}; >+} >+ >+/// The Virtio driver trait. >+/// >+/// Drivers must implement this trait in order to get a virtio driver registered. >+pub trait Driver: Send { >+ /// The type holding information about each device id supported by the driver. >+ // TODO: Use `associated_type_defaults` once stabilized: >+ // >+ // ``` >+ // type IdInfo: 'static = (); >+ // ``` >+ type IdInfo: 'static; >+ >+ /// The table of device ids supported by the driver. >+ const ID_TABLE: IdTable<Self::IdInfo>; >+ >+ /// virtio driver probe. >+ /// >+ /// Called when a new virtio device is added or discovered. Implementers should >+ /// attempt to initialize the device here, but should try not sleep since driver data is set >+ /// after this method returns successfully. >+ fn probe(dev: &Device<crate::device::Core>) -> impl PinInit<Self, Error>; >+ >+ /// virtio driver init. >+ /// >+ /// Called after a virtio device is probed successfully, can sleep. >+ fn init(&self, dev: &Device<crate::device::Bound>) -> Result; >+ >+ /// virtio driver remove. >+ /// >+ /// Called when a [`Device`] is removed from its [`Driver`]. Implementing this callback >+ /// is optional. >+ /// >+ /// This callback serves as a place for drivers to perform teardown operations that require a >+ /// `&Device<Core>` or `&Device<Bound>` reference. For instance, drivers may try to perform I/O >+ /// operations to gracefully tear down the device. >+ /// >+ /// Otherwise, release operations for driver resources should be performed in `Self::drop`. >+ fn remove(dev: &Device<crate::device::Core>, this: Pin<&Self>) { >+ _ = (dev, this); >+ } >+} >+ >+/// Abstraction for the virtio device structure (`struct virtio_device`). >+/// >+/// [`struct virtio_device`]: srctree/include/linux/virtio.h >+#[repr(transparent)] >+pub struct Device<Ctx: crate::device::DeviceContext = crate::device::Normal>( >+ Opaque<bindings::virtio_device>, >+ PhantomData<Ctx>, >+); >+ >+impl<Ctx: crate::device::DeviceContext> Device<Ctx> { >+ #[inline] >+ fn as_raw(&self) -> *mut bindings::virtio_device { >+ self.0.get() >+ } >+} >+ >+// SAFETY: `virtio::Device` is a transparent wrapper of `struct virtio_device`. >+// The offset is guaranteed to point to a valid device field inside `virtio::Device`. >+unsafe impl<Ctx: crate::device::DeviceContext> crate::device::AsBusDevice<Ctx> for Device<Ctx> { >+ const OFFSET: usize = core::mem::offset_of!(bindings::virtio_device, dev); >+} >+ >+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic >+// argument. >+kernel::impl_device_context_deref!(unsafe { Device }); >+ >+impl<Ctx: crate::device::DeviceContext> Device<Ctx> { >+ // TODO: return VirtioID >+ /// Returns the virtio device ID. >+ #[inline] >+ pub fn device_id(&self) -> u32 { >+ // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a >+ // `struct virtio_device`. >+ unsafe { (*self.as_raw()).id.device } >+ } >+ >+ /// Returns the virtio vendor ID. >+ #[inline] >+ pub fn vendor_id(&self) -> u32 { >+ // SAFETY: `self.as_raw` is a valid pointer to a `struct virtio_device`. >+ unsafe { (*self.as_raw()).id.vendor } >+ } >+ >+ /// Reset device. >+ #[doc(alias = "virtio_reset_device")] >+ #[inline] >+ pub fn reset(&self) { >+ // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a >+ // `struct virtio_device`. >+ unsafe { bindings::virtio_reset_device(self.as_raw()) } >+ } >+ >+ /// Mark device as ready. >+ #[doc(alias = "virtio_device_ready")] >+ #[inline] >+ pub fn ready(&self) { >+ // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a >+ // `struct virtio_device`. >+ unsafe { bindings::virtio_device_ready(self.as_raw()) } >+ } >+ >+ /// Return virtqueues for this device. >+ #[doc(alias = "virtio_find_vqs")] >+ pub fn find_vqs(&self, info: &[virtqueue::VirtqueueInfo]) -> Result<virtqueue::Virtqueues> { >+ let mut vqs = KVec::with_capacity(info.len(), GFP_KERNEL)?; >+ // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a >+ // `struct virtio_device`. >+ to_result(unsafe { >+ bindings::virtio_find_vqs( >+ self.as_raw(), >+ info.len().try_into()?, >+ vqs.spare_capacity_mut().as_mut_ptr().cast(), >+ info.as_ptr().cast_mut().cast(), >+ core::ptr::null_mut(), >+ ) >+ })?; >+ // SAFETY: virtio_find_vqs returned successfully so `vqs` must be populated. >+ unsafe { vqs.inc_len(info.len()) }; >+ let mut inner = KVec::with_capacity(vqs.len(), GFP_KERNEL)?; >+ for vq in vqs { >+ inner.push(NonNull::new(vq).ok_or(EINVAL)?, GFP_KERNEL)?; >+ } >+ Ok(virtqueue::Virtqueues { inner }) >+ } >+ >+ /// Delete virtqueues from this device. >+ pub(crate) fn del_vqs(&self) { >+ // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a >+ // `struct virtio_device`. >+ let config = unsafe { (*self.as_raw()).config }; >+ // SAFETY: `config` points to a valid virtqueue config struct. >+ if let Some(del_vqs) = unsafe { (*config).del_vqs } { >+ // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a >+ // `struct virtio_device`. >+ unsafe { del_vqs(self.as_raw()) } >+ } >+ } >+ >+ /// Checks if the device has a feature bit. >+ #[inline] >+ pub fn has_feature(&self, fbit: c_uint) -> bool { >+ // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a >+ // `struct virtio_device`. >+ unsafe { bindings::virtio_has_feature(self.as_raw(), fbit) } >+ } >+} >+ >+impl<Ctx: crate::device::DeviceContext> AsRef<crate::device::Device<Ctx>> for Device<Ctx> { >+ #[inline] >+ fn as_ref(&self) -> &crate::device::Device<Ctx> { >+ // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid >+ // `struct virtio_device`. >+ let dev = unsafe { core::ptr::addr_of_mut!((*self.as_raw()).dev) }; >+ >+ // SAFETY: `dev` points to a valid `struct device`. >+ unsafe { crate::device::Device::from_raw(dev) } >+ } >+} >+ >+/// An adapter for the registration of virtio drivers. >+pub struct Adapter<T: Driver>(T); >+ >+// SAFETY: >+// - `bindings::virtio_driver` is a C type declared as `repr(C)`. >+// - `T` is the type of the driver's device private data. >+// - `struct virtio_driver` embeds a `struct device_driver`. >+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. >+unsafe impl<T: Driver + 'static> crate::driver::DriverLayout for Adapter<T> { >+ type DriverType = bindings::virtio_driver; >+ type DriverData = T; >+ const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); >+} >+ >+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if >+// a preceding call to `register` has been successful. >+unsafe impl<T: Driver + 'static> crate::driver::RegistrationOps for Adapter<T> { >+ unsafe fn register( >+ vdrv: &Opaque<Self::DriverType>, >+ name: &'static CStr, >+ module: &'static ThisModule, >+ ) -> Result { >+ // SAFETY: It's safe to set the fields of `struct virtio_driver` on initialization. >+ unsafe { >+ (*vdrv.get()).driver.name = name.as_char_ptr(); >+ (*vdrv.get()).id_table = T::ID_TABLE.as_ptr(); >+ (*vdrv.get()).probe = Some(Self::probe_callback); >+ (*vdrv.get()).remove = Some(Self::remove_callback); >+ } >+ >+ // SAFETY: `vdrv` is guaranteed to be a valid `DriverType`. >+ to_result(unsafe { bindings::__register_virtio_driver(vdrv.get(), module.0) }) >+ } >+ >+ unsafe fn unregister(vdrv: &Opaque<Self::DriverType>) { >+ // SAFETY: `vdrv` is guaranteed to be a valid `DriverType`. >+ unsafe { bindings::unregister_virtio_driver(vdrv.get()) } >+ } >+} >+ >+impl<T: Driver + 'static> Adapter<T> { >+ extern "C" fn probe_callback(vdev: *mut bindings::virtio_device) -> c_int { >+ // SAFETY: The kernel only ever calls the probe callback with a valid pointer to a `struct >+ // virtio_device`. >+ // >+ // INVARIANT: `vdev` is valid for the duration of `probe_callback()`. >+ let dev = unsafe { &*vdev.cast::<Device<crate::device::CoreInternal>>() }; >+ from_result(|| { >+ let data = T::probe(dev); >+ >+ dev.as_ref().set_drvdata(data)?; >+ // SAFETY: `Device::set_drvdata()` was just called so it's safe to borrow the data. >+ let data = unsafe { dev.as_ref().drvdata_borrow::<T>() }; >+ dev.ready(); >+ if let Err(err) = T::init(&data, dev) { >+ // SAFETY: `Device::set_drvdata()` was just called so it's safe to re-obtain the >+ // data. >+ let data = unsafe { dev.as_ref().drvdata_obtain::<T>() }.unwrap(); >+ T::remove(dev, data.as_ref()); >+ drop(data); >+ return Err(err); >+ } >+ Ok(0) >+ }) >+ } >+ >+ extern "C" fn remove_callback(vdev: *mut bindings::virtio_device) { >+ // SAFETY: The kernel only ever calls the remove callback with a valid pointer to a `struct >+ // virtio_device`. >+ // >+ // INVARIANT: `vdev` is valid for the duration of `remove_callback()`. >+ let dev = unsafe { &*vdev.cast::<Device<crate::device::CoreInternal>>() }; >+ >+ // SAFETY: `remove_callback` is only ever called after a successful call to >+ // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called >+ // and stored a `Pin<KBox<T>>`. >+ let data = unsafe { dev.as_ref().drvdata_borrow::<T>() }; >+ >+ T::remove(dev, data); >+ dev.reset(); >+ } >+} >+ >+/// Any vendor >+pub const VIRTIO_DEV_ANY_ID: u32 = 0xffffffff; >+ >+/// Virtio IDs >+/// >+/// C header: [`include/uapi/linux/virtio_ids.h`](srctree/include/uapi/linux/virtio_ids.h) >+#[repr(u32)] >+pub enum VirtioID { >+ /// virtio net >+ Net = bindings::VIRTIO_ID_NET, >+ /// virtio block >+ Block = bindings::VIRTIO_ID_BLOCK, >+ /// virtio console >+ Console = bindings::VIRTIO_ID_CONSOLE, >+ /// virtio rng >+ Rng = bindings::VIRTIO_ID_RNG, >+ /// virtio balloon >+ Balloon = bindings::VIRTIO_ID_BALLOON, >+ /// virtio ioMemory >+ IOMem = bindings::VIRTIO_ID_IOMEM, >+ /// virtio remote processor messaging >+ RPMSG = bindings::VIRTIO_ID_RPMSG, >+ /// virtio scsi >+ Scsi = bindings::VIRTIO_ID_SCSI, >+ /// 9p virtio console >+ NineP = bindings::VIRTIO_ID_9P, >+ /// virtio WLAN MAC >+ Mac80211Wlan = bindings::VIRTIO_ID_MAC80211_WLAN, >+ /// virtio remoteproc serial link >+ RPROCSerial = bindings::VIRTIO_ID_RPROC_SERIAL, >+ /// Virtio caif >+ CAIF = bindings::VIRTIO_ID_CAIF, >+ /// virtio memory balloon >+ MemoryBalloon = bindings::VIRTIO_ID_MEMORY_BALLOON, >+ /// virtio GPU >+ GPU = bindings::VIRTIO_ID_GPU, >+ /// virtio clock/timer >+ Clock = bindings::VIRTIO_ID_CLOCK, >+ /// virtio input >+ Input = bindings::VIRTIO_ID_INPUT, >+ /// virtio vsock transport >+ VSock = bindings::VIRTIO_ID_VSOCK, >+ /// virtio crypto >+ Crypto = bindings::VIRTIO_ID_CRYPTO, >+ /// virtio signal distribution device >+ SignalDist = bindings::VIRTIO_ID_SIGNAL_DIST, >+ /// virtio pstore device >+ Pstore = bindings::VIRTIO_ID_PSTORE, >+ /// virtio IOMMU >+ Iommu = bindings::VIRTIO_ID_IOMMU, >+ /// virtio mem >+ Mem = bindings::VIRTIO_ID_MEM, >+ /// virtio sound >+ Sound = bindings::VIRTIO_ID_SOUND, >+ /// virtio filesystem >+ FS = bindings::VIRTIO_ID_FS, >+ /// virtio pmem >+ PMem = bindings::VIRTIO_ID_PMEM, >+ /// virtio rpmb >+ RPMB = bindings::VIRTIO_ID_RPMB, >+ /// virtio mac80211-hwsim >+ Mac80211Hwsim = bindings::VIRTIO_ID_MAC80211_HWSIM, >+ /// virtio video encoder >+ VideoEncoder = bindings::VIRTIO_ID_VIDEO_ENCODER, >+ /// virtio video decoder >+ VideoDecoder = bindings::VIRTIO_ID_VIDEO_DECODER, >+ /// virtio SCMI >+ SCMI = bindings::VIRTIO_ID_SCMI, >+ /// virtio nitro secure module >+ NitroSecMod = bindings::VIRTIO_ID_NITRO_SEC_MOD, >+ /// virtio i2c adapter >+ I2CAdapter = bindings::VIRTIO_ID_I2C_ADAPTER, >+ /// virtio watchdog >+ Watchdog = bindings::VIRTIO_ID_WATCHDOG, >+ /// virtio can >+ CAN = bindings::VIRTIO_ID_CAN, >+ /// virtio dmabuf >+ DMABuf = bindings::VIRTIO_ID_DMABUF, >+ /// virtio parameter server >+ ParamServ = bindings::VIRTIO_ID_PARAM_SERV, >+ /// virtio audio policy >+ AudioPolicy = bindings::VIRTIO_ID_AUDIO_POLICY, >+ /// virtio bluetooth >+ BT = bindings::VIRTIO_ID_BT, >+ /// virtio gpio >+ GPIO = bindings::VIRTIO_ID_GPIO, >+ /// virtio spi >+ SPI = bindings::VIRTIO_ID_SPI, >+} >diff --git a/rust/kernel/virtio/utils.rs b/rust/kernel/virtio/utils.rs >new file mode 100644 >index 0000000000000000000000000000000000000000..8dca373f10a6906b891a9420c13cd8e9e929c412 >--- /dev/null >+++ b/rust/kernel/virtio/utils.rs >@@ -0,0 +1,57 @@ >+// SPDX-License-Identifier: GPL-2.0 >+ >+//! Helper types and utilities >+ >+macro_rules! endian_type { >+ ($old_type:ident, $new_type:ident, $to_new:ident, $from_new:ident) => { >+ /// An unsigned integer type of with an explicit endianness. >+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Default, pin_init::Zeroable)] >+ #[repr(transparent)] >+ pub struct $new_type($old_type); >+ >+ $crate::static_assert!( >+ ::core::mem::align_of::<$new_type>() == ::core::mem::align_of::<$old_type>() >+ ); >+ $crate::static_assert!( >+ ::core::mem::size_of::<$new_type>() == ::core::mem::size_of::<$old_type>() >+ ); >+ >+ impl $new_type { >+ /// Convert to CPU/native endianness. >+ pub const fn to_cpu(self) -> $old_type { >+ $old_type::$from_new(self.0) >+ } >+ } >+ >+ impl PartialEq<$old_type> for $new_type { >+ fn eq(&self, other: &$old_type) -> bool { >+ self.0 == $old_type::$to_new(*other) >+ } >+ } >+ >+ impl PartialEq<$new_type> for $old_type { >+ fn eq(&self, other: &$new_type) -> bool { >+ $old_type::$to_new(other.0) == *self >+ } >+ } >+ >+ impl From<$new_type> for $old_type { >+ fn from(v: $new_type) -> $old_type { >+ v.to_cpu() >+ } >+ } >+ >+ impl From<$old_type> for $new_type { >+ fn from(v: $old_type) -> $new_type { >+ $new_type($old_type::$to_new(v)) >+ } >+ } >+ }; >+} >+ >+endian_type!(u16, Le16, to_le, from_le); >+endian_type!(u32, Le32, to_le, from_le); >+endian_type!(u64, Le64, to_le, from_le); >+endian_type!(u16, Be16, to_be, from_be); >+endian_type!(u32, Be32, to_be, from_be); >+endian_type!(u64, Be64, to_be, from_be); >diff --git a/rust/kernel/virtio/virtqueue.rs b/rust/kernel/virtio/virtqueue.rs >new file mode 100644 >index 0000000000000000000000000000000000000000..781326c1723eb67a8c62524795ba431141fea202 >--- /dev/null >+++ b/rust/kernel/virtio/virtqueue.rs >@@ -0,0 +1,314 @@ >+// SPDX-License-Identifier: GPL-2.0 >+ >+//! Virtqueue functionality. >+//! >+//! # Discovering virtqueues >+//! >+//! Inside your driver's [`kernel::virtio::Driver::probe`] method, call >+//! [`kernel::virtio::Device::find_vqs`] method with your [`VirtqueueInfo`] struct. >+//! >+//! # Passing data to virtqueues >+//! >+//! Create your data as owned [`SGTable`] with: >+//! >+//! - [`Virtqueue::new_readable_sgtable`] for data that can be read from the device, and >+//! - [`Virtqueue::new_writable_sgtable`] for data that can be written from the device >+//! >+//! These methods will make sure to create the scatter-gather tables and DMA map them to the >+//! appropriate VIRTIO transport. >+//! >+//! To add the tables to the virtqueue, call [`Virtqueue::add_sgs`]. >+ >+use crate::{ >+ alloc::{ >+ allocator::VmallocPageIter, >+ Flags, // >+ }, >+ bindings, >+ device::Bound, >+ dma::DataDirection, >+ error::{ >+ code::{ >+ EINVAL, >+ ENOENT, // >+ }, >+ to_result, >+ Error, >+ Result, // >+ }, >+ page::AsPageIter, >+ prelude::*, >+ scatterlist::{ >+ Owned, >+ SGTable, // >+ }, >+ str::{ >+ self, >+ CStr, // >+ }, >+ types::Opaque, >+ virtio::Device, // >+}; >+ >+use core::{ >+ ptr::NonNull, // >+}; >+ >+/// Info for a virtqueue. >+/// >+/// [`struct virtqueue_info`]: srctree/include/linux/virtio_config.h >+#[doc(alias = "virtqueue_info")] >+#[repr(transparent)] >+pub struct VirtqueueInfo(Opaque<bindings::virtqueue_info>); >+ >+impl VirtqueueInfo { >+ #[inline] >+ /// Create a new [`VirtqueueInfo`] >+ pub const fn new( >+ name: &'static CStr, >+ ctx: bool, >+ callback: Option<unsafe extern "C" fn(*mut bindings::virtqueue)>, >+ ) -> Self { >+ Self(Opaque::new(bindings::virtqueue_info { >+ name: str::as_char_ptr_in_const_context(name), >+ ctx, >+ callback, >+ })) >+ } >+} >+ >+/// A container for discovered virtqueues returned by [`Device::find_vqs`] method. >+/// >+/// This type dereferences to a `NonNull<Virtqueue>` slice. >+/// >+/// It deletes the virtqueues when dropped. >+pub struct Virtqueues { >+ pub(crate) inner: KVec<NonNull<Virtqueue>>, >+} >+ >+impl Drop for Virtqueues { >+ fn drop(&mut self) { >+ let inner = core::mem::take(&mut self.inner); >+ let Some(first) = inner.into_iter().next() else { >+ return; >+ }; >+ let first_ref = unsafe { first.as_ref() }; >+ let Ok(vdev) = first_ref.dev() else { >+ return; >+ }; >+ vdev.del_vqs(); >+ } >+} >+ >+impl core::ops::Deref for Virtqueues { >+ type Target = [NonNull<Virtqueue>]; >+ >+ #[inline] >+ fn deref(&self) -> &Self::Target { >+ &self.inner >+ } >+} >+ >+/// An opaque handler for a virtqueue. >+/// >+/// [`struct virtqueue`]: srctree/include/linux/virtio.h >+#[repr(transparent)] >+pub struct Virtqueue(Opaque<bindings::virtqueue>); >+ >+impl Virtqueue { >+ /// Create a [`Virtqueue`] from a raw pointer. >+ /// >+ /// # Safety >+ /// >+ /// Callers must ensure that `ptr` is a properly initialized valid `virtqueue` pointer. >+ #[inline] >+ pub unsafe fn from_raw<'a>(ptr: *mut bindings::virtqueue) -> &'a Self { >+ // SAFETY: The safety requirements of this function guarantee that `ptr` is a valid >+ // pointer to a `struct virtqueue` for the duration of `'a`. >+ unsafe { &*ptr.cast() } >+ } >+ >+ /// Obtain the raw `struct virtqueue *`. >+ #[inline] >+ pub(crate) fn as_raw(&self) -> *mut bindings::virtqueue { >+ self.0.get() >+ } >+ >+ /// Get the [`Device`] associated with this virtqueue. >+ #[inline] >+ pub fn dev(&self) -> Result<&Device<Bound>> { >+ // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct >+ // virtqueue`. >+ if unsafe { (*self.as_raw()).vdev }.is_null() { >+ return Err(ENOENT); >+ } >+ // SAFETY: the pointer has been promised to be valid when self was created >+ Ok(unsafe { &*(&*self.as_raw()).vdev.cast::<Device<Bound>>() }) >+ } >+ >+ /// Get the vring size. >+ #[inline] >+ #[doc(alias = "virtqueue_get_vring_size")] >+ pub fn vring_size(&self) -> u32 { >+ // SAFETY: the pointer has been promised to be valid when self was created >+ unsafe { bindings::virtqueue_get_vring_size(self.as_raw()) } >+ } >+ >+ /// Notify virtqueue. >+ #[inline] >+ #[doc(alias = "virtqueue_notify")] >+ pub fn notify(&self) -> bool { >+ // SAFETY: the pointer has been promised to be valid when self was created >+ unsafe { bindings::virtqueue_notify(self.as_raw()) } >+ } >+ >+ /// Kick and prepare virtqueue. >+ #[inline] >+ #[doc(alias = "virtqueue_kick_prepare")] >+ pub fn kick_prepare(&self) -> bool { >+ // SAFETY: the pointer has been promised to be valid when self was created >+ unsafe { bindings::virtqueue_kick_prepare(self.as_raw()) } >+ } >+ >+ /// Kick virtqueue. >+ #[inline] >+ #[doc(alias = "virtqueue_kick")] >+ pub fn kick(&self) -> bool { >+ // SAFETY: the pointer has been promised to be valid when self was created >+ unsafe { bindings::virtqueue_kick(self.as_raw()) } >+ } >+ >+ /// Enable virtqueue's callback. >+ #[inline] >+ #[doc(alias = "virtqueue_enable_cb")] >+ pub fn enable_cb(&self) -> bool { >+ // SAFETY: the pointer has been promised to be valid when self was created >+ unsafe { bindings::virtqueue_enable_cb(self.as_raw()) } >+ } >+ >+ /// Disable virtqueue's callback. >+ #[inline] >+ #[doc(alias = "virtqueue_disable_cb")] >+ pub fn disable_cb(&self) { >+ // SAFETY: the pointer has been promised to be valid when self was created >+ unsafe { bindings::virtqueue_disable_cb(self.as_raw()) } >+ } >+ >+ /// Get a buffer from the virtqueue, if available. >+ /// >+ /// This method returns a pointer to the `token` value passed in [`Virtqueue::add_sgs`] method >+ /// and the amount of bytes that were written by the device. >+ #[inline] >+ #[doc(alias = "virtqueue_get_buf")] >+ pub fn get_buf(&'_ self) -> Option<(NonNull<u8>, u32)> { >+ let mut len = 0; >+ // SAFETY: the pointer has been promised to be valid when self was created >+ let ptr = unsafe { bindings::virtqueue_get_buf(self.as_raw(), &mut len) }; >+ Some((NonNull::new(ptr.cast())?, len)) >+ } >+ >+ /// Add a list of scatter-gather lists to virtqueue. >+ #[inline] >+ #[doc(alias = "virtqueue_add_sgs")] >+ pub fn add_sgs<'token, PIn, POut, Token>( >+ &'_ self, >+ out_sgs: &'token SGTableReadable<POut>, >+ in_sgs: &'token SGTableWritable<PIn>, >+ token: Pin<&'token Token>, >+ gfp: Flags, >+ ) -> Result >+ where >+ for<'a> PIn: AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static, >+ for<'a> POut: AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static, >+ { >+ let out_sgs_num = u32::try_from(out_sgs.inner.iter().count())?; >+ let in_sgs_num = u32::try_from(in_sgs.inner.iter().count())?; >+ >+ let Some(total_size) = out_sgs_num.checked_add(in_sgs_num) else { >+ return Err(EINVAL); >+ }; >+ >+ let mut sgs = KVec::with_capacity(2, GFP_KERNEL)?; Should we use the `gfp` flags insteaf of GFP_KERNEL in this function ? The rest LGTM with a quick look. Stefano