Rust for Linux List
 help / color / mirror / Atom feed
* Re: [PATCH 1/4] rust: pci: make Vendor::from_raw() public
From: Gary Guo @ 2026-06-14 16:47 UTC (permalink / raw)
  To: Maurice Hieronymus, Danilo Krummrich, Bjorn Helgaas,
	Krzysztof Wilczyński, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan
  Cc: linux-pci, rust-for-linux, linux-kernel
In-Reply-To: <20260614-b4-rust-pci-edu-driver-v1-1-e3f2471b595c@mailbox.org>

On Sun Jun 14, 2026 at 4:59 PM BST, Maurice Hieronymus wrote:
> Vendor::from_raw() is currently pub(super), so a Vendor can only be
> obtained through the named constants generated from the PCI_VENDOR_ID_*
> defines in <linux/pci_ids.h>. A driver therefore cannot match a device
> whose vendor ID has no symbolic name.

nit: quote these code blocks inside commit text for visual clarity
>
> Such devices exist. QEMU's "edu" educational device and the legacy
> qemu/Bochs stdvga both use vendor ID 0x1234, which is not registered in
> pci_ids.h. Per the policy stated at the top of that header, IDs are only
> added there when shared between multiple drivers; a single-driver ID is
> expected to be open-coded in the driver instead. C drivers already do
> this -- see drivers/gpu/drm/tiny/bochs.c, which matches with a bare
> ".vendor = 0x1234".
>
> The Rust abstraction has no equivalent escape hatch: there is no public
> way to express an unregistered vendor. Make Vendor::from_raw() public (and
> const, so it can be used in the const device-ID tables built by
> pci_device_table!) so that drivers can construct a Vendor from a raw ID,
> matching what C drivers can already do.
>
> Signed-off-by: Maurice Hieronymus <mhi@mailbox.org>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
>  rust/kernel/pci/id.rs | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/rust/kernel/pci/id.rs b/rust/kernel/pci/id.rs
> index 50005d176561..bd3cf17fd8de 100644
> --- a/rust/kernel/pci/id.rs
> +++ b/rust/kernel/pci/id.rs
> @@ -156,7 +156,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
>  impl Vendor {
>      /// Create a Vendor from a raw 16-bit vendor ID.
>      #[inline]
> -    pub(super) fn from_raw(vendor_id: u16) -> Self {
> +    pub const fn from_raw(vendor_id: u16) -> Self {
>          Self(vendor_id)
>      }
>  



^ permalink raw reply

* [PATCH 4/4] rust: samples: add EDU PCI driver sample
From: Maurice Hieronymus @ 2026-06-14 15:59 UTC (permalink / raw)
  To: Danilo Krummrich, Bjorn Helgaas, Krzysztof Wilczyński,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan
  Cc: linux-pci, rust-for-linux, linux-kernel, Maurice Hieronymus
In-Reply-To: <20260614-b4-rust-pci-edu-driver-v1-0-e3f2471b595c@mailbox.org>

Add a Rust sample driver for the QEMU EDU device, wired up via a new
SAMPLE_RUST_DRIVER_EDU Kconfig option and the samples Makefile.

Signed-off-by: Maurice Hieronymus <mhi@mailbox.org>
---
 samples/rust/Kconfig            |  11 ++
 samples/rust/Makefile           |   1 +
 samples/rust/rust_driver_edu.rs | 379 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 391 insertions(+)

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index c49ab9106345..742c42262e9b 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -118,6 +118,17 @@ config SAMPLE_RUST_DRIVER_PCI
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_EDU
+	tristate "EDU Driver"
+	depends on PCI
+	help
+	  This option builds the Rust EDU driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_edu.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_DRIVER_PLATFORM
 	tristate "Platform Driver"
 	help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 6c0aaa58cccc..c24a328243b1 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_SAMPLE_RUST_DMA)			+= rust_dma.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C)		+= rust_driver_i2c.o
 obj-$(CONFIG_SAMPLE_RUST_I2C_CLIENT)		+= rust_i2c_client.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_EDU)		+= rust_driver_edu.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
diff --git a/samples/rust/rust_driver_edu.rs b/samples/rust/rust_driver_edu.rs
new file mode 100644
index 000000000000..c092117473d3
--- /dev/null
+++ b/samples/rust/rust_driver_edu.rs
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust EDU driver sample (based on QEMU's `edu`).
+//!
+//! To make this driver probe, QEMU must be run with `-device edu`.
+
+use kernel::{
+    device::Bound,
+    devres::Devres,
+    dma::{Coherent, Device, DmaMask},
+    io::{
+        poll::read_poll_timeout,
+        register,
+        Io, //
+    },
+    irq::{self, Flags},
+    pci::{self, IrqTypes},
+    prelude::*,
+    sync::{aref::ARef, Arc, Completion},
+    time::Delta, //
+};
+
+const QEMU_VENDOR_ID: u16 = 0x1234;
+const QEMU_EDU_DEVICE_ID: u32 = 0x11e8;
+const QEMU_EDU_DEVICE_MAGIC: u8 = 0xed;
+const QEMU_DMA_BASE: u64 = 0x40000;
+
+const IRQ_MAGIC_VALUE: u32 = 42;
+
+/// Bit set in `IRQ_STATUS` when a DMA transfer has completed.
+const DMA_IRQ: u32 = 0x100;
+
+mod regs {
+    use super::*;
+
+    register! {
+        pub(super) IDENTIFICATION(u32) @ 0x0 {
+            31:24 major;
+            23:16 minor;
+            7:0 magic;
+        }
+
+        pub(super) LIVENESS_CHECK(u32) @ 0x04 {}
+
+        pub(super) FACTORIAL(u32) @ 0x08 {}
+
+        pub(super) STATUS(u32) @ 0x20 {
+            0:0 computing;
+            7:7 raise_interrupt;
+        }
+
+        pub(super) IRQ_STATUS(u32) @ 0x24 {}
+        pub(super) IRQ_RAISE(u32) @ 0x60 {}
+        pub(super) IRQ_ACK(u32) @ 0x64 {}
+
+        pub(super) DMA_SRC(u64) @ 0x80 {}
+        pub(super) DMA_DST(u64) @ 0x88 {}
+        pub(super) DMA_COUNT(u64) @ 0x90 {}
+        pub(super) DMA_COMMAND(u64) @ 0x98 {
+            0:0 start_transfer;
+            1:1 direction;
+            2:2 raise_irq;
+        }
+    }
+
+    pub(super) const END: usize = 0xA0;
+}
+
+type Bar0 = pci::Bar<{ regs::END }>;
+
+#[pin_data(PinnedDrop)]
+struct EduDriver {
+    pdev: ARef<pci::Device>,
+    data: Arc<EduDriverData>,
+    #[pin]
+    irq_handler: irq::Registration<Arc<EduDriverData>>,
+}
+
+#[pin_data]
+struct EduDriverData {
+    #[pin]
+    bar: Devres<Bar0>,
+    #[pin]
+    irq_test_completion: Completion,
+    #[pin]
+    irq_dma_completion: Completion,
+    dma: Coherent<u64>,
+}
+
+impl EduDriver {
+    fn init(pdev: &pci::Device<Bound>, bar: &Bar0, data: &Arc<EduDriverData>) -> 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)?;
+        Ok(())
+    }
+
+    fn magic(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+        let identification = bar.read(regs::IDENTIFICATION);
+
+        let magic: u8 = identification.magic().into();
+
+        if magic != QEMU_EDU_DEVICE_MAGIC {
+            dev_err!(
+                pdev,
+                "magic mismatch: expected {:#x} got {:#x}\n",
+                QEMU_EDU_DEVICE_MAGIC,
+                magic
+            );
+            return Err(ENODEV);
+        }
+
+        dev_info!(
+            pdev,
+            "major: {:#x} minor: {:#x}\n",
+            identification.major(),
+            identification.minor()
+        );
+        Ok(())
+    }
+
+    fn liveness_check(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+        let test_value = 0xabcd;
+
+        bar.write(regs::LIVENESS_CHECK, test_value.into());
+
+        let inverse_value = bar.read(regs::LIVENESS_CHECK).into_raw();
+
+        if inverse_value != !test_value {
+            dev_err!(
+                pdev,
+                "inverse mismatch: expected {:#x} got {:#x}\n",
+                !test_value,
+                inverse_value
+            );
+            return Err(ENODEV);
+        }
+
+        dev_info!(pdev, "inverse test successful\n");
+        Ok(())
+    }
+
+    fn factorial(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+        Self::wait_until_compute_has_finished(pdev, bar)?;
+
+        bar.write(regs::FACTORIAL, 4.into());
+
+        Self::wait_until_compute_has_finished(pdev, bar)?;
+
+        let result: u32 = bar.read(regs::FACTORIAL).into();
+
+        let expected = 24;
+
+        if result != expected {
+            dev_err!(
+                pdev,
+                "factorial result wrong: expected {} got {}\n",
+                expected,
+                result
+            );
+            return Err(ENODEV);
+        }
+
+        dev_info!(pdev, "factorial test successful\n");
+        Ok(())
+    }
+
+    fn test_irq(pdev: &pci::Device<Bound>, bar: &Bar0, data: &Arc<EduDriverData>) -> Result {
+        dev_dbg!(pdev, "raising irq\n");
+
+        bar.write(regs::IRQ_RAISE, IRQ_MAGIC_VALUE.into());
+
+        data.irq_test_completion.wait_for_completion();
+        Ok(())
+    }
+
+    fn test_dma(pdev: &pci::Device<Bound>, bar: &Bar0, data: &Arc<EduDriverData>) -> Result {
+        dev_dbg!(pdev, "testing dma\n");
+
+        let dma = &data.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(
+            regs::DMA_COMMAND,
+            regs::DMA_COMMAND::zeroed()
+                .with_start_transfer(true)
+                .with_direction(false)
+                .with_raise_irq(true),
+        );
+
+        data.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(
+            regs::DMA_COMMAND,
+            regs::DMA_COMMAND::zeroed()
+                .with_start_transfer(true)
+                .with_direction(true)
+                .with_raise_irq(true),
+        );
+
+        data.irq_dma_completion.wait_for_completion();
+
+        let result = kernel::dma_read!(dma,);
+
+        if result != DMA_VALUE {
+            dev_err!(
+                pdev,
+                "dma result wrong: expected {} got {}\n",
+                DMA_VALUE,
+                result
+            );
+            return Err(ENODEV);
+        }
+
+        dev_info!(pdev, "dma test successful\n");
+        Ok(())
+    }
+
+    fn wait_until_compute_has_finished(pdev: &pci::Device<Bound>, bar: &Bar0) -> Result {
+        if read_poll_timeout(
+            || Ok(bar.read(regs::STATUS)),
+            |status| status.computing() == 0,
+            Delta::from_millis(10),
+            Delta::from_millis(100),
+        )
+        .is_err()
+        {
+            dev_err!(pdev, "computation bit did not clear before timeout\n");
+            return Err(ETIMEDOUT);
+        }
+        Ok(())
+    }
+}
+
+impl pci::Driver for EduDriver {
+    type IdInfo = ();
+
+    const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
+
+    fn probe(
+        pdev: &pci::Device<kernel::device::Core>,
+        _id_info: &Self::IdInfo,
+    ) -> impl PinInit<Self, Error> {
+        pin_init::pin_init_scope(move || {
+            let vendor = pdev.vendor_id();
+            dev_dbg!(
+                pdev,
+                "Probe Rust EDU driver sample (PCI ID: {}, 0x{:x}).\n",
+                vendor,
+                pdev.device_id()
+            );
+
+            pdev.enable_device()?;
+            pdev.set_master();
+
+            let mask = DmaMask::new::<28>();
+
+            // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives.
+            unsafe { pdev.dma_set_mask_and_coherent(mask)? };
+
+            let ca: Coherent<u64> = Coherent::zeroed(pdev.as_ref(), GFP_KERNEL)?;
+
+            let irq = pdev
+                .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"),
+                    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()),
+            );
+
+            // 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 {
+                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.
+                _: {
+                    let bar = data.bar.access(pdev.as_ref())?;
+                    EduDriver::init(pdev, bar, &data)?;
+                    dev_info!(
+                        pdev,
+                        "rust_driver_edu successfully initialized\n",
+                    );
+                },
+                data,
+                pdev: pdev.into()
+            }))
+        })
+    }
+}
+
+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();
+
+        // DMA_IRQ
+        if status & DMA_IRQ != 0 {
+            dev_dbg!(pdev, "handling dma completion in irq\n");
+            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());
+            self.irq_test_completion.complete();
+        }
+
+        irq::IrqReturn::Handled
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for EduDriver {
+    fn drop(self: Pin<&mut Self>) {
+        dev_dbg!(self.pdev, "Remove Rust EDU driver sample.\n");
+    }
+}
+
+kernel::pci_device_table!(
+    PCI_TABLE,
+    MODULE_PCI_TABLE,
+    <EduDriver as pci::Driver>::IdInfo,
+    [(
+        pci::DeviceId::from_id(pci::Vendor::from_raw(QEMU_VENDOR_ID), QEMU_EDU_DEVICE_ID),
+        ()
+    )]
+);
+
+kernel::module_pci_driver! {
+    type: EduDriver,
+    name: "rust_driver_edu",
+    authors: ["Maurice Hieronymus"],
+    description: "Rust EDU driver",
+    license: "GPL v2",
+}

