All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device
@ 2026-05-10 13:38 Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
                   ` (6 more replies)
  0 siblings, 7 replies; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-10 13:38 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Manos Pitsidianakis, Peter Hilber, Stefano Garzarella,
	Stefan Hajnoczi, Viresh Kumar, Michael S. Tsirkin, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel, Manos Pitsidianakis

Hi all, this RFC series adds Rust bindings for Virtio drivers
(frontends in virtio parlance).

As a PoC, it also adds a sample virtio-rtc driver which performs
capability discovery through the virtqueue without registering any clock.

Before I send a cleaned-up non-RFC I would like some initial feedback
(i.e. is it something the upstream wants?)

This was tested with the rust-vmm vhost-device-rtc device backend that I
wrote[^0]:

[^0]: https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-rtc

Instructions:

  Run the daemon in a separate terminal:

  $ cargo run --bin vhost-device-rtc -- -s /tmp/rtc.sock

  Then run the VM:

  $ qemu-system-aarch64 \
    -machine type=virt,virtualization=off,acpi=on \
    -cpu host \
    -smp 8 \
    -accel kvm \
    -drive if=virtio,format=qcow2,file=./debian-13-nocloud-arm64-daily.qcow2 \
    -device virtio-net-pci,netdev=unet \
    -device virtio-scsi-pci \
    -serial mon:stdio \
    -m 8192 \
    -object memory-backend-memfd,id=mem,size=8G,share=on \
    -numa node,memdev=mem \
    -display none \
    -vga none \
    -kernel /path/to/linux/build/arch/arm64/boot/Image \
    -device vhost-user-test-device,chardev=rtc,id=rtc,virtio-id=17,num_vqs=2,vq_size=1024 \
    -chardev socket,path=/tmp/rtc.sock,id=rtc \
    ...

  Example output:
    [    1.105238] rust_virtio_rtc: Probe Rust virtio driver sample.
    [    1.105645] rust_virtio_rtc: Found 1 vqs.
    [    1.136050] rust_virtio_rtc: process_requestq got buf 16 bytes
    [    1.136125] rust_virtio_rtc: Got response! Ok(RespCfg { head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] }, num_clocks: Le16(3), reserved: [0, 0, 0, 0, 0, 0] })
    [    1.136701] rust_virtio_rtc: Got response! Ok(RespClockCap { head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] }, clock_type: 3, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0, 0, 0] })
    [    1.136724] rust_virtio_rtc virtio0: cannot expose clock 0 (type 3, variant 0, flags 0) to userspace
    [    1.137259] rust_virtio_rtc: Got response! Ok(RespRead { head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] }, clock_reading: Le64(1777890485031060388) })
    [    1.137277] rust_virtio_rtc: #0 clock reading = 1777890485031060388
    [    1.137749] rust_virtio_rtc: Got response! Ok(RespClockCap { head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] }, clock_type: 1, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0, 0, 0] })
    [    1.137769] rust_virtio_rtc virtio0: cannot expose clock 1 (type 1, variant 0, flags 0) to userspace
    [    1.138247] rust_virtio_rtc: Got response! Ok(RespRead { head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] }, clock_reading: Le64(1777890485032086075) })
    [    1.138264] rust_virtio_rtc: #1 clock reading = 1777890485032086075
    [    1.138730] rust_virtio_rtc: Got response! Ok(RespClockCap { head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] }, clock_type: 2, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0, 0, 0] })
    [    1.138751] rust_virtio_rtc virtio0: cannot expose clock 2 (type 2, variant 0, flags 0) to userspace
    [    1.139253] rust_virtio_rtc: Got response! Ok(RespRead { head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] }, clock_reading: Le64(338567896865557) })
    [    1.139270] rust_virtio_rtc: #2 clock reading = 338567896865557

Concerns - Notes - TODOs
========================

- Virtqueue lifetimes don't neatly apply to Rust as expected, so a lot
  of times we have to go through unsafe pointer dereferences (though
  which are guaranteed by Virtio subsystem to be valid, for example when
  a callback is called with the vq argument). There's a potential for
  misuse and definitely could use better thinking.
- `struct virtio_device` is not reference-counted like other implemented
  device types in rust/kernel. Maybe we need to change C API first to
  make them reference counted, assuming this doesn't break anything?
- The sample driver obviously conflicts with the C implementation, so
  this would either need to move out of samples/ or figure out some way
  to handle this in kbuild.
- kernel::virtio module and its types need a few rustdoc examples that I
  will add in followup series
- Note that the registration of RTC clocks etc in the sample driver is
  not done, I'm putting it off until I receive some feedback first. The
  sample driver otherwise does send and receive data from the virtqueue
  as a PoC.

PS: No LLMs used so any mistakes and goofs are solely written by me.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
Changes in v3:
- Removed unused methods from virtio API
- Clean up how scattergather lists are added to virtqueues by using
  owned SGTables only, and make the API safe(r)
- Add RAII cleanup for find_vqs return value that calls del_vqs
- Reset device after remove callback
- Significantly clean up sample driver as a result of the other cleanups
- Link to v2: https://lore.kernel.org/r/20260509-rust-virtio-v2-0-c1e30ec2bd21@pitsidianak.is

Changes in v2:
- Move helper ifdefs to helper file (thanks Alice)
- Changed CONFIG checks to IS_ENABLED to allow for CONFIG_VIRTIO=m
- Split all use imports to one item per line according to style guide
- Fixed wait_for_completion_interruptible*() rustdocs
- Use Jiffy type alias in wait_for_completion_interruptible_timeout()
- Pepper and salt #[inline]s wherever appropriate as per style guide
- Split probe() into probe() and init() to allow cleaning up if init
  fails
- Remove unnecessary Send and Sync unsafe impls for
  kernel::virtio::Device
- Remove unnecessary LeSize and BeSize 
- Accept Option<_> for virtqueue callback when creating a VirtqueueInfo
- Made all vq buffer adding operations unsafe
- Use AtomicU16 instead of Cell<u16> for sample virtio driver
- Fix RespHead field types in sample virtio driver
- Fix response error checking in sample virtio driver
- Change some device contexts in method signatures
- Link to v1: https://lore.kernel.org/r/20260505-rust-virtio-v1-0-9563383909e4@pitsidianak.is

---
Manos Pitsidianakis (6):
      rust/bindings: generate virtio bindings
      rust/helpers: add virtio.c
      rust/kernel/device: return parent at same context
      rust: add virtio module
      rust: impl interruptible waits for Completion
      samples/rust: Add sample virtio-rtc driver [WIP]

 MAINTAINERS                     |   9 +
 rust/bindings/bindings_helper.h |   5 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/virtio.c           |  37 ++++
 rust/kernel/device.rs           |   2 +-
 rust/kernel/lib.rs              |   2 +
 rust/kernel/sync/completion.rs  |  42 +++-
 rust/kernel/virtio.rs           | 423 ++++++++++++++++++++++++++++++++++++++++
 rust/kernel/virtio/utils.rs     |  57 ++++++
 rust/kernel/virtio/virtqueue.rs | 314 +++++++++++++++++++++++++++++
 samples/rust/Kconfig            |  15 ++
 samples/rust/Makefile           |   1 +
 samples/rust/rust_virtio_rtc.rs | 403 ++++++++++++++++++++++++++++++++++++++
 13 files changed, 1309 insertions(+), 2 deletions(-)
---
base-commit: 028ef9c96e96197026887c0f092424679298aae8
change-id: 20260504-rust-virtio-8523b01dfdc2

Best regards,
-- 
Manos Pitsidianakis <manos@pitsidianak.is>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH RFC v3 1/6] rust/bindings: generate virtio bindings
  2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
@ 2026-05-10 13:38 ` Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-10 13:38 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Manos Pitsidianakis, Peter Hilber, Stefano Garzarella,
	Stefan Hajnoczi, Viresh Kumar, Michael S. Tsirkin, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel, Manos Pitsidianakis

Add virtio headers if CONFIG_VIRTIO is enabled.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
 rust/bindings/bindings_helper.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952c2b29ab82d5d481063a1cf48bccf..1cbe1a4b5647fd646c1be3c5c80fb24ae7b97a4a 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -151,3 +151,8 @@ const vm_flags_t RUST_CONST_HELPER_VM_NOHUGEPAGE = VM_NOHUGEPAGE;
 #include "../../drivers/android/binder/rust_binder_events.h"
 #include "../../drivers/android/binder/page_range_helper.h"
 #endif
+
+#if IS_ENABLED(CONFIG_VIRTIO)
+#include <linux/virtio_config.h>
+#include <uapi/linux/virtio_ids.h>
+#endif /* IS_ENABLED(CONFIG_VIRTIO) */

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC v3 2/6] rust/helpers: add virtio.c
  2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
