The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH RFC 0/6] Add Rust virtio bindings and sample device
@ 2026-05-05  8:14 Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-05  8:14 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, 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?
- Not sure if adding data smaller than PAGE_SIZE to virtqueue is safe
  (which current C code does a lot), so I made those allocations at
  least PAGE_SIZE.
- 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. The code and data structures can be cleaned up further
  before followup series.

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

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
Manos Pitsidianakis (6):
      rust/bindings: generate virtio bindings
      rust/helpers: add virtio.c
      rust: add virtio module
      rust/scatterlist: add SGEntry::init_one
      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          |   3 +
 rust/helpers/virtio.c           |  35 +++
 rust/kernel/lib.rs              |   2 +
 rust/kernel/scatterlist.rs      |  20 +-
 rust/kernel/sync/completion.rs  |  39 ++++
 rust/kernel/virtio.rs           | 415 +++++++++++++++++++++++++++++++++++
 rust/kernel/virtio/utils.rs     |  65 ++++++
 rust/kernel/virtio/virtqueue.rs | 181 ++++++++++++++++
 samples/rust/Kconfig            |  15 ++
 samples/rust/Makefile           |   1 +
 samples/rust/rust_virtio_rtc.rs | 470 ++++++++++++++++++++++++++++++++++++++++
 13 files changed, 1259 insertions(+), 1 deletion(-)
---
base-commit: 028ef9c96e96197026887c0f092424679298aae8
change-id: 20260504-rust-virtio-8523b01dfdc2

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


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

* [PATCH RFC 1/6] rust/bindings: generate virtio bindings
  2026-05-05  8:14 [PATCH RFC 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
@ 2026-05-05  8:14 ` Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-05  8:14 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, 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..2b0a3cf49fdaf14517afc88688c545aaa977b5c6 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 defined(CONFIG_VIRTIO)
+#include <linux/virtio_config.h>
+#include <uapi/linux/virtio_ids.h>
+#endif /* defined(CONFIG_VIRTIO) */

-- 
2.47.3


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

* [PATCH RFC 2/6] rust/helpers: add virtio.c
  2026-05-05  8:14 [PATCH RFC 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
@ 2026-05-05  8:14 ` Manos Pitsidianakis
  2026-05-07  8:21   ` Philippe Mathieu-Daudé
  2026-05-07  8:28   ` Alice Ryhl
  2026-05-05  8:14 ` [PATCH RFC 3/6] rust: add virtio module Manos Pitsidianakis
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-05  8:14 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, 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 |  3 +++
 rust/helpers/virtio.c  | 35 +++++++++++++++++++++++++++++++++++
 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..84b54690d95be37699ef9a9c4d7cedec0bbae6d3 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -62,6 +62,9 @@
 #include "uaccess.c"
 #include "usb.c"
 #include "vmalloc.c"
+#if defined(CONFIG_VIRTIO)
+#include "virtio.c"
+#endif /* defined(CONFIG_VIRTIO) */
 #include "wait.c"
 #include "workqueue.c"
 #include "xarray.c"
diff --git a/rust/helpers/virtio.c b/rust/helpers/virtio.c
new file mode 100644
index 0000000000000000000000000000000000000000..cd8a811d59960e7b6aea1c08016f4154b29d5a97
--- /dev/null
+++ b/rust/helpers/virtio.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#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);
+}

-- 
2.47.3


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

* [PATCH RFC 3/6] rust: add virtio module
  2026-05-05  8:14 [PATCH RFC 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
@ 2026-05-05  8:14 ` Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 4/6] rust/scatterlist: add SGEntry::init_one Manos Pitsidianakis
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-05  8:14 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, 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/scatterlist.rs      |   2 +-
 rust/kernel/virtio.rs           | 415 ++++++++++++++++++++++++++++++++++++++++
 rust/kernel/virtio/utils.rs     |  65 +++++++
 rust/kernel/virtio/virtqueue.rs | 181 ++++++++++++++++++
 6 files changed, 666 insertions(+), 1 deletion(-)

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..c1fe1b06fd89e80f23c5de22aeb36c80f653e1ab 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)]
+pub mod virtio;
 pub mod workqueue;
 pub mod xarray;
 
diff --git a/rust/kernel/scatterlist.rs b/rust/kernel/scatterlist.rs
index b83c468b5c63311f3a6d92f0f1bf05f6dfe12076..146e738cbd4351b41c11dd39a45e20f404c5cd64 100644
--- a/rust/kernel/scatterlist.rs
+++ b/rust/kernel/scatterlist.rs
@@ -75,7 +75,7 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::scatterlist) -> &'a Self {
 
     /// Obtain the raw `struct scatterlist *`.
     #[inline]