-- 
2.51.2


^ permalink raw reply related

* [PATCH 3/4] rust: completion: add complete()
From: Maurice Hieronymus @ 2026-06-14 15:59 UTC (permalink / raw)
  To: Danilo Krummrich, Bjorn Helgaas, Krzysztof Wilczyński,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan
  Cc: linux-pci, rust-for-linux, linux-kernel, Maurice Hieronymus
In-Reply-To: <20260614-b4-rust-pci-edu-driver-v1-0-e3f2471b595c@mailbox.org>

The initial completion abstraction only added complete_all() and
wait_for_completion(). complete_all() marks the completion permanently
done, which makes a single Completion unsuitable for signalling the same
event repeatedly: once complete_all() has run, every subsequent
wait_for_completion() returns immediately without waiting.

Add complete(), which wakes a single waiter and increments the internal
counter by one. Paired one-to-one with wait_for_completion(), it allows
the same completion to be reused across multiple cycles, e.g. to wait for
consecutive DMA transfers to finish.

Signed-off-by: Maurice Hieronymus <mhi@mailbox.org>
---
 rust/kernel/sync/completion.rs | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/rust/kernel/sync/completion.rs b/rust/kernel/sync/completion.rs
index c50012a940a3..b386b84222f9 100644
--- a/rust/kernel/sync/completion.rs
+++ b/rust/kernel/sync/completion.rs
@@ -90,6 +90,20 @@ fn as_raw(&self) -> *mut bindings::completion {
         self.inner.get()
     }
 
+    /// Signal one task waiting on this completion.
+    ///
+    /// This method wakes up a single task waiting on this completion and increments the
+    /// internal counter by one. If no task is currently waiting, the next
+    /// [`Completion::wait_for_completion`] returns immediately.
+    ///
+    /// Unlike [`Completion::complete_all`], the completion is not marked permanently done, so a
+    /// single [`Completion`] paired one-to-one with [`Completion::wait_for_completion`] can be
+    /// reused to signal the same event repeatedly.
+    pub fn complete(&self) {
+        // SAFETY: `self.as_raw()` is a pointer to a valid `struct completion`.
+        unsafe { bindings::complete(self.as_raw()) };
+    }
+
     /// Signal all tasks waiting on this completion.
     ///
     /// This method wakes up all tasks waiting on this completion; after this operation the

-- 
2.51.2


^ permalink raw reply related

* [PATCH 2/4] rust: pci: add managed Device::enable_device()
From: Maurice Hieronymus @ 2026-06-14 15:59 UTC (permalink / raw)
  To: Danilo Krummrich, Bjorn Helgaas, Krzysztof Wilczyński,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan
  Cc: linux-pci, rust-for-linux, linux-kernel, Maurice Hieronymus
In-Reply-To: <20260614-b4-rust-pci-edu-driver-v1-0-e3f2471b595c@mailbox.org>

Add a managed counterpart to Device::enable_device_mem() that wraps
pcim_enable_device(). In addition to enabling the device, it registers a
pci_disable_device() cleanup that runs automatically when the device is
unbound from its driver, keeping the device's enable count balanced
across unbind/rebind cycles.

The existing enable_device_mem() wraps the unmanaged
pci_enable_device_mem() and has no disable counterpart, so the enable
count is leaked on unbind. On the next probe pci_enable_device_flags()
sees a non-zero enable count and returns early, skipping the power-state
transition back to D0. For a device without a PCI power management
capability the power state cannot be re-read from hardware and stays
PCI_UNKNOWN, which makes __pci_enable_msi_range() reject the subsequent
MSI allocation with -EINVAL.

Signed-off-by: Maurice Hieronymus <mhi@mailbox.org>
---
 rust/kernel/pci.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index af74ddff6114..ca04548c82c3 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -452,6 +452,22 @@ pub fn enable_device_mem(&self) -> Result {
         to_result(unsafe { bindings::pci_enable_device_mem(self.as_raw()) })
     }
 