@ 2026-05-10 13:38 ` Manos Pitsidianakis
  2026-05-21  9:03   ` Eugenio Perez Martin
  2026-05-10 13:38 ` [PATCH RFC v3 3/6] rust/kernel/device: return parent at same context Manos Pitsidianakis
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-10 13:38 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Manos Pitsidianakis, Peter Hilber, Stefano Garzarella,
	Stefan Hajnoczi, Viresh Kumar, Michael S. Tsirkin, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel, Manos Pitsidianakis

Some internal kernel virtio API functions are inline macros, so define
their symbols in a helper file.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
 MAINTAINERS            |  6 ++++++
 rust/helpers/helpers.c |  1 +
 rust/helpers/virtio.c  | 37 +++++++++++++++++++++++++++++++++++++
 3 files changed, 44 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f004da89b1aa339116908f642e894..48c9c666d90b5a256ab6fae1f42508b789a0ce50 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27930,6 +27930,12 @@ F:	include/uapi/linux/virtio_*.h
 F:	net/vmw_vsock/virtio*
 F:	tools/virtio/
 
+VIRTIO CORE API BINDINGS [RUST]
+M:	Manos Pitsidianakis <manos@pitsidianak.is>
+L:	virtualization@lists.linux.dev
+S:	Maintained
+F:	rust/helpers/virtio.c
+
 VIRTIO CRYPTO DRIVER
 M:	Gonglei <arei.gonglei@huawei.com>
 L:	virtualization@lists.linux.dev
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index a3c42e51f00a0990bea81ebce6e99bb397ce7533..5dc0d2f2ee6bd2ae8e6abfe4baa247c1963967f6 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -61,6 +61,7 @@
 #include "time.c"
 #include "uaccess.c"
 #include "usb.c"
+#include "virtio.c"
 #include "vmalloc.c"
 #include "wait.c"
 #include "workqueue.c"
diff --git a/rust/helpers/virtio.c b/rust/helpers/virtio.c
new file mode 100644
index 0000000000000000000000000000000000000000..46aeeb063158823e66477777b3cd4bd1525df330
--- /dev/null
+++ b/rust/helpers/virtio.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef CONFIG_VIRTIO
+#include <linux/virtio_config.h>
+
+__rust_helper bool
+rust_helper_virtio_has_feature(const struct virtio_device *vdev,
+			       unsigned int fbit)
+{
+	return virtio_has_feature(vdev, fbit);
+}
+__rust_helper void rust_helper_virtio_get_features(struct virtio_device *vdev,
+						   u64 *features_out)
+{
+	return virtio_get_features(vdev, features_out);
+}
+
+__rust_helper int rust_helper_virtio_find_vqs(struct virtio_device *vdev,
+					      unsigned int nvqs,
+					      struct virtqueue *vqs[],
+					      struct virtqueue_info vqs_info[],
+					      struct irq_affinity *desc)
+{
+	return virtio_find_vqs(vdev, nvqs, vqs, vqs_info, desc);
+}
+
+__rust_helper void rust_helper_virtio_device_ready(struct virtio_device *dev)
+{
+	return virtio_device_ready(dev);
+}
+
+__rust_helper bool
+rust_helper_virtio_is_little_endian(struct virtio_device *vdev)
+{
+	return virtio_is_little_endian(vdev);
+}
+#endif /* CONFIG_VIRTIO */

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC v3 3/6] rust/kernel/device: return parent at same context
  2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
@ 2026-05-10 13:38 ` Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 4/6] rust: add virtio module Manos Pitsidianakis
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-10 13:38 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Manos Pitsidianakis, Peter Hilber, Stefano Garzarella,
	Stefan Hajnoczi, Viresh Kumar, Michael S. Tsirkin, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel, Manos Pitsidianakis