-    fn as_raw(&self) -> *mut bindings::scatterlist {
+    pub(crate) fn as_raw(&self) -> *mut bindings::scatterlist {
         self.0.get()
     }
 
diff --git a/rust/kernel/virtio.rs b/rust/kernel/virtio.rs
new file mode 100644
index 0000000000000000000000000000000000000000..38e4f273ce76ab2dfa2e91b4ff8c4d5ddde0121c
--- /dev/null
+++ b/rust/kernel/virtio.rs
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! VIRTIO abstraction.
+
+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, //
+};
+
+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)
+    }
+
+    /// Create a new device id with vendor
+    pub const fn new_with_vendor(device: VirtioID, vendor: u32) -> Self {
+        // Replace with `bindings::of_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 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::Core>) -> 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, this: Pin<&Self>);
+}
+
+/// 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")]
+    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")]
+    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<KVec<*mut virtqueue::Virtqueue>> {
+        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()) };
+        Ok(vqs)
+    }
+
+    /// Delete virtqueues from this device.
+    pub 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.
+    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) }
+    }
+
+    /// Return all feature bits for this device.
+    pub fn get_features(&self) -> u64 {
+        let mut features = 0;
+        // SAFETY: By its type invariant `self.as_raw` is always a valid pointer to a
+        // `struct virtio_device`.
+        unsafe { bindings::virtio_get_features(self.as_raw(), &raw mut features) };
+        features
+    }
+}
+
+impl<Ctx: crate::device::DeviceContext> AsRef<crate::device::Device<Ctx>> for Device<Ctx> {
+    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) }
+    }
+}
+
+// SAFETY: A `Device` can be used rom any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: `Device` can be shared among threads because all methods of `Device`
+// (i.e. `Device<Normal>) are thread safe.
+unsafe impl Sync for Device {}
+
+/// 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>() };
+            T::init(&data, dev)?;
+            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);
+    }
+}
+
+/// 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..0c078202915127d38c3ba0bb1675c7f4cd94df6e
--- /dev/null
+++ b/rust/kernel/virtio/utils.rs
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Helper types and utilities
+
+macro_rules! endian_type {
+    (le $old_type:ident, $new_type:ident) => {
+        endian_type!($old_type, $new_type, to_le, from_le);
+    };
+    (be $old_type:ident, $new_type:ident) => {
+        endian_type!($old_type, $new_type, to_be, from_be);
+    };
+    ($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!(usize, LeSize, 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);
+endian_type!(usize, BeSize, to_be, from_be);
diff --git a/rust/kernel/virtio/virtqueue.rs b/rust/kernel/virtio/virtqueue.rs
new file mode 100644
index 0000000000000000000000000000000000000000..754fdad8c10199ee10e77658a7e773c8e4e95286
--- /dev/null
+++ b/rust/kernel/virtio/virtqueue.rs
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Virtqueue functionality.
+
+use crate::{
+    alloc::{
+        Flags, //
+    },
+    bindings,
+    error::{
+        code::EINVAL,
+        to_result,
+        Result, //
+    },
+    scatterlist::SGEntry,
+    str::CStr,
+    types::Opaque,
+    virtio::Device, //
+};
+
+use core::{
+    ffi::c_uint,
+    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 {
+    /// Create a new [`VirtqueueInfo`]
+    pub const fn new(
+        name: &'static CStr,
+        ctx: bool,
+        callback: unsafe extern "C" fn(*mut bindings::virtqueue),
+    ) -> Self {
+        Self(Opaque::new(bindings::virtqueue_info {
+            name: name.as_ptr(),
+            ctx,
+            callback: Some(callback),
+        }))
+    }
+}
+
+/// 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.
+    pub fn dev<Ctx: crate::device::DeviceContext>(&self) -> &Device<Ctx> {
+        // SAFETY: the pointer has been promised to be valid when self was created
+        let vdev = unsafe { *self.as_raw() }.vdev;
+        // SAFETY: the pointer has been promised to be valid when self was created
+        unsafe { &*vdev.cast::<Device<Ctx>>() }
+    }
+
+    /// Get the vring size.
+    #[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.
+    #[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.
+    #[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.
+    #[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.
+    #[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.
+    #[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.
+    #[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))
+    }
+
+    /// Make a device write-only buffer available.
+    #[doc(alias = "virtqueue_add_inbuf")]
+    pub fn add_inbuf(&'_ self, sg: &SGEntry, token: *mut u8, gfp: Flags) -> Result {
+        // SAFETY: the pointer has been promised to be valid when self was created
+        to_result(unsafe {
+            bindings::virtqueue_add_inbuf(self.as_raw(), sg.as_raw(), 1, token.cast(), gfp.as_raw())
+        })
+    }
+
+    /// Make a device read-only buffer available.
+    #[doc(alias = "virtqueue_add_outbuf")]
+    pub fn add_outbuf(&'_ self, sg: &SGEntry, token: *mut u8, gfp: Flags) -> Result {
+        // SAFETY: the pointer has been promised to be valid when self was created
+        to_result(unsafe {
+            bindings::virtqueue_add_outbuf(
+                self.as_raw(),
+                sg.as_raw(),
+                1,
+                token.cast(),
+                gfp.as_raw(),
+            )
+        })
+    }
+
+    /// Add a list of scatter-gather lists to virtqueue.
+    #[doc(alias = "virtqueue_add_sgs")]
+    pub fn add_sgs(
+        &'_ self,
+        sgs: &[&SGEntry],
+        out_sgs: c_uint,
+        in_sgs: c_uint,
+        token: *mut u8,
+        gfp: Flags,
+    ) -> Result {
+        if (out_sgs + in_sgs) as usize != sgs.len() {
+            return Err(EINVAL);
+        }
+        // SAFETY: the pointer 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,
+                in_sgs,
+                token.cast(),
+                gfp.as_raw(),
+            )
+        })
+    }
+}

-- 
2.47.3


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