+    /// Enable I/O and memory resources for this device, with automatic cleanup.
+    ///
+    /// This is the managed version of `pci_enable_device()`: it enables the device's I/O and
+    /// memory resources and registers a `pci_disable_device()` call that runs automatically
+    /// when the device is unbound from its driver. In contrast, [`Device::enable_device_mem`]
+    /// is unmanaged and only enables memory resources.
+    ///
+    /// The automatic cleanup keeps the device's enable count balanced across driver
+    /// unbind/rebind cycles. With an unbalanced (leaked) enable count, a re-probe skips the
+    /// power-state transition back to `D0`, which makes subsequent MSI allocation fail with
+    /// `EINVAL`.
+    pub fn enable_device(&self) -> Result {
+        // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
+        to_result(unsafe { bindings::pcim_enable_device(self.as_raw()) })
+    }
+
     /// Enable bus-mastering for this device.
     #[inline]
     pub fn set_master(&self) {

-- 
2.51.2


^ permalink raw reply related

* [PATCH 0/4] rust: samples: add an EDU PCI driver sample (MMIO + IRQ + DMA)
From: Maurice Hieronymus @ 2026-06-14 15:59 UTC (permalink / raw)
  To: Danilo Krummrich, Bjorn Helgaas, Krzysztof Wilczyński,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan
  Cc: linux-pci, rust-for-linux, linux-kernel, Maurice Hieronymus

The Rust sample drivers currently exercise PCI facilities in isolation:
rust_driver_pci covers MMIO and rust_dma covers DMA, but there is no
single in-tree example that combines memory-mapped I/O, interrupts and
DMA in one driver.

This series adds one. It targets QEMU's `edu` device -- a small,
well-documented educational PCI device that supports exactly these three
facilities and ships with any recent QEMU (`-device edu`), so the sample
runs without special hardware.

The sample maps BAR0 and runs a set of MMIO self-tests (identification,
liveness, factorial), allocates an MSI vector and registers an IRQ
handler, and performs a DMA round-trip -- each stage waiting on a
Completion that the IRQ handler signals.

Prerequisites, that had to be implemented:

- pci: make Vendor::from_raw() public, so a driver can match a device
  whose vendor ID has no symbolic name in pci_ids.h (QEMU's 0x1234),
  matching what C drivers already do.
- pci: add a managed enable_device() wrapping pcim_enable_device(), so
  the enable count stays balanced across unbind/rebind.
- completion: add complete(), so a single Completion can be reused to
  wait for consecutive events (e.g. back-to-back DMA transfers).

Tested with QEMU `-device edu`;

Signed-off-by: Maurice Hieronymus <mhi@mailbox.org>
---
Maurice Hieronymus (4):
      rust: pci: make Vendor::from_raw() public
      rust: pci: add managed Device::enable_device()
      rust: completion: add complete()
      rust: samples: add EDU PCI driver sample

 rust/kernel/pci.rs              |  16 ++
 rust/kernel/pci/id.rs           |   2 +-
 rust/kernel/sync/completion.rs  |  14 ++
 samples/rust/Kconfig            |  11 ++
 samples/rust/Makefile           |   1 +
 samples/rust/rust_driver_edu.rs | 379 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 422 insertions(+), 1 deletion(-)
---
base-commit: 48b375e482027ba6566107cec40c1b21b453fa4e
change-id: 20260614-b4-rust-pci-edu-driver-3e50db2dda0f

Best regards,
-- 
Maurice Hieronymus <mhi@mailbox.org>


^ permalink raw reply

* [PATCH 1/4] rust: pci: make Vendor::from_raw() public
From: Maurice Hieronymus @ 2026-06-14 15:59 UTC (permalink / raw)
  To: Danilo Krummrich, Bjorn Helgaas, Krzysztof Wilczyński,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan
  Cc: linux-pci, rust-for-linux, linux-kernel, Maurice Hieronymus
In-Reply-To: <20260614-b4-rust-pci-edu-driver-v1-0-e3f2471b595c@mailbox.org>

Vendor::from_raw() is currently pub(super), so a Vendor can only be
obtained through the named constants generated from the PCI_VENDOR_ID_*
defines in <linux/pci_ids.h>. A driver therefore cannot match a device
whose vendor ID has no symbolic name.

Such devices exist. QEMU's "edu" educational device and the legacy
qemu/Bochs stdvga both use vendor ID 0x1234, which is not registered in
pci_ids.h. Per the policy stated at the top of that header, IDs are only
added there when shared between multiple drivers; a single-driver ID is
expected to be open-coded in the driver instead. C drivers already do
this -- see drivers/gpu/drm/tiny/bochs.c, which matches with a bare
".vendor = 0x1234".

The Rust abstraction has no equivalent escape hatch: there is no public
way to express an unregistered vendor. Make Vendor::from_raw() public (and
const, so it can be used in the const device-ID tables built by
pci_device_table!) so that drivers can construct a Vendor from a raw ID,
matching what C drivers can already do.

Signed-off-by: Maurice Hieronymus <mhi@mailbox.org>
---
 rust/kernel/pci/id.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rust/kernel/pci/id.rs b/rust/kernel/pci/id.rs
index 50005d176561..bd3cf17fd8de 100644
--- a/rust/kernel/pci/id.rs
+++ b/rust/kernel/pci/id.rs
@@ -156,7 +156,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 impl Vendor {
     /// Create a Vendor from a raw 16-bit vendor ID.
     #[inline]
-    pub(super) fn from_raw(vendor_id: u16) -> Self {
+    pub const fn from_raw(vendor_id: u16) -> Self {
         Self(vendor_id)
     }
 

-- 
2.51.2


^ permalink raw reply related

* Re: [PATCH 2/2] rust: doctest: trim function name for reproducbility
From: Gary Guo @ 2026-06-14 13:01 UTC (permalink / raw)
  To: David Gow, Gary Guo, Brendan Higgins, Rae Moar, Miguel Ojeda,
	Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Igor Korotin
  Cc: rust-for-linux, linux-kselftest, kunit-dev, linux-kernel
In-Reply-To: <5f5c5555-466d-41ac-8187-8b9ddc14965f@davidgow.net>

On Sun Jun 14, 2026 at 8:07 AM BST, David Gow wrote:
> Le 13/06/2026 à 10:04 PM, Gary Guo a écrit :
>> From: Gary Guo <gary@garyguo.net>
>> 
>> Currently rustdoc will generate function names like
>> "_doctest_main__home_gary_Projects_linux_rust_kernel_io_rs_824_0" for a
>> doctest located at rust/kernel/io.rs:824, when building with separate
>> outdir using `O=`. This creates overlong symbol names and is also not
>> reproducible.
>> 
>> Fix it by doing a custom remapping to trim it to something like
>> `_doctest_main_rust_kernel_io_rs_824_0`.
>> 
>> Signed-off-by: Gary Guo <gary@garyguo.net>
>> ---
>
> Hi Gary,
>
> This seems to break older versions of rustc. For example, with
> rust 1.93.1, the build now fails with errors like:
>
> ERROR:root:error[E0425]: cannot find function `_doctest_main_rust_kernel_alloc_allocator_rs_176_0` in this scope
>   --> rust/doctests_kernel_generated.rs:74:27
>    |
> 74 | } let test_return_value = _doctest_main_rust_kernel_alloc_allocator_rs_176_0(); assert!(test_return_value.is_ok()); }
>    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
>
>
> Am I missing something? I'm using kunit.py to build:
> ./tools/testing/kunit/kunit.py run --kconfig_add CONFIG_RUST=y rust_doctests_kernel
>
> Seems to be working okay with 1.96, though.

It looks like upstream rustdoc is using fully qualified path from 1.87+, so the
replacement doesn't find it anymore. Not sure why it works in 1.96, though.

Sashiko suggested doing a full text replacement which I think makes more sense
anyway and would work for all versions (and also for unit-returning functions).
Will do it for the next version.

Best,
Gary

>
> Cheers,
> -- David
>
>>  scripts/rustdoc_test_builder.rs | 14 +++++++++-----
>>  1 file changed, 9 insertions(+), 5 deletions(-)
>> 
>> diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs
>> index 2b1f9ba01839..fda7284355f8 100644
>> --- a/scripts/rustdoc_test_builder.rs
>> +++ b/scripts/rustdoc_test_builder.rs
>> @@ -47,11 +47,18 @@ fn main() {
>>          })
>>          .expect("No test function found in `rustdoc`'s output.");
>>  
>> +    // Figure out a smaller test name based on the generated function name.
>> +    let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;
>> +
>> +    // The rustdoc function name can include the absolute path when building with `O=` which is
>> +    // undesireable and create overlong symbol names. Remap it to relative path.
>> +    let trimmed_function_name = format!("_doctest_main_rust_kernel_{name}");
>> +
>>      // Qualify `Result` to avoid the collision with our own `Result` coming from the prelude.
>>      let body = body.replace(
>>          &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"),
>>          &format!(
>> -            "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
>> +            "{trimmed_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
>>          ),
>>      );
>>  
>> @@ -62,12 +69,9 @@ fn main() {
>>      // We save the result in a variable so that the failed assertion message looks nicer.
>>      let body = body.replace(
>>          &format!("}} {rustdoc_function_name}().unwrap() }}"),
>> -        &format!("}} let test_return_value = {rustdoc_function_name}(); assert!(test_return_value.is_ok()); }}"),
>> +        &format!("}} let test_return_value = {trimmed_function_name}(); assert!(test_return_value.is_ok()); }}"),
>>      );
>>  
>> -    // Figure out a smaller test name based on the generated function name.
>> -    let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;
>> -
>>      let path = format!("rust/test/doctests/kernel/{name}");
>>  
>>      std::fs::write(path, body.as_bytes()).unwrap();



^ permalink raw reply

* Re: [PATCH 2/2] rust: doctest: trim function name for reproducbility
From: David Gow @ 2026-06-14  7:07 UTC (permalink / raw)
  To: Gary Guo, Brendan Higgins, Rae Moar, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman, Igor Korotin
  Cc: rust-for-linux, linux-kselftest, kunit-dev, linux-kernel
In-Reply-To: <20260613140431.232471-2-gary@kernel.org>

Le 13/06/2026 à 10:04 PM, Gary Guo a écrit :
> From: Gary Guo <gary@garyguo.net>
> 
> Currently rustdoc will generate function names like
> "_doctest_main__home_gary_Projects_linux_rust_kernel_io_rs_824_0" for a
> doctest located at rust/kernel/io.rs:824, when building with separate
> outdir using `O=`. This creates overlong symbol names and is also not
> reproducible.
> 
> Fix it by doing a custom remapping to trim it to something like
> `_doctest_main_rust_kernel_io_rs_824_0`.
> 
> Signed-off-by: Gary Guo <gary@garyguo.net>
> ---

Hi Gary,

This seems to break older versions of rustc. For example, with
rust 1.93.1, the build now fails with errors like:

ERROR:root:error[E0425]: cannot find function `_doctest_main_rust_kernel_alloc_allocator_rs_176_0` in this scope
  --> rust/doctests_kernel_generated.rs:74:27
   |
74 | } let test_return_value = _doctest_main_rust_kernel_alloc_allocator_rs_176_0(); assert!(test_return_value.is_ok()); }
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope


Am I missing something? I'm using kunit.py to build:
./tools/testing/kunit/kunit.py run --kconfig_add CONFIG_RUST=y rust_doctests_kernel

Seems to be working okay with 1.96, though.

Cheers,
-- David

