From f720efda2db5e609b32100c25d9cf383f082d945 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Sun, 13 Apr 2025 19:37:03 +0200 Subject: rust: devres: require a bound device Require the Bound device context to be able to a new Devres container. This ensures that we can't register devres callbacks for unbound devices. Link: https://lore.kernel.org/r/20250413173758.12068-9-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index ddb1ce4a78d9..1e58f5d22044 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -8,7 +8,7 @@ use crate::{ alloc::Flags, bindings, - device::Device, + device::{Bound, Device}, error::{Error, Result}, ffi::c_void, prelude::*, @@ -45,7 +45,7 @@ struct DevresInner { /// # Example /// /// ```no_run -/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::{Io, IoRaw}}; +/// # use kernel::{bindings, c_str, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}}; /// # use core::ops::Deref; /// /// // See also [`pci::Bar`] for a real example. @@ -83,13 +83,10 @@ struct DevresInner { /// unsafe { Io::from_raw(&self.0) } /// } /// } -/// # fn no_run() -> Result<(), Error> { -/// # // SAFETY: Invalid usage; just for the example to get an `ARef` instance. -/// # let dev = unsafe { Device::get_device(core::ptr::null_mut()) }; -/// +/// # fn no_run(dev: &Device) -> Result<(), Error> { /// // SAFETY: Invalid usage for example purposes. /// let iomem = unsafe { IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD)? }; -/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?; +/// let devres = Devres::new(dev, iomem, GFP_KERNEL)?; /// /// let res = devres.try_access().ok_or(ENXIO)?; /// res.write8(0x42, 0x0); @@ -99,7 +96,7 @@ struct DevresInner { pub struct Devres(Arc>); impl DevresInner { - fn new(dev: &Device, data: T, flags: Flags) -> Result>> { + fn new(dev: &Device, data: T, flags: Flags) -> Result>> { let inner = Arc::pin_init( pin_init!( DevresInner { dev: dev.into(), @@ -171,7 +168,7 @@ impl DevresInner { impl Devres { /// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the /// returned `Devres` instance' `data` will be revoked once the device is detached. - pub fn new(dev: &Device, data: T, flags: Flags) -> Result { + pub fn new(dev: &Device, data: T, flags: Flags) -> Result { let inner = DevresInner::new(dev, data, flags)?; Ok(Devres(inner)) @@ -179,7 +176,7 @@ impl Devres { /// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data` /// is owned by devres and will be revoked / dropped, once the device is detached. - pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result { + pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result { let _ = DevresInner::new(dev, data, flags)?; Ok(()) -- cgit From f301cb978c068faa8fcd630be2cb317a2d0ec063 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 28 Apr 2025 16:00:28 +0200 Subject: rust: devres: implement Devres::access() Implement a direct accessor for the data stored within the Devres for cases where we can prove that we own a reference to a Device (i.e. a bound device) of the same device that was used to create the corresponding Devres container. Usually, when accessing the data stored within a Devres container, it is not clear whether the data has been revoked already due to the device being unbound and, hence, we have to try whether the access is possible and subsequently keep holding the RCU read lock for the duration of the access. However, when we can prove that we hold a reference to Device matching the device the Devres container has been created with, we can guarantee that the device is not unbound for the duration of the lifetime of the Device reference and, hence, it is not possible for the data within the Devres container to be revoked. Therefore, in this case, we can bypass the atomic check and the RCU read lock, which is a great optimization and simplification for drivers. Reviewed-by: Christian Schrefl Reviewed-by: Alexandre Courbot Acked-by: Boqun Feng Reviewed-by: Joel Fernandes Link: https://lore.kernel.org/r/20250428140137.468709-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 1e58f5d22044..b67247db92c7 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -181,6 +181,44 @@ impl Devres { Ok(()) } + + /// Obtain `&'a T`, bypassing the [`Revocable`]. + /// + /// This method allows to directly obtain a `&'a T`, bypassing the [`Revocable`], by presenting + /// a `&'a Device` of the same [`Device`] this [`Devres`] instance has been created with. + /// + /// # Errors + /// + /// An error is returned if `dev` does not match the same [`Device`] this [`Devres`] instance + /// has been created with. + /// + /// # Example + /// + /// ```no_run + /// # use kernel::{device::Core, devres::Devres, pci}; + /// + /// fn from_core(dev: &pci::Device, devres: Devres>) -> Result { + /// let bar = devres.access(dev.as_ref())?; + /// + /// let _ = bar.read32(0x0); + /// + /// // might_sleep() + /// + /// bar.write32(0x42, 0x0); + /// + /// Ok(()) + /// } + /// ``` + pub fn access<'a>(&'a self, dev: &'a Device) -> Result<&'a T> { + if self.0.dev.as_raw() != dev.as_raw() { + return Err(EINVAL); + } + + // SAFETY: `dev` being the same device as the device this `Devres` has been created for + // proves that `self.0.data` hasn't been revoked and is guaranteed to not be revoked as + // long as `dev` lives; `dev` lives at least as long as `self`. + Ok(unsafe { self.deref().access() }) + } } impl Deref for Devres { -- cgit From 42055939a3a4cac8afbebff85a29571c2bd3238c Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 11 May 2025 20:25:33 +0200 Subject: rust: devres: fix doctest build under `!CONFIG_PCI` The doctest requires `CONFIG_PCI`: error[E0432]: unresolved import `kernel::pci` --> rust/doctests_kernel_generated.rs:2689:44 | 2689 | use kernel::{device::Core, devres::Devres, pci}; | ^^^ no `pci` in the root | note: found an item that was configured out --> rust/kernel/lib.rs:96:9 note: the item is gated here --> rust/kernel/lib.rs:95:1 Thus conditionally compile it (which still checks the syntax). Fixes: f301cb978c06 ("rust: devres: implement Devres::access()") Signed-off-by: Miguel Ojeda Link: https://lore.kernel.org/r/20250511182533.1016163-1-ojeda@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index b67247db92c7..0f79a2ec9474 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -195,6 +195,7 @@ impl Devres { /// # Example /// /// ```no_run + /// # #![cfg(CONFIG_PCI)] /// # use kernel::{device::Core, devres::Devres, pci}; /// /// fn from_core(dev: &pci::Device, devres: Devres>) -> Result { -- cgit