The return value of `Device::parent` method was defaulting to `Normal`
context, instead of having the same context as `self`.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
 rust/kernel/device.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 94e0548e76871d8b7de309c1f1c7b77bb49738ed..07d2f1225cd87f4fc091a5d6cb626726ae175894 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -341,7 +341,7 @@ pub(crate) fn as_raw(&self) -> *mut bindings::device {
 
     /// Returns a reference to the parent device, if any.
     #[cfg_attr(not(CONFIG_AUXILIARY_BUS), expect(dead_code))]
-    pub(crate) fn parent(&self) -> Option<&Device> {
+    pub(crate) fn parent(&self) -> Option<&Device<Ctx>> {
         // SAFETY:
         // - By the type invariant `self.as_raw()` is always valid.
         // - The parent device is only ever set at device creation.

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC v3 4/6] rust: add virtio module
  2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
                   ` (2 preceding siblings ...)
  2026-05-10 13:38 ` [PATCH RFC v3 3/6] rust/kernel/device: return parent at same context Manos Pitsidianakis
@ 2026-05-10 13:38 ` Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 5/6] rust: impl interruptible waits for Completion Manos Pitsidianakis
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-10 13:38 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Manos Pitsidianakis, Peter Hilber, Stefano Garzarella,
	Stefan Hajnoczi, Viresh Kumar, Michael S. Tsirkin, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel, Manos Pitsidianakis

Add module that exposes bindings for the virtio API.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
 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 <manos@pitsidianak.is>
 L:	virtualization@lists.linux.dev
 S:	Maintained
 F:	rust/helpers/virtio.c
+F:	rust/kernel/virtio.rs
+F:	rust/kernel/virtio/
 
 VIRTIO CRYPTO DRIVER
 M:	Gonglei <arei.gonglei@huawei.com>
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)?;
+
+        for entry in out_sgs.inner.iter() {
+            sgs.push(entry, GFP_KERNEL)?;
+        }
+        for entry in in_sgs.inner.iter() {
+            sgs.push(entry, GFP_KERNEL)?;
+        }
+
+        if usize::try_from(total_size) != Ok(sgs.len()) {
+            return Err(EINVAL);
+        }
+        // SAFETY: `self` has been promised to be valid when self was created
+        to_result(unsafe {
+            bindings::virtqueue_add_sgs(
+                self.as_raw(),
+                sgs.as_ptr().cast_mut().cast(),
+                out_sgs_num,
+                in_sgs_num,
+                NonNull::new(core::ptr::from_ref::<Token>(&*token.as_ref()).cast_mut())
+                    .unwrap()
+                    .as_ptr()
+                    .cast(),
+                gfp.as_raw(),
+            )
+        })
+    }
+
+    /// Create a scatter-gather table readable by the device.
+    pub fn new_readable_sgtable<P>(
+        &self,
+        pages: P,
+        flags: Flags,
+    ) -> impl PinInit<SGTableReadable<P>, Error> + '_
+    where
+        for<'a> P: AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static,
+    {
+        pin_init!(SGTableReadable {
+            inner <- SGTable::new(
+                self.dev().unwrap().as_ref().parent().unwrap(),
+                pages,
+                DataDirection::ToDevice,
+                flags,
+            ),
+        }? Error)
+    }
+
+    /// Create a scatter-gather table writable by the device.
+    pub fn new_writable_sgtable<P>(
+        &self,
+        pages: P,
+        flags: Flags,
+    ) -> impl PinInit<SGTableWritable<P>, Error> + '_
+    where
+        for<'a> P: AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static,
+    {
+        pin_init!(SGTableWritable {
+            inner <- SGTable::new(
+                self.dev().unwrap().as_ref().parent().unwrap(),
+                pages,
+                DataDirection::FromDevice,
+                flags,
+            ),
+        }? Error)
+    }
+}
+
+/// An [`SGTable<Owned<P>>`] that is guaranteed to have been DMA-mapped as device-readable.
+///
+/// Created by [`Virtqueue::new_readable_sgtable`].
+#[pin_data]
+pub struct SGTableReadable<P> {
+    #[pin]
+    inner: SGTable<Owned<P>>,
+}
+
+/// An [`SGTable<Owned<P>>`] that is guaranteed to have been DMA-mapped as device-writable.
+///
+/// Created by [`Virtqueue::new_writable_sgtable`].
+#[pin_data]
+pub struct SGTableWritable<P> {
+    #[pin]
+    inner: SGTable<Owned<P>>,
+}

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC v3 5/6] rust: impl interruptible waits for Completion
  2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
                   ` (3 preceding siblings ...)
  2026-05-10 13:38 ` [PATCH RFC v3 4/6] rust: add virtio module Manos Pitsidianakis
@ 2026-05-10 13:38 ` Manos Pitsidianakis
  2026-05-10 13:38 ` [PATCH RFC v3 6/6] samples/rust: Add sample virtio-rtc driver [WIP] Manos Pitsidianakis
  2026-05-18  8:12 ` Rust virtio drivers in Linux Manos Pitsidianakis
  6 siblings, 0 replies; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-10 13:38 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Manos Pitsidianakis, Peter Hilber, Stefano Garzarella,
	Stefan Hajnoczi, Viresh Kumar, Michael S. Tsirkin, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel, Manos Pitsidianakis

Allow Completion to wait interruptibly.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
 rust/kernel/sync/completion.rs | 42 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/sync/completion.rs b/rust/kernel/sync/completion.rs
index c50012a940a3c7a3e0edf302c8f833bdc4415200..17c9e48a5359c0c885be9ebf6843e74d5abe56e5 100644
--- a/rust/kernel/sync/completion.rs
+++ b/rust/kernel/sync/completion.rs
@@ -6,7 +6,12 @@
 //!
 //! C header: [`include/linux/completion.h`](srctree/include/linux/completion.h)
 
-use crate::{bindings, prelude::*, types::Opaque};
+use crate::{
+    bindings,
+    prelude::*,
+    time::Jiffies,
+    types::Opaque, //
+};
 
 /// Synchronization primitive to signal when a certain task has been completed.
 ///
@@ -109,4 +114,39 @@ pub fn wait_for_completion(&self) {
         // SAFETY: `self.as_raw()` is a pointer to a valid `struct completion`.
         unsafe { bindings::wait_for_completion(self.as_raw()) };
     }
+
+    /// Wait for completion of an interruptible task without a timeout.
+    ///
+    /// See also [`Completion::complete_all`].
+    #[inline]
+    pub fn wait_for_completion_interruptible(&self) -> Result {
+        // SAFETY: `self.as_raw()` is a pointer to a valid `struct completion`.
+        let err = unsafe { bindings::wait_for_completion_interruptible(self.as_raw()) };
+        if err < 0 {
+            Err(Error::from_errno(err))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Wait for completion of an interruptible task with a timeout.
+    ///
+    /// See also [`Completion::complete_all`].
+    #[inline]
+    pub fn wait_for_completion_interruptible_timeout(
+        &self,
+        timeout_jiffies: Jiffies,
+    ) -> Result<Jiffies> {
+        // SAFETY: `self.as_raw()` is a pointer to a valid `struct completion`.
+        let ret: c_long = unsafe {
+            bindings::wait_for_completion_interruptible_timeout(self.as_raw(), timeout_jiffies)
+        };
+        if ret == 0 {
+            return Err(ETIMEDOUT);
+        }
+        match Jiffies::try_from(ret) {
+            Ok(ret) => Ok(ret),
+            Err(_) => Err(Error::from_errno(ret as c_int)),
+        }
+    }
 }

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC v3 6/6] samples/rust: Add sample virtio-rtc driver [WIP]
  2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
                   ` (4 preceding siblings ...)
  2026-05-10 13:38 ` [PATCH RFC v3 5/6] rust: impl interruptible waits for Completion Manos Pitsidianakis
@ 2026-05-10 13:38 ` Manos Pitsidianakis
  2026-05-18  8:12 ` Rust virtio drivers in Linux Manos Pitsidianakis
  6 siblings, 0 replies; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-10 13:38 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Manos Pitsidianakis, Peter Hilber, Stefano Garzarella,
	Stefan Hajnoczi, Viresh Kumar, Michael S. Tsirkin, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel, Manos Pitsidianakis

While the driver queries clocks and capabilities for each clock, it
doesn't actually register them yet (TODO).

Until I implement missing functionality, there is some dead code and
some missing SAFETY comments.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
 MAINTAINERS                     |   1 +
 samples/rust/Kconfig            |  15 ++
 samples/rust/Makefile           |   1 +
 samples/rust/rust_virtio_rtc.rs | 403 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 420 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e8012f708df5d4ee858c82aec3269e615fc8caad..3ed579e8d3cc64d1749cf261cd68f6338a830c4d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27937,6 +27937,7 @@ S:	Maintained
 F:	rust/helpers/virtio.c
 F:	rust/kernel/virtio.rs
 F:	rust/kernel/virtio/
+F:	samples/rust/rust_virtio_rtc.rs
 
 VIRTIO CRYPTO DRIVER
 M:	Gonglei <arei.gonglei@huawei.com>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index c49ab910634596aea4a1a73dac87585e084f420a..96a16aecc27198fd99f4ffd0ecdf0bc0876860c6 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -179,4 +179,19 @@ config SAMPLE_RUST_HOSTPROGS
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_VIRTIO_RTC
+	tristate "Rust Virtio RTC driver"
+	depends on VIRTIO
+	depends on PTP_1588_CLOCK_OPTIONAL
+	help
+	 This driver provides current time from a Virtio RTC device. The driver
+	 provides the time through one or more clocks. The Virtio RTC PTP
+	 clocks and/or the Real Time Clock driver for Virtio RTC must be
+	 enabled to expose the clocks to userspace.
+
+	 To compile this code as a module, choose M here: the module will be
+	 called rust_virtio_rtc.
+
+	 If unsure, say M.
+
 endif # SAMPLES_RUST
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 6c0aaa58ccccfd12ef019f68ca784f6d977bc668..0142fd8656bb8cdc95b7ef54e3183b5e51358954 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o
 obj-$(CONFIG_SAMPLE_RUST_CONFIGFS)		+= rust_configfs.o
 obj-$(CONFIG_SAMPLE_RUST_SOC)			+= rust_soc.o
+obj-$(CONFIG_SAMPLE_RUST_VIRTIO_RTC)		+= rust_virtio_rtc.o
 
 rust_print-y := rust_print_main.o rust_print_events.o
 
diff --git a/samples/rust/rust_virtio_rtc.rs b/samples/rust/rust_virtio_rtc.rs
new file mode 100644
index 0000000000000000000000000000000000000000..81f8b377f6c92716c17ea11542fb4262cfd90e1c
--- /dev/null
+++ b/samples/rust/rust_virtio_rtc.rs
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust virtio driver sample.
+
+use core::{
+    ptr::NonNull, //
+    sync::atomic::{
+        AtomicU16,
+        Ordering, //
+    },
+};
+
+use kernel::{
+    device::{
+        Bound,
+        Core, //
+    },
+    new_mutex,    //
+    new_spinlock, //
+    prelude::*,
+    sync::{
+        Completion,
+        Mutex,
+        SpinLock, //
+    },
+    virtio::{
+        self,
+        utils::*,
+        virtqueue::*, //
+    },
+};
+
+use pin_init::{
+    stack_pin_init,
+    stack_try_pin_init, //
+};
+
+#[pin_data]
+struct Token {
+    resp_actual_size: u32,
+    #[pin]
+    responded: Completion,
+}
+
+#[derive(Copy, Clone, Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_req_head")]
+struct ReqHead {
+    msg_type: Le16,
+    reserved: [u8; 6],
+}
+
+#[derive(Copy, Clone, Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_resp_head")]
+struct RespHead {
+    status: u8,
+    reserved: [u8; 7],
+}
+
+#[derive(Copy, Clone, Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_resp_cfg")]
+struct RespCfg {
+    head: RespHead,
+    /** # of clocks -> clock ids < num_clocks are valid */
+    num_clocks: Le16,
+    reserved: [u8; 6],
+}
+
+#[derive(Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_req_clock_cap")]
+struct ReqClockCap {
+    head: ReqHead,
+    clock_id: Le16,
+    reserved: [u8; 6],
+}
+
+#[derive(Copy, Clone, Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_resp_clock_cap")]
+struct RespClockCap {
+    head: RespHead,
+    clock_type: u8,
+    leap_second_smearing: u8,
+    flags: u8,
+    reserved: [u8; 5],
+}
+
+#[derive(Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_req_read")]
+struct ReqRead {
+    head: ReqHead,
+    clock_id: Le16,
+    reserved: [u8; 6],
+}
+
+#[derive(Copy, Clone, Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_resp_read")]
+struct RespRead {
+    head: RespHead,
+    clock_reading: Le64,
+}
+
+#[repr(u8)]
+enum ClockType {
+    #[doc(alias = "VIRTIO_RTC_CLOCK_UTC")]
+    Utc = 0,
+    #[doc(alias = "VIRTIO_RTC_CLOCK_TAI")]
+    Tai = 1,
+    #[doc(alias = "VIRTIO_RTC_CLOCK_MONOTONIC")]
+    Monotonic = 2,
+    #[doc(alias = "VIRTIO_RTC_CLOCK_UTC_SMEARED")]
+    UtcSmeared = 3,
+    #[doc(alias = "VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED")]
+    UtcMaybeSmeared = 4,
+}
+
+/// Send a message and receive reply
+fn send<Request: Zeroable + 'static, Response: Zeroable + Copy + 'static>(
+    req_data: Request,
+    vq: &SpinLock<VirtioRtcVq>,
+    timeout_jiffies: c_ulong,
+) -> Result<Response> {
+    // FIXME: This lock should also disable irqs.
+    let guard = vq.lock();
+
+    let req = VBox::<Request>::new(req_data, GFP_KERNEL)?;
+    let mut resp = VBox::<Response>::new_uninit(GFP_KERNEL)?;
+    let resp_ptr = NonNull::new(resp.as_mut_ptr()).unwrap();
+
+    stack_pin_init!(let token = pin_init!(Token {
+        resp_actual_size: 0,
+        responded <- Completion::new(),
+    }));
+    stack_try_pin_init!(let req_sgs = guard.reqvq().new_readable_sgtable(req, GFP_KERNEL));
+    let req_sgs: Pin<&mut _> = req_sgs?;
+    stack_try_pin_init!(let resp_sgs = guard.reqvq().new_writable_sgtable(resp, GFP_KERNEL));
+    let resp_sgs: Pin<&mut _> = resp_sgs?;
+
+    guard
+        .reqvq()
+        .add_sgs(&req_sgs, &resp_sgs, token.as_ref(), GFP_ATOMIC)?;
+
+    if guard.reqvq().kick_prepare() {
+        guard.reqvq().notify();
+    }
+    drop(guard);
+
+    if timeout_jiffies > 0 {
+        token
+            .responded
+            .wait_for_completion_interruptible_timeout(timeout_jiffies)?;
+    } else {
+        token.responded.wait_for_completion_interruptible()?;
+    }
+
+    if token.resp_actual_size as usize >= core::mem::size_of::<RespHead>() {
+        // SAFETY: all response types contain a `RespHead` header at the start.
+        let head: &RespHead = unsafe { resp_ptr.cast().as_ref() };
+        match head.status {
+            0 => {
+                // OK, do nothing.
+            }
+            1 => return Err(ENOTSUPP),
+            2 => return Err(ENODEV),
+            3 => return Err(EINVAL),
+            4 | 5_u8..=u8::MAX => return Err(EIO),
+        }
+    } else if token.resp_actual_size as usize != core::mem::size_of::<Response>() {
+        return Err(EINVAL);
+    }
+    // SAFETY: we have checked that the device wrote the correct amount of bytes for this type and
+    // has returned a successful status code.
+    let resp = unsafe { *resp_ptr.as_ref() };
+
+    Ok(resp)
+}
+
+const VIRTIO_RTC_REQ_READ: u16 = 0x0001;
+const VIRTIO_RTC_REQ_CFG: u16 = 0x1000;
+const VIRTIO_RTC_REQ_CLOCK_CAP: u16 = 0x1001;
+
+struct VirtioRtcVq {
+    inner: Virtqueues,
+}
+
+// SAFETY: `VirtioRtcVq` is safe to be send to any task.
+unsafe impl Send for VirtioRtcVq {}
+
+impl VirtioRtcVq {
+    fn new(inner: Virtqueues) -> impl PinInit<SpinLock<Self>> {
+        new_spinlock!(Self { inner })
+    }
+
+    fn reqvq(&self) -> &Virtqueue {
+        unsafe { self.inner[0].as_ref() }
+    }
+}
+
+#[pin_data(PinnedDrop)]
+struct VirtioRtcDriver {
+    #[pin]
+    virtqueues: SpinLock<VirtioRtcVq>,
+    num_clocks: AtomicU16,
+    #[pin]
+    registered_clocks: Mutex<KVec<()>>,
+}
+
+#[pinned_drop]
+impl PinnedDrop for VirtioRtcDriver {
+    fn drop(self: Pin<&mut Self>) {
+        pr_info!("Remove Rust virtio driver sample.\n");
+    }
+}
+
+extern "C" fn vq_requestq_callback(vq: *mut kernel::bindings::virtqueue) {
+    // SAFETY: The kernel called this virtqueue callback and it must have provided a valid `vq`
+    // pointer
+    let vq = unsafe { Virtqueue::from_raw(vq) };
+    let dev: &virtio::Device<Bound> = vq.dev().expect("Could not get device");
+    let data = dev
+        .as_ref()
+        .drvdata::<VirtioRtcDriver>()
+        .expect("Could not borrow drvdata");
+    data.process_requestq();
+}
+
+impl VirtioRtcDriver {
+    /// Submit `VIRTIO_RTC_REQ_CFG` and return response (`num_clocks`)
+    fn req_cfg(&self) -> Result<u16> {
+        let head = ReqHead {
+            msg_type: VIRTIO_RTC_REQ_CFG.into(),
+            reserved: [0; 6],
+        };
+        let response: RespCfg = send(head, &self.virtqueues, 0)?;
+        pr_info!("Got response! {response:?}\n");
+
+        Ok(response.num_clocks.into())
+    }
+
+    fn process_requestq(&self) {
+        let mut cb_enabled = true;
+        loop {
+            // FIXME: This lock should also disable irqs.
+            let guard = self.virtqueues.lock();
+            if cb_enabled {
+                guard.reqvq().disable_cb();
+                cb_enabled = false;
+            }
+            if let Some((token, len)) = guard.reqvq().get_buf() {
+                drop(guard);
+                pr_info!("process_requestq got buf {len} bytes\n");
+                let mut token = token.cast::<Token>();
+                // SAFETY: pointer points to a valid Token that we have added to the virtqueue.
+                let token_ref = unsafe { token.as_mut() };
+                token_ref.resp_actual_size = len;
+                token_ref.responded.complete_all();
+                pr_info!("process_requestq ok\n");
+            } else {
+                if guard.reqvq().enable_cb() {
+                    return;
+                }
+                cb_enabled = true;
+            }
+        }
+    }
+
+    fn clock_cap(&self, clock_id: u16) -> Result<RespClockCap> {
+        let req = ReqClockCap {
+            head: ReqHead {
+                msg_type: VIRTIO_RTC_REQ_CLOCK_CAP.into(),
+                reserved: [0; 6],
+            },
+            clock_id: clock_id.into(),
+            reserved: [0; 6],
+        };
+        let response: RespClockCap = send(req, &self.virtqueues, 0)?;
+        pr_info!("Got response: {response:?}\n");
+        Ok(response)
+    }
+
+    fn read(&self, clock_id: u16) -> Result<u64> {
+        let req = ReqRead {
+            head: ReqHead {
+                msg_type: VIRTIO_RTC_REQ_READ.into(),
+                reserved: [0; 6],
+            },
+            clock_id: clock_id.into(),
+            reserved: [0; 6],
+        };
+        let response: RespRead = send(req, &self.virtqueues, 0)?;
+        pr_info!("Got response: {response:?}\n");
+        Ok(response.clock_reading.into())
+    }
+}
+
+impl virtio::Driver for VirtioRtcDriver {
+    type IdInfo = ();
+
+    /// The table of device ids supported by the driver.
+    const ID_TABLE: virtio::IdTable<Self::IdInfo> = &VIRTIO_RTC_TABLE;
+
+    fn probe(vdev: &virtio::Device<Core>) -> impl PinInit<Self, Error> {
+        let vqs_info: [VirtqueueInfo; 1] = [
+            VirtqueueInfo::new(c"requestq", false, Some(vq_requestq_callback)),
+            //VirtqueueInfo::new(c"alarmq", false, vq_callback),
+        ];
+        try_pin_init!(Self {
+            num_clocks: AtomicU16::new(0),
+            virtqueues <- {
+                pr_info!("Probe Rust virtio driver sample.\n");
+                let vqs = match vdev.find_vqs(&vqs_info) {
+                    Ok(vqs) => {
+                        pr_info!("Found {} vqs.\n", vqs.len());
+                        vqs
+                    }
+                    Err(err) => {
+                        pr_info!("Could not find vqs: {err:?}.\n");
+
+                        return Err(err);
+                    }
+                };
+
+                VirtioRtcVq::new(vqs)
+            },
+            registered_clocks <- new_mutex!(KVec::with_capacity(0, GFP_KERNEL)?),
+        })
+    }
+
+    fn init(&self, vdev: &virtio::Device<Bound>) -> Result {
+        let num_clocks = self.req_cfg()?;
+        self.num_clocks.store(num_clocks, Ordering::SeqCst);
+        for i in 0..num_clocks {
+            let mut is_exposed = false;
+
+            let resp = self.clock_cap(i)?;
+            let (clock_type, leap_second_smearing, flags) =
+                (resp.clock_type, resp.leap_second_smearing, resp.flags);
+            if cfg!(CONFIG_VIRTIO_RTC_CLASS)
+                && (clock_type == ClockType::Utc as u8
+                    || clock_type == ClockType::UtcSmeared as u8
+                    || clock_type == ClockType::UtcMaybeSmeared as u8)
+            {
+                // TODO:
+
+                // 	ret = viortc_init_rtc_class_clock(viortc, vio_clk_id,
+                // 					  clock_type, flags);
+                // 	if (ret < 0)
+                // 		return ret;
+                // 	if (ret > 0)
+                // 		is_exposed = true;
+                dev_warn!(vdev.as_ref(), "CONFIG_VIRTIO_RTC_CLASS TODO ");
+            }
+
+            if cfg!(CONFIG_VIRTIO_RTC_PTP) {
+                // TODO:
+
+                // 	ret = viortc_init_ptp_clock(viortc, vio_clk_id, clock_type,
+                // 				    leap_second_smearing);
+                // 	if (ret < 0)
+                // 		return ret;
+                // 	if (ret > 0)
+                // 		is_exposed = true;
+                // todo!()
+                dev_warn!(vdev.as_ref(), "CONFIG_VIRTIO_RTC_PTP TODO ");
+            }
+
+            if !is_exposed {
+                dev_warn!(
+                    vdev.as_ref(),
+                    "cannot expose clock {i} (type {clock_type}, variant {leap_second_smearing}, \
+                    flags {flags}) to userspace\n"
+                );
+            }
+            let clock_reading = self.read(i)?;
+            pr_info!("#{i} clock reading = {clock_reading}\n");
+        }
+        Ok(())
+    }
+
+    fn remove(_: &virtio::Device<Core>, _this: Pin<&Self>) {
+        pr_info!("Removing Rust virtio driver sample.\n");
+    }
+}
+
+kernel::virtio_device_table!(
+    VIRTIO_RTC_TABLE,
+    MODULE_VIRTIO_RTC_TABLE,
+    <VirtioRtcDriver as virtio::Driver>::IdInfo,
+    [(virtio::DeviceId::new(virtio::VirtioID::Clock), ())]
+);
+
+kernel::module_virtio_driver! {
+    type: VirtioRtcDriver,
+    name: "rust_virtio_rtc",
+    authors: ["Manos Pitsidianakis"],
+    description: "Rust virtio driver",
+    license: "GPL v2",
+}

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Rust virtio drivers in Linux
  2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
                   ` (5 preceding siblings ...)
  2026-05-10 13:38 ` [PATCH RFC v3 6/6] samples/rust: Add sample virtio-rtc driver [WIP] Manos Pitsidianakis
@ 2026-05-18  8:12 ` Manos Pitsidianakis
  2026-05-18  8:44   ` Michael S. Tsirkin
  6 siblings, 1 reply; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-18  8:12 UTC (permalink / raw)
  To: VirtIO Dev List; +Cc: Miguel Ojeda, Michael S. Tsirkin, manos

Hello all,

I have a new device type proposal for the virtio spec I hope to
publish to virtio-comment soon and I have a reference C driver
implementation for Linux. However I would prefer to implement the
driver in Rust, and should it be accepted upstream, abandon the C
driver out-of-tree.

In preparation for that, I wrote bindings for the virtio subsystem and
sent them to LKML. I had forgotten to CC virtio-dev.

The RFC I sent 2 weeks ago has a sample virtio-rtc driver that doesn't
register itself to the rtc subsystem. Since then I wrote a virtio-rng
driver instead that is fully equivalent to the C driver. I haven't
sent it yet because my last revision did not receive any comments yet.
My WIP tree is https://github.com/epilys/linux/tree/rust-virtio

I'd like to hear any feedback and suggestions, especially from the
kernel virtio and Rust maintainers.

Note: An issue with virtio drivers is that they require bindings both
for virtio and the device subsystem. For example I had to write hwrng
bindings for the virtio-rng driver. Does this require more
co-operation from kernel maintainers in order to get things merged?

Miguel: WDYT?

Thanks in advance!

---------- Forwarded message ---------
From: Manos Pitsidianakis <manos@pitsidianak.is>
Date: Sun, May 10, 2026 at 4:38 PM
Subject: [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device
To: Miguel Ojeda <ojeda@kernel.org>
Cc: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>, Peter Hilber
<peter.hilber@oss.qualcomm.com>, Stefano Garzarella
<sgarzare@redhat.com>, Stefan Hajnoczi <stefanha@redhat.com>, Viresh
Kumar <viresh.kumar@linaro.org>, Michael S. Tsirkin <mst@redhat.com>,
Boqun Feng <boqun@kernel.org>, Gary Guo <gary@garyguo.net>, Björn Roy
Baron <bjorn3_gh@protonmail.com>, Benno Lossin <lossin@kernel.org>,
Andreas Hindborg <a.hindborg@kernel.org>, Alice Ryhl
<aliceryhl@google.com>, Trevor Gross <tmgross@umich.edu>, Danilo
Krummrich <dakr@kernel.org>, Daniel Almeida
<daniel.almeida@collabora.com>, <rust-for-linux@vger.kernel.org>,
Jason Wang <jasowang@redhat.com>, Xuan Zhuo
<xuanzhuo@linux.alibaba.com>, Eugenio Pérez <eperezma@redhat.com>,
<virtualization@lists.linux.dev>, <linux-kernel@vger.kernel.org>,
Manos Pitsidianakis <manos@pitsidianak.is>


Hi all, this RFC series adds Rust bindings for Virtio drivers
(frontends in virtio parlance).

As a PoC, it also adds a sample virtio-rtc driver which performs
capability discovery through the virtqueue without registering any clock.

Before I send a cleaned-up non-RFC I would like some initial feedback
(i.e. is it something the upstream wants?)

This was tested with the rust-vmm vhost-device-rtc device backend that I
wrote[^0]:

[^0]: https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-rtc

Instructions:

  Run the daemon in a separate terminal:

  $ cargo run --bin vhost-device-rtc -- -s /tmp/rtc.sock

  Then run the VM:

  $ qemu-system-aarch64 \
    -machine type=virt,virtualization=off,acpi=on \
    -cpu host \
    -smp 8 \
    -accel kvm \
    -drive if=virtio,format=qcow2,file=./debian-13-nocloud-arm64-daily.qcow2 \
    -device virtio-net-pci,netdev=unet \
    -device virtio-scsi-pci \
    -serial mon:stdio \
    -m 8192 \
    -object memory-backend-memfd,id=mem,size=8G,share=on \
    -numa node,memdev=mem \
    -display none \
    -vga none \
    -kernel /path/to/linux/build/arch/arm64/boot/Image \
    -device vhost-user-test-device,chardev=rtc,id=rtc,virtio-id=17,num_vqs=2,vq_size=1024
\
    -chardev socket,path=/tmp/rtc.sock,id=rtc \
    ...

  Example output:
    [    1.105238] rust_virtio_rtc: Probe Rust virtio driver sample.
    [    1.105645] rust_virtio_rtc: Found 1 vqs.
    [    1.136050] rust_virtio_rtc: process_requestq got buf 16 bytes
    [    1.136125] rust_virtio_rtc: Got response! Ok(RespCfg { head:
ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
num_clocks: Le16(3), reserved: [0, 0, 0, 0, 0, 0] })
    [    1.136701] rust_virtio_rtc: Got response! Ok(RespClockCap {
head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
clock_type: 3, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0,
0, 0] })
    [    1.136724] rust_virtio_rtc virtio0: cannot expose clock 0
(type 3, variant 0, flags 0) to userspace
    [    1.137259] rust_virtio_rtc: Got response! Ok(RespRead { head:
ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
clock_reading: Le64(1777890485031060388) })
    [    1.137277] rust_virtio_rtc: #0 clock reading = 1777890485031060388
    [    1.137749] rust_virtio_rtc: Got response! Ok(RespClockCap {
head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
clock_type: 1, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0,
0, 0] })
    [    1.137769] rust_virtio_rtc virtio0: cannot expose clock 1
(type 1, variant 0, flags 0) to userspace
    [    1.138247] rust_virtio_rtc: Got response! Ok(RespRead { head:
ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
clock_reading: Le64(1777890485032086075) })
    [    1.138264] rust_virtio_rtc: #1 clock reading = 1777890485032086075
    [    1.138730] rust_virtio_rtc: Got response! Ok(RespClockCap {
head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
clock_type: 2, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0,
0, 0] })
    [    1.138751] rust_virtio_rtc virtio0: cannot expose clock 2