>  scripts/rustdoc_test_builder.rs | 14 +++++++++-----
>  1 file changed, 9 insertions(+), 5 deletions(-)
> 
> diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs
> index 2b1f9ba01839..fda7284355f8 100644
> --- a/scripts/rustdoc_test_builder.rs
> +++ b/scripts/rustdoc_test_builder.rs
> @@ -47,11 +47,18 @@ fn main() {
>          })
>          .expect("No test function found in `rustdoc`'s output.");
>  
> +    // Figure out a smaller test name based on the generated function name.
> +    let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;
> +
> +    // The rustdoc function name can include the absolute path when building with `O=` which is
> +    // undesireable and create overlong symbol names. Remap it to relative path.
> +    let trimmed_function_name = format!("_doctest_main_rust_kernel_{name}");
> +
>      // Qualify `Result` to avoid the collision with our own `Result` coming from the prelude.
>      let body = body.replace(
>          &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"),
>          &format!(
> -            "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
> +            "{trimmed_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
>          ),
>      );
>  
> @@ -62,12 +69,9 @@ fn main() {
>      // We save the result in a variable so that the failed assertion message looks nicer.
>      let body = body.replace(
>          &format!("}} {rustdoc_function_name}().unwrap() }}"),
> -        &format!("}} let test_return_value = {rustdoc_function_name}(); assert!(test_return_value.is_ok()); }}"),
> +        &format!("}} let test_return_value = {trimmed_function_name}(); assert!(test_return_value.is_ok()); }}"),
>      );
>  
> -    // Figure out a smaller test name based on the generated function name.
> -    let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;
> -
>      let path = format!("rust/test/doctests/kernel/{name}");
>  
>      std::fs::write(path, body.as_bytes()).unwrap();


^ permalink raw reply

* [GIT PULL] Driver core changes for 7.2-rc1
From: Danilo Krummrich @ 2026-06-13 17:52 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Saravana Kannan,
	Andrew Morton, driver-core, rust-for-linux, linux-kernel

Hi Linus,

Please pull these driver-core changes.

All commits have been in linux-next for at least a few rounds; the Rust-native
device driver lifetime series has been shared with the DRM tree via a signed
tag.

- Danilo

The following changes since commit e7ae89a0c97ce2b68b0983cd01eda67cf373517d:

  Linux 7.1-rc5 (2026-05-24 13:48:06 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git tags/driver-core-7.2-rc1

for you to fetch changes up to fe221742e388bea3f5856b5d9b2cb0a037020ea4:

  software node: allow passing reference args to PROPERTY_ENTRY_REF() (2026-06-10 00:58:04 +0200)

----------------------------------------------------------------
Driver core changes for 7.2-rc1

- deferred probe:
  - Fix race where deferred probe timeout work could be permanently
    canceled by using mod_delayed_work()
  - Fix missing jiffies conversion in deferred_probe_extend_timeout()
  - Guard timeout extension with delayed_work_pending() to prevent
    premature firing
  - Use system_percpu_wq instead of the deprecated system_wq
  - Update deferred_probe_timeout documentation

- device:
  - Replace direct struct device bitfield access (can_match, dma_iommu,
    dma_skip_sync, dma_ops_bypass, state_synced, dma_coherent,
    of_node_reused, offline, offline_disabled) with flag-based
    accessors using bit operations
  - Reject devices with unregistered buses
  - Delete unused DEVICE_ATTR_PREALLOC()
  - Add low-level device attribute macros with const show/store
    callbacks, allowing device attributes to reside in read-only memory
  - Move core device attributes to read-only memory
  - Constify group array pointers in driver_add_groups() /
    driver_remove_groups(), struct bus_type, and struct device_driver

- device property:
  - Fix fwnode reference leak in fwnode_graph_get_endpoint_by_id()
  - Initialize all fields of fwnode_handle in fwnode_init()
  - Provide swnode_get()/swnode_put() wrappers around kobject_get/put()
  - Allow passing struct software_node_ref_args pointers directly to
    PROPERTY_ENTRY_REF()

- driver_override:
  - Migrate amba, cdx, vmbus, and rpmsg to the generic driver_override
    infrastructure, fixing a UAF from unsynchronized access to
    driver_override in bus match() callbacks
  - Remove the now-unused driver_set_override()

- firmware loader:
  - Fix recursive lock deadlock in device_cache_fw_images() when async
    work falls back to synchronous execution
  - Fix device reference leak in firmware_upload_register()

- platform:
  - Pass KBUILD_MODNAME through the platform driver registration macro
    to create module symlinks in sysfs for built-in drivers; move
    module_kset initialization to a pure_initcall and tegra cbb
    registration to core_initcall to ensure correct ordering
  - Pass THIS_MODULE implicitly through a coresight_init_driver() macro

- sysfs:
  - Upgrade OOB write detection in sysfs_kf_seq_show() from printk to
    WARN
  - Add return value clamping to sysfs_kf_read()

- Rust:
  - ACPI:
    - Fix missing match data for PRP0001 by exporting
      acpi_of_match_device()

  - Auxiliary:
    - Replace drvdata() with dedicated registration data on
      auxiliary_device. drvdata() exposed the driver's bus device
      private data beyond the driver's own scope, creating ordering
      constraints and forcing the data to outlive all registrations
      that access it. Registration data is instead scoped structurally
      to the Registration object, making lifecycle ordering enforced
      by construction rather than convention.

  - Rust-native device driver lifetimes (HRT):
    - Allow Rust device drivers to carry a lifetime parameter on their
      bus device private data, tied to the device binding scope -- the
      interval during which a bus device is bound to a driver. Device
      resources like pci::Bar<'a> and IoMem<'a> can be stored directly
      in the driver's bus device private data with a lifetime bounded
      by the binding scope, so the compiler enforces at build time that
      they do not outlive the binding. This removes Devres indirection
      from every access site and eliminates try_access() failure paths
      in destructors.

      Bus driver traits use a Generic Associated Type (GAT)
      Data<'bound> to introduce the lifetime on the private data,
      rather than parameterizing the Driver trait itself. Auxiliary
      registration data, where the lifetime is not introduced by a
      trait callback but must be threaded through Registration, uses
      the ForLt trait (a type-level abstraction for types generic over
      a lifetime).

- Misc:
  - Fix DT overlayed devices not probing by reverting the broken
    treewide overlay fix and re-running fw_devlink consumer pickup when
    an overlay is applied to a bound device
  - Use root_device_register() for faux bus root device; add sanity
    check for failed bus init
  - Fix dev_has_sync_state() data race with READ_ONCE() and move it to
    base.h
  - Avoid spurious device_links warning when removing a device while
    its supplier is unbinding
  - Switch ISA bus to dynamic root device
  - Fix suspicious RCU usage in kernfs_put()
  - Remove devcoredump exit callback
  - Constify devfreq_event_class

----------------------------------------------------------------
Alexey Dobriyan (2):
      sysfs: upgrade OOB write by buggy .show hook into WARNing
      driver core: delete useless forward declaration of "struct class"

Bartosz Golaszewski (2):
      software node: provide wrappers around kobject_get/put()
      device property: initialize the remaining fields of fwnode_handle in fwnode_init()

Conor Kotwasinski (1):
      kernfs: fix suspicious RCU usage in kernfs_put()

Danilo Krummrich (37):
      driver core: use READ_ONCE() for dev->driver in dev_has_sync_state()
      driver core: move dev_has_sync_state() to drivers/base/base.h
      rust: alloc: add Box::zeroed()
      rust: auxiliary: add registration data to auxiliary devices
      rust: driver core: remove drvdata() and driver_type
      Merge patch series "rust: auxiliary: replace drvdata() with registration data"
      Merge tag 'v7.1-rc5' into driver-core-next
      driver core: Fix missing jiffies conversion in deferred_probe_extend_timeout()
      driver core: Guard deferred probe timeout extension with delayed_work_pending()
      rust: pci: use 'static lifetime for PCI BAR resource names
      rust: driver: decouple driver private data from driver type
      rust: driver core: drop drvdata before devres release
      rust: pci: implement Sync for Device<Bound>
      rust: platform: implement Sync for Device<Bound>
      rust: auxiliary: implement Sync for Device<Bound>
      rust: usb: implement Sync for Device<Bound>
      rust: device: implement Sync for Device<Bound>
      rust: device: make Core and CoreInternal lifetime-parameterized
      rust: pci: make Driver trait lifetime-parameterized
      rust: platform: make Driver trait lifetime-parameterized
      rust: auxiliary: make Driver trait lifetime-parameterized
      rust: usb: make Driver trait lifetime-parameterized
      rust: i2c: make Driver trait lifetime-parameterized
      rust: driver: update module documentation for GAT-based Data type
      rust: pci: make Bar lifetime-parameterized
      rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized
      samples: rust: rust_driver_pci: use HRT lifetime for Bar
      gpu: nova-core: separate driver type from driver data
      rust: auxiliary: generalize Registration over ForLt
      samples: rust: rust_driver_auxiliary: showcase lifetime-bound registration data
      Merge patch series "rust: device: Higher-Ranked Lifetime Types for device drivers"
      rust: devres: add 'static bound to Devres<T>
      amba: use generic driver_override infrastructure
      cdx: use generic driver_override infrastructure
      Drivers: hv: vmbus: use generic driver_override infrastructure
      rpmsg: use generic driver_override infrastructure
      driver core: remove driver_set_override()

Dmitry Torokhov (1):
      software node: allow passing reference args to PROPERTY_ENTRY_REF()

Dmitry Vyukov (1):
      firmware_loader: Fix recursive lock in device_cache_fw_images()

Douglas Anderson (8):
      driver core: Replace dev->can_match with dev_can_match()
      driver core: Replace dev->dma_iommu with dev_dma_iommu()
      driver core: Replace dev->dma_skip_sync with dev_dma_skip_sync()
      driver core: Replace dev->dma_ops_bypass with dev_dma_ops_bypass()
      driver core: Replace dev->state_synced with dev_state_synced()
      driver core: Replace dev->dma_coherent with dev_dma_coherent()
      driver core: Replace dev->of_node_reused with dev_of_node_reused()
      driver core: Replace dev->offline + ->offline_disabled with accessors

Gary Guo (3):
      rust: alloc: remove `'static` bound on `ForeignOwnable`
      rust: driver: move 'static bounds to constructor
      rust: types: add `ForLt` trait for higher-ranked lifetime support

Greg Kroah-Hartman (1):
      sysfs: clamp show() return value in sysfs_kf_read()

Guangshuo Li (1):
      firmware_loader: fix device reference leak in firmware_upload_register()

Hans de Goede (1):
      Documentation: update deferred_probe_timeout cmdline parameter documentation

Heiner Kallweit (4):
      devcoredump: Remove exit call
      driver core: constify group arrays arguments in driver_add_groups and driver_remove_groups
      driver core: make struct bus_type groups members constant arrays
      device core: make struct device_driver groups members constant arrays

Herve Codina (1):
      driver core: Avoid warning when removing a device while its supplier is unbinding

Johan Hovold (4):
      driver core: faux: fix root device registration
      driver core: faux: clean up init error handling
      driver core: reject devices with unregistered buses
      isa: switch to dynamic root device

Jori Koolstra (1):
      devfreq: change devfreq_event_class to a const struct

Markus Probst (1):
      rust: ACPI: fix missing match data for PRP0001

Nathan Chancellor (1):
      driver core: Use system_percpu_wq instead of system_wq

Prabhudasu Vatala (1):
      driver core: class: fix typo in struct class documentation

Saravana Kannan (2):
      Revert "treewide: Fix probing of devices in DT overlays"
      of: dynamic: Fix overlayed devices not probing because of fw_devlink

Shashank Balaji (4):
      soc/tegra: cbb: Move driver registration from pure_initcall to core_initcall
      kernel: param: initialize module_kset in a pure_initcall
      coresight: pass THIS_MODULE implicitly through a macro
      driver core: platform: set mod_name in driver registration

Stepan Ionichev (1):
      device property: fix fwnode reference leak in fwnode_graph_get_endpoint_by_id()

Thomas Weißschuh (5):
      driver core: Delete DEVICE_ATTR_PREALLOC()
      driver core: Add low-level macros for device attributes
      driver core: Stop using generic sysfs macros for device attributes
      driver core: Allow the constification of device attributes
      driver core: Constify core device attributes

Zhang Yuwei (1):
      driver core: Use mod_delayed_work to prevent lost deferred probe work

 Documentation/admin-guide/kernel-parameters.txt    |   5 +-
 Documentation/driver-api/driver-model/platform.rst |   3 +-
 MAINTAINERS                                        |   1 +
 arch/arc/mm/dma.c                                  |   4 +-
 arch/arm/mach-highbank/highbank.c                  |   2 +-
 arch/arm/mach-mvebu/coherency.c                    |   2 +-
 arch/arm/mm/dma-mapping-nommu.c                    |   4 +-
 arch/arm/mm/dma-mapping.c                          |  28 +++++-----
 arch/arm64/kernel/cpufeature.c                     |   2 +-
 arch/arm64/mm/dma-mapping.c                        |   2 +-
 arch/mips/include/asm/mips_mt.h                    |   1 -
 arch/mips/mm/dma-noncoherent.c                     |   2 +-
 arch/powerpc/kernel/dma-iommu.c                    |   8 +--
 arch/powerpc/platforms/pseries/hotplug-memory.c    |   4 +-
 arch/riscv/mm/dma-noncoherent.c                    |   2 +-
 drivers/acpi/bus.c                                 |   6 +--
 drivers/acpi/scan.c                                |   2 +-
 drivers/amba/bus.c                                 |  37 +++----------
 drivers/base/base.h                                |  36 ++++++-------
 drivers/base/bus.c                                 |  11 +++-
 drivers/base/core.c                                | 184 +++++++++++++++++++++++++++++++++++++++++++---------------------
 drivers/base/cpu.c                                 |   4 +-
 drivers/base/dd.c                                  |  27 +++++-----
 drivers/base/devcoredump.c                         |   7 ---
 drivers/base/driver.c                              |  79 +---------------------------
 drivers/base/faux.c                                |  32 ++++++------
 drivers/base/firmware_loader/main.c                |   3 +-
 drivers/base/firmware_loader/sysfs_upload.c        |   8 ++-
 drivers/base/isa.c                                 |  12 ++---
 drivers/base/memory.c                              |   2 +-
 drivers/base/pinctrl.c                             |   2 +-
 drivers/base/platform.c                            |  23 +++++---
 drivers/base/property.c                            |   4 +-
 drivers/base/swnode.c                              |  40 ++++++++------
 drivers/bus/imx-weim.c                             |   6 ---
 drivers/cdx/cdx.c                                  |  40 ++------------
 drivers/cpufreq/rcpufreq_dt.rs                     |   9 ++--
 drivers/devfreq/devfreq-event.c                    |  23 ++++----
 drivers/dma/ti/k3-udma-glue.c                      |   6 +--
 drivers/dma/ti/k3-udma.c                           |   6 +--
 drivers/gpu/drm/nova/driver.rs                     |   6 ++-
 drivers/gpu/drm/tyr/driver.rs                      |  13 ++---
 drivers/gpu/nova-core/driver.rs                    |  38 ++++++++------
 drivers/gpu/nova-core/gpu.rs                       |   2 +-
 drivers/gpu/nova-core/nova_core.rs                 |   2 +-
 drivers/hv/vmbus_drv.c                             |  43 ++++-----------
 drivers/hwtracing/coresight/coresight-catu.c       |   2 +-
 drivers/hwtracing/coresight/coresight-core.c       |   9 ++--
 drivers/hwtracing/coresight/coresight-cpu-debug.c  |   3 +-
 drivers/hwtracing/coresight/coresight-funnel.c     |   3 +-
 drivers/hwtracing/coresight/coresight-replicator.c |   3 +-
 drivers/hwtracing/coresight/coresight-stm.c        |   2 +-
 drivers/hwtracing/coresight/coresight-tmc-core.c   |   2 +-
 drivers/hwtracing/coresight/coresight-tnoc.c       |   2 +-
 drivers/hwtracing/coresight/coresight-tpdm.c       |   3 +-
 drivers/hwtracing/coresight/coresight-tpiu.c       |   2 +-
 drivers/i2c/i2c-core-of.c                          |   5 --
 drivers/iommu/dma-iommu.c                          |   9 ++--
 drivers/iommu/iommu.c                              |   5 +-
 drivers/net/pcs/pcs-xpcs-plat.c                    |   2 +-
 drivers/of/device.c                                |   6 +--
 drivers/of/dynamic.c                               |   1 -
 drivers/of/overlay.c                               |  15 ++++++
 drivers/of/platform.c                              |   5 --
 drivers/pci/of.c                                   |   2 +-
 drivers/pci/pwrctrl/core.c                         |   2 +-
 drivers/pwm/pwm_th1520.rs                          |  13 ++---
 drivers/rpmsg/qcom_glink_native.c                  |   2 -
 drivers/rpmsg/rpmsg_core.c                         |  43 +++------------
 drivers/rpmsg/virtio_rpmsg_bus.c                   |   1 -
 drivers/soc/tegra/cbb/tegra194-cbb.c               |   2 +-
 drivers/soc/tegra/cbb/tegra234-cbb.c               |   2 +-
 drivers/spi/spi.c                                  |   5 --
 drivers/tty/serial/serial_base_bus.c               |   2 +-
 drivers/usb/gadget/udc/aspeed-vhub/dev.c           |   2 +-
 fs/kernfs/dir.c                                    |  11 ++--
 fs/sysfs/file.c                                    |   9 ++--
 include/acpi/acpi_bus.h                            |  11 ++++
 include/linux/amba/bus.h                           |   5 --
 include/linux/auxiliary_bus.h                      |   4 ++
 include/linux/cdx/cdx_bus.h                        |   4 --
 include/linux/coresight.h                          |   7 ++-
 include/linux/device.h                             | 221 +++++++++++++++++++++++++++++++++++++++++++++++------------------------------
 include/linux/device/bus.h                         |   6 +--
 include/linux/device/class.h                       |   2 +-
 include/linux/device/driver.h                      |  10 ++--
 include/linux/dma-map-ops.h                        |   6 +--
 include/linux/dma-mapping.h                        |   2 +-
 include/linux/fwnode.h                             |   3 ++
 include/linux/hyperv.h                             |   5 --
 include/linux/iommu-dma.h                          |   3 +-
 include/linux/platform_device.h                    |  17 +++---
 include/linux/property.h                           |   8 ++-
 include/linux/rpmsg.h                              |   4 --
 kernel/cpu.c                                       |   4 +-
 kernel/dma/mapping.c                               |  12 ++---
 kernel/params.c                                    |   8 +--
 mm/hmm.c                                           |   2 +-
 rust/Makefile                                      |   1 +
 rust/helpers/acpi.c                                |  16 ++++++
 rust/helpers/helpers.c                             |   1 +
 rust/kernel/alloc/kbox.rs                          |  46 +++++++++++++---
 rust/kernel/auxiliary.rs                           | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
 rust/kernel/cpufreq.rs                             |   9 ++--
 rust/kernel/device.rs                              | 121 ++++++++++++++++---------------------------
 rust/kernel/devres.rs                              |   8 +--
 rust/kernel/dma.rs                                 |   2 +-
 rust/kernel/driver.rs                              | 115 ++++++++++++++++++++++++++++++----------
 rust/kernel/i2c.rs                                 |  61 ++++++++++++----------
 rust/kernel/io/mem.rs                              | 121 ++++++++++++++++++++++---------------------
 rust/kernel/pci.rs                                 |  51 +++++++++++-------
 rust/kernel/pci/id.rs                              |   2 +-
 rust/kernel/pci/io.rs                              |  54 +++++++++++--------
 rust/kernel/platform.rs                            |  56 ++++++++++++--------
 rust/kernel/types.rs                               |  12 ++++-
 rust/kernel/types/for_lt.rs                        | 122 +++++++++++++++++++++++++++++++++++++++++++
 rust/kernel/usb.rs                                 |  57 ++++++++++++--------
 rust/macros/for_lt.rs                              | 248 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 rust/macros/lib.rs                                 |  13 +++++
 samples/rust/rust_debugfs.rs                       |  11 ++--
 samples/rust/rust_dma.rs                           |   6 ++-
 samples/rust/rust_driver_auxiliary.rs              |  79 ++++++++++++++++++++--------
 samples/rust/rust_driver_i2c.rs                    |  13 ++---
 samples/rust/rust_driver_pci.rs                    |  90 +++++++++++++++-----------------
 samples/rust/rust_driver_platform.rs               |   9 ++--
 samples/rust/rust_driver_usb.rs                    |  15 +++---
 samples/rust/rust_i2c_client.rs                    |  14 +++--
 samples/rust/rust_soc.rs                           |   9 ++--
 128 files changed, 1815 insertions(+), 1134 deletions(-)
 create mode 100644 rust/helpers/acpi.c
 create mode 100644 rust/kernel/types/for_lt.rs
 create mode 100644 rust/macros/for_lt.rs

^ permalink raw reply

* [PATCH 2/2] rust: doctest: trim function name for reproducbility
From: Gary Guo @ 2026-06-13 14:04 UTC (permalink / raw)
  To: Brendan Higgins, David Gow, Rae Moar, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Igor Korotin
  Cc: rust-for-linux, linux-kselftest, kunit-dev, linux-kernel
In-Reply-To: <20260613140431.232471-1-gary@kernel.org>

From: Gary Guo <gary@garyguo.net>

Currently rustdoc will generate function names like
"_doctest_main__home_gary_Projects_linux_rust_kernel_io_rs_824_0" for a
doctest located at rust/kernel/io.rs:824, when building with separate
outdir using `O=`. This creates overlong symbol names and is also not
reproducible.

Fix it by doing a custom remapping to trim it to something like
`_doctest_main_rust_kernel_io_rs_824_0`.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 scripts/rustdoc_test_builder.rs | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs
index 2b1f9ba01839..fda7284355f8 100644
--- a/scripts/rustdoc_test_builder.rs
+++ b/scripts/rustdoc_test_builder.rs
@@ -47,11 +47,18 @@ fn main() {
         })
         .expect("No test function found in `rustdoc`'s output.");
 
+    // Figure out a smaller test name based on the generated function name.
+    let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;
+
+    // The rustdoc function name can include the absolute path when building with `O=` which is
+    // undesireable and create overlong symbol names. Remap it to relative path.
+    let trimmed_function_name = format!("_doctest_main_rust_kernel_{name}");
+
     // Qualify `Result` to avoid the collision with our own `Result` coming from the prelude.
     let body = body.replace(
         &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"),
         &format!(
-            "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
+            "{trimmed_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
         ),
     );
 
@@ -62,12 +69,9 @@ fn main() {
     // We save the result in a variable so that the failed assertion message looks nicer.
     let body = body.replace(
         &format!("}} {rustdoc_function_name}().unwrap() }}"),
-        &format!("}} let test_return_value = {rustdoc_function_name}(); assert!(test_return_value.is_ok()); }}"),
+        &format!("}} let test_return_value = {trimmed_function_name}(); assert!(test_return_value.is_ok()); }}"),
     );
 
-    // Figure out a smaller test name based on the generated function name.
-    let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;
-
     let path = format!("rust/test/doctests/kernel/{name}");
 
     std::fs::write(path, body.as_bytes()).unwrap();
-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/2] rust: doctest: fix incorrect pattern in replacement
From: Gary Guo @ 2026-06-13 14:04 UTC (permalink / raw)
  To: Brendan Higgins, David Gow, Rae Moar, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Igor Korotin
  Cc: rust-for-linux, linux-kselftest, kunit-dev, linux-kernel

From: Gary Guo <gary@garyguo.net>

The `-> Result<(), impl core::fmt::Debug>` string is generated by rustdoc
and by adding "::" into the string it no longer finds anything, and making
the line useless. Remove the "::" in the pattern (but keep it in the
replacement result).

Fixes: de7cd3e4d638 ("rust: use absolute paths in macros referencing core and kernel")
Signed-off-by: Gary Guo <gary@garyguo.net>
---
 scripts/rustdoc_test_builder.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs
index f7540bcf595a..2b1f9ba01839 100644
--- a/scripts/rustdoc_test_builder.rs
+++ b/scripts/rustdoc_test_builder.rs
@@ -49,7 +49,7 @@ fn main() {
 
     // Qualify `Result` to avoid the collision with our own `Result` coming from the prelude.
     let body = body.replace(
-        &format!("{rustdoc_function_name}() -> Result<(), impl ::core::fmt::Debug> {{"),
+        &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"),
         &format!(
             "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
         ),

base-commit: abe651837cb394f76d738a7a747322fca3bf17ba
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v3] rust: add procedural macro for declaring configfs attributes
From: Miguel Ojeda @ 2026-06-13 10:41 UTC (permalink / raw)
  To: Malte Wechter
  Cc: Andreas Hindborg, Breno Leitao, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Jens Axboe, Luis Chamberlain,
	Petr Pavlu, Daniel Gomez, Sami Tolvanen, Aaron Tomlin,
	linux-kernel, rust-for-linux, linux-block, linux-modules
In-Reply-To: <20260612-configfs-syn-v3-1-3292fbc5cc32@gmail.com>

Hi Malte,

Some quick notes...

On Fri, Jun 12, 2026 at 3:29 PM Malte Wechter <maltewechter@gmail.com> wrote:
>
> +/// ```ignore

Empty /// before examples.

> +///     // This will extract "foo: <field>" into a variable named "foo".

` instead of "

i.e. please use Markdown

> +///```

Missing space indentation

> +/// Expands the following output:
> +///    let item_type = {

Missing example block, both at the beginning and the end.

Please double-check by generating the docs and looking at how they
appear in the browser.

The prefix of the title should likely be `rust: configfs:`.

Thanks!

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH v4 01/20] rust: io: add dynamically-sized `Region` type
From: Miguel Ojeda @ 2026-06-13 10:05 UTC (permalink / raw)
  To: Gary Guo
  Cc: Alice Ryhl, Daniel Almeida, Greg Kroah-Hartman, Rafael J. Wysocki,
	Miguel Ojeda, Boqun Feng, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Bjorn Helgaas,
	Krzysztof Wilczyński, Abdiel Janulgue, Robin Murphy,
	Alexandre Courbot, David Airlie, Simona Vetter, Danilo Krummrich,
	driver-core, rust-for-linux, linux-kernel, linux-pci, nova-gpu,
	dri-devel
In-Reply-To: <20260611-io_projection-v4-1-1f7224b02dcb@garyguo.net>

On Thu, Jun 11, 2026 at 6:28 PM Gary Guo <gary@garyguo.net> wrote:
>
> +    /// Create a raw mutable pointer from given base address and size.
> +    ///
> +    /// The alignment of `base` is checked, and `size` is checked against the minimum size specified
> +    /// via const generics.
> +    #[inline]
> +    pub fn ptr_try_from_raw_parts_mut(base: *mut u8, size: usize) -> Result<*mut Self> {
> +        if size < SIZE || base.align_offset(4) != 0 || !size.is_multiple_of(4) {
> +            return Err(EINVAL);
> +        }
> +
> +        Ok(Self::ptr_from_raw_parts_mut(base, size))
> +    }

Hmm... I wonder if, at least for the `try_` one, we should have `//
INVARIANT:` on top of the `Ok`, since we at least checked that part.

> +// Stable since Rust 1.87.0.
> +#![feature(unsigned_is_multiple_of)]

It indeed appears available without changes since Rust 1.82.0 <= MSRV.

Acked-by: Miguel Ojeda <ojeda@kernel.org>

I think Danilo asked for the Acked-by back in the first version but
you dropped it since the patch changed, so here it is again :)

