From patchwork Thu Mar 13 02:13:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 14014213 X-Patchwork-Delegate: bhelgaas@google.com Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A393113790B; Thu, 13 Mar 2025 02:17:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832257; cv=none; b=HF3mzdks6foDbimv1oCsjwVM2IoiuzcN4kX6MLSwUgFN9mMZT0YhrPjVdYfd3ROW3HTphtFCFRwu+3hpnK686M94duSRuj8kqvaeKdlxdNTytlP0CaVMppjQni2FU47iJrqhKz46euARz2BJ2caR2jZkDiqqS6y3XUWnQJShn2U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832257; c=relaxed/simple; bh=HvX+AHLHCHoEnMD4UbcyM9MX38qRT1eA7SvQJ4hOUWY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NU5tUkhCv8XdsqQ+uXHtLww1nKe0CfrZ1jn+BU2ZPyS9U0k8JhFqOOYNBvkFcpRNIO/r2b3q7McWKyBSPgtIaN+L10S4VoONoqQ4sFQ2g5Ql4qGdEjeCBHqI1+GrtoEET4KD6i6qraUR+GtjH9mxKrVvqtU9hX7b389OUDs6ZI8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WJzMIPiX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WJzMIPiX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DC7CCC4CEEF; Thu, 13 Mar 2025 02:17:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1741832257; bh=HvX+AHLHCHoEnMD4UbcyM9MX38qRT1eA7SvQJ4hOUWY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WJzMIPiXqDXBVNj3ZIF+MAt/vHAmDU+T3LeBTd8wwj8l6JBVBc7waQXIeyoPSn420 zgkRQLQSUig7Zn/JDS5cRBj7PVsDgqV0gFX6QdJ14UOUx3DrcgyYWdrB+4q7YUj3da Ens5ElD5bPzG9bDaeJ9HZfPcGMzBvE1UQUkrofJwd/NVp5QUlaNN7GXPDkVVEVLBGX bXQz8P6sAQH+Ock5MhJjVqUXQr1wGnXC/BVApzOvPgRS8265juUy1B9Yg6gZIVtyfu TDVR+cn1JhY/+io1vFyj2AICLvwGc7cSIe4RQ6EpDcQaGeoOdJUVSWDysCDcqr4Sw8 S8wsg/0fC7K/w== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, rust-for-linux@vger.kernel.org, Danilo Krummrich Subject: [PATCH 1/4] rust: pci: use to_result() in enable_device_mem() Date: Thu, 13 Mar 2025 03:13:31 +0100 Message-ID: <20250313021550.133041-2-dakr@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250313021550.133041-1-dakr@kernel.org> References: <20250313021550.133041-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Simplify enable_device_mem() by using to_result() to handle the return value of the corresponding FFI call. Signed-off-by: Danilo Krummrich Reviewed-by: Benno Lossin --- rust/kernel/pci.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 4c98b5b9aa1e..386484dcf36e 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -382,12 +382,7 @@ pub fn device_id(&self) -> u16 { /// Enable memory resources for this device. pub fn enable_device_mem(&self) -> Result { // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. - let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) }; - if ret != 0 { - Err(Error::from_errno(ret)) - } else { - Ok(()) - } + to_result(unsafe { bindings::pci_enable_device_mem(self.as_raw()) }) } /// Enable bus-mastering for this device. From patchwork Thu Mar 13 02:13:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 14014214 X-Patchwork-Delegate: bhelgaas@google.com Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 525F611CBA; Thu, 13 Mar 2025 02:17:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832261; cv=none; b=oOgDv2Sh3hrAYjq8Q8VuwR1hJdy7+FFA5QAMt+6OdAsQeSzAkZPY4Yo9yNBl7ry5+EBlz3uR7RMeYRijCv6Bfm0N7+9/P3066alDjTYpVbZah33BdJmTVgG5j+uIIQocicwZHRUFlTeI1SCgx0y8dWBJd56X/dA+A+u6gZeFcSA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832261; c=relaxed/simple; bh=DRVCIISdaFeehvx3l2ubIzGkweyu9N3EeZWWt1VR9Gg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=s/9K5eh6zo9mHaqYIHb9Y06DLgHdhl4oRGlRN0XBcZR07T9WE3k3nhtQcUjY54QRii8/jUHHCmIYpzgQzMrkwZP6j9nY7qs9NhiXpRFDf6e2VakAaTaT1dMfL36/z7102/NG9+EKk0NougroT3wpDw4PC8Hi23zoI6FPxBCsxlI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=c0eNfg4i; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="c0eNfg4i" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8E83FC4CEE3; Thu, 13 Mar 2025 02:17:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1741832260; bh=DRVCIISdaFeehvx3l2ubIzGkweyu9N3EeZWWt1VR9Gg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=c0eNfg4innYmycjPdGIJ5KQtsK6SlQTBl42ZvmS+lBh8vRCl5MQZGCBDfkW7Nd/z9 +60ERt9HAbNH6/hhe8WHsZqYNJFXZrM8R58NYrmCgSgZohSS1IEStjcCN3TC9Bno4/ PW2KDZPM4qJwnmk03AiZ9FlnqVSDaUPhMvU+LBcyFFSv3OCSXcvZ/61TZhwqo1Zi1F kBi0lIDnr7UrasNwXz0pPXOe9QIhqFFr/WgPbzP3mwpChQSN5B/S7y9t43oYxdtjwM fBmglVoozD/SKc7uSRmmFtBspOz7Bvkq3cBA46S5Giv7GH5gCVMhoNO4YswlrdOA35 H8att4sj0oiGw== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, rust-for-linux@vger.kernel.org, Danilo Krummrich Subject: [PATCH 2/4] rust: device: implement device context marker Date: Thu, 13 Mar 2025 03:13:32 +0100 Message-ID: <20250313021550.133041-3-dakr@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250313021550.133041-1-dakr@kernel.org> References: <20250313021550.133041-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Some bus device functions should only be called from bus callbacks, such as probe(), remove(), resume(), suspend(), etc. To ensure this add device context marker structs, that can be used as generics for bus device implementations. Suggested-by: Benno Lossin Signed-off-by: Danilo Krummrich Reviewed-by: Benno Lossin --- rust/kernel/device.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index db2d9658ba47..39793740a95c 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -209,6 +209,24 @@ unsafe impl Send for Device {} // synchronization in `struct device`. unsafe impl Sync for Device {} +/// Marker trait for the context of a bus specific device. +/// +/// Some functions of a bus specific device should only be called from a certain context, i.e. bus +/// callbacks, such as `probe()`. +/// +/// This is the marker trait for structures representing the context of a bus specific device. +pub trait DeviceContext {} + +/// The [`Normal`] context is the context of a bus specific device when it is not an argument of +/// any bus callback. +pub struct Normal; +impl DeviceContext for Normal {} + +/// The [`Core`] context is the context of a bus specific device when it is supplied as argument of +/// any of the bus callbacks, such as `probe()`. +pub struct Core; +impl DeviceContext for Core {} + #[doc(hidden)] #[macro_export] macro_rules! dev_printk { From patchwork Thu Mar 13 02:13:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 14014215 X-Patchwork-Delegate: bhelgaas@google.com Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F25BD11CBA; Thu, 13 Mar 2025 02:17:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832265; cv=none; b=U0Cg6VybEMtw4c5WQY0D3XZvqP3b6GzE4MX69jlPz9dGOm+DAevkHISYkJnGYTTdL/CTosBCGpXtWxxo7R/Oen9maEP76Vy2+/JKjWinvA8QeJ/WZEGMmgpgLWOjTtJ2Hiwz4J6IGmi+NLIEC8TNk3LooKHgq0ZYKY9qq5MPCSU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832265; c=relaxed/simple; bh=wya8TJx1t18pZaEuN0r+Dcnq3RsZ3bs7QD7n7ftGnUw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dnmizmuHd5MLHlbYyMs+drL8Kij6vDZldiYIVxKclVf7Vgt56gjnQo3RYJmooGPJtpqYQv8GGhvXm6t02ttR4j/qdtpwHdcQu3Vcnf7uOHS77CLmuaYsW5OsZtFvUC89U/iylO2xkB3zXDvW5mxqVkeF2fJQCT/fa2hrPMvcivw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VKiV4GnO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="VKiV4GnO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 42054C4CEDD; Thu, 13 Mar 2025 02:17:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1741832264; bh=wya8TJx1t18pZaEuN0r+Dcnq3RsZ3bs7QD7n7ftGnUw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VKiV4GnOvs7MkuzkV/mq9RQdCIn+WVAItgWdSb6bQ/qGniYYMFUyCfUbD9Y2NVCph IzUSbym+996FfA1LnCSLEODgRvrrLRIq47E/kjNo7xShqSXx4h4q9oB+HO+h9Tydlx 5ZZd8/+H5+/0nnYF6JKgfcUhKQ4NYOUxVDbqXRik/HawyKS4DpttKNsNtNpidmNJET bhdHFs+jKZ98ckijgdqOb45yk3gnHwEBqgOgIraAf/LA162c/mdA6V1rjD9JODbsWq M2j6M0x8GQdEu+aJoW3NWh3dCchZHgV/nZuoGEfEhJEYyyh537JDGg8He+iiDkHdSc 5s3hxY3uD6XQQ== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, rust-for-linux@vger.kernel.org, Danilo Krummrich Subject: [PATCH 3/4] rust: pci: fix unrestricted &mut pci::Device Date: Thu, 13 Mar 2025 03:13:33 +0100 Message-ID: <20250313021550.133041-4-dakr@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250313021550.133041-1-dakr@kernel.org> References: <20250313021550.133041-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 As by now, pci::Device is implemented as: #[derive(Clone)] pub struct Device(ARef); This may be convenient, but has the implication that drivers can call device methods that require a mutable reference concurrently at any point of time. Instead define pci::Device as pub struct Device( Opaque, PhantomData, ); and manually implement the AlwaysRefCounted trait. With this we can implement methods that should only be called from bus callbacks (such as probe()) for pci::Device. Consequently, we make this type accessible in bus callbacks only. Arbitrary references taken by the driver are still of type ARef and hence don't provide access to methods that are reserved for bus callbacks. Fixes: 1bd8b6b2c5d3 ("rust: pci: add basic PCI device / driver abstractions") Signed-off-by: Danilo Krummrich Reviewed-by: Benno Lossin --- drivers/gpu/nova-core/driver.rs | 4 +- rust/kernel/pci.rs | 126 ++++++++++++++++++++------------ samples/rust/rust_driver_pci.rs | 8 +- 3 files changed, 85 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 63c19f140fbd..a08fb6599267 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use kernel::{bindings, c_str, pci, prelude::*}; +use kernel::{bindings, c_str, device::Core, pci, prelude::*}; use crate::gpu::Gpu; @@ -27,7 +27,7 @@ impl pci::Driver for NovaCore { type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result>> { + fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); pdev.enable_device_mem()?; diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 386484dcf36e..6357b4ff8d65 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -6,7 +6,7 @@ use crate::{ alloc::flags::*, - bindings, container_of, device, + bindings, device, device_id::RawDeviceId, devres::Devres, driver, @@ -17,7 +17,11 @@ types::{ARef, ForeignOwnable, Opaque}, ThisModule, }; -use core::{ops::Deref, ptr::addr_of_mut}; +use core::{ + marker::PhantomData, + ops::Deref, + ptr::{addr_of_mut, NonNull}, +}; use kernel::prelude::*; /// An adapter for the registration of PCI drivers. @@ -60,17 +64,16 @@ extern "C" fn probe_callback( ) -> kernel::ffi::c_int { // SAFETY: The PCI bus only ever calls the probe callback with a valid pointer to a // `struct pci_dev`. - let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) }; - // SAFETY: `dev` is guaranteed to be embedded in a valid `struct pci_dev` by the call - // above. - let mut pdev = unsafe { Device::from_dev(dev) }; + // + // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. + let pdev = unsafe { &*pdev.cast::>() }; // 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()); - match T::probe(&mut pdev, info) { + match T::probe(pdev, info) { Ok(data) => { // Let the `struct pci_dev` own a reference of the driver's private data. // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a @@ -192,7 +195,7 @@ macro_rules! pci_device_table { /// # Example /// ///``` -/// # use kernel::{bindings, pci}; +/// # use kernel::{bindings, device::Core, pci}; /// /// struct MyDriver; /// @@ -210,7 +213,7 @@ macro_rules! pci_device_table { /// const ID_TABLE: pci::IdTable = &PCI_TABLE; /// /// fn probe( -/// _pdev: &mut pci::Device, +/// _pdev: &pci::Device, /// _id_info: &Self::IdInfo, /// ) -> Result>> { /// Err(ENODEV) @@ -234,20 +237,23 @@ pub trait Driver { /// /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. - fn probe(dev: &mut Device, id_info: &Self::IdInfo) -> Result>>; + fn probe(dev: &Device, id_info: &Self::IdInfo) -> Result>>; } /// The PCI device representation. /// -/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI -/// device, hence, also increments the base device' reference count. +/// This structure represents the Rust abstraction for a C `struct pci_dev`. The implementation +/// abstracts the usage of an already existing C `struct pci_dev` within Rust code that we get +/// passed from the C side. /// /// # Invariants /// -/// `Device` hold a valid reference of `ARef` whose underlying `struct device` is a -/// member of a `struct pci_dev`. -#[derive(Clone)] -pub struct Device(ARef); +/// A [`Device`] instance represents a valid `struct device` created by the C portion of the kernel. +#[repr(transparent)] +pub struct Device( + Opaque, + PhantomData, +); /// A PCI BAR to perform I/O-Operations on. /// @@ -256,13 +262,13 @@ pub trait Driver { /// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O /// memory mapped PCI bar and its size. pub struct Bar { - pdev: Device, + pdev: ARef, io: IoRaw, num: i32, } impl Bar { - fn new(pdev: Device, num: u32, name: &CStr) -> Result { + fn new(pdev: &Device, num: u32, name: &CStr) -> Result { let len = pdev.resource_len(num)?; if len == 0 { return Err(ENOMEM); @@ -300,12 +306,16 @@ fn new(pdev: Device, num: u32, name: &CStr) -> Result { // `pdev` is valid by the invariants of `Device`. // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. // `num` is checked for validity by a previous call to `Device::resource_len`. - unsafe { Self::do_release(&pdev, ioptr, num) }; + unsafe { Self::do_release(pdev, ioptr, num) }; return Err(err); } }; - Ok(Bar { pdev, io, num }) + Ok(Bar { + pdev: pdev.into(), + io, + num, + }) } /// # Safety @@ -351,20 +361,8 @@ fn deref(&self) -> &Self::Target { } impl Device { - /// Create a PCI Device instance from an existing `device::Device`. - /// - /// # Safety - /// - /// `dev` must be an `ARef` whose underlying `bindings::device` is a member of - /// a `bindings::pci_dev`. - pub unsafe fn from_dev(dev: ARef) -> Self { - Self(dev) - } - fn as_raw(&self) -> *mut bindings::pci_dev { - // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device` - // embedded in `struct pci_dev`. - unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ } + self.0.get() } /// Returns the PCI vendor ID. @@ -379,18 +377,6 @@ pub fn device_id(&self) -> u16 { unsafe { (*self.as_raw()).device } } - /// Enable memory resources for this device. - pub fn enable_device_mem(&self) -> Result { - // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. - to_result(unsafe { bindings::pci_enable_device_mem(self.as_raw()) }) - } - - /// Enable bus-mastering for this device. - pub fn set_master(&self) { - // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. - unsafe { bindings::pci_set_master(self.as_raw()) }; - } - /// Returns the size of the given PCI bar resource. pub fn resource_len(&self, bar: u32) -> Result { if !Bar::index_is_valid(bar) { @@ -410,7 +396,7 @@ pub fn iomap_region_sized( bar: u32, name: &CStr, ) -> Result>> { - let bar = Bar::::new(self.clone(), bar, name)?; + let bar = Bar::::new(self, bar, name)?; let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; Ok(devres) @@ -422,8 +408,54 @@ pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result> { } } +impl Device { + /// Enable memory resources for this device. + pub fn enable_device_mem(&self) -> Result { + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. + to_result(unsafe { bindings::pci_enable_device_mem(self.as_raw()) }) + } + + /// Enable bus-mastering for this device. + pub fn set_master(&self) { + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. + unsafe { bindings::pci_set_master(self.as_raw()) }; + } +} + +impl Deref for Device { + type Target = Device; + + fn deref(&self) -> &Self::Target { + let ptr: *const Self = self; + + // CAST: `Device` is a transparent wrapper of `Opaque`. + let ptr = ptr.cast::(); + + // SAFETY: `ptr` was derived from `&self`. + unsafe { &*ptr } + } +} + +// SAFETY: Instances of `Device` are always reference-counted. +unsafe impl crate::types::AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. + unsafe { bindings::pci_dev_get(self.as_raw()) }; + } + + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::pci_dev_put(obj.cast().as_ptr()) } + } +} + impl AsRef for Device { fn as_ref(&self) -> &device::Device { - &self.0 + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid + // `struct pci_dev`. + let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) }; + + // SAFETY: `dev` points to a valid `struct device`. + unsafe { device::Device::as_ref(dev) } } } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 1fb6e44f3395..b90df5f9d1d0 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -4,7 +4,7 @@ //! //! To make this driver probe, QEMU must be run with `-device pci-testdev`. -use kernel::{bindings, c_str, devres::Devres, pci, prelude::*}; +use kernel::{bindings, c_str, device::Core, devres::Devres, pci, prelude::*, types::ARef}; struct Regs; @@ -26,7 +26,7 @@ impl TestIndex { } struct SampleDriver { - pdev: pci::Device, + pdev: ARef, bar: Devres, } @@ -62,7 +62,7 @@ impl pci::Driver for SampleDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &mut pci::Device, info: &Self::IdInfo) -> Result>> { + fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> Result>> { dev_dbg!( pdev.as_ref(), "Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n", @@ -77,7 +77,7 @@ fn probe(pdev: &mut pci::Device, info: &Self::IdInfo) -> Result>> let drvdata = KBox::new( Self { - pdev: pdev.clone(), + pdev: (&**pdev).into(), bar, }, GFP_KERNEL, From patchwork Thu Mar 13 02:13:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Danilo Krummrich X-Patchwork-Id: 14014216 X-Patchwork-Delegate: bhelgaas@google.com Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BBE6F155326; Thu, 13 Mar 2025 02:17:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832268; cv=none; b=S05K+n6leSRWag+lxO/G5FoJNhLN0SJg8160B5glmfq4vq14GlJG7uElj3DEnAortyHS7EWmSxyc4FS2bX+/RpsabJJNh/EZl72nAd4N+h5ZXhmb4OHTDv0KQmIf/0ZX5KMUJBMT8ZQahz8Nz1uBghMsgyJ52uKnna7eU7l1ey8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741832268; c=relaxed/simple; bh=mqDFFPi4nAfJBJc1IyFPq2rVJ3Jp3UyQkGeG1puHpwQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Sxnc+7YB/IRiISVeIkEfYdj4ifnbsxBJ/leVrwXu4jbVd57X0ncnCDajgegA5/dBrRYjyDVCoQJWlo10Ej5f/6MjVqdlZdlJ45AM8GZH5VRZwERLuCqEPyziAzvDmGLIGFkJwFUc2R8X2ybS5jkUgkOxn7RSHlCY+sMBqd82suU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pqqO7bNS; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="pqqO7bNS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EB37AC4CEED; Thu, 13 Mar 2025 02:17:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1741832268; bh=mqDFFPi4nAfJBJc1IyFPq2rVJ3Jp3UyQkGeG1puHpwQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pqqO7bNSEN8xpK89054mf1LSka3f9Mshy8zkau1w2uCX1kKdjzR06rj2xbWkKvRGV xhAUESaIrQrYU0vZaoppHu3PGJi/8vn11B/k40VtWPV6Gg5+ylqCYhWgRkhfqmXyoh yRCHSVFC5lvRisAthYo2EUnZQ8bC5oG8xAmILErNKGZ7A0UUbXHlPGqpXDRRwZExwb 3G4jVyVIFgaLool/MJToouvSpP6gsIfbsmPHjWPq+tSzdRo+6aW/W0D3TMa73BGKXZ b9Rf5ok4dwNwDywU+IGHe+Csclj5IZk3Ba9xT/7mXqeuJzM5//RtOUoNh1alo2O8Y0 e9rWvbq9tLfmw== From: Danilo Krummrich To: gregkh@linuxfoundation.org, rafael@kernel.org, bhelgaas@google.com, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, rust-for-linux@vger.kernel.org, Danilo Krummrich Subject: [PATCH 4/4] rust: platform: fix unrestricted &mut platform::Device Date: Thu, 13 Mar 2025 03:13:34 +0100 Message-ID: <20250313021550.133041-5-dakr@kernel.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250313021550.133041-1-dakr@kernel.org> References: <20250313021550.133041-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 As by now, platform::Device is implemented as: #[derive(Clone)] pub struct Device(ARef); This may be convenient, but has the implication that drivers can call device methods that require a mutable reference concurrently at any point of time. Instead define platform::Device as pub struct Device( Opaque, PhantomData, ); and manually implement the AlwaysRefCounted trait. With this we can implement methods that should only be called from bus callbacks (such as probe()) for platform::Device. Consequently, we make this type accessible in bus callbacks only. Arbitrary references taken by the driver are still of type ARef and hence don't provide access to methods that are reserved for bus callbacks. Fixes: 683a63befc73 ("rust: platform: add basic platform device / driver abstractions") Signed-off-by: Danilo Krummrich Reviewed-by: Benno Lossin --- rust/kernel/platform.rs | 93 ++++++++++++++++++---------- samples/rust/rust_driver_platform.rs | 16 +++-- 2 files changed, 74 insertions(+), 35 deletions(-) diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 50e6b0421813..f7a055739143 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -5,16 +5,20 @@ //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) use crate::{ - bindings, container_of, device, driver, + bindings, device, driver, error::{to_result, Result}, of, prelude::*, str::CStr, - types::{ARef, ForeignOwnable, Opaque}, + types::{ForeignOwnable, Opaque}, ThisModule, }; -use core::ptr::addr_of_mut; +use core::{ + marker::PhantomData, + ops::Deref, + ptr::{addr_of_mut, NonNull}, +}; /// An adapter for the registration of platform drivers. pub struct Adapter(T); @@ -54,14 +58,14 @@ unsafe fn unregister(pdrv: &Opaque) { impl Adapter { extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int { - // SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`. - let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) }; - // SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the - // call above. - let mut pdev = unsafe { Device::from_dev(dev) }; + // SAFETY: The platform bus only ever calls the probe callback with a valid pointer to a + // `struct platform_device`. + // + // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. + let pdev = unsafe { &*pdev.cast::>() }; let info = ::id_info(pdev.as_ref()); - match T::probe(&mut pdev, info) { + match T::probe(pdev, info) { Ok(data) => { // Let the `struct platform_device` own a reference of the driver's private data. // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a @@ -120,7 +124,7 @@ macro_rules! module_platform_driver { /// # Example /// ///``` -/// # use kernel::{bindings, c_str, of, platform}; +/// # use kernel::{bindings, c_str, device::Core, of, platform}; /// /// struct MyDriver; /// @@ -138,7 +142,7 @@ macro_rules! module_platform_driver { /// const OF_ID_TABLE: Option> = Some(&OF_TABLE); /// /// fn probe( -/// _pdev: &mut platform::Device, +/// _pdev: &platform::Device, /// _id_info: Option<&Self::IdInfo>, /// ) -> Result>> { /// Err(ENODEV) @@ -160,41 +164,68 @@ pub trait Driver { /// /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. - fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result>>; + fn probe( + dev: &Device, + id_info: Option<&Self::IdInfo>, + ) -> Result>>; } /// The platform device representation. /// -/// A platform device is based on an always reference counted `device:Device` instance. Cloning a -/// platform device, hence, also increments the base device' reference count. +/// This structure represents the Rust abstraction for a C `struct platform_device`. The +/// implementation abstracts the usage of an already existing C `struct platform_device` within Rust +/// code that we get passed from the C side. /// /// # Invariants /// -/// `Device` holds a valid reference of `ARef` whose underlying `struct device` is a -/// member of a `struct platform_device`. -#[derive(Clone)] -pub struct Device(ARef); +/// A [`Device`] instance represents a valid `struct platform_device` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct Device( + Opaque, + PhantomData, +); impl Device { - /// Convert a raw kernel device into a `Device` - /// - /// # Safety - /// - /// `dev` must be an `Aref` whose underlying `bindings::device` is a member of a - /// `bindings::platform_device`. - unsafe fn from_dev(dev: ARef) -> Self { - Self(dev) + fn as_raw(&self) -> *mut bindings::platform_device { + self.0.get() } +} - fn as_raw(&self) -> *mut bindings::platform_device { - // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device` - // embedded in `struct platform_device`. - unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut() +impl Deref for Device { + type Target = Device; + + fn deref(&self) -> &Self::Target { + let ptr: *const Self = self; + + // CAST: `Device` is a transparent wrapper of `Opaque`. + let ptr = ptr.cast::(); + + // SAFETY: `ptr` was derived from `&self`. + unsafe { &*ptr } + } +} + +// SAFETY: Instances of `Device` are always reference-counted. +unsafe impl crate::types::AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. + unsafe { bindings::get_device(self.as_ref().as_raw()) }; + } + + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::platform_device_put(obj.cast().as_ptr()) } } } impl AsRef for Device { fn as_ref(&self) -> &device::Device { - &self.0 + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid + // `struct platform_device`. + let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) }; + + // SAFETY: `dev` points to a valid `struct device`. + unsafe { device::Device::as_ref(dev) } } } diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 8120609e2940..0fb4c6033d60 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -2,10 +2,10 @@ //! Rust Platform driver sample. -use kernel::{c_str, of, platform, prelude::*}; +use kernel::{c_str, device::Core, of, platform, prelude::*, types::ARef}; struct SampleDriver { - pdev: platform::Device, + pdev: ARef, } struct Info(u32); @@ -21,14 +21,22 @@ impl platform::Driver for SampleDriver { type IdInfo = Info; const OF_ID_TABLE: Option> = Some(&OF_TABLE); - fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result>> { + fn probe( + pdev: &platform::Device, + info: Option<&Self::IdInfo>, + ) -> Result>> { dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n"); if let Some(info) = info { dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0); } - let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?; + let drvdata = KBox::new( + Self { + pdev: (&**pdev).into(), + }, + GFP_KERNEL, + )?; Ok(drvdata.into()) }