From: Stefano Garzarella <sgarzare@redhat.com>
To: Manos Pitsidianakis <manos@pitsidianak.is>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
"Peter Hilber" <peter.hilber@oss.qualcomm.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
Subject: Re: [PATCH RFC v3 6/6] samples/rust: Add sample virtio-rtc driver [WIP]
Date: Fri, 7 Aug 2026 16:09:28 +0200 [thread overview]
Message-ID: <anXiIAJelE4aj3wq@sgarzare-redhat> (raw)
In-Reply-To: <20260510-rust-virtio-v3-6-1427f14d67e1@pitsidianak.is>
On Sun, May 10, 2026 at 04:38:19PM +0300, Manos Pitsidianakis 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 | 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)?;
I think we need to be careful with GFP_KERNEL allocations and spinlock,
since IIRC GFP_KERNEL allocations may sleep.
>+ 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>() {
I think we should remove the else here, or check this condition first.
>+ 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() };
Could this be an UB?
IIUC send() still holds a Pin<&mut Token> on its stack, blocked in the
completion wait, so we will have 2 exclusive references to the same
object.
>+ 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
>
next prev parent reply other threads:[~2026-08-07 14:09 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
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-08-07 14:09 ` Stefano Garzarella
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-08-07 14:09 ` Stefano Garzarella [this message]
2026-07-21 6:21 ` [PATCH RFC v3 0/6] Add Rust virtio bindings and sample device Manos Pitsidianakis
2026-07-21 7:46 ` Stefano Garzarella
2026-07-22 8:49 ` Eugenio Perez Martin
2026-07-22 8:55 ` Manos Pitsidianakis
2026-07-22 9:31 ` Eugenio Perez Martin
2026-07-22 9:55 ` Manos Pitsidianakis
2026-08-07 14:30 ` Stefano Garzarella
2026-08-17 13:01 ` R: " Matteo Giugni
2026-08-17 15:52 ` Matteo Giugni
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=anXiIAJelE4aj3wq@sgarzare-redhat \
--to=sgarzare@redhat.com \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=eperezma@redhat.com \
--cc=gary@garyguo.net \
--cc=jasowang@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=manos.pitsidianakis@linaro.org \
--cc=manos@pitsidianak.is \
--cc=mst@redhat.com \
--cc=ojeda@kernel.org \
--cc=peter.hilber@oss.qualcomm.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=stefanha@redhat.com \
--cc=tmgross@umich.edu \
--cc=viresh.kumar@linaro.org \
--cc=virtualization@lists.linux.dev \
--cc=xuanzhuo@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®