Thanks!

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH v4] rust: make `build_assert` module the home of related macros
From: Miguel Ojeda @ 2026-06-13  9:49 UTC (permalink / raw)
  To: Gary Guo
  Cc: Miguel Ojeda, a.hindborg, acourbot, airlied, aliceryhl, bjorn3_gh,
	boqun, dakr, daniel.almeida, dri-devel, driver-core,
	fujita.tomonori, linux-kernel, longman, lossin, mark.rutland,
	mingo, netdev, nova-gpu, peterz, rust-for-linux, simona, tamird,
	tmgross, will, yury.norov
In-Reply-To: <DJ65HZ2867HB.2ZK56NEAURVJO@garyguo.net>

On Thu, Jun 11, 2026 at 1:50 PM Gary Guo <gary@garyguo.net> wrote:
>
> > Good first issue?
>
> Agreed.

For future reference:

  https://github.com/Rust-for-Linux/linux/issues/1242

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH v20 3/4] rust: faux: Allow retrieving a bound Device
From: Miguel Ojeda @ 2026-06-13  8:34 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Gary Guo, Lyude Paul, dri-devel, rust-for-linux, nouveau,
	Alexandre Courbot, Christian König, driver-core,
	Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
	linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
	Thomas Zimmermann, Maxime Ripard, David Airlie, Benno Lossin,
	linaro-mm-sig, Mukesh Kumar Chaurasiya, Asahi Lina,
	Daniel Almeida, Greg Kroah-Hartman