(type 2, variant 0, flags 0) to userspace
    [    1.139253] rust_virtio_rtc: Got response! Ok(RespRead { head:
ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
clock_reading: Le64(338567896865557) })
    [    1.139270] rust_virtio_rtc: #2 clock reading = 338567896865557

Concerns - Notes - TODOs
========================

- Virtqueue lifetimes don't neatly apply to Rust as expected, so a lot
  of times we have to go through unsafe pointer dereferences (though
  which are guaranteed by Virtio subsystem to be valid, for example when
  a callback is called with the vq argument). There's a potential for
  misuse and definitely could use better thinking.
- `struct virtio_device` is not reference-counted like other implemented
  device types in rust/kernel. Maybe we need to change C API first to
  make them reference counted, assuming this doesn't break anything?
- The sample driver obviously conflicts with the C implementation, so
  this would either need to move out of samples/ or figure out some way
  to handle this in kbuild.
- kernel::virtio module and its types need a few rustdoc examples that I
  will add in followup series
- Note that the registration of RTC clocks etc in the sample driver is
  not done, I'm putting it off until I receive some feedback first. The
  sample driver otherwise does send and receive data from the virtqueue
  as a PoC.

PS: No LLMs used so any mistakes and goofs are solely written by me.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
Changes in v3:
- Removed unused methods from virtio API
- Clean up how scattergather lists are added to virtqueues by using
  owned SGTables only, and make the API safe(r)
