From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id D16ABCD3439 for ; Wed, 6 May 2026 21:52:30 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3B66F10EEBE; Wed, 6 May 2026 21:52:30 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="q/kTinFp"; dkim-atps=neutral Received: from sea.source.kernel.org (sea.source.kernel.org [172.234.252.31]) by gabe.freedesktop.org (Postfix) with ESMTPS id 6BC8710EEBE for ; Wed, 6 May 2026 21:52:29 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sea.source.kernel.org (Postfix) with ESMTP id 4070B4383F; Wed, 6 May 2026 21:52:29 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D7111C2BCC4; Wed, 6 May 2026 21:52:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1778104349; bh=ig5DV0Sc4uVWVdLoZr3ZmpFzcTtS822JAW+IY4+QRxw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=q/kTinFpYiLdcgsk9g/ipcufmJ28uyG5/TtU456pVzuI2LTwykr3G+FevCNrd3vOg nkBXwjxjoLzHEwAuusi7/YSalnUrIx4Rq71ix5vF7xCm3dq0c6+3YLfdAbLMP7xpCL ikGPvnUdEztaO99lvtasnh63Z9QSNUjy/NV+2BANeL8hzrSiDcNckXU0bPf1AGj6Pr l9XQmxZEij7lLji3u2dqmAdhO/P6bh5JWewcwZ1Lt+vXepe1Ksa5svXoKyTGGI/vm4 T9enMIfsFlQjK6JXfXZRKLat0C6GNgvwmZRPCthfTB5hNm96JW1vVc5WCy/pqs81sy BDPMHSa0cOAIA== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, acourbot@nvidia.com, aliceryhl@google.com, david.m.ertman@intel.com, ira.weiny@intel.com, leon@kernel.org, viresh.kumar@linaro.org, m.wilczynski@samsung.com, ukleinek@kernel.org, bhelgaas@google.com, kwilczynski@kernel.org, abdiel.janulgue@gmail.com, robin.murphy@arm.com, markus.probst@posteo.de, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, tmgross@umich.edu, igor.korotin@linux.dev, daniel.almeida@collabora.com Cc: driver-core@lists.linux.dev, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, linux-pm@vger.kernel.org, linux-pwm@vger.kernel.org, linux-pci@vger.kernel.org, rust-for-linux@vger.kernel.org, Danilo Krummrich Subject: [PATCH v2 10/25] rust: pci: make Driver trait lifetime-parameterized Date: Wed, 6 May 2026 23:50:46 +0200 Message-ID: <20260506215113.851360-11-dakr@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506215113.851360-1-dakr@kernel.org> References: <20260506215113.851360-1-dakr@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Make pci::Driver take a lifetime parameter 'bound that ties device resources to the binding scope. Internally, Adapter becomes Adapter with a HRTB bound for<'bound> F::Of<'bound>: Driver<'bound>; module_pci_driver! wraps the driver type in ForLt!() so drivers don't have to. Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/driver.rs | 9 ++- drivers/gpu/nova-core/nova_core.rs | 4 +- rust/kernel/pci.rs | 80 +++++++++++++++++++-------- samples/rust/rust_dma.rs | 9 ++- samples/rust/rust_driver_auxiliary.rs | 13 +++-- samples/rust/rust_driver_pci.rs | 11 ++-- 6 files changed, 87 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 8fe484d357f6..d0ccfbc8d0ea 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -50,7 +50,7 @@ pub(crate) struct NovaCore { kernel::pci_device_table!( PCI_TABLE, MODULE_PCI_TABLE, - ::IdInfo, + >::IdInfo, [ // Modern NVIDIA GPUs will show up as either VGA or 3D controllers. ( @@ -72,11 +72,14 @@ pub(crate) struct NovaCore { ] ); -impl pci::Driver for NovaCore { +impl<'bound> pci::Driver<'bound> for NovaCore { type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe( + pdev: &'bound pci::Device, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { pin_init::pin_init_scope(move || { dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 04a1fa6b25f8..49c093a0cb42 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -7,6 +7,7 @@ driver::Registration, pci, prelude::*, + types::ForLt, InPlaceModule, // }; @@ -46,8 +47,9 @@ fn drop(&mut self) { struct NovaCoreModule { // Fields are dropped in declaration order, so `_driver` is dropped first, // then `_debugfs_guard` clears `DEBUGFS_ROOT`. + #[allow(clippy::type_complexity)] #[pin] - _driver: Registration>, + _driver: Registration>, _debugfs_guard: DebugfsRootGuard, } diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 6f82f2e6c74f..1335857cae94 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -58,22 +58,35 @@ }; /// An adapter for the registration of PCI drivers. -pub struct Adapter(T); +/// +/// `F` is a [`ForLt`](trait@ForLt) type that maps lifetimes to the driver's device +/// private data type, i.e. `F::Of<'bound>` is the driver struct +/// parameterized by `'bound`. The macro `module_pci_driver!` generates +/// this automatically via `ForLt!()`. +pub struct Adapter(PhantomData); // SAFETY: // - `bindings::pci_driver` is a C type declared as `repr(C)`. -// - `T` is the type of the driver's device private data. +// - `F::Of<'static>` is the stored type of the driver's device private data. // - `struct pci_driver` embeds a `struct device_driver`. // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`. -unsafe impl driver::DriverLayout for Adapter { +unsafe impl driver::DriverLayout for Adapter +where + F: ForLt + 'static, + for<'bound> F::Of<'bound>: Driver<'bound>, +{ type DriverType = bindings::pci_driver; - type DriverData = ForLt!(T); + type DriverData = F; const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver); } // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if // a preceding call to `register` has been successful. -unsafe impl driver::RegistrationOps for Adapter { +unsafe impl driver::RegistrationOps for Adapter +where + F: ForLt + 'static, + for<'bound> F::Of<'bound>: Driver<'bound>, +{ unsafe fn register( pdrv: &Opaque, name: &'static CStr, @@ -84,7 +97,7 @@ unsafe fn register( (*pdrv.get()).name = name.as_char_ptr(); (*pdrv.get()).probe = Some(Self::probe_callback); (*pdrv.get()).remove = Some(Self::remove_callback); - (*pdrv.get()).id_table = T::ID_TABLE.as_ptr(); + (*pdrv.get()).id_table = as Driver<'static>>::ID_TABLE.as_ptr(); } // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. @@ -99,7 +112,11 @@ unsafe fn unregister(pdrv: &Opaque) { } } -impl Adapter { +impl Adapter +where + F: ForLt + 'static, + for<'bound> F::Of<'bound>: Driver<'bound>, +{ extern "C" fn probe_callback( pdev: *mut bindings::pci_dev, id: *const bindings::pci_device_id, @@ -113,12 +130,12 @@ extern "C" fn probe_callback( // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct pci_device_id` and // does not add additional invariants, so it's safe to transmute. let id = unsafe { &*id.cast::() }; - let info = T::ID_TABLE.info(id.index()); from_result(|| { - let data = T::probe(pdev, info); + let info = as Driver<'_>>::ID_TABLE.info(id.index()); + let data = as Driver<'_>>::probe(pdev, info); - pdev.as_ref().set_drvdata::(data)?; + pdev.as_ref().set_drvdata::(data)?; Ok(0) }) } @@ -131,16 +148,18 @@ extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { let pdev = unsafe { &*pdev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to - // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called - // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_borrow::() }; + // `probe_callback`, hence it's guaranteed that drvdata has been set. + let data = unsafe { pdev.as_ref().drvdata_borrow::() }; - T::unbind(pdev, data); + as Driver<'_>>::unbind(pdev, data); } } /// Declares a kernel module that exposes a single PCI driver. /// +/// The `type` field accepts a driver type, optionally with a lifetime placeholder `'_` for +/// lifetime-parameterized drivers. The macro wraps it in [`ForLt!`] automatically. +/// /// # Examples /// ///```ignore @@ -152,10 +171,16 @@ extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { /// license: "GPL v2", /// } ///``` +/// +/// [`ForLt!`]: macro@ForLt +/// [`ForLt`]: trait@ForLt #[macro_export] macro_rules! module_pci_driver { -($($f:tt)*) => { - $crate::module_driver!(, $crate::pci::Adapter, { $($f)* }); +(type: $type:ty, $($rest:tt)*) => { + $crate::module_driver!(, $crate::pci::Adapter, { + type: $crate::types::ForLt!($type), + $($rest)* + }); }; } @@ -261,6 +286,9 @@ macro_rules! pci_device_table { /// The PCI driver trait. /// +/// Drivers implement this trait with a lifetime parameter `'bound` that ties +/// device resources to the device scope. +/// /// # Examples /// ///``` @@ -271,7 +299,7 @@ macro_rules! pci_device_table { /// kernel::pci_device_table!( /// PCI_TABLE, /// MODULE_PCI_TABLE, -/// ::IdInfo, +/// >::IdInfo, /// [ /// ( /// pci::DeviceId::from_id(pci::Vendor::REDHAT, bindings::PCI_ANY_ID as u32), @@ -280,21 +308,22 @@ macro_rules! pci_device_table { /// ] /// ); /// -/// impl pci::Driver for MyDriver { +/// impl<'bound> pci::Driver<'bound> for MyDriver { /// type IdInfo = (); /// const ID_TABLE: pci::IdTable = &PCI_TABLE; /// /// fn probe( -/// _pdev: &pci::Device, -/// _id_info: &Self::IdInfo, -/// ) -> impl PinInit { +/// _pdev: &'bound pci::Device, +/// _id_info: &'bound Self::IdInfo, +/// ) -> impl PinInit + 'bound { /// Err(ENODEV) /// } /// } ///``` +/// /// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the /// `Adapter` documentation for an example. -pub trait Driver: Send { +pub trait Driver<'bound>: Send { /// The type holding information about each device id supported by the driver. // TODO: Use `associated_type_defaults` once stabilized: // @@ -310,7 +339,10 @@ pub trait Driver: Send { /// /// Called when a new pci device is added or discovered. Implementers should /// attempt to initialize the device here. - fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; + fn probe( + dev: &'bound Device, + id_info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound; /// PCI driver unbind. /// @@ -322,7 +354,7 @@ pub trait Driver: Send { /// operations to gracefully tear down the device. /// /// Otherwise, release operations for driver resources should be performed in `Self::drop`. - fn unbind(dev: &Device, this: Pin<&Self>) { + fn unbind(dev: &'bound Device, this: Pin<&'bound Self>) { let _ = (dev, this); } } diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 129bb4b39c04..e8b3e2e799f3 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -52,15 +52,18 @@ unsafe impl kernel::transmute::FromBytes for MyStruct {} kernel::pci_device_table!( PCI_TABLE, MODULE_PCI_TABLE, - ::IdInfo, + >::IdInfo, [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())] ); -impl pci::Driver for DmaSampleDriver { +impl<'bound> pci::Driver<'bound> for DmaSampleDriver { type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe( + pdev: &'bound pci::Device, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { pin_init::pin_init_scope(move || { dev_info!(pdev, "Probe DMA test driver.\n"); diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 319ef734c02b..a1b42d30580e 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -14,6 +14,7 @@ driver, pci, prelude::*, + types::ForLt, InPlaceModule, // }; @@ -59,16 +60,19 @@ struct ParentDriver { kernel::pci_device_table!( PCI_TABLE, MODULE_PCI_TABLE, - ::IdInfo, + >::IdInfo, [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())] ); -impl pci::Driver for ParentDriver { +impl<'bound> pci::Driver<'bound> for ParentDriver { type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + fn probe( + pdev: &'bound pci::Device, + _info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { Ok(Self { _reg0: auxiliary::Registration::new( pdev.as_ref(), @@ -116,7 +120,8 @@ fn connect(adev: &auxiliary::Device) -> Result { #[pin_data] struct SampleModule { #[pin] - _pci_driver: driver::Registration>, + #[allow(clippy::type_complexity)] + _pci_driver: driver::Registration>, #[pin] _aux_driver: driver::Registration>, } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 47d3e84fab63..794311691d1e 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -77,7 +77,7 @@ struct SampleDriver { kernel::pci_device_table!( PCI_TABLE, MODULE_PCI_TABLE, - ::IdInfo, + >::IdInfo, [( pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), TestIndex::NO_EVENTFD @@ -138,12 +138,15 @@ fn config_space(pdev: &pci::Device) { } } -impl pci::Driver for SampleDriver { +impl<'bound> pci::Driver<'bound> for SampleDriver { type IdInfo = TestIndex; const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> impl PinInit { + fn probe( + pdev: &'bound pci::Device, + info: &'bound Self::IdInfo, + ) -> impl PinInit + 'bound { pin_init::pin_init_scope(move || { let vendor = pdev.vendor_id(); dev_dbg!( @@ -174,7 +177,7 @@ fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> impl PinInit, this: Pin<&Self>) { + fn unbind(pdev: &'bound pci::Device, this: Pin<&'bound Self>) { if let Ok(bar) = this.bar.access(pdev.as_ref()) { // Reset pci-testdev by writing a new test index. bar.write_reg(regs::TEST::zeroed().with_index(this.index)); -- 2.54.0