In-Reply-To: <DJ7CJCZRHT1R.3LZDILZ7HTXQ3@kernel.org>

On Fri, Jun 12, 2026 at 10:17 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> As far as I'm concerned there's no need to do that for things that are not
> rendered anyway. But also feel free to do it anyway of course.

It is not a huge deal if there are gaps here or there, but we use
Markdown in comments too for consistency.

What we don't do is intra-doc links there -- it would have been nice
if `rustdoc` could use those in comments for the source view, but it
doesn't, and while I talked with upstream about it, it isn't likely it
will happen soon if at all...

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH] rust: bitfield: mark `Debug` impl as `#[inline]`
From: Miguel Ojeda @ 2026-06-13  8:25 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: Gary Guo, Gary Guo, Yury Norov, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, rust-for-linux, linux-kernel
In-Reply-To: <DJ7OKONXCQPN.1N5F9PJSNNGIR@nvidia.com>

On Sat, Jun 13, 2026 at 7:43 AM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> Btw, should this be
>
> Fixes: b7b8b4ccdad4 ("rust: extract `bitfield!` macro from `register!`")
>
> ?

I think it is reasonable either way. One can see it as a "size
improvement" or as a "fix" if it was unintentional to not have the
annotation.

It also depends on the magnitude of the size reduction -- if it were
very low, would we see it as a fix?

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH] rust: bitfield: mark `Debug` impl as `#[inline]`
From: Miguel Ojeda @ 2026-06-13  8:24 UTC (permalink / raw)
  To: Gary Guo
  Cc: Alexandre Courbot, Yury Norov, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, rust-for-linux, linux-kernel
In-Reply-To: <20260611190555.2298991-1-gary@kernel.org>

On Thu, Jun 11, 2026 at 9:06 PM Gary Guo <gary@kernel.org> wrote:
>
> This reduces nova-core.o .text by 17% (from 151922 bytes to 125676 bytes).

That's definitely something :)

I wonder if we could lint this for `fmt` bits.

Cheers,
Miguel

^ permalink raw reply

* [PATCH v10 5/5] MAINTAINERS: add Rust SRCU files to SRCU entry
From: Onur Özkan @ 2026-06-13  6:40 UTC (permalink / raw)
  To: rcu, rust-for-linux, linux-kernel
  Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
	tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
	paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
In-Reply-To: <20260613065348.96750-1-work@onurozkan.dev>

Include Rust side implementation files to the SRCU maintainer
entry.

Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 MAINTAINERS | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e0b307b2108c..7739a435f258 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24624,6 +24624,7 @@ SLEEPABLE READ-COPY UPDATE (SRCU)
 M:	Lai Jiangshan <jiangshanlai@gmail.com>
 M:	"Paul E. McKenney" <paulmck@kernel.org>
 M:	Josh Triplett <josh@joshtriplett.org>
+M:	Onur Özkan <work@onurozkan.dev> (RUST)
 R:	Steven Rostedt <rostedt@goodmis.org>
 R:	Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
 L:	rcu@vger.kernel.org
@@ -24632,6 +24633,8 @@ W:	http://www.rdrop.com/users/paulmck/RCU/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
 F:	include/linux/srcu*.h
 F:	kernel/rcu/srcu*.c
+F:	rust/helpers/srcu.c
+F:	rust/kernel/sync/srcu.rs
 
 SMACK SECURITY MODULE
 M:	Casey Schaufler <casey@schaufler-ca.com>
-- 
2.51.2


^ permalink raw reply related

* [PATCH v10 4/5] rust: sync: add SRCU abstraction
From: Onur Özkan @ 2026-06-13  6:40 UTC (permalink / raw)
  To: rcu, rust-for-linux, linux-kernel
  Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
	tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
	paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
In-Reply-To: <20260613065348.96750-1-work@onurozkan.dev>

Add a Rust abstraction for sleepable RCU (SRCU), backed by C srcu_struct.
Provide FFI helpers and a safe wrapper with a guard-based API for read-side
critical sections.

Cleanup is handled via `PinnedDrop`. It first checks for active read-side
sections and emits a warning if any guards were leaked. In that case, it
waits in `synchronize_srcu()` rather than risking a UAF by freeing the
`srcu_struct` that is still reachable from the C side. It then uses
`srcu_barrier()` to drain pending callbacks before finally calling
`cleanup_srcu_struct()`.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/kernel/sync.rs      |   2 +
 rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 173 insertions(+)
 create mode 100644 rust/kernel/sync/srcu.rs

diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 993dbf2caa0e..0d6a5f1300c3 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -21,6 +21,7 @@
 pub mod rcu;
 mod refcount;
 mod set_once;
+pub mod srcu;
 
 pub use arc::{Arc, ArcBorrow, UniqueArc};
 pub use completion::Completion;
@@ -31,6 +32,7 @@
 pub use locked_by::LockedBy;
 pub use refcount::Refcount;
 pub use set_once::SetOnce;
+pub use srcu::Srcu;
 
 /// Represents a lockdep class.
 ///
diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
new file mode 100644
index 000000000000..723e5e277fd6
--- /dev/null
+++ b/rust/kernel/sync/srcu.rs
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Sleepable read-copy update (SRCU) support.
+//!
+//! C header: [`include/linux/srcu.h`](srctree/include/linux/srcu.h)
+
+use crate::{
+    bindings,
+    error::to_result,
+    prelude::*,
+    sync::LockClassKey,
+    types::{
+        NotThreadSafe,
+        Opaque, //
+    },
+};
+
+use pin_init::pin_data;
+
+/// Creates an [`Srcu`] initialiser with the given name and a newly-created lock class.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! new_srcu {
+    ($($name:literal)?) => {
+        $crate::sync::Srcu::new($crate::optional_name!($($name)?), $crate::static_lock_class!())
+    };
+}
+pub use new_srcu;
+
+/// Sleepable read-copy update primitive.
+///
+/// SRCU readers may sleep while holding the read-side guard.
+///
+/// The destructor waits for active readers and callbacks, so it may sleep.
+/// If a read-side guard has been leaked, dropping an [`Srcu`] may never return.
+///
+/// # Invariants
+///
+/// This represents a valid `struct srcu_struct` initialized by the C SRCU API
+/// and it remains pinned and valid until the pinned destructor runs.
+#[repr(transparent)]
+#[pin_data(PinnedDrop)]
+pub struct Srcu {
+    #[pin]
+    inner: Opaque<bindings::srcu_struct>,
+}
+
+impl Srcu {
+    /// Creates a new SRCU instance.
+    #[inline]
+    pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self, Error> {
+        try_pin_init!(Self {
+            // INVARIANT: On success, the C initializer creates a valid `srcu_struct` and
+            // it remains pinned until `PinnedDrop` runs.
+            inner <- Opaque::try_ffi_init(|ptr: *mut bindings::srcu_struct| {
+                // SAFETY: `ptr` points to valid uninitialised memory for a `srcu_struct`.
+                to_result(unsafe {
+                    bindings::init_srcu_struct_with_key(ptr, name.as_char_ptr(), key.as_ptr())
+                })
+            }),
+        })
+    }
+
+    /// Enters an SRCU read-side critical section.
+    ///
+    /// Leaking the returned [`Guard`] leaves the SRCU read-side critical
+    /// section active and makes `drop` sleep forever.
+    #[inline]
+    pub fn read_lock(&self) -> Guard<'_> {
+        // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.
+        let idx = unsafe { bindings::srcu_read_lock(self.inner.get()) };
+
+        // INVARIANT: `idx` was returned by `srcu_read_lock()` for this `Srcu`.
+        Guard {
+            srcu: self,
+            idx,
+            _not_send: NotThreadSafe,
+        }
+    }
+
+    /// Waits until all pre-existing SRCU readers have completed.
+    #[inline]
+    pub fn synchronize(&self) {
+        // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.
+        unsafe { bindings::synchronize_srcu(self.inner.get()) };
+    }
+
+    /// Waits until all pre-existing SRCU readers have completed, expedited.
+    ///
+    /// This requests a lower-latency grace period than [`Srcu::synchronize`] typically
+    /// at the cost of higher system-wide overhead. Prefer [`Srcu::synchronize`] by default
+    /// and use this variant only when reducing reset or teardown latency is more important
+    /// than the extra cost.
+    #[inline]
+    pub fn synchronize_expedited(&self) {
+        // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.
+        unsafe { bindings::synchronize_srcu_expedited(self.inner.get()) };
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for Srcu {
+    fn drop(self: Pin<&mut Self>) {
+        let ptr = self.inner.get();
+
+        if crate::warn_on!(
+            // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
+            // and `srcu_readers_active()` only checks the active reader count.
+            unsafe { bindings::srcu_readers_active(ptr) }
+        ) {
+            // `cleanup_srcu_struct()` may return early if there are still active readers.
+            // This should only happen if a guard was leaked with `mem::forget`, which is
+            // "WRONG" code and may cause a UAF because Rust will free the `srcu_struct`
+            // while it is still referenced from the C side (e.g. by `call_srcu()` callbacks).
+            //
+            // Another consequence of leaking guards is that `call_srcu()` callbacks will
+            // never run because the grace period can never complete due to permanently
+            // active readers (i.e. leaked guards).
+            //
+            // If this ever happens, that means the guard was leaked by mistake and the
+            // caller must fix the bug. Sleeping here is intentional and less harmful
+            // than risking a UAF.
+            //
+            // SAFETY: By the type invariants, `self` contains a valid and pinned
+            // `struct srcu_struct`.
+            unsafe { bindings::synchronize_srcu(ptr) };
+        }
+
+        // Ensure all SRCU callbacks have been finished before freeing.
+        // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`.
+        unsafe { bindings::srcu_barrier(ptr) };
+
+        // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`.
+        unsafe { bindings::cleanup_srcu_struct(ptr) };
+    }
+}
+
+// SAFETY: `srcu_struct` may be shared and used across threads.
+unsafe impl Send for Srcu {}
+// SAFETY: `srcu_struct` may be shared and used concurrently.
+unsafe impl Sync for Srcu {}
+
+/// Guard for an active SRCU read-side critical section on a particular [`Srcu`].
+///
+/// Leaking this guard with [`core::mem::forget`] leaves the SRCU read-side
+/// critical section active and makes dropping the associated [`Srcu`] sleep forever.
+///
+/// # Invariants
+///
+/// `idx` is the index returned by `srcu_read_lock()` for `srcu`.
+#[must_use = "if unused, the lock will be immediately unlocked"]
+pub struct Guard<'a> {
+    srcu: &'a Srcu,
+    idx: i32,
+    _not_send: NotThreadSafe,
+}
+
+impl Guard<'_> {
+    /// Explicitly releases the SRCU read-side critical section.
+    #[inline]
+    pub fn unlock(self) {}
+}
+
+impl Drop for Guard<'_> {
+    #[inline]
+    fn drop(&mut self) {
+        // SAFETY: `Guard` is only constructible through `Srcu::read_lock()`,
+        // which returns a valid index for the SRCU instance.
+        unsafe { bindings::srcu_read_unlock(self.srcu.inner.get(), self.idx) };
+    }
+}
-- 
2.51.2