- Add RAII cleanup for find_vqs return value that calls del_vqs
- Reset device after remove callback
- Significantly clean up sample driver as a result of the other cleanups
- Link to v2: https://lore.kernel.org/r/20260509-rust-virtio-v2-0-c1e30ec2bd21@pitsidianak.is

Changes in v2:
- Move helper ifdefs to helper file (thanks Alice)
- Changed CONFIG checks to IS_ENABLED to allow for CONFIG_VIRTIO=m
- Split all use imports to one item per line according to style guide
- Fixed wait_for_completion_interruptible*() rustdocs
- Use Jiffy type alias in wait_for_completion_interruptible_timeout()
- Pepper and salt #[inline]s wherever appropriate as per style guide
- Split probe() into probe() and init() to allow cleaning up if init
  fails
- Remove unnecessary Send and Sync unsafe impls for
  kernel::virtio::Device
- Remove unnecessary LeSize and BeSize
- Accept Option<_> for virtqueue callback when creating a VirtqueueInfo
- Made all vq buffer adding operations unsafe
- Use AtomicU16 instead of Cell<u16> for sample virtio driver
- Fix RespHead field types in sample virtio driver
- Fix response error checking in sample virtio driver
- Change some device contexts in method signatures
- Link to v1: https://lore.kernel.org/r/20260505-rust-virtio-v1-0-9563383909e4@pitsidianak.is

