All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Danilo Krummrich" <dakr@kernel.org>
To: "Maurice Hieronymus" <mhi@mailbox.org>
Cc: "Bjorn Helgaas" <bhelgaas@google.com>,
	"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"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>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Tamir Duberstein" <tamird@kernel.org>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	"Onur Özkan" <work@onurozkan.dev>,
	"Lyude Paul" <lyude@redhat.com>,
	linux-pci@vger.kernel.org, rust-for-linux@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 4/4] rust: samples: add EDU PCI driver sample
Date: Fri, 03 Jul 2026 23:12:14 +0200	[thread overview]
Message-ID: <DJP8UPM9XT95.102LJRRRY4P2S@kernel.org> (raw)
In-Reply-To: <DJEQ64V8HE19.2DMBHY4XRPMG6@kernel.org>

On Sun Jun 21, 2026 at 2:27 PM CEST, Danilo Krummrich wrote:
> I'm going to send a patch to make the irq::Registration compatible with the
> device driver lifetime rework soon. Please rebase onto that once it's sent, so
> this sample can land as idiomatic as possible.

Please find the patch in [1]; purely mechanical conversion of the EDU driver for
testing purposes in [2].

[1] https://lore.kernel.org/driver-core/20260703210936.1128698-1-dakr@kernel.org/

[2]