^ permalink raw reply related

* [PATCH v10 3/5] srcu: expose srcu_readers_active()
From: Onur Özkan @ 2026-06-13  6:40 UTC (permalink / raw)
  To: rcu, rust-for-linux, linux-kernel
  Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
	tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
	paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
In-Reply-To: <20260613065348.96750-1-work@onurozkan.dev>

This is needed by rust/helpers/srcu.c which now adds
rust_helper_srcu_readers_active() as a wrapper around the SRCU helper
for Rust callers.

To achive this:

1- Move the srcu_readers_active() implementation from
   "kernel/rcu/srcutree.c" to "include/linux/srcutree.h".

2- Implement a matching srcu_readers_active() in
   "include/linux/srcutiny.h" and use it on the existing open-coded
   WARN_ON() check in cleanup_srcu_struct().

Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 include/linux/srcutiny.h | 13 +++++++++++++
 include/linux/srcutree.h | 24 ++++++++++++++++++++++++
 kernel/rcu/srcutiny.c    |  2 +-
 kernel/rcu/srcutree.c    | 25 -------------------------
 rust/helpers/srcu.c      |  5 +++++
 5 files changed, 43 insertions(+), 26 deletions(-)

diff --git a/include/linux/srcutiny.h b/include/linux/srcutiny.h
index 905b629e8fa3..fbcf13bc12d1 100644
--- a/include/linux/srcutiny.h
+++ b/include/linux/srcutiny.h
@@ -154,4 +154,17 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp,
 		 data_race(READ_ONCE(ssp->srcu_idx_max)));
 }
 
+/**
+ * srcu_readers_active - returns true if there are readers. and false otherwise.
+ * @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
+ *
+ * Note that this is not an atomic primitive, and can therefore suffer
+ * severe errors when invoked on an active srcu_struct. That said, it
+ * can be useful as an error check at cleanup time.
+ */
+static inline bool srcu_readers_active(struct srcu_struct *ssp)
+{
+	return READ_ONCE(ssp->srcu_lock_nesting[0]) || READ_ONCE(ssp->srcu_lock_nesting[1]);
+}
+
 #endif
diff --git a/include/linux/srcutree.h b/include/linux/srcutree.h
index fd1a9270cb9a..75e54e4f963f 100644
--- a/include/linux/srcutree.h
+++ b/include/linux/srcutree.h
@@ -374,4 +374,28 @@ static inline void srcu_check_read_flavor(struct srcu_struct *ssp, int read_flav
 		__srcu_check_read_flavor(ssp, read_flavor);
 }
 
+/**
+ * srcu_readers_active - returns true if there are readers. and false otherwise.
+ * @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
+ *
+ * Note that this is not an atomic primitive, and can therefore suffer
+ * severe errors when invoked on an active srcu_struct. That said, it
+ * can be useful as an error check at cleanup time.
+ */
+static inline bool srcu_readers_active(struct srcu_struct *ssp)
+{
+	int cpu;
+	unsigned long sum = 0;
+
+	for_each_possible_cpu(cpu) {
+		struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
+
+		sum += atomic_long_read(&sdp->srcu_ctrs[0].srcu_locks);
+		sum += atomic_long_read(&sdp->srcu_ctrs[1].srcu_locks);
+		sum -= atomic_long_read(&sdp->srcu_ctrs[0].srcu_unlocks);
+		sum -= atomic_long_read(&sdp->srcu_ctrs[1].srcu_unlocks);
+	}
+	return sum;
+}
+
 #endif
diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
index 47d48ed31848..558ba8d316db 100644
--- a/kernel/rcu/srcutiny.c
+++ b/kernel/rcu/srcutiny.c
@@ -85,7 +85,7 @@ EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
  */
 void cleanup_srcu_struct(struct srcu_struct *ssp)
 {
-	WARN_ON(ssp->srcu_lock_nesting[0] || ssp->srcu_lock_nesting[1]);
+	WARN_ON(srcu_readers_active(ssp));
 	irq_work_sync(&ssp->srcu_irq_work);
 	flush_work(&ssp->srcu_work);
 	WARN_ON(ssp->srcu_gp_running);
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
index e4496488a7d8..7a3f5c8c882a 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -599,31 +599,6 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)
 	return srcu_readers_lock_idx(ssp, idx, did_gp, unlocks);
 }
 
-/**
- * srcu_readers_active - returns true if there are readers. and false
- *                       otherwise
- * @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
- *
- * Note that this is not an atomic primitive, and can therefore suffer
- * severe errors when invoked on an active srcu_struct.  That said, it
- * can be useful as an error check at cleanup time.
- */
-static bool srcu_readers_active(struct srcu_struct *ssp)
-{
-	int cpu;
-	unsigned long sum = 0;
-
-	for_each_possible_cpu(cpu) {
-		struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
-
-		sum += atomic_long_read(&sdp->srcu_ctrs[0].srcu_locks);
-		sum += atomic_long_read(&sdp->srcu_ctrs[1].srcu_locks);
-		sum -= atomic_long_read(&sdp->srcu_ctrs[0].srcu_unlocks);
-		sum -= atomic_long_read(&sdp->srcu_ctrs[1].srcu_unlocks);
-	}
-	return sum;
-}
-
 /*
  * We use an adaptive strategy for synchronize_srcu() and especially for
  * synchronize_srcu_expedited().  We spin for a fixed time period
diff --git a/rust/helpers/srcu.c b/rust/helpers/srcu.c
index 225b3bf9334a..1a2f563640e0 100644
--- a/rust/helpers/srcu.c
+++ b/rust/helpers/srcu.c
@@ -9,6 +9,11 @@ __rust_helper int rust_helper_init_srcu_struct_with_key(struct srcu_struct *ssp,
 	return __init_srcu_struct(ssp, name, key);
 }
 
+__rust_helper bool rust_helper_srcu_readers_active(struct srcu_struct *ssp)
+{
+	return srcu_readers_active(ssp);
+}
+
 __rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp)
 {
 	return srcu_read_lock(ssp);
-- 
2.51.2


^ permalink raw reply related

* [PATCH v10 2/5] rust: helpers: add SRCU helpers
From: Onur Özkan @ 2026-06-13  6:40 UTC (permalink / raw)
  To: rcu, rust-for-linux, linux-kernel
  Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
	tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
	paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
In-Reply-To: <20260613065348.96750-1-work@onurozkan.dev>

Add helper wrappers for SRCU functions that are exposed to Rust
through generated bindings.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/helpers/helpers.c |  1 +
 rust/helpers/srcu.c    | 30 ++++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)
 create mode 100644 rust/helpers/srcu.c

diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 625921e27dfb..f3562d3b3888 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -88,6 +88,7 @@
 #include "signal.c"
 #include "slab.c"
 #include "spinlock.c"
+#include "srcu.c"
 #include "sync.c"
 #include "task.c"
 #include "time.c"
diff --git a/rust/helpers/srcu.c b/rust/helpers/srcu.c
new file mode 100644
index 000000000000..225b3bf9334a
--- /dev/null
+++ b/rust/helpers/srcu.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/srcu.h>
+
+__rust_helper int rust_helper_init_srcu_struct_with_key(struct srcu_struct *ssp,
+							const char *name,
+							struct lock_class_key *key)
+{
+	return __init_srcu_struct(ssp, name, key);
+}
+
+__rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp)
+{
+	return srcu_read_lock(ssp);
+}
+
+__rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx)
+{
+	srcu_read_unlock(ssp, idx);
+}
+
+__rust_helper void rust_helper_srcu_barrier(struct srcu_struct *ssp)
+{
+	srcu_barrier(ssp);
+}
+
+__rust_helper void rust_helper_synchronize_srcu_expedited(struct srcu_struct *ssp)
+{
+	synchronize_srcu_expedited(ssp);
+}
-- 
2.51.2


^ permalink raw reply related

* [PATCH v10 1/5] srcu: make init_srcu_struct() consistently wrap __init_srcu_struct()
From: Onur Özkan @ 2026-06-13  6:40 UTC (permalink / raw)
  To: rcu, rust-for-linux, linux-kernel
  Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
	tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
	paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
In-Reply-To: <20260613065348.96750-1-work@onurozkan.dev>

Restructure the SRCU initialization functions so it always follows
one direction:

    init_srcu_struct() -> __init_srcu_struct() -> lockdep or generic

This uses the same wrapper style as mutex. It avoids the old confusing
style where init_srcu_struct() and __init_srcu_struct() called each
other in different configs. It also helps Rust side to have simpler
helper for SRCU initialization.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 include/linux/srcu.h  | 29 ++++++++++++++++++++---------
 kernel/rcu/srcutiny.c | 12 ++++++------
 kernel/rcu/srcutree.c | 11 ++++++-----
 3 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/include/linux/srcu.h b/include/linux/srcu.h
index 81b1938512d5..a028a5b5ebef 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 
-int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
+int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
+				     struct lock_class_key *key);
+static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
+				     struct lock_class_key *key)
+{
+	return init_srcu_struct_lockdep(ssp, name, key);
+}
 #ifndef CONFIG_TINY_SRCU
 int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
 int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
 				   struct lock_class_key *key);
 #endif // #ifndef CONFIG_TINY_SRCU
 
-#define init_srcu_struct(ssp) \
-({ \
-	static struct lock_class_key __srcu_key; \
-	\
-	__init_srcu_struct((ssp), #ssp, &__srcu_key); \
-})
-
 #define init_srcu_struct_fast(ssp) \
 ({ \
 	static struct lock_class_key __srcu_key; \
@@ -56,7 +55,12 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
 #define __SRCU_DEP_MAP_INIT(srcu_name)	.dep_map = { .name = #srcu_name },
 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
-int init_srcu_struct(struct srcu_struct *ssp);
+int init_srcu_struct_generic(struct srcu_struct *ssp);
+static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
+				     struct lock_class_key *key)
+{
+	return init_srcu_struct_generic(ssp);
+}
 #ifndef CONFIG_TINY_SRCU
 int init_srcu_struct_fast(struct srcu_struct *ssp);
 int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
@@ -65,6 +69,13 @@ int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
 #define __SRCU_DEP_MAP_INIT(srcu_name)
 #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
+#define init_srcu_struct(ssp) \
+({ \
+	static struct lock_class_key __srcu_key; \
+	\
+	__init_srcu_struct((ssp), #ssp, &__srcu_key); \
+})
+
 /* Values for SRCU Tree srcu_data ->srcu_reader_flavor, but also used by rcutorture. */
 #define SRCU_READ_FLAVOR_NORMAL		0x1		// srcu_read_lock().
 #define SRCU_READ_FLAVOR_NMI		0x2		// srcu_read_lock_nmisafe().
diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
index a2e2d516e51b..47d48ed31848 100644
--- a/kernel/rcu/srcutiny.c
+++ b/kernel/rcu/srcutiny.c
@@ -48,31 +48,31 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp)
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 
-int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
-		       struct lock_class_key *key)
+int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
+		     struct lock_class_key *key)
 {
 	/* Don't re-initialize a lock while it is held. */
 	debug_check_no_locks_freed((void *)ssp, sizeof(*ssp));
 	lockdep_init_map(&ssp->dep_map, name, key, 0);
 	return init_srcu_struct_fields(ssp);
 }
-EXPORT_SYMBOL_GPL(__init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
 
 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
 /*
- * init_srcu_struct - initialize a sleep-RCU structure
+ * init_srcu_struct_generic - initialize a sleep-RCU structure
  * @ssp: structure to initialize.
  *
  * Must invoke this on a given srcu_struct before passing that srcu_struct
  * to any other function.  Each srcu_struct represents a separate domain
  * of SRCU protection.
  */
-int init_srcu_struct(struct srcu_struct *ssp)
+int init_srcu_struct_generic(struct srcu_struct *ssp)
 {
 	return init_srcu_struct_fields(ssp);
 }
-EXPORT_SYMBOL_GPL(init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
 
 #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
index 0d01cd8c4b4a..e4496488a7d8 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lock
 	return init_srcu_struct_fields(ssp, false);
 }
 
-int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
+int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
+			     struct lock_class_key *key)
 {
 	ssp->srcu_reader_flavor = 0;
 	return __init_srcu_struct_common(ssp, name, key);
 }
-EXPORT_SYMBOL_GPL(__init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
 
 int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
 {
@@ -291,7 +292,7 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
 /**
- * init_srcu_struct - initialize a sleep-RCU structure
+ * init_srcu_struct_generic - initialize a sleep-RCU structure
  * @ssp: structure to initialize.
  *
  * Use this in place of DEFINE_SRCU() and DEFINE_STATIC_SRCU()
@@ -301,12 +302,12 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
  * to any other function.  Each srcu_struct represents a separate domain
  * of SRCU protection.
  */
-int init_srcu_struct(struct srcu_struct *ssp)
+int init_srcu_struct_generic(struct srcu_struct *ssp)
 {
 	ssp->srcu_reader_flavor = 0;
 	return init_srcu_struct_fields(ssp, false);
 }
-EXPORT_SYMBOL_GPL(init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
 
 /**
  * init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure
-- 
2.51.2


^ permalink raw reply related

* [PATCH v10 0/5] rust: add SRCU abstraction
From: Onur Özkan @ 2026-06-13  6:40 UTC (permalink / raw)
  To: rcu, rust-for-linux, linux-kernel
  Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
	tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
	paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan

The immediate motivation is the Tyr reset infrastructure [1] which needs
to serialize reset sensitive hardware access against reset and teardown
paths. That reset series started to require many independent dependencies
so this SRCU support is split out as a standalone Rust API to keep the
reset series focused on the reset logic and easier to review, rebase and
land.


Changes since v9:

- SRCU initialization restructuring is now a separate patch (the first
  one in this series).
- Comment fix on C initialization functions.


Changes since v8:

- Refactor srcu init handling to provide better init API (motivated by
  the "mutex.h" initializers.
- Use warn_on! instead of pr_warn! on leaked guard detection.


Changes since v7:

- Moved synchronize_srcu() call inside "if srcu_readers_active()"
  condition in "impl PinnedDrop for Srcu".
- Improved comments on the synchronize_srcu() call in
  "impl PinnedDrop for Srcu".


Changes since v6:

- Removed "CONFIG_DEBUG_LOCK_ALLOC" condition from "rust/helpers/srcu.c"
  and created a simple wrapper inside "include/linux/srcu.h" for it.


Changes since v5:

- Created separate srcu_readers_active() variants for "srcutiny.h" and
  "srcutree.h".


Changes since v4:

- Exposed srcu_readers_active from C side and wired it to the Rust
  helpers.
- Used srcu_readers_active() in SRCU drop and logged with pr_warn if
  there are leaked guards during the drop.


Changes since v3 (which are for Sashiko notes [2]):

- Added rust helpers for srcu_barrier() and synchronize_srcu_expedited()
  so the abstraction builds with CONFIG_TINY_SRCU, where these are
  static inline functions.
- Added missing INVARIANT comment in Srcu::new() about why the type
  invariants hold after successful initialization.


Changes since v2:

- Removed closure-based API.
- Added #[doc(hidden)] on new_srcu macro.
- Added #[must_use..] on srcu::Guard.
- Improved the clean-up path (PinnedDrop implementation) which
  eventually made read_lock safe with leaked guards.


Changes since v1:

- Made the owned SRCU read-side guard API unsafe and added a safe closure
  based helper for callers that do not need to keep the guard. This is to
  avoid UB on the C side cleanup_srcu_struct where the SRCU struct is freed
  while there are still active guards, which can happen if the caller leaks
  the guard e.g., with mem::forget().
- Improved doc comments.


v1: https://lore.kernel.org/all/20260428103437.156236-1-work@onurozkan.dev
v2: https://lore.kernel.org/all/20260502162833.34334-1-work@onurozkan.dev
v3: https://lore.kernel.org/all/20260522054228.114814-1-work@onurozkan.dev
v4: https://lore.kernel.org/all/20260525175706.124910-1-work@onurozkan.dev
v5: https://lore.kernel.org/all/20260527174120.510447-1-work@onurozkan.dev
v6: https://lore.kernel.org/all/20260527203615.163688-1-work@onurozkan.dev
v7: https://lore.kernel.org/all/20260528062810.256212-1-work@onurozkan.dev
v8: https://lore.kernel.org/all/20260529114449.112066-1-work@onurozkan.dev
v9: https://lore.kernel.org/all/20260529134004.396743-1-work@onurozkan.dev

[1]: https://lore.kernel.org/all/20260416171728.205141-1-work@onurozkan.dev
[2]: https://sashiko.dev/#/patchset/20260522054228.114814-1-work@onurozkan.dev?part=2

Onur Özkan (5):
  srcu: make init_srcu_struct() consistently wrap __init_srcu_struct()
  rust: helpers: add SRCU helpers
  srcu: expose srcu_readers_active()
  rust: sync: add SRCU abstraction
  MAINTAINERS: add Rust SRCU files to SRCU entry

 MAINTAINERS              |   3 +
 include/linux/srcu.h     |  29 ++++---
 include/linux/srcutiny.h |  13 +++
 include/linux/srcutree.h |  24 ++++++
 kernel/rcu/srcutiny.c    |  14 ++--
 kernel/rcu/srcutree.c    |  36 ++-------
 rust/helpers/helpers.c   |   1 +
 rust/helpers/srcu.c      |  35 ++++++++
 rust/kernel/sync.rs      |   2 +
 rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++
 10 files changed, 282 insertions(+), 46 deletions(-)
 create mode 100644 rust/helpers/srcu.c
 create mode 100644 rust/kernel/sync/srcu.rs

-- 
2.51.2


^ permalink raw reply

* Re: [PATCH] rust: bitfield: mark `Debug` impl as `#[inline]`
From: Alexandre Courbot @ 2026-06-13  5:43 UTC (permalink / raw)
  To: Gary Guo
  Cc: Gary Guo, Yury Norov, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, rust-for-linux, linux-kernel
In-Reply-To: <DJ6SM9BJ7GHN.JX9HXO3ZMBPM@nvidia.com>

On Fri Jun 12, 2026 at 1:40 PM JST, Alexandre Courbot wrote:
> On Fri Jun 12, 2026 at 4:05 AM JST, Gary Guo wrote:
>> From: Gary Guo <gary@garyguo.net>
>>
>> A `Debug` impl is for debugging and is normally not used, and therefore
>> should ideally not be code-generated unless used. However, Rust has no way
>> of knowing if a dependent crate is going to use the trait impl or not, so
>> unless it is marked as `#[inline]`, it will be code-generated in the
>> defining crate (as it is not generic).
>>
>> Mark the impl generated by bitfield macro `#[inline]`, so they do not stay
>> in the binary unless used.
>>
>> This reduces nova-core.o .text by 17% (from 151922 bytes to 125676 bytes).
>>
>> Signed-off-by: Gary Guo <gary@garyguo.net>
>
> Ouch, well-spotted. Thanks for this.
>
> Acked-by: Alexandre Courbot <acourbot@nvidia.com>

Btw, should this be

Fixes: b7b8b4ccdad4 ("rust: extract `bitfield!` macro from `register!`")

?

^ permalink raw reply

* Re: [PATCH] gpu: nova-core: remove `#[allow(non_snake_case)]`
From: Alexandre Courbot @ 2026-06-12 23:28 UTC (permalink / raw)
  To: Gary Guo
  Cc: Gary Guo, Danilo Krummrich, Alice Ryhl, David Airlie,
	Simona Vetter, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, nova-gpu, dri-devel,
	linux-kernel, rust-for-linux
In-Reply-To: <20260612123401.2684025-1-gary@kernel.org>

On Fri Jun 12, 2026 at 9:34 PM JST, Gary Guo wrote:
> From: Gary Guo <gary@garyguo.net>
>
> Since commit 5423ef9d4db8 ("rust: pin-init: internal: suppress
> `non_snake_case` lint in `[pin_]init!`"), mere use of the non-snake-case
> identifiers would not cause the warning to be generated. Thus remove these
> allows.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>

Acked-by: Alexandre Courbot <acourbot@nvidia.com>

^ permalink raw reply


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