---
Manos Pitsidianakis (6):
      rust/bindings: generate virtio bindings
      rust/helpers: add virtio.c
      rust/kernel/device: return parent at same context
      rust: add virtio module
      rust: impl interruptible waits for Completion
      samples/rust: Add sample virtio-rtc driver [WIP]

 MAINTAINERS                     |   9 +
 rust/bindings/bindings_helper.h |   5 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/virtio.c           |  37 ++++
 rust/kernel/device.rs           |   2 +-
 rust/kernel/lib.rs              |   2 +
 rust/kernel/sync/completion.rs  |  42 +++-
 rust/kernel/virtio.rs           | 423 ++++++++++++++++++++++++++++++++++++++++
 rust/kernel/virtio/utils.rs     |  57 ++++++
 rust/kernel/virtio/virtqueue.rs | 314 +++++++++++++++++++++++++++++
 samples/rust/Kconfig            |  15 ++
 samples/rust/Makefile           |   1 +
 samples/rust/rust_virtio_rtc.rs | 403 ++++++++++++++++++++++++++++++++++++++
 13 files changed, 1309 insertions(+), 2 deletions(-)
---
base-commit: 028ef9c96e96197026887c0f092424679298aae8
change-id: 20260504-rust-virtio-8523b01dfdc2

Best regards,
--
Manos Pitsidianakis <manos@pitsidianak.is>

-- 
Manos Pitsidianakis
Emulation and Virtualization Engineer at Linaro Ltd

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Rust virtio drivers in Linux
  2026-05-18  8:12 ` Rust virtio drivers in Linux Manos Pitsidianakis
@ 2026-05-18  8:44   ` Michael S. Tsirkin
  0 siblings, 0 replies; 11+ messages in thread
From: Michael S. Tsirkin @ 2026-05-18  8:44 UTC (permalink / raw)
  To: Manos Pitsidianakis; +Cc: VirtIO Dev List, Miguel Ojeda, manos

On Mon, May 18, 2026 at 11:12:06AM +0300, Manos Pitsidianakis wrote:
> Hello all,
> 
> I have a new device type proposal for the virtio spec I hope to
> publish to virtio-comment soon and I have a reference C driver
> implementation for Linux. However I would prefer to implement the
> driver in Rust, and should it be accepted upstream, abandon the C
> driver out-of-tree.
> 
> In preparation for that, I wrote bindings for the virtio subsystem and
> sent them to LKML. I had forgotten to CC virtio-dev.
> 
> The RFC I sent 2 weeks ago has a sample virtio-rtc driver that doesn't
> register itself to the rtc subsystem. Since then I wrote a virtio-rng
> driver instead that is fully equivalent to the C driver. I haven't
> sent it yet because my last revision did not receive any comments yet.
> My WIP tree is https://github.com/epilys/linux/tree/rust-virtio

Yea, sorry I didn't start reviewing yet. My problem is my rust knowledge is
limited, and kernel rust - nonexistent. I did the crab book a while ago
but didn't do much since except a small terminal emulator project.

> I'd like to hear any feedback and suggestions, especially from the
> kernel virtio and Rust maintainers.
> 
> Note: An issue with virtio drivers is that they require bindings both
> for virtio and the device subsystem. For example I had to write hwrng
> bindings for the virtio-rng driver. Does this require more
> co-operation from kernel maintainers in order to get things merged?

Well the rust kernel doc at Documentation/rust/general-information.rst
says you are supposed to also write
"abstractions" as opposed to just "bindings" and they have to
be "ergonomic". I will be frank I'm not the best judge
about "egonomic". So yes, more kernel maintainers who understand
this thing would help a lot.

> Miguel: WDYT?
> 
> Thanks in advance!
> 
> ---------- Forwarded message ---------
> From: Manos Pitsidianakis <manos@pitsidianak.is>
> Date: Sun, May 10, 2026 at 4:38 PM
> Subject: [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device
> To: Miguel Ojeda <ojeda@kernel.org>
> Cc: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>, Peter Hilber
> <peter.hilber@oss.qualcomm.com>, Stefano Garzarella
> <sgarzare@redhat.com>, Stefan Hajnoczi <stefanha@redhat.com>, Viresh
> Kumar <viresh.kumar@linaro.org>, Michael S. Tsirkin <mst@redhat.com>,
> Boqun Feng <boqun@kernel.org>, Gary Guo <gary@garyguo.net>, Björn Roy
> Baron <bjorn3_gh@protonmail.com>, Benno Lossin <lossin@kernel.org>,
> Andreas Hindborg <a.hindborg@kernel.org>, Alice Ryhl
> <aliceryhl@google.com>, Trevor Gross <tmgross@umich.edu>, Danilo
> Krummrich <dakr@kernel.org>, Daniel Almeida
> <daniel.almeida@collabora.com>, <rust-for-linux@vger.kernel.org>,
> Jason Wang <jasowang@redhat.com>, Xuan Zhuo
> <xuanzhuo@linux.alibaba.com>, Eugenio Pérez <eperezma@redhat.com>,
> <virtualization@lists.linux.dev>, <linux-kernel@vger.kernel.org>,
> Manos Pitsidianakis <manos@pitsidianak.is>
> 
> 
> Hi all, this RFC series adds Rust bindings for Virtio drivers
> (frontends in virtio parlance).
> 
> As a PoC, it also adds a sample virtio-rtc driver which performs
> capability discovery through the virtqueue without registering any clock.
> 
> Before I send a cleaned-up non-RFC I would like some initial feedback
> (i.e. is it something the upstream wants?)
> 
> This was tested with the rust-vmm vhost-device-rtc device backend that I
> wrote[^0]:
> 
> [^0]: https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-rtc
> 
> Instructions:
> 
>   Run the daemon in a separate terminal:
> 
>   $ cargo run --bin vhost-device-rtc -- -s /tmp/rtc.sock
> 
>   Then run the VM:
> 
>   $ qemu-system-aarch64 \
>     -machine type=virt,virtualization=off,acpi=on \
>     -cpu host \
>     -smp 8 \
>     -accel kvm \
>     -drive if=virtio,format=qcow2,file=./debian-13-nocloud-arm64-daily.qcow2 \
>     -device virtio-net-pci,netdev=unet \
>     -device virtio-scsi-pci \
>     -serial mon:stdio \
>     -m 8192 \
>     -object memory-backend-memfd,id=mem,size=8G,share=on \
>     -numa node,memdev=mem \
>     -display none \
>     -vga none \
>     -kernel /path/to/linux/build/arch/arm64/boot/Image \
>     -device vhost-user-test-device,chardev=rtc,id=rtc,virtio-id=17,num_vqs=2,vq_size=1024
> \
>     -chardev socket,path=/tmp/rtc.sock,id=rtc \
>     ...
> 
>   Example output:
>     [    1.105238] rust_virtio_rtc: Probe Rust virtio driver sample.
>     [    1.105645] rust_virtio_rtc: Found 1 vqs.
>     [    1.136050] rust_virtio_rtc: process_requestq got buf 16 bytes
>     [    1.136125] rust_virtio_rtc: Got response! Ok(RespCfg { head:
> ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
> num_clocks: Le16(3), reserved: [0, 0, 0, 0, 0, 0] })
>     [    1.136701] rust_virtio_rtc: Got response! Ok(RespClockCap {
> head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
> clock_type: 3, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0,
> 0, 0] })
>     [    1.136724] rust_virtio_rtc virtio0: cannot expose clock 0
> (type 3, variant 0, flags 0) to userspace
>     [    1.137259] rust_virtio_rtc: Got response! Ok(RespRead { head:
> ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
> clock_reading: Le64(1777890485031060388) })
>     [    1.137277] rust_virtio_rtc: #0 clock reading = 1777890485031060388
>     [    1.137749] rust_virtio_rtc: Got response! Ok(RespClockCap {
> head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
> clock_type: 1, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0,
> 0, 0] })
>     [    1.137769] rust_virtio_rtc virtio0: cannot expose clock 1
> (type 1, variant 0, flags 0) to userspace
>     [    1.138247] rust_virtio_rtc: Got response! Ok(RespRead { head:
> ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
> clock_reading: Le64(1777890485032086075) })
>     [    1.138264] rust_virtio_rtc: #1 clock reading = 1777890485032086075
>     [    1.138730] rust_virtio_rtc: Got response! Ok(RespClockCap {
> head: ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
> clock_type: 2, leap_second_smearing: 0, flags: 0, reserved: [0, 0, 0,
> 0, 0] })
>     [    1.138751] rust_virtio_rtc virtio0: cannot expose clock 2
> (type 2, variant 0, flags 0) to userspace
>     [    1.139253] rust_virtio_rtc: Got response! Ok(RespRead { head:
> ReqHead { msg_type: Le16(0), reserved: [0, 0, 0, 0, 0, 0] },
> clock_reading: Le64(338567896865557) })
>     [    1.139270] rust_virtio_rtc: #2 clock reading = 338567896865557
> 
> Concerns - Notes - TODOs
> ========================
> 
> - Virtqueue lifetimes don't neatly apply to Rust as expected, so a lot
>   of times we have to go through unsafe pointer dereferences (though
>   which are guaranteed by Virtio subsystem to be valid, for example when
>   a callback is called with the vq argument). There's a potential for
>   misuse and definitely could use better thinking.
> - `struct virtio_device` is not reference-counted like other implemented
>   device types in rust/kernel. Maybe we need to change C API first to
>   make them reference counted, assuming this doesn't break anything?
> - The sample driver obviously conflicts with the C implementation, so
>   this would either need to move out of samples/ or figure out some way
>   to handle this in kbuild.
> - kernel::virtio module and its types need a few rustdoc examples that I
>   will add in followup series
> - Note that the registration of RTC clocks etc in the sample driver is
>   not done, I'm putting it off until I receive some feedback first. The
>   sample driver otherwise does send and receive data from the virtqueue
>   as a PoC.
> 
> PS: No LLMs used so any mistakes and goofs are solely written by me.
> 
> Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
> ---
> Changes in v3:
> - Removed unused methods from virtio API
> - Clean up how scattergather lists are added to virtqueues by using
>   owned SGTables only, and make the API safe(r)
> - Add RAII cleanup for find_vqs return value that calls del_vqs
> - Reset device after remove callback
> - Significantly clean up sample driver as a result of the other cleanups
> - Link to v2: https://lore.kernel.org/r/20260509-rust-virtio-v2-0-c1e30ec2bd21@pitsidianak.is
> 
> Changes in v2:
> - Move helper ifdefs to helper file (thanks Alice)
> - Changed CONFIG checks to IS_ENABLED to allow for CONFIG_VIRTIO=m
> - Split all use imports to one item per line according to style guide
> - Fixed wait_for_completion_interruptible*() rustdocs
> - Use Jiffy type alias in wait_for_completion_interruptible_timeout()
> - Pepper and salt #[inline]s wherever appropriate as per style guide
> - Split probe() into probe() and init() to allow cleaning up if init
>   fails
> - Remove unnecessary Send and Sync unsafe impls for
>   kernel::virtio::Device
> - Remove unnecessary LeSize and BeSize
> - Accept Option<_> for virtqueue callback when creating a VirtqueueInfo
> - Made all vq buffer adding operations unsafe
> - Use AtomicU16 instead of Cell<u16> for sample virtio driver
> - Fix RespHead field types in sample virtio driver
> - Fix response error checking in sample virtio driver
> - Change some device contexts in method signatures
> - Link to v1: https://lore.kernel.org/r/20260505-rust-virtio-v1-0-9563383909e4@pitsidianak.is
> 
> ---
> Manos Pitsidianakis (6):
>       rust/bindings: generate virtio bindings
>       rust/helpers: add virtio.c
>       rust/kernel/device: return parent at same context
>       rust: add virtio module
>       rust: impl interruptible waits for Completion
>       samples/rust: Add sample virtio-rtc driver [WIP]
> 
>  MAINTAINERS                     |   9 +
>  rust/bindings/bindings_helper.h |   5 +
>  rust/helpers/helpers.c          |   1 +
>  rust/helpers/virtio.c           |  37 ++++
>  rust/kernel/device.rs           |   2 +-
>  rust/kernel/lib.rs              |   2 +
>  rust/kernel/sync/completion.rs  |  42 +++-
>  rust/kernel/virtio.rs           | 423 ++++++++++++++++++++++++++++++++++++++++
>  rust/kernel/virtio/utils.rs     |  57 ++++++
>  rust/kernel/virtio/virtqueue.rs | 314 +++++++++++++++++++++++++++++
>  samples/rust/Kconfig            |  15 ++
>  samples/rust/Makefile           |   1 +
>  samples/rust/rust_virtio_rtc.rs | 403 ++++++++++++++++++++++++++++++++++++++
>  13 files changed, 1309 insertions(+), 2 deletions(-)
> ---
> base-commit: 028ef9c96e96197026887c0f092424679298aae8
> change-id: 20260504-rust-virtio-8523b01dfdc2
> 
> Best regards,
> --
> Manos Pitsidianakis <manos@pitsidianak.is>
> 
> -- 
> Manos Pitsidianakis
> Emulation and Virtualization Engineer at Linaro Ltd


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC v3 2/6] rust/helpers: add virtio.c
  2026-05-10 13:38 ` [PATCH RFC v3 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
@ 2026-05-21  9:03   ` Eugenio Perez Martin
  2026-05-25  7:51     ` Manos Pitsidianakis
  0 siblings, 1 reply; 11+ messages in thread
From: Eugenio Perez Martin @ 2026-05-21  9:03 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: Miguel Ojeda, Manos Pitsidianakis, Peter Hilber,
	Stefano Garzarella, Stefan Hajnoczi, Viresh Kumar,
	Michael S. Tsirkin, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, rust-for-linux, Jason Wang,
	Xuan Zhuo, virtualization, linux-kernel

On Sun, May 10, 2026 at 3:38 PM Manos Pitsidianakis
<manos@pitsidianak.is> wrote:
>
> Some internal kernel virtio API functions are inline macros, so define
> their symbols in a helper file.
>
> Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
> ---
>  MAINTAINERS            |  6 ++++++
>  rust/helpers/helpers.c |  1 +
>  rust/helpers/virtio.c  | 37 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 44 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d1cc0e12fe1f004da89b1aa339116908f642e894..48c9c666d90b5a256ab6fae1f42508b789a0ce50 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -27930,6 +27930,12 @@ F:     include/uapi/linux/virtio_*.h
>  F:     net/vmw_vsock/virtio*
>  F:     tools/virtio/
>
> +VIRTIO CORE API BINDINGS [RUST]
> +M:     Manos Pitsidianakis <manos@pitsidianak.is>
> +L:     virtualization@lists.linux.dev
> +S:     Maintained
> +F:     rust/helpers/virtio.c
> +
>  VIRTIO CRYPTO DRIVER
>  M:     Gonglei <arei.gonglei@huawei.com>
>  L:     virtualization@lists.linux.dev
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index a3c42e51f00a0990bea81ebce6e99bb397ce7533..5dc0d2f2ee6bd2ae8e6abfe4baa247c1963967f6 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -61,6 +61,7 @@
>  #include "time.c"
>  #include "uaccess.c"
>  #include "usb.c"
> +#include "virtio.c"
>  #include "vmalloc.c"
>  #include "wait.c"
>  #include "workqueue.c"
> diff --git a/rust/helpers/virtio.c b/rust/helpers/virtio.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..46aeeb063158823e66477777b3cd4bd1525df330
> --- /dev/null
> +++ b/rust/helpers/virtio.c
> @@ -0,0 +1,37 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#ifdef CONFIG_VIRTIO
> +#include <linux/virtio_config.h>
> +
> +__rust_helper bool
> +rust_helper_virtio_has_feature(const struct virtio_device *vdev,
> +                              unsigned int fbit)
> +{
> +       return virtio_has_feature(vdev, fbit);
> +}
> +__rust_helper void rust_helper_virtio_get_features(struct virtio_device *vdev,
> +                                                  u64 *features_out)
> +{
> +       return virtio_get_features(vdev, features_out);

As a suggestion, perhaps an API that allows getting feature bits > 64,
like VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO (65) or VIRTIO_NET_F_IPSEC (70)
could save the need to add more functions in the future.

> +}
> +
> +__rust_helper int rust_helper_virtio_find_vqs(struct virtio_device *vdev,
> +                                             unsigned int nvqs,
> +                                             struct virtqueue *vqs[],
> +                                             struct virtqueue_info vqs_info[],
> +                                             struct irq_affinity *desc)
> +{
> +       return virtio_find_vqs(vdev, nvqs, vqs, vqs_info, desc);
> +}
> +
> +__rust_helper void rust_helper_virtio_device_ready(struct virtio_device *dev)
> +{
> +       return virtio_device_ready(dev);
> +}
> +
> +__rust_helper bool
> +rust_helper_virtio_is_little_endian(struct virtio_device *vdev)
> +{
> +       return virtio_is_little_endian(vdev);
> +}
> +#endif /* CONFIG_VIRTIO */
>
> --
> 2.47.3
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC v3 2/6] rust/helpers: add virtio.c
  2026-05-21  9:03   ` Eugenio Perez Martin
@ 2026-05-25  7:51     ` Manos Pitsidianakis
  0 siblings, 0 replies; 11+ messages in thread
From: Manos Pitsidianakis @ 2026-05-25  7:51 UTC (permalink / raw)
  To: Eugenio Perez Martin
  Cc: Miguel Ojeda, Manos Pitsidianakis, Peter Hilber,
	Stefano Garzarella, Stefan Hajnoczi, Viresh Kumar,
	Michael S. Tsirkin, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Daniel Almeida, rust-for-linux, Jason Wang,
	Xuan Zhuo, virtualization, linux-kernel

On Thu, 21 May 2026 12:03, Eugenio Perez Martin <eperezma@redhat.com> wrote:
>On Sun, May 10, 2026 at 3:38 PM Manos Pitsidianakis
><manos@pitsidianak.is> wrote:
>>
>> Some internal kernel virtio API functions are inline macros, so define
>> their symbols in a helper file.
>>
>> Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
>> ---
>>  MAINTAINERS            |  6 ++++++
>>  rust/helpers/helpers.c |  1 +
>>  rust/helpers/virtio.c  | 37 +++++++++++++++++++++++++++++++++++++
>>  3 files changed, 44 insertions(+)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index d1cc0e12fe1f004da89b1aa339116908f642e894..48c9c666d90b5a256ab6fae1f42508b789a0ce50 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -27930,6 +27930,12 @@ F:     include/uapi/linux/virtio_*.h
>>  F:     net/vmw_vsock/virtio*
>>  F:     tools/virtio/
>>
>> +VIRTIO CORE API BINDINGS [RUST]
>> +M:     Manos Pitsidianakis <manos@pitsidianak.is>
>> +L:     virtualization@lists.linux.dev
>> +S:     Maintained
>> +F:     rust/helpers/virtio.c
>> +
>>  VIRTIO CRYPTO DRIVER
>>  M:     Gonglei <arei.gonglei@huawei.com>
>>  L:     virtualization@lists.linux.dev
>> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
>> index a3c42e51f00a0990bea81ebce6e99bb397ce7533..5dc0d2f2ee6bd2ae8e6abfe4baa247c1963967f6 100644
>> --- a/rust/helpers/helpers.c
>> +++ b/rust/helpers/helpers.c
>> @@ -61,6 +61,7 @@
>>  #include "time.c"
>>  #include "uaccess.c"
>>  #include "usb.c"
>> +#include "virtio.c"
>>  #include "vmalloc.c"
>>  #include "wait.c"
>>  #include "workqueue.c"
>> diff --git a/rust/helpers/virtio.c b/rust/helpers/virtio.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..46aeeb063158823e66477777b3cd4bd1525df330
>> --- /dev/null
>> +++ b/rust/helpers/virtio.c
>> @@ -0,0 +1,37 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +#ifdef CONFIG_VIRTIO
>> +#include <linux/virtio_config.h>
>> +
>> +__rust_helper bool
>> +rust_helper_virtio_has_feature(const struct virtio_device *vdev,
>> +                              unsigned int fbit)
>> +{
>> +       return virtio_has_feature(vdev, fbit);
>> +}
>> +__rust_helper void rust_helper_virtio_get_features(struct virtio_device *vdev,
>> +                                                  u64 *features_out)
>> +{
>> +       return virtio_get_features(vdev, features_out);
>
>As a suggestion, perhaps an API that allows getting feature bits > 64,
>like VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO (65) or VIRTIO_NET_F_IPSEC (70)
>could save the need to add more functions in the future.

Yes, this will be needed indeed. I think however that as long as no 
driver that uses features exists, this would be dead code so we should 
not include it until it's needed.

I plan on dropping these helper bindings that have no user in my patches 
in the next revisions.

>
>> +}
>> +
>> +__rust_helper int rust_helper_virtio_find_vqs(struct virtio_device *vdev,
>> +                                             unsigned int nvqs,
>> +                                             struct virtqueue *vqs[],
>> +                                             struct virtqueue_info vqs_info[],
>> +                                             struct irq_affinity *desc)
>> +{
>> +       return virtio_find_vqs(vdev, nvqs, vqs, vqs_info, desc);
>> +}
>> +
>> +__rust_helper void rust_helper_virtio_device_ready(struct virtio_device *dev)
>> +{
>> +       return virtio_device_ready(dev);
>> +}
>> +
>> +__rust_helper bool
>> +rust_helper_virtio_is_little_endian(struct virtio_device *vdev)
>> +{
>> +       return virtio_is_little_endian(vdev);
>> +}
>> +#endif /* CONFIG_VIRTIO */
>>
>> --
>> 2.47.3
>>
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-05-25  7:54 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-10 13:38 [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
2026-05-10 13:38 ` [PATCH RFC v3 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
2026-05-10 13:38 ` [PATCH RFC v3 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
2026-05-21  9:03   ` Eugenio Perez Martin
2026-05-25  7:51     ` Manos Pitsidianakis
2026-05-10 13:38 ` [PATCH RFC v3 3/6] rust/kernel/device: return parent at same context Manos Pitsidianakis
2026-05-10 13:38 ` [PATCH RFC v3 4/6] rust: add virtio module Manos Pitsidianakis
2026-05-10 13:38 ` [PATCH RFC v3 5/6] rust: impl interruptible waits for Completion Manos Pitsidianakis
2026-05-10 13:38 ` [PATCH RFC v3 6/6] samples/rust: Add sample virtio-rtc driver [WIP] Manos Pitsidianakis
2026-05-18  8:12 ` Rust virtio drivers in Linux Manos Pitsidianakis
2026-05-18  8:44   ` Michael S. Tsirkin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.