* [PATCH RFC 4/6] rust/scatterlist: add SGEntry::init_one
  2026-05-05  8:14 [PATCH RFC 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
                   ` (2 preceding siblings ...)
  2026-05-05  8:14 ` [PATCH RFC 3/6] rust: add virtio module Manos Pitsidianakis
@ 2026-05-05  8:14 ` Manos Pitsidianakis
  2026-05-07  8:23   ` Philippe Mathieu-Daudé
  2026-05-05  8:14 ` [PATCH RFC 5/6] rust: impl interruptible waits for Completion Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 6/6] samples/rust: Add sample virtio-rtc driver [WIP] Manos Pitsidianakis
  5 siblings, 1 reply; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-05  8:14 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, rust-for-linux,
	Jason Wang, Xuan Zhuo, Eugenio Pérez, virtualization,
	linux-kernel, Manos Pitsidianakis

Add a method to allow creation of an SGEntry with borrowed data.
Analogous of `sg_init_one` in C.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
---
 rust/kernel/scatterlist.rs | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/rust/kernel/scatterlist.rs b/rust/kernel/scatterlist.rs
index 146e738cbd4351b41c11dd39a45e20f404c5cd64..b065762af1a734fcdd5d3acde89199f1a626fd89 100644
--- a/rust/kernel/scatterlist.rs
+++ b/rust/kernel/scatterlist.rs
@@ -95,6 +95,24 @@ pub fn dma_len(&self) -> ResourceSize {
         // SAFETY: `self.as_raw()` is a valid pointer to a `struct scatterlist`.
         unsafe { bindings::sg_dma_len(self.as_raw()) }.into()
     }
+
+    /// Initialize a new entry with borrowed data.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that:
+    /// - `buf` is a valid pointer
+    /// - `buf_len` describes a valid allocation size for this pointer
+    pub unsafe fn init_one(
+        val: &'_ mut core::mem::MaybeUninit<bindings::scatterlist>,
+        buf: NonNull<u8>,
+        buf_len: u32,
+    ) -> &'_ Self {
+        // SAFETY: `val` points to a correctly sized `struct scatterlist`.
+        unsafe { bindings::sg_init_one(val.as_mut_ptr(), buf.as_ptr().cast(), buf_len) };
+        // SAFETY: `val` points to an initialized `struct scatterlist`.
+        unsafe { Self::from_raw(val.as_mut_ptr()) }
+    }
 }
 
 /// The borrowed generic type of an [`SGTable`], representing a borrowed or externally managed

-- 
2.47.3


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

* [PATCH RFC 5/6] rust: impl interruptible waits for Completion
  2026-05-05  8:14 [PATCH RFC 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
                   ` (3 preceding siblings ...)
  2026-05-05  8:14 ` [PATCH RFC 4/6] rust/scatterlist: add SGEntry::init_one Manos Pitsidianakis
@ 2026-05-05  8:14 ` Manos Pitsidianakis
  2026-05-05  8:14 ` [PATCH RFC 6/6] samples/rust: Add sample virtio-rtc driver [WIP] Manos Pitsidianakis
  5 siblings, 0 replies; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-05  8:14 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, 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 | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/rust/kernel/sync/completion.rs b/rust/kernel/sync/completion.rs
index c50012a940a3c7a3e0edf302c8f833bdc4415200..958e26f00a8e645ab080cb5d8529e31ac3042dd0 100644
--- a/rust/kernel/sync/completion.rs
+++ b/rust/kernel/sync/completion.rs
@@ -109,4 +109,43 @@ 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 a task.
+    ///
+    /// This method waits for the completion of a task; it is not interruptible and there is no
+    /// timeout.
+    ///
+    /// See also [`Completion::complete_all`].
+    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 a task.
+    ///
+    /// This method waits for the completion of a task; it is not interruptible and there is no
+    /// timeout.
+    ///
+    /// See also [`Completion::complete_all`].
+    pub fn wait_for_completion_interruptible_timeout(
+        &self,
+        timeout_jiffies: c_ulong,
+    ) -> Result<c_long> {
+        // 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 {
+            Err(ETIMEDOUT)
+        } else if ret < 0 {
+            Err(Error::from_errno(ret as c_int))
+        } else {
+            Ok(ret)
+        }
+    }
 }

-- 
2.47.3


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

* [PATCH RFC 6/6] samples/rust: Add sample virtio-rtc driver [WIP]
  2026-05-05  8:14 [PATCH RFC 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
                   ` (4 preceding siblings ...)
  2026-05-05  8:14 ` [PATCH RFC 5/6] rust: impl interruptible waits for Completion Manos Pitsidianakis
@ 2026-05-05  8:14 ` Manos Pitsidianakis
  2026-05-06  7:09   ` Manos Pitsidianakis
  5 siblings, 1 reply; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-05  8:14 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, 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 | 470 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 487 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..f580ed83a0a57a4b051372a51f56b787d53ed602
--- /dev/null
+++ b/samples/rust/rust_virtio_rtc.rs
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust virtio driver sample.
+
+use core::{
+    cell::Cell,
+    marker::PhantomData,
+    ptr::NonNull, //
+};
+
+use kernel::{
+    device::{
+        Core,
+        CoreInternal, //
+    },
+    new_mutex, new_spinlock, page,
+    prelude::*,
+    scatterlist::SGEntry,
+    sync::Completion,
+    sync::{Mutex, SpinLock},
+    virtio::{
+        self,
+        utils::*,
+        virtqueue::*, //
+    },
+};
+
+use pin_init::stack_try_pin_init;
+
+#[pin_data]
+struct Token {
+    resp_actual_size: u32,
+    #[pin]
+    responded: Completion,
+}
+
+#[pin_data]
+struct Message<Request: Zeroable, Response: Zeroable> {
+    msg_type: u16,
+    #[pin]
+    req: KVec<u8>,
+    #[pin]
+    resp: KVec<u8>,
+    req_ptr: NonNull<Request>,
+    resp_ptr: NonNull<Response>,
+    #[pin]
+    token: Token,
+    _ph_req: PhantomData<Request>,
+    _ph_resp: PhantomData<Response>,
+}
+
+#[derive(Copy, Clone, Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_req_head")]
+struct ReqHead {
+    msg_type: Le16,
+    reserved: [u8; 6],
+}
+
+#[derive(Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_resp_head")]
+struct RespHead {
+    status: u8,
+    reserved: [u8; 7],
+}
+
+#[derive(Debug, Zeroable)]
+#[repr(C)]
+#[doc(alias = "virtio_rtc_resp_cfg")]
+struct RespCfg {
+    head: ReqHead,
+    /** # 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: ReqHead,
+    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: ReqHead,
+    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,
+}
+
+// SAFETY: `Message` is safe to be send to any task.
+unsafe impl<Request: Zeroable, Response: Zeroable> Send for Message<Request, Response> {}
+
+// SAFETY: `Message` is safe to be accessed concurrently.
+unsafe impl<Request: Zeroable, Response: Zeroable> Sync for Message<Request, Response> {}
+
+impl<Request: Zeroable, Response: Zeroable> Message<Request, Response> {
+    /// Create an initializer for a new [`Message`].
+    fn new(req_data: Request, msg_type: u16) -> Result<impl PinInit<Self, Error>> {
+        macro_rules! alloc_buf {
+            ($t:ty) => {{
+                let size = (core::mem::size_of::<$t>() / page::PAGE_SIZE + 1) * page::PAGE_SIZE;
+                KVec::<u8>::with_capacity(size, GFP_KERNEL)
+            }};
+        }
+        let mut req = alloc_buf!(Request)?;
+        let mut resp = alloc_buf!(Response)?;
+        let req_ptr: NonNull<Request> = NonNull::new(req.as_mut_ptr().cast()).unwrap();
+        let resp_ptr = NonNull::new(resp.as_mut_ptr().cast()).unwrap();
+        // SAFETY: `req_ptr` is a valid Request allocation
+        unsafe {
+            core::ptr::write(req_ptr.as_ptr(), req_data);
+        }
+        Ok(pin_init!(Self {
+            req,
+            resp,
+            msg_type,
+            req_ptr,
+            resp_ptr,
+            token <- pin_init!(Token {
+                resp_actual_size: 0,
+                responded <- Completion::new(),
+            }),
+            _ph_req: PhantomData,
+            _ph_resp: PhantomData,
+        }? Error))
+    }
+
+    fn get_response(&self) -> Result<&Response, Error> {
+        if self.token.resp_actual_size as usize != core::mem::size_of::<Response>() {
+            if self.token.resp_actual_size as usize >= core::mem::size_of::<RespHead>() {
+                let head: &RespHead = unsafe { self.resp_ptr.cast().as_ref() };
+                return match head.status {
+                    0 | 3 => Err(EINVAL),
+                    1 => Err(ENOTSUPP),
+                    2 => Err(ENODEV),
+                    4 | 5_u8..=u8::MAX => Err(EIO),
+                };
+            }
+            return Err(EINVAL);
+        }
+        Ok(unsafe { self.resp_ptr.as_ref() })
+    }
+
+    fn send(&self, vq: &SpinLock<VirtioRtcVq>, timeout_jiffies: c_ulong) -> Result {
+        let guard = vq.lock();
+
+        let mut sg_in = core::mem::MaybeUninit::zeroed();
+        let mut sg_out = core::mem::MaybeUninit::zeroed();
+        let req = unsafe {
+            SGEntry::init_one(
+                &mut sg_out,
+                self.req_ptr.cast(),
+                core::mem::size_of::<Request>() as u32,
+            )
+        };
+        let resp = unsafe {
+            SGEntry::init_one(
+                &mut sg_in,
+                self.resp_ptr.cast(),
+                core::mem::size_of::<Response>() as u32,
+            )
+        };
+        let sgs = [req, resp];
+        guard.as_ref().add_sgs(
+            &sgs,
+            1,
+            1,
+            (&raw const self.token).cast_mut().cast(),
+            GFP_ATOMIC,
+        )?;
+
+        if guard.as_ref().kick_prepare() {
+            guard.as_ref().notify();
+        }
+        drop(guard);
+
+        if timeout_jiffies > 0 {
+            self.token
+                .responded
+                .wait_for_completion_interruptible_timeout(timeout_jiffies)?;
+        } else {
+            self.token.responded.wait_for_completion_interruptible()?;
+        }
+        Ok(())
+    }
+}
+
+// TODO: use a proper enum
+
+const VIRTIO_RTC_REQ_READ: u16 = 0x0001;
+const VIRTIO_RTC_REQ_CFG: u16 = 0x1000;
+const VIRTIO_RTC_REQ_CLOCK_CAP: u16 = 0x1001;
+
+struct VirtioRtcVq {
+    ptr: NonNull<Virtqueue>,
+}
+
+// SAFETY: `VirtioRtcVq` is safe to be send to any task.
+unsafe impl Send for VirtioRtcVq {}
+
+impl VirtioRtcVq {
+    fn new(ptr: *mut Virtqueue) -> impl PinInit<SpinLock<Self>> {
+        let ptr = NonNull::new(ptr).unwrap();
+        new_spinlock!(Self { ptr })
+    }
+
+    fn as_ref(&self) -> &Virtqueue {
+        unsafe { self.ptr.as_ref() }
+    }
+}
+
+struct VirtioRtcDriver {
+    reqvq: Pin<KBox<SpinLock<VirtioRtcVq>>>,
+    alarmvq: Option<Pin<KBox<SpinLock<VirtioRtcVq>>>>,
+    num_clocks: Cell<u16>,
+    registered_clocks: Pin<KBox<Mutex<KVec<()>>>>,
+}
+
+impl Drop for VirtioRtcDriver {
+    fn drop(&mut self) {
+        pr_info!("Remove Rust virtio driver sample.\n");
+    }
+}
+
+unsafe extern "C" fn vq_requestq_callback(vq: *mut kernel::bindings::virtqueue) {
+    let vq = unsafe { Virtqueue::from_raw(vq) };
+    let dev: &virtio::Device<CoreInternal> = vq.dev();
+    let data = unsafe { dev.as_ref().drvdata_borrow::<VirtioRtcDriver>() };
+    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],
+        };
+        stack_try_pin_init!(
+            let msg: Message::<ReqHead, RespCfg> =
+                Message::new(head, VIRTIO_RTC_REQ_CFG)?);
+        let msg: core::pin::Pin<&mut Message<ReqHead, RespCfg>> = msg?;
+        msg.send(&self.reqvq, 0)?;
+        pr_info!("Got response! {:?}\n", msg.get_response());
+
+        let response: &RespCfg = msg.get_response()?;
+        Ok(response.num_clocks.into())
+    }
+
+    fn process_requestq(&self) {
+        let mut cb_enabled = true;
+        loop {
+            let guard = self.reqvq.lock();
+            if cb_enabled {
+                guard.as_ref().disable_cb();
+                cb_enabled = false;
+            }
+            if let Some((token, len)) = guard.as_ref().get_buf() {
+                drop(guard);
+                pr_info!("process_requestq got buf {len} bytes\n");
+                let mut token = token.cast::<Token>();
+
+                unsafe { token.as_mut().resp_actual_size = len };
+                unsafe { token.as_mut().responded.complete_all() };
+            } else {
+                if guard.as_ref().enable_cb() {
+                    return;
+                }
+                cb_enabled = true;
+            }
+        }
+    }
+
+    fn clock_cap(&self, clock_id: u16) -> Result<RespClockCap> {
+        type ClockCapMsg = Message<ReqClockCap, 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],
+        };
+        stack_try_pin_init!(
+            let msg: ClockCapMsg = Message::new(req, VIRTIO_RTC_REQ_CLOCK_CAP)?
+        );
+        let msg: core::pin::Pin<&mut ClockCapMsg> = msg?;
+        msg.send(&self.reqvq, 0)?;
+        pr_info!("Got response! {:?}\n", msg.get_response());
+        let response: &RespClockCap = msg.get_response()?;
+        Ok(*response)
+    }
+
+    fn read(&self, clock_id: u16) -> Result<u64> {
+        type ReadMsg = Message<ReqRead, RespRead>;
+
+        let req = ReqRead {
+            head: ReqHead {
+                msg_type: VIRTIO_RTC_REQ_READ.into(),
+                reserved: [0; 6],
+            },
+            clock_id: clock_id.into(),
+            reserved: [0; 6],
+        };
+        stack_try_pin_init!(
+            let msg: ReadMsg = Message::new(req, VIRTIO_RTC_REQ_CLOCK_CAP)?
+        );
+        let msg: core::pin::Pin<&mut ReadMsg> = msg?;
+        msg.send(&self.reqvq, 0)?;
+        pr_info!("Got response! {:?}\n", msg.get_response());
+        let response: &RespRead = msg.get_response()?;
+        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> {
+        const VQS_INFO: [VirtqueueInfo; 1] = [
+            VirtqueueInfo::new(c"requestq", false, vq_requestq_callback),
+            //VirtqueueInfo::new(c"alarmq", false, vq_callback),
+        ];
+        let init_fn = move |slot: *mut Self| {
+            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);
+                }
+            };
+            let reqvq = KBox::pin_init(VirtioRtcVq::new(vqs[0]), GFP_ATOMIC)?;
+            let registered_clocks =
+                KBox::pin_init(new_mutex!(KVec::with_capacity(0, GFP_KERNEL)?), GFP_KERNEL)?;
+            unsafe {
+                core::ptr::write(
+                    slot,
+                    Self {
+                        num_clocks: Cell::new(0),
+                        reqvq,
+                        alarmvq: None,
+                        registered_clocks,
+                    },
+                )
+            };
+            Ok(())
+        };
+        unsafe { pin_init::pin_init_from_closure(init_fn) }
+    }
+
+    fn init(&self, vdev: &virtio::Device<Core>) -> Result {
+        vdev.ready();
+        self.num_clocks.set(self.req_cfg()?);
+        for i in 0..(self.num_clocks.get()) {
+            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(vdev: &virtio::Device, _this: Pin<&Self>) {
+        pr_info!("Removing Rust virtio driver sample.\n");
+        vdev.reset();
+        vdev.del_vqs();
+    }
+}
+
+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] 12+ messages in thread

* Re: [PATCH RFC 6/6] samples/rust: Add sample virtio-rtc driver [WIP]
  2026-05-05  8:14 ` [PATCH RFC 6/6] samples/rust: Add sample virtio-rtc driver [WIP] Manos Pitsidianakis
@ 2026-05-06  7:09   ` Manos Pitsidianakis
  0 siblings, 0 replies; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-06  7:09 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, rust-for-linux,
	Jason Wang, Xuan Zhuo, Eugenio Pérez, virtualization,
	linux-kernel, Manos Pitsidianakis

Some stuff that needs to be addressed:

On Tue, 05 May 2026 11:14, Manos Pitsidianakis <manos@pitsidianak.is> wrote:
>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 | 470 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 487 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..f580ed83a0a57a4b051372a51f56b787d53ed602
>--- /dev/null
>+++ b/samples/rust/rust_virtio_rtc.rs
>@@ -0,0 +1,470 @@
>+// SPDX-License-Identifier: GPL-2.0
>+
>+//! Rust virtio driver sample.
>+
>+use core::{
>+    cell::Cell,
>+    marker::PhantomData,
>+    ptr::NonNull, //
>+};
>+
>+use kernel::{
>+    device::{
>+        Core,
>+        CoreInternal, //
>+    },
>+    new_mutex, new_spinlock, page,

Formatting

>+    prelude::*,
>+    scatterlist::SGEntry,
>+    sync::Completion,
>+    sync::{Mutex, SpinLock},

Ditto

>+    virtio::{
>+        self,
>+        utils::*,
>+        virtqueue::*, //
>+    },
>+};
>+
>+use pin_init::stack_try_pin_init;
>+
>+#[pin_data]
>+struct Token {
>+    resp_actual_size: u32,
>+    #[pin]
>+    responded: Completion,
>+}
>+
>+#[pin_data]
>+struct Message<Request: Zeroable, Response: Zeroable> {
>+    msg_type: u16,
>+    #[pin]
>+    req: KVec<u8>,
>+    #[pin]
>+    resp: KVec<u8>,
>+    req_ptr: NonNull<Request>,
>+    resp_ptr: NonNull<Response>,
>+    #[pin]
>+    token: Token,
>+    _ph_req: PhantomData<Request>,
>+    _ph_resp: PhantomData<Response>,
>+}
>+
>+#[derive(Copy, Clone, Debug, Zeroable)]
>+#[repr(C)]
>+#[doc(alias = "virtio_rtc_req_head")]
>+struct ReqHead {
>+    msg_type: Le16,
>+    reserved: [u8; 6],
>+}
>+
>+#[derive(Debug, Zeroable)]
>+#[repr(C)]
>+#[doc(alias = "virtio_rtc_resp_head")]
>+struct RespHead {
>+    status: u8,
>+    reserved: [u8; 7],
>+}
>+
>+#[derive(Debug, Zeroable)]
>+#[repr(C)]
>+#[doc(alias = "virtio_rtc_resp_cfg")]
>+struct RespCfg {
>+    head: ReqHead,
>+    /** # 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: ReqHead,
>+    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: ReqHead,
>+    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,
>+}
>+
>+// SAFETY: `Message` is safe to be send to any task.
>+unsafe impl<Request: Zeroable, Response: Zeroable> Send for Message<Request, Response> {}
>+
>+// SAFETY: `Message` is safe to be accessed concurrently.
>+unsafe impl<Request: Zeroable, Response: Zeroable> Sync for Message<Request, Response> {}
>+
>+impl<Request: Zeroable, Response: Zeroable> Message<Request, Response> {
>+    /// Create an initializer for a new [`Message`].
>+    fn new(req_data: Request, msg_type: u16) -> Result<impl PinInit<Self, Error>> {
>+        macro_rules! alloc_buf {
>+            ($t:ty) => {{
>+                let size = (core::mem::size_of::<$t>() / page::PAGE_SIZE + 1) * page::PAGE_SIZE;
>+                KVec::<u8>::with_capacity(size, GFP_KERNEL)

Not sure if KVec is the proper allocation type here...

>+            }};
>+        }
>+        let mut req = alloc_buf!(Request)?;
>+        let mut resp = alloc_buf!(Response)?;
>+        let req_ptr: NonNull<Request> = NonNull::new(req.as_mut_ptr().cast()).unwrap();
>+        let resp_ptr = NonNull::new(resp.as_mut_ptr().cast()).unwrap();
>+        // SAFETY: `req_ptr` is a valid Request allocation
>+        unsafe {
>+            core::ptr::write(req_ptr.as_ptr(), req_data);
>+        }
>+        Ok(pin_init!(Self {
>+            req,
>+            resp,
>+            msg_type,
>+            req_ptr,
>+            resp_ptr,
>+            token <- pin_init!(Token {
>+                resp_actual_size: 0,
>+                responded <- Completion::new(),
>+            }),
>+            _ph_req: PhantomData,
>+            _ph_resp: PhantomData,
>+        }? Error))
>+    }
>+
>+    fn get_response(&self) -> Result<&Response, Error> {
>+        if self.token.resp_actual_size as usize != core::mem::size_of::<Response>() {
>+            if self.token.resp_actual_size as usize >= core::mem::size_of::<RespHead>() {
>+                let head: &RespHead = unsafe { self.resp_ptr.cast().as_ref() };
>+                return match head.status {
>+                    0 | 3 => Err(EINVAL),
>+                    1 => Err(ENOTSUPP),
>+                    2 => Err(ENODEV),
>+                    4 | 5_u8..=u8::MAX => Err(EIO),
>+                };
>+            }
>+            return Err(EINVAL);
>+        }
>+        Ok(unsafe { self.resp_ptr.as_ref() })
>+    }
>+
>+    fn send(&self, vq: &SpinLock<VirtioRtcVq>, timeout_jiffies: c_ulong) -> Result {
>+        let guard = vq.lock();

This is a spinlock, but we don't disable interrupts (spin_lock_irqsave 
in C). This method seems to be missing from 
rust/kernel/sync/lock/spinlock.rs, I will try adding it.

>+
>+        let mut sg_in = core::mem::MaybeUninit::zeroed();
>+        let mut sg_out = core::mem::MaybeUninit::zeroed();
>+        let req = unsafe {
>+            SGEntry::init_one(
>+                &mut sg_out,
>+                self.req_ptr.cast(),
>+                core::mem::size_of::<Request>() as u32,
>+            )
>+        };
>+        let resp = unsafe {
>+            SGEntry::init_one(
>+                &mut sg_in,
>+                self.resp_ptr.cast(),
>+                core::mem::size_of::<Response>() as u32,
>+            )
>+        };
>+        let sgs = [req, resp];
>+        guard.as_ref().add_sgs(
>+            &sgs,
>+            1,
>+            1,
>+            (&raw const self.token).cast_mut().cast(),
>+            GFP_ATOMIC,
>+        )?;
>+
>+        if guard.as_ref().kick_prepare() {
>+            guard.as_ref().notify();
>+        }
>+        drop(guard);
>+
>+        if timeout_jiffies > 0 {
>+            self.token
>+                .responded
>+                .wait_for_completion_interruptible_timeout(timeout_jiffies)?;
>+        } else {
>+            self.token.responded.wait_for_completion_interruptible()?;
>+        }

Failure here should not Drop the message, it should be reference-counted 
and dropped when the virtqueue callback gets a reply for that token (or 
when the device is reset).

>+        Ok(())
>+    }
>+}
>+
>+// TODO: use a proper enum
>+
>+const VIRTIO_RTC_REQ_READ: u16 = 0x0001;
>+const VIRTIO_RTC_REQ_CFG: u16 = 0x1000;
>+const VIRTIO_RTC_REQ_CLOCK_CAP: u16 = 0x1001;
>+
>+struct VirtioRtcVq {
>+    ptr: NonNull<Virtqueue>,
>+}
>+
>+// SAFETY: `VirtioRtcVq` is safe to be send to any task.
>+unsafe impl Send for VirtioRtcVq {}
>+
>+impl VirtioRtcVq {
>+    fn new(ptr: *mut Virtqueue) -> impl PinInit<SpinLock<Self>> {
>+        let ptr = NonNull::new(ptr).unwrap();
>+        new_spinlock!(Self { ptr })
>+    }
>+
>+    fn as_ref(&self) -> &Virtqueue {
>+        unsafe { self.ptr.as_ref() }
>+    }
>+}
>+
>+struct VirtioRtcDriver {
>+    reqvq: Pin<KBox<SpinLock<VirtioRtcVq>>>,
>+    alarmvq: Option<Pin<KBox<SpinLock<VirtioRtcVq>>>>,
>+    num_clocks: Cell<u16>,

Use atomic u16 here

>+    registered_clocks: Pin<KBox<Mutex<KVec<()>>>>,
>+}
>+
>+impl Drop for VirtioRtcDriver {
>+    fn drop(&mut self) {
>+        pr_info!("Remove Rust virtio driver sample.\n");
>+    }
>+}
>+
>+unsafe extern "C" fn vq_requestq_callback(vq: *mut kernel::bindings::virtqueue) {
>+    let vq = unsafe { Virtqueue::from_raw(vq) };
>+    let dev: &virtio::Device<CoreInternal> = vq.dev();
>+    let data = unsafe { dev.as_ref().drvdata_borrow::<VirtioRtcDriver>() };
>+    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],
>+        };
>+        stack_try_pin_init!(
>+            let msg: Message::<ReqHead, RespCfg> =
>+                Message::new(head, VIRTIO_RTC_REQ_CFG)?);
>+        let msg: core::pin::Pin<&mut Message<ReqHead, RespCfg>> = msg?;
>+        msg.send(&self.reqvq, 0)?;
>+        pr_info!("Got response! {:?}\n", msg.get_response());
>+
>+        let response: &RespCfg = msg.get_response()?;
>+        Ok(response.num_clocks.into())
>+    }
>+
>+    fn process_requestq(&self) {
>+        let mut cb_enabled = true;
>+        loop {
>+            let guard = self.reqvq.lock();
>+            if cb_enabled {
>+                guard.as_ref().disable_cb();
>+                cb_enabled = false;
>+            }
>+            if let Some((token, len)) = guard.as_ref().get_buf() {
>+                drop(guard);
>+                pr_info!("process_requestq got buf {len} bytes\n");
>+                let mut token = token.cast::<Token>();
>+
>+                unsafe { token.as_mut().resp_actual_size = len };
>+                unsafe { token.as_mut().responded.complete_all() };
>+            } else {
>+                if guard.as_ref().enable_cb() {
>+                    return;
>+                }
>+                cb_enabled = true;
>+            }
>+        }
>+    }
>+
>+    fn clock_cap(&self, clock_id: u16) -> Result<RespClockCap> {
>+        type ClockCapMsg = Message<ReqClockCap, 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],
>+        };
>+        stack_try_pin_init!(
>+            let msg: ClockCapMsg = Message::new(req, VIRTIO_RTC_REQ_CLOCK_CAP)?
>+        );
>+        let msg: core::pin::Pin<&mut ClockCapMsg> = msg?;
>+        msg.send(&self.reqvq, 0)?;
>+        pr_info!("Got response! {:?}\n", msg.get_response());
>+        let response: &RespClockCap = msg.get_response()?;
>+        Ok(*response)
>+    }
>+
>+    fn read(&self, clock_id: u16) -> Result<u64> {
>+        type ReadMsg = Message<ReqRead, RespRead>;
>+
>+        let req = ReqRead {
>+            head: ReqHead {
>+                msg_type: VIRTIO_RTC_REQ_READ.into(),
>+                reserved: [0; 6],
>+            },
>+            clock_id: clock_id.into(),
>+            reserved: [0; 6],
>+        };
>+        stack_try_pin_init!(
>+            let msg: ReadMsg = Message::new(req, VIRTIO_RTC_REQ_CLOCK_CAP)?

s/CLOCK_CAP/READ (the Message type is messy, must polish it)

>+        );
>+        let msg: core::pin::Pin<&mut ReadMsg> = msg?;
>+        msg.send(&self.reqvq, 0)?;
>+        pr_info!("Got response! {:?}\n", msg.get_response());
>+        let response: &RespRead = msg.get_response()?;
>+        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> {
>+        const VQS_INFO: [VirtqueueInfo; 1] = [
>+            VirtqueueInfo::new(c"requestq", false, vq_requestq_callback),
>+            //VirtqueueInfo::new(c"alarmq", false, vq_callback),
>+        ];
>+        let init_fn = move |slot: *mut Self| {
>+            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);
>+                }
>+            };
>+            let reqvq = KBox::pin_init(VirtioRtcVq::new(vqs[0]), GFP_ATOMIC)?;
>+            let registered_clocks =
>+                KBox::pin_init(new_mutex!(KVec::with_capacity(0, GFP_KERNEL)?), GFP_KERNEL)?;
>+            unsafe {
>+                core::ptr::write(
>+                    slot,
>+                    Self {
>+                        num_clocks: Cell::new(0),
>+                        reqvq,
>+                        alarmvq: None,
>+                        registered_clocks,
>+                    },
>+                )
>+            };
>+            Ok(())
>+        };
>+        unsafe { pin_init::pin_init_from_closure(init_fn) }
>+    }
>+
>+    fn init(&self, vdev: &virtio::Device<Core>) -> Result {
>+        vdev.ready();
>+        self.num_clocks.set(self.req_cfg()?);
>+        for i in 0..(self.num_clocks.get()) {
>+            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(vdev: &virtio::Device, _this: Pin<&Self>) {
>+        pr_info!("Removing Rust virtio driver sample.\n");
>+        vdev.reset();
>+        vdev.del_vqs();
>+    }
>+}
>+
>+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	[flat|nested] 12+ messages in thread

* Re: [PATCH RFC 2/6] rust/helpers: add virtio.c
  2026-05-05  8:14 ` [PATCH RFC 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
@ 2026-05-07  8:21   ` Philippe Mathieu-Daudé
  2026-05-07  8:28   ` Alice Ryhl
  1 sibling, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-07  8:21 UTC (permalink / raw)
  To: Manos Pitsidianakis, 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, rust-for-linux,
	Jason Wang, Xuan Zhuo, Eugenio Pérez, virtualization,
	linux-kernel

On 5/5/26 10:14, Manos Pitsidianakis 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 |  3 +++
>   rust/helpers/virtio.c  | 35 +++++++++++++++++++++++++++++++++++
>   3 files changed, 44 insertions(+)

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>

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

* Re: [PATCH RFC 4/6] rust/scatterlist: add SGEntry::init_one
  2026-05-05  8:14 ` [PATCH RFC 4/6] rust/scatterlist: add SGEntry::init_one Manos Pitsidianakis
@ 2026-05-07  8:23   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-07  8:23 UTC (permalink / raw)
  To: Manos Pitsidianakis, 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, rust-for-linux,
	Jason Wang, Xuan Zhuo, Eugenio Pérez, virtualization,
	linux-kernel

On 5/5/26 10:14, Manos Pitsidianakis wrote:
> Add a method to allow creation of an SGEntry with borrowed data.
> Analogous of `sg_init_one` in C.
> 
> Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
> ---
>   rust/kernel/scatterlist.rs | 18 ++++++++++++++++++
>   1 file changed, 18 insertions(+)

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>

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

* Re: [PATCH RFC 2/6] rust/helpers: add virtio.c
  2026-05-05  8:14 ` [PATCH RFC 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
  2026-05-07  8:21   ` Philippe Mathieu-Daudé
@ 2026-05-07  8:28   ` Alice Ryhl
  2026-05-07  8:29     ` Manos Pitsidianakis
  1 sibling, 1 reply; 12+ messages in thread
From: Alice Ryhl @ 2026-05-07  8:28 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, Trevor Gross, Danilo Krummrich,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel

On Tue, May 5, 2026 at 10:14 AM 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 |  3 +++
>  rust/helpers/virtio.c  | 35 +++++++++++++++++++++++++++++++++++
>  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..84b54690d95be37699ef9a9c4d7cedec0bbae6d3 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -62,6 +62,9 @@
>  #include "uaccess.c"
>  #include "usb.c"
>  #include "vmalloc.c"
> +#if defined(CONFIG_VIRTIO)
> +#include "virtio.c"
> +#endif /* defined(CONFIG_VIRTIO) */

Please move this ifdef inside the file instead, to match all other
files with similar ifdefs.

Alice

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

* Re: [PATCH RFC 2/6] rust/helpers: add virtio.c
  2026-05-07  8:28   ` Alice Ryhl
@ 2026-05-07  8:29     ` Manos Pitsidianakis
  0 siblings, 0 replies; 12+ messages in thread
From: Manos Pitsidianakis @ 2026-05-07  8:29 UTC (permalink / raw)
  To: Alice Ryhl
  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, Trevor Gross, Danilo Krummrich,
	rust-for-linux, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	virtualization, linux-kernel

On Thu, 07 May 2026 11:28, Alice Ryhl <aliceryhl@google.com> wrote:
>On Tue, May 5, 2026 at 10:14 AM 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 |  3 +++
>>  rust/helpers/virtio.c  | 35 +++++++++++++++++++++++++++++++++++
>>  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..84b54690d95be37699ef9a9c4d7cedec0bbae6d3 100644
>> --- a/rust/helpers/helpers.c
>> +++ b/rust/helpers/helpers.c
>> @@ -62,6 +62,9 @@
>>  #include "uaccess.c"
>>  #include "usb.c"
>>  #include "vmalloc.c"
>> +#if defined(CONFIG_VIRTIO)
>> +#include "virtio.c"
>> +#endif /* defined(CONFIG_VIRTIO) */
>
>Please move this ifdef inside the file instead, to match all other
>files with similar ifdefs.
>
>Alice

Will do, thanks. Should this also be IS_ENABLED(CONFIG_VIRTIO) btw?

Manos

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

end of thread, other threads:[~2026-05-07  8:30 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-05  8:14 [PATCH RFC 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
2026-05-05  8:14 ` [PATCH RFC 1/6] rust/bindings: generate virtio bindings Manos Pitsidianakis
2026-05-05  8:14 ` [PATCH RFC 2/6] rust/helpers: add virtio.c Manos Pitsidianakis
2026-05-07  8:21   ` Philippe Mathieu-Daudé
2026-05-07  8:28   ` Alice Ryhl
2026-05-07  8:29     ` Manos Pitsidianakis
2026-05-05  8:14 ` [PATCH RFC 3/6] rust: add virtio module Manos Pitsidianakis
2026-05-05  8:14 ` [PATCH RFC 4/6] rust/scatterlist: add SGEntry::init_one Manos Pitsidianakis
2026-05-07  8:23   ` Philippe Mathieu-Daudé
2026-05-05  8:14 ` [PATCH RFC 5/6] rust: impl interruptible waits for Completion Manos Pitsidianakis
2026-05-05  8:14 ` [PATCH RFC 6/6] samples/rust: Add sample virtio-rtc driver [WIP] Manos Pitsidianakis
2026-05-06  7:09   ` Manos Pitsidianakis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox