[PATCH v4 0/7] samples: rust: add an EDU PCI driver sample (MMIO + IRQ + DMA)
From: Maurice Hieronymus
Date: Wed Nov 04 2026 - 19:09:16 EST
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, reads the PCI configuration space, 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.
The IRQ and DMA tests wait on a `Completion` signalled by the IRQ
handler; the factorial test polls for completion.
As requested by Danilo in the v2 review, the sample replaces
rust_driver_pci, which it covers a superset of; the removal is the
first patch.
Prerequisites that had to be implemented:
- pci: rework the device enabling API: replace enable_device_mem()
with enable_device(), which returns a DeviceEnableGuard that
disables the device again on drop, so the enable count stays
balanced across unbind/rebind. This follows the design Danilo
proposed in the review of the standalone patch [1], which this
series absorbs.
- pci: keep `set_master()` on `Device<Core>` and require a borrowed
`DeviceEnableGuard` for the same device, preserving callback
synchronization while ensuring the device is enabled.
- pci: add PCI_VENDOR_ID_QEMU (0x1234) to pci_ids.h and expose it as
Vendor::QEMU, so the sample can match the device.
- completion: add complete(), so a single Completion can be reused to
wait for consecutive events (e.g. back-to-back DMA transfers).
The series is based on rust/rust-next.
Note: DeviceEnableGuard drops from a bound scope, so pci_disable_device()
can race the pci_dev bitfield word as discussed in [1]; that race
predates this series and is triggerable from sysfs today. The bitops
conversion is under way separately on linux-pci [3].
Tested with QEMU `-device edu`.
Not taken from the v3 review (Sashiko):
- The IRQ and DMA completion waits remain unbounded; missing
interrupts can block probe.
- enable_device() uses pci_enable_device(), not
pci_enable_device_mem(): it is the single enable API Danilo proposed,
and enabling I/O decoding is harmless for memory-only devices.
[1] https://lore.kernel.org/rust-for-linux/DJOEYVBS17MJ.1YD3TNGQBWHNK@xxxxxxxxxx
[2] https://lore.kernel.org/r/20260719153631.559341-1-dakr@xxxxxxxxxx
[3] https://lore.kernel.org/linux-pci/20260714-pci-dev-flags-v2-1-a1d7dc441cf3@xxxxxxxxxxx/
Signed-off-by: Maurice Hieronymus <mhi@xxxxxxxxxxx>
---
Changes in v4:
- Drop "rust: pci: make Vendor::from_raw() public" (and its R-b tags);
instead add PCI_VENDOR_ID_QEMU to pci_ids.h and expose it as
Vendor::QEMU (new patches 4-5) (Alice, John).
- New patch: pci: require a borrowed enable guard for the same device
in `set_master()`, retaining `Device<Core>` callback synchronization;
convert nova-core. This enforces enabling before bus-mastering
(Danilo) while addressing the concurrency concern (Sashiko).
- pci: mark DeviceEnableGuard #[must_use] and enable_device() #[inline]
(Sashiko, Fiona).
- completion: reference complete() from wait_for_completion()'s docs
(Gary).
- samples/edu: turn init() and the test helpers into methods on
EduDriverData; rename init() to selftest() (Danilo).
- samples/edu: initialize EduDriverData in a single try_pin_init!(),
holding the IRQ vectors and registering the handler with
irq::Registration::new() instead of pci::Device::request_irq(); run
selftest() via pin_chain() (Danilo).
- samples/edu: make the magic and DMA base constants local to their
users; check the magic via an IDENTIFICATION constant (Danilo).
- samples/edu: describe the DMA and test IRQ bits as fields of
IRQ_STATUS/IRQ_RAISE/IRQ_ACK instead of a DMA_IRQ constant (Danilo).
- samples/edu: model the DMA registers as a struct accessed through
io_project!()/io_write!().
- samples/edu: compare the liveness pattern via a Not impl on
LIVENESS_CHECK (Danilo).
- samples/edu: replace `as` casts with safe casts (Danilo).
- samples/edu: return IrqReturn::None for interrupts that are not ours
(Sashiko).
- samples/edu: depend on 64BIT for the 64-bit MMIO accesses (Sashiko).
- samples/edu: expand the commit message and Kconfig help to describe
the sample's coverage, QEMU usage and the 64BIT requirement; mention
the MAINTAINERS entry in the commit message (Sashiko).
- samples/edu: ack stale interrupts before registering the handler
(Sashiko).
- samples/edu: spell out why the IRQ Registration is not leaked (Ewan).
- Drop the dependency on Danilo's irq Registration rework [2], now in
mainline.
- Rebase on rust-next; adapt to register!() `base:`, Option<&IdInfo>
in probe() and the Coherent read_val()/write_val() API.
- Link to v3: https://lore.kernel.org/r/20260812-b4-rust-pci-edu-driver-v3-0-5d0b5594e52b@xxxxxxxxxxx
Changes in v3:
- Absorb the device enabling rework [1] into this series, redesigned
around DeviceEnableGuard as proposed by Danilo; convert nova-core.
- Remove rust_driver_pci, replaced by this sample (Danilo).
- Add access to PCI configuration space (Danilo).
- Rebase on rust/rust-next on top of Danilo's irq Registration rework
[2]; register the handler via pci::Device::request_irq().
- Use vertical import style.
- Link to v2: https://lore.kernel.org/r/20260620-b4-rust-pci-edu-driver-v2-0-6fd6684f2c14@xxxxxxxxxxx
Changes in v2:
- pci: Vendor::from_raw(): collected Reviewed-by from Gary Guo;
wrapped code identifiers in the commit message in backticks (Gary).
- pci: enable_device(): collected Reviewed-by from Fiona Behrens;
made enable_device_mem() #[inline] and added a cross-reference to
enable_device() in its docs (Fiona).
- completion: complete(): tightened the doc comment per Gary's review
(emphasise "single", drop the internal-counter detail, drop the
complete_all comparison).
- samples/edu: take &EduDriverData instead of &Arc<EduDriverData> in
init()/test_irq()/test_dma() (Ewan Chorynski).
- samples/edu: simplify wait_until_compute_has_finished() to forward
read_poll_timeout()'s error via inspect_err() instead of returning a
hard-coded ETIMEDOUT (Ewan Chorynski / Miguel Ojeda).
- samples/edu: Rebased on rust/rust-next and adapt to the updated
pci::Bar / device::Core lifetimes and pci::Driver::Data<'bound>, and
obtain the BAR via into_devres().
- Link to v1: https://lore.kernel.org/r/20260614-b4-rust-pci-edu-driver-v1-0-e3f2471b595c@xxxxxxxxxxx
---
Maurice Hieronymus (7):
samples: rust: remove the rust_driver_pci sample
rust: pci: rework device enabling API
rust: pci: require an enable guard for `set_master()`
PCI: Add QEMU vendor ID
rust: pci: add Vendor::QEMU
rust: completion: add complete()
samples: rust: add EDU PCI driver sample
MAINTAINERS | 2 +-
drivers/gpu/nova-core/driver.rs | 9 +-
include/linux/pci_ids.h | 2 +
rust/kernel/pci.rs | 45 +++-
rust/kernel/pci/id.rs | 1 +
rust/kernel/sync/completion.rs | 13 +-
samples/rust/Kconfig | 14 +-
samples/rust/Makefile | 2 +-
samples/rust/rust_driver_edu.rs | 475 ++++++++++++++++++++++++++++++++++++++++
samples/rust/rust_driver_pci.rs | 198 -----------------
10 files changed, 547 insertions(+), 214 deletions(-)
---
base-commit: d89e1d186ec9271bf78e62771779e9fc03bf0fc3
change-id: 20260614-b4-rust-pci-edu-driver-3e50db2dda0f
Best regards,
--
Maurice Hieronymus <mhi@xxxxxxxxxxx>