diff --git a/samples/rust/rust_driver_edu.rs b/samples/rust/rust_driver_edu.rs
index 5f4efd514032..301b780773cb 100644
--- a/samples/rust/rust_driver_edu.rs
+++ b/samples/rust/rust_driver_edu.rs
@@ -6,7 +6,6 @@

 use kernel::{
     device::Bound,
-    devres::Devres,
     dma::{Coherent, Device, DmaMask},
     io::{
         poll::read_poll_timeout,
@@ -16,7 +15,7 @@
     irq::{self, Flags},
     pci::{self, IrqTypes},
     prelude::*,
-    sync::{aref::ARef, Arc, Completion},
+    sync::Completion,
     time::Delta, //
 };

@@ -66,20 +65,21 @@ mod regs {
     pub(super) const END: usize = 0xA0;
 }

-type Bar0 = pci::Bar<'static, { regs::END }>;
+type Bar0<'a> = pci::Bar<'a, { regs::END }>;
+
+struct EduDriver;

 #[pin_data(PinnedDrop)]
-struct EduDriver {
-    pdev: ARef<pci::Device>,
-    data: Arc<EduDriverData>,
+struct EduDriverData<'bound> {
+    pdev: &'bound pci::Device,
     #[pin]
-    irq_handler: irq::Registration<Arc<EduDriverData>>,
+    irq_handler: irq::Registration<'bound, IrqHandler<'bound>>,
 }

 #[pin_data]
-struct EduDriverData {
-    #[pin]
-    bar: Devres<Bar0>,
+struct IrqHandler<'a> {
+    pdev: &'a pci::Device,
+    bar: Bar0<'a>,
     #[pin]
     irq_test_completion: Completion,
     #[pin]
@@ -88,16 +88,16 @@ struct EduDriverData {
 }

 impl EduDriver {
-    fn init(pdev: &pci::Device<Bound>, bar: &Bar0, data: &EduDriverData) -> Result {
+    fn init(pdev: &pci::Device<Bound>, bar: &Bar0<'_>, handler: &IrqHandler<'_>) -> Result {
         Self::magic(pdev, bar)?;
         Self::liveness_check(pdev, bar)?;
         Self::factorial(pdev, bar)?;
-        Self::test_irq(pdev, bar, data)?;
-        Self::test_dma(pdev, bar, data)?;
+        Self::test_irq(pdev, handler)?;
+        Self::test_dma(pdev, handler)?;
         Ok(())
     }

-    fn magic(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+    fn magic(pdev: &pci::Device<Bound>, bar: &Bar0<'_>) -> Result {
         let identification = bar.read(regs::IDENTIFICATION);

         let magic: u8 = identification.magic().into();
@@ -121,7 +121,7 @@ fn magic(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
         Ok(())
     }

-    fn liveness_check(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+    fn liveness_check(pdev: &pci::Device<Bound>, bar: &Bar0<'_>) -> Result {
         let test_value = 0xabcd;

         bar.write(regs::LIVENESS_CHECK, test_value.into());
@@ -142,7 +142,7 @@ fn liveness_check(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
         Ok(())
     }

-    fn factorial(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+    fn factorial(pdev: &pci::Device<Bound>, bar: &Bar0<'_>) -> Result {
         Self::wait_until_compute_has_finished(pdev, bar)?;

         bar.write(regs::FACTORIAL, 4.into());
@@ -167,28 +167,30 @@ fn factorial(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
         Ok(())
     }

-    fn test_irq(pdev: &pci::Device<Bound>, bar: &Bar0, data: &EduDriverData) -> Result {
+    fn test_irq(pdev: &pci::Device<Bound>, handler: &IrqHandler<'_>) -> Result {
         dev_dbg!(pdev, "raising irq\n");

-        bar.write(regs::IRQ_RAISE, IRQ_MAGIC_VALUE.into());
+        handler.bar.write(regs::IRQ_RAISE, IRQ_MAGIC_VALUE.into());

-        data.irq_test_completion.wait_for_completion();
+        handler.irq_test_completion.wait_for_completion();
         Ok(())
     }

-    fn test_dma(pdev: &pci::Device<Bound>, bar: &Bar0, data: &EduDriverData) -> Result {
+    fn test_dma(pdev: &pci::Device<Bound>, handler: &IrqHandler<'_>) -> Result {
         dev_dbg!(pdev, "testing dma\n");

-        let dma = &data.dma;
+        let dma = &handler.dma;

         const DMA_VALUE: u64 = 42;

         kernel::dma_write!(dma, , DMA_VALUE);

-        bar.write(regs::DMA_SRC, dma.dma_handle().into());
-        bar.write(regs::DMA_DST, QEMU_DMA_BASE.into());
-        bar.write(regs::DMA_COUNT, (dma.size() as u64).into());
-        bar.write(
+        handler.bar.write(regs::DMA_SRC, dma.dma_handle().into());
+        handler.bar.write(regs::DMA_DST, QEMU_DMA_BASE.into());
+        handler
+            .bar
+            .write(regs::DMA_COUNT, (dma.size() as u64).into());
+        handler.bar.write(
             regs::DMA_COMMAND,
             regs::DMA_COMMAND::zeroed()
                 .with_start_transfer(true)
@@ -196,15 +198,17 @@ fn test_dma(pdev: &pci::Device<Bound>, bar: &Bar0, data: &EduDriverData) -> Resu
                 .with_raise_irq(true),
         );

-        data.irq_dma_completion.wait_for_completion();
+        handler.irq_dma_completion.wait_for_completion();

         // Destroy previous value to test roundtrip
         kernel::dma_write!(dma, , 0);

-        bar.write(regs::DMA_SRC, QEMU_DMA_BASE.into());
-        bar.write(regs::DMA_DST, dma.dma_handle().into());
-        bar.write(regs::DMA_COUNT, (dma.size() as u64).into());
-        bar.write(
+        handler.bar.write(regs::DMA_SRC, QEMU_DMA_BASE.into());
+        handler.bar.write(regs::DMA_DST, dma.dma_handle().into());
+        handler
+            .bar
+            .write(regs::DMA_COUNT, (dma.size() as u64).into());
+        handler.bar.write(
             regs::DMA_COMMAND,
             regs::DMA_COMMAND::zeroed()
                 .with_start_transfer(true)
@@ -212,7 +216,7 @@ fn test_dma(pdev: &pci::Device<Bound>, bar: &Bar0, data: &EduDriverData) -> Resu
                 .with_raise_irq(true),
         );

-        data.irq_dma_completion.wait_for_completion();
+        handler.irq_dma_completion.wait_for_completion();

         let result = kernel::dma_read!(dma,);

@@ -230,7 +234,7 @@ fn test_dma(pdev: &pci::Device<Bound>, bar: &Bar0, data: &EduDriverData) -> Resu
         Ok(())
     }

-    fn wait_until_compute_has_finished(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+    fn wait_until_compute_has_finished(pdev: &pci::Device<Bound>, bar: &Bar0<'_>) -> Result {
         read_poll_timeout(
             || Ok(bar.read(regs::STATUS)),
             |status| status.computing() == 0,
@@ -244,7 +248,7 @@ fn wait_until_compute_has_finished(pdev: &pci::Device<Bound>, bar: &Bar0) -> Res

 impl pci::Driver for EduDriver {
     type IdInfo = ();
-    type Data<'bound> = Self;
+    type Data<'bound> = EduDriverData<'bound>;

     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;

@@ -275,76 +279,61 @@ fn probe<'bound>(
                 .alloc_irq_vectors(1, 1, IrqTypes::default().with(pci::IrqType::Msi))
                 .inspect_err(|e| dev_err!(pdev, "alloc_irq_vectors failed: {:?}\n", e))?;

-            // State shared with the IRQ handler (the BAR and the completion the
-            // handler signals) lives in an `Arc<EduDriverData>`. `EduDriverData`
-            // itself implements `irq::Handler`, and the registration takes an
-            // `Arc<T>` via the `impl Handler for Arc<T>` blanket impl. This keeps
-            // the handler's state out of `EduDriver` and avoids a self-reference.
-            let data = Arc::pin_init(
-                try_pin_init!(EduDriverData {
-                    bar <- pdev
-                        .iomap_region_sized(0, c"rust_driver_edu")
-                        .and_then(|bar| bar.into_devres()),
-                    irq_test_completion <- Completion::new(),
-                    irq_dma_completion <- Completion::new(),
-                    dma: ca,
-                }),
-                GFP_KERNEL,
-            )?;
-
-            let req = irq::Registration::new(
-                (*irq.start()).try_into()?,
-                Flags::TRIGGER_NONE,
-                c"rust_edu_irq",
-                Ok(data.clone()),
-            );
+            let bar = pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_edu")?;

-            // Ordering matters: the handler is registered (`irq_handler <- req`)
-            // *before* the `_:` block runs the self-tests, one of which raises an
-            // interrupt and waits for the handler. Raising before the handler is
-            // registered would hang (the completion is never signalled).
-            Ok(try_pin_init!(Self {
+            // SAFETY: The Registration is not leaked.
+            let req = unsafe {
+                irq::Registration::new(
+                    (*irq.start()).try_into()?,
+                    Flags::TRIGGER_NONE,
+                    c"rust_edu_irq",
+                    try_pin_init!(IrqHandler {
+                        pdev,
+                        bar,
+                        irq_test_completion <- Completion::new(),
+                        irq_dma_completion <- Completion::new(),
+                        dma: ca,
+                    }? Error),
+                )
+            };
+
+            Ok(try_pin_init!(EduDriverData {
                 irq_handler <- req,
-                // Side-effect block: run the staged self-tests against the mapped
-                // BAR now that the handler is live. A failure here aborts probe.
+                // Ordering matters: the handler is registered (`irq_handler <- req`)
+                // *before* the `_:` block runs the self-tests, one of which raises an
+                // interrupt and waits for the handler. Raising before the handler is
+                // registered would hang (the completion is never signalled).
                 _: {
-                    let bar = data.bar.access(pdev.as_ref())?;
-                    EduDriver::init(pdev, bar, &data)?;
+                    let handler = irq_handler.handler();
+                    EduDriver::init(pdev, &handler.bar, handler)?;
                     dev_info!(
                         pdev,
                         "rust_driver_edu successfully initialized\n",
                     );
                 },
-                data,
-                pdev: pdev.into()
+                pdev,
             }))
         })
     }
 }

-impl irq::Handler for EduDriverData {
-    fn handle(&self, pdev: &kernel::device::Device<Bound>) -> irq::IrqReturn {
-        dev_dbg!(pdev, "irq handler called\n");
-        // `access()` only fails on device mismatch, so this branch is
-        // structurally unreachable here, but it must be handled.
-        let Ok(bar) = self.bar.access(pdev.as_ref()) else {
-            dev_err!(pdev, "cannot access bar register inside irq handler\n");
-            return irq::IrqReturn::None;
-        };
-        let status: u32 = bar.read(regs::IRQ_STATUS).into();
+impl irq::Handler for IrqHandler<'_> {
+    fn handle(&self) -> irq::IrqReturn {
+        dev_dbg!(self.pdev, "irq handler called\n");
+        let status: u32 = self.bar.read(regs::IRQ_STATUS).into();

         // DMA_IRQ
         if status & DMA_IRQ != 0 {
-            dev_dbg!(pdev, "handling dma completion in irq\n");
-            bar.write(regs::IRQ_ACK, DMA_IRQ.into());
+            dev_dbg!(self.pdev, "handling dma completion in irq\n");
+            self.bar.write(regs::IRQ_ACK, DMA_IRQ.into());
             self.irq_dma_completion.complete();
         }

         // TEST_IRQ
         let magic = status & !DMA_IRQ;
         if magic == IRQ_MAGIC_VALUE {
-            dev_dbg!(pdev, "handling test completion in irq\n");
-            bar.write(regs::IRQ_ACK, magic.into());
+            dev_dbg!(self.pdev, "handling test completion in irq\n");
+            self.bar.write(regs::IRQ_ACK, magic.into());
             self.irq_test_completion.complete();
         }

@@ -353,7 +342,7 @@ fn handle(&self, pdev: &kernel::device::Device<Bound>) -> irq::IrqReturn {
 }

 #[pinned_drop]
-impl PinnedDrop for EduDriver {
+impl PinnedDrop for EduDriverData<'_> {
     fn drop(self: Pin<&mut Self>) {
         dev_dbg!(self.pdev, "Remove Rust EDU driver sample.\n");
     }

      parent reply	other threads:[~2026-07-03 21:12 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-20  8:45 [PATCH v2 0/4] rust: samples: add an EDU PCI driver sample (MMIO + IRQ + DMA) Maurice Hieronymus
2026-06-20  8:45 ` [PATCH v2 1/4] rust: pci: make Vendor::from_raw() public Maurice Hieronymus
2026-06-20  8:50   ` sashiko-bot
2026-06-20  9:48   ` Onur Özkan
2026-06-20  8:45 ` [PATCH v2 2/4] rust: pci: add managed Device::enable_device() Maurice Hieronymus
2026-06-20  9:00   ` sashiko-bot
2026-06-20  9:54   ` Onur Özkan
2026-06-20 22:19     ` Maurice Hieronymus
2026-06-21 12:02       ` Danilo Krummrich
2026-06-20  8:45 ` [PATCH v2 3/4] rust: completion: add complete() Maurice Hieronymus
2026-06-20  8:51   ` sashiko-bot
2026-06-20  9:48   ` Onur Özkan
2026-07-02 14:11     ` Maurice Hieronymus
2026-07-02 14:42     ` Gary Guo
2026-07-02 14:43   ` Gary Guo
2026-06-20  8:45 ` [PATCH v2 4/4] rust: samples: add EDU PCI driver sample Maurice Hieronymus
2026-06-20  8:59   ` sashiko-bot
2026-06-20  9:45   ` Onur Özkan
2026-06-21 12:27   ` Danilo Krummrich
2026-07-02 15:24     ` Maurice Hieronymus
2026-07-02 15:40       ` Danilo Krummrich
2026-07-03 21:12     ` Danilo Krummrich [this message]

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=DJP8UPM9XT95.102LJRRRY4P2S@kernel.org \
    --to=dakr@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=aliceryhl@google.com \
    --cc=bhelgaas@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=gary@garyguo.net \
    --cc=kwilczynski@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=mhi@mailbox.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tamird@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=work@onurozkan.dev \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.