Re: [PATCH v2 1/3] rust: add runtime PM support
Onur Özkan <[email protected]> Mon, 27 Jul 2026 17:56:20 +0300
| Newsgroups | dev.linux.lists.driver-core,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
On Tue, 21 Jul 2026 17:34:02 +0200 Beata Michalska <[email protected]> wrote: > Add a Rust abstraction for Linux runtime PM, allowing Rust drivers to > register runtime PM callbacks and use scoped runtime PM requests while > retaining the behavior and guarantees of the underlying C PM core. > > Runtime PM is managed through a registration object tied to the device > lifetime. The registration stores the callback payload and 'ensures' that > the registration, its `PMContext`, and the payload do not outlive the > device. All that while `PMContext` holds a lifetime-annotated device reference > so that runtime PM requests are made only while the device remains bound. > > Drivers define runtime PM callbacks through the `PMOps` trait. The > generated `dev_pm_ops` callbacks take the stored payload, pass ownership > to the corresponding Rust callback, and store the returned payload for > the next invocation. Each callback must return a valid payload, > regardless of whether the power transition succeeds or not. > > `PMContext` provides scoped helpers for common runtime PM operations: > > - `ResumeScope` resumes the device for the lifetime of the scope. > - `AwakeScope` resumes the device and holds a runtime PM usage reference. > - `RetainScope` increments the runtime PM usage count without resuming the > device. > > Each scope performs the corresponding inverse operation when dropped, > according to the selected driver-defined profile. > > The abstraction does not change the semantics of the C runtime PM API. > Asynchronous requests may only queue work, non-waiting requests may fail > instead of sleeping, and callbacks remain subject to the existing driver > core rules. > > The current implementation requires the PM registration and `PMOps` > implementation to use the same type because callback dispatch interprets > the stored registration data using that relationship. This requirement is > not yet fully enforced by the type system. > > Signed-off-by: Beata Michalska <[email protected]> > --- > drivers/base/base.h | 3 + > rust/bindings/bindings_helper.h | 1 + > rust/helpers/helpers.c | 1 + > rust/helpers/pm_runtime.c | 44 ++ > rust/kernel/lib.rs | 1 + > rust/kernel/pm.rs | 1020 +++++++++++++++++++++++++++++++ > 6 files changed, 1070 insertions(+) > create mode 100644 rust/helpers/pm_runtime.c > create mode 100644 rust/kernel/pm.rs > > diff --git a/drivers/base/base.h b/drivers/base/base.h > index a19f4cda2c83..c49303e4a86a 100644 > --- a/drivers/base/base.h > +++ b/drivers/base/base.h > @@ -119,6 +119,9 @@ struct device_private { > const struct device_driver *async_driver; > char *deferred_probe_reason; > struct device *device; > +#ifdef CONFIG_RUST > + void *rust_private; > +#endif > u8 dead:1; > }; > #define to_device_private_parent(obj) \ > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > index 1124785e210b..78a9153c6a01 100644 > --- a/rust/bindings/bindings_helper.h > +++ b/rust/bindings/bindings_helper.h > @@ -77,6 +77,7 @@ > #include <linux/pid_namespace.h> > #include <linux/platform_device.h> > #include <linux/pm_opp.h> > +#include <linux/pm_runtime.h> > #include <linux/poll.h> > #include <linux/property.h> > #include <linux/pwm.h> > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c > index 4488a87223b9..14d93a5d8b1f 100644 > --- a/rust/helpers/helpers.c > +++ b/rust/helpers/helpers.c > @@ -76,6 +76,7 @@ > #include "pci.c" > #include "pid_namespace.c" > #include "platform.c" > +#include "pm_runtime.c" > #include "poll.c" > #include "processor.c" > #include "property.c" > diff --git a/rust/helpers/pm_runtime.c b/rust/helpers/pm_runtime.c > new file mode 100644 > index 000000000000..d0d71fcb0097 > --- /dev/null > +++ b/rust/helpers/pm_runtime.c > @@ -0,0 +1,44 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include <linux/pm_runtime.h> > + > +__rust_helper void rust_helper_pm_runtime_get_noresume(struct device *dev) > +{ > + pm_runtime_get_noresume(dev); > +} > + > +__rust_helper void rust_helper_pm_runtime_put_noidle(struct device *dev) > +{ > + pm_runtime_put_noidle(dev); > +} > + > +__rust_helper void rust_helper_pm_runtime_mark_last_busy(struct device *dev) > +{ > + pm_runtime_mark_last_busy(dev) ; > +} > + > +__rust_helper bool rust_helper_pm_runtime_active(struct device *dev) > +{ > + return pm_runtime_active(dev); > +} > + > +__rust_helper bool rust_helper_pm_runtime_suspended(struct device *dev) > +{ > + return pm_runtime_suspended(dev); > +} > + > +__rust_helper void rust_helper_pm_suspend_ignore_children(struct device *dev, > + bool enable) > +{ > + pm_suspend_ignore_children(dev, enable); > +} > + > +__rust_helper int rust_helper_pm_runtime_set_active(struct device *dev) > +{ > + return pm_runtime_set_active(dev); > +} > + > +__rust_helper int rust_helper_pm_runtime_set_suspended(struct device *dev) > +{ > + return pm_runtime_set_suspended(dev); > +} > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index b72b2fbe046d..7e51e497e2f0 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -105,6 +105,7 @@ > pub mod pci; > pub mod pid_namespace; > pub mod platform; > +pub mod pm; > pub mod prelude; > pub mod print; > pub mod processor; > diff --git a/rust/kernel/pm.rs b/rust/kernel/pm.rs > new file mode 100644 > index 000000000000..b6a884fbe1ba > --- /dev/null > +++ b/rust/kernel/pm.rs > @@ -0,0 +1,1020 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Rust Runtime Power Management abstraction. > +//! > +//! C header: [`include/linux/pm_runtime.h`](srctree/include/linux/pm_runtime.h) > + > +use crate::{ > + bindings, > + device::{ > + self, > + AsBusDevice, // > + }, > + error::{ > + to_result, > + VTABLE_DEFAULT_ERROR, // > + }, > + macros::paste, > + prelude::*, > + sync::atomic::{ > + ordering, > + Atomic, // > + }, > + sync::Arc, > + types::ForeignOwnable, // > +}; > + > +use core::{ > + cell::UnsafeCell, > + marker::PhantomData > +}; > + > +/// Runtime Power Management modes that determine how a particular PM > +/// transition is to be carried out. > +/// Corresponds to C Runtime PM flag argument bits: > +/// - `RPM_ASYNC` > +/// - `RPM_NOWAIT` > +/// - `RPM_GET_PUT` > +/// - `RPM_AUTO` > +#[derive(Clone, Copy, PartialEq, Eq, Debug)] > +struct Mode(u32); > + > +impl Mode { > + /// Synchronous PM operations - default. > + const SYNC: Mode = Mode(0); > + /// Allow asynchronous PM operations. > + const ASYNC: Mode = Mode(bindings::RPM_ASYNC); > + /// Do not wait for any pending requests to finish. > + const NOWAIT: Mode = Mode(bindings::RPM_NOWAIT); > + /// Acquire a runtime-PM usage reference. > + const ACQUIRE: Mode = Mode(bindings::RPM_GET_PUT); > + /// Use autosuspend. > + const AUTO: Mode = Mode(bindings::RPM_AUTO); > + /// Additional mode for devices supporting idle states. > + /// No counterpart. > + const IDLE: Mode = Mode(1 << 16); > + > + const fn join(self, other: Self) -> Self { > + Self(self.0 | other.0) > + } > + > + fn includes(self, other: Self) -> bool { > + (self.0 & other.0) != 0 > + } > +} > + > +impl core::ops::BitOr for Mode { > + type Output = Self; > + fn bitor(self, rhs: Self) -> Self::Output { > + Self(self.0 | rhs.0) > + } > +} > + > +impl core::ops::BitAnd for Mode { > + type Output = Self; > + fn bitand(self, rhs: Self) -> Self::Output { > + Self(self.0 & rhs.0) > + } > +} > + > +impl core::ops::Not for Mode { > + type Output = Self; > + fn not(self) -> Self::Output { > + Self(!self.0) > + } > +} > + > +impl From<Mode> for core::ffi::c_int { > + #[inline] > + fn from(mode: Mode) -> core::ffi::c_int { > + mode.0 as core::ffi::c_int > + } > +} > + > +/// Utility macro for combining multiple request modes > +macro_rules! mode { > + ($mode:expr $(, $args:expr)+ $(,)?) => {{ > + let mut new_mode = $mode; > + $( new_mode = new_mode.join($args); )+ > + new_mode > + }}; > +} > + > +/// Device's runtime power management status > +#[repr(i32)] > +pub enum RuntimePMState { > + /// Runtime PM has not been initialized for this device yet. > + UNKNOWN = bindings::rpm_status_RPM_INVALID, > + /// The device is expected to be runtime active and in it's normal operating state > + RESUMED = bindings::rpm_status_RPM_ACTIVE, > + /// The device is expected to be suspended, unavailable for normal operations > + SUSPENDED = bindings::rpm_status_RPM_SUSPENDED, > +} > + > +/// Runtime power transition scope. > +pub struct Scope<'a, Tag> { > + dev: &'a device::Device<device::Bound>, > + mode: Mode, > + _tag: PhantomData<Tag>, > +} > + > +/// Device resumed without incrementing the device's usage count > +pub struct Resume; > +/// Device resumed with the device's usage count being incremented > +pub struct Awake; > +/// Device with increased usage reference > +pub struct Retain; > + > +/// Resumes the device without acquiring the usage reference. > +/// Note: This does not guarantee the device will be kept active > +/// for the lifetime of the scope due to potential pending/incoming > +/// suspend requests. > +/// > +/// On drop: > +/// - If `Mode::IDLE`, calls `__pm_runtime_idle()`: > +/// triggers idle notification before attempting to suspend > +/// - If `Mode::AUTO`, marks last busy then calls `__pm_runtime_suspend()`. > +/// - Otherwise calls `__pm_runtime_suspend()`. > +/// > +/// The guard must be dropped from a context matching the requested transition > +/// mode: sync vs async. > +#[must_use = "dropping this guard issues the matching runtime PM release request"] > +pub struct ResumeScope<'a>(Scope<'a, Resume>); > + > +/// Acquires a runtime-PM usage reference and keeps the device powered. > +/// > +/// Requires `Mode::ACQUIRE`. Drop behavior matches `ResumeScope`. > +/// The guard must be dropped from a context matching the requested transition > +/// mode: sync vs async. > +#[must_use = "dropping this guard releases its runtime PM hold"] > +pub struct AwakeScope<'a>(Scope<'a, Awake>); > + > +/// Prevents the device from getting suspended by holding the usage reference > +/// count. > +/// > +/// On drop, calls `pm_runtime_put_noidle()`. > +#[must_use = "dropping this guard releases its runtime PM hold"] > +pub struct RetainScope<'a>(Scope<'a, Retain>); > + > +impl<'a> ResumeScope<'a> { > + fn new(dev: &'a device::Device<device::Bound>, mode: Mode) -> Result<Self> { > + if mode.includes(Mode::ACQUIRE) { > + // Mode::ACQUIRE is intended to be used with Awake scope > + // Avoid mixing the modes. > + return Err(EINVAL); > + } > + > + // Mode::IDLE is internal so strip it of before passing further > + Request::resume(dev, mode & !Mode::IDLE).map(|()| { > + Self(Scope::<Resume> { > + dev, > + mode, > + _tag: PhantomData, > + }) > + }) > + } > + > + fn release_inner(&self) -> Result { > + let scope_mode = self.0.mode & !Mode::IDLE; > + > + match self.0.mode { > + mode if mode.includes(Mode::IDLE) => { > + Request::idle(self.0.dev, scope_mode & (Mode::ASYNC | Mode::NOWAIT)) > + } > + mode if mode.includes(Mode::AUTO) => { > + Request::mark_last_busy(self.0.dev); > + Request::suspend(self.0.dev, scope_mode) > + } > + _ => Request::suspend(self.0.dev, scope_mode), > + } > + } > + > + /// Explicitly release the scope > + /// This should be used in favor of regular drop > + /// when error handling is required. > + pub fn release(self) -> Result { > + let result = self.release_inner(); > + core::mem::forget(self); > + result > + } > +} > + > +impl<'a> Drop for ResumeScope<'a> { > + fn drop(&mut self) { > + let _ = self.release_inner(); > + } > +} > + > +impl<'a> AwakeScope<'a> { > + fn new(dev: &'a device::Device<device::Bound>, mode: Mode) -> Result<Self> { > + if !mode.includes(Mode::ACQUIRE) { > + return Err(EINVAL); > + } > + // Mode::IDLE is internal so strip it of before passing further > + Request::resume(dev, mode & !Mode::IDLE) > + .inspect_err(|_| Request::put_noidle(dev)) > + .map(|()| { > + Self(Scope::<Awake> { > + dev, > + mode, > + _tag: PhantomData, > + }) > + }) > + } > + > + fn release_inner(&self) -> Result { > + let scope_mode = self.0.mode & !Mode::IDLE; > + match self.0.mode { > + mode if mode.includes(Mode::IDLE) => Request::idle(self.0.dev, scope_mode), > + mode if mode.includes(Mode::AUTO) => { > + Request::mark_last_busy(self.0.dev); > + Request::suspend(self.0.dev, scope_mode) > + } > + _ => Request::idle(self.0.dev, scope_mode), > + } > + } > + > + /// Explicitly release the scope > + /// This should be used in favor of regular drop > + /// when error handling is required. > + pub fn release(self) -> Result { > + let result = self.release_inner(); > + core::mem::forget(self); > + result > + } > +} > + > +impl<'a> Drop for AwakeScope<'a> { > + fn drop(&mut self) { > + let _ = self.release_inner(); > + } > +} > + > +impl<'a> RetainScope<'a> { > + fn new(dev: &'a device::Device<device::Bound>) -> Result<Self> { > + Request::get_noresume(dev); > + Ok(Self(Scope::<Retain> { > + dev, > + mode: Mode(0), > + _tag: PhantomData, > + })) > + } > + > + fn try_new(dev: &'a device::Device<device::Bound>) -> Result<Self> { > + Request::get_if_active(dev)?; > + Ok(Self(Scope::<Retain> { > + dev, > + mode: Mode(0), > + _tag: PhantomData, > + })) > + } > + > + fn release_inner(&self) { > + Request::put_noidle(self.0.dev); > + } > + > + /// Explicitly release the scope > + /// This should be used in favor of regular drop > + /// when error handling is required. > + pub fn release(self) -> Result { > + self.release_inner(); > + core::mem::forget(self); > + Ok(()) > + } > +} > + > +impl<'a> Drop for RetainScope<'a> { > + fn drop(&mut self) { > + self.release_inner(); > + } > +} > + > +/// Runtime PM helpers - wrappers around C runtime PM interface. > +/// All methods require a reference to a bound device. > +struct Request; > + > +#[cfg(CONFIG_PM)] > +impl Request { > + #[inline] > + fn active(dev: &device::Device<device::Bound>) -> bool { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::pm_runtime_active(dev.as_raw()) } > + } > + > + #[inline] > + fn suspended(dev: &device::Device<device::Bound>) -> bool { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::pm_runtime_suspended(dev.as_raw()) } > + } > + > + #[inline] > + fn resume(dev: &device::Device<device::Bound>, mode: Mode) -> Result { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + to_result(unsafe { bindings::__pm_runtime_resume(dev.as_raw(), mode.into()) }) > + } > + > + #[inline] > + fn idle(dev: &device::Device<device::Bound>, mode: Mode) -> Result { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + to_result(unsafe { bindings::__pm_runtime_idle(dev.as_raw(), mode.into()) }) > + } > + > + #[inline] > + fn mark_last_busy(dev: &device::Device<device::Bound>) { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { > + bindings::pm_runtime_mark_last_busy(dev.as_raw()); > + } > + } > + > + #[inline] > + fn suspend(dev: &device::Device<device::Bound>, mode: Mode) -> Result { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + to_result(unsafe { bindings::__pm_runtime_suspend(dev.as_raw(), mode.into()) }) > + } > + > + #[inline] > + fn get_if_active(dev: &device::Device<device::Bound>) -> Result { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + match unsafe { bindings::pm_runtime_get_if_active(dev.as_raw()) } { > + ret if ret < 0 => Err(Error::from_errno(ret)), > + 0 => Err(EAGAIN), > + _ => Ok(()), > + } > + } > + > + #[inline] > + fn runtime_enable(dev: &device::Device<device::Bound>) { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::pm_runtime_enable(dev.as_raw()) } > + } > + > + #[inline] > + fn runtime_disable(dev: &device::Device<device::Bound>) { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::__pm_runtime_disable(dev.as_raw(), true) }; > + } What happens when you call these twice i.e. runtime_enable() when it's already enabled or runtime_disable() when it's already disabled? > +} > + > +#[cfg(not(CONFIG_PM))] > +impl Request { > + #[inline] > + fn active(dev: &device::Device<device::Bound>) -> bool { > + true > + } > + > + #[inline] > + fn suspended(dev: &device::Device<device::Bound>) -> bool { > + false > + } > + > + #[inline] > + fn resume(_dev: &device::Device<device::Bound>, _mode: Mode) -> Result { > + Ok(()) > + } > + > + #[inline] > + fn idle(_dev: &device::Device<device::Bound>, _mode: Mode) -> Result { > + Err(ENOSYS) > + } > + > + #[inline] > + fn mark_last_busy(_dev: &device::Device<device::Bound>) {} > + > + #[inline] > + fn suspend(_dev: &device::Device<device::Bound>, _mode: Mode) -> Result { > + Err(ENOSYS) > + } > + > + #[inline] > + fn get_if_active(dev: &device::Device<device::Bound>) -> Result { > + Err(EINVAL) > + } > + > + #[inline] > + fn runtime_enable(_dev: &device::Device<device::Bound>) {} > + > + #[inline] > + fn runtime_disable(dev: &device::Device<device::Bound>) {} > +} > + > +impl Request { > + #[inline] > + fn get_noresume(dev: &device::Device<device::Bound>) { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::pm_runtime_get_noresume(dev.as_raw()) }; > + } > + > + #[inline] > + fn put_noidle(dev: &device::Device<device::Bound>) { > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::pm_runtime_put_noidle(dev.as_raw()) }; > + } > + > + #[allow(unused)] > + #[inline] > + fn mark_active(dev: &device::Device<device::Bound>) -> Result { > + to_result( > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::pm_runtime_set_active(dev.as_raw()) }, > + ) > + } > + > + #[allow(unused)] > + #[inline] > + fn mark_suspended(dev: &device::Device<device::Bound>) -> Result { > + to_result( > + // SAFETY: `dev.as_raw()` must provide a valid pointer to > + // `struct device` for the duration of the call. > + // The `Device<Bound>` reference provides that guarantee. > + unsafe { bindings::pm_runtime_set_suspended(dev.as_raw()) }, > + ) > + } > +} > + > +/// Common runtime PM callback entry point > +/// > +/// The generated extern "C" callbacks call into this helper with the raw > +/// `struct device *` provided by the PM core. It rebuilds the Rust device > +/// reference, retrieves the device's PM registration data and performs > +/// handoff to corresponding driver callback. > +fn runtime_pm_callback<T, F>(dev: *mut bindings::device, cb: F) -> Result > +where > + T: PMOps, > + F: FnOnce( > + &<T as PMOps>::DeviceType, > + Option<<T as PMOps>::RuntimePayloadType>, > + ) -> Result< > + Option<<T as PMOps>::RuntimePayloadType>, > + (Option<<T as PMOps>::RuntimePayloadType>, Error), > + >, > +{ > + let dev: &device::Device<device::Bound> = > + // SAFETY: `dev` is provided by the PM core and remains > + // valid for the duration of the callback. > + unsafe { device::Device::from_raw(dev) }; > + > + // SAFETY: `dev` is provided by the PM core and remains > + // valid for the duration of the callback. > + let ptr = unsafe { (*(*dev.as_raw()).p).rust_private }; > + > + if ptr.is_null() { > + return Err(ENODEV); > + } > + > + // SAFETY: The runtime PM callback can only be triggered for bound device > + // and once the runtime PM is enabled. > + // `rust_private` is guaranteed to be valid and points to > + // associated RegistrationData<T> type object at least for the duration > + // of this call. > + let payload: Pin<&RegistrationData<'_, T>> = > + unsafe { <Pin<KBox<RegistrationData<'_, T>>> as ForeignOwnable>::borrow(ptr) }; > + > + let pm_dev: &T::DeviceType = > + // SAFETY: The generated `dev_pm_ops` for `T` is installed on devices whose > + // bus-specific type is `T::DeviceType`. Therefore the base `Device<Bound>` > + // passed by the PM core is embedded in a valid `T::DeviceType`; the > + // `AsBusDevice` implementation supplies the correct offset for this cast. > + unsafe { T::DeviceType::from_device(dev) }; > + > + payload.data.transition(|payload| cb(pm_dev, payload)) > +} > + > +/// Defines generated C runtime PM callback. > +/// > +/// This macro generates the corresponding `unsafe extern "C"` function for > +/// requested PM operation and forwards it to [`runtime_pm_callback`]. > +/// > +/// # Safety > +/// > +/// The generated function may only be installed in a `dev_pm_ops` table for a > +/// device whose PM registration data was created by `Registration<T>`. In other > +/// words, the type parameter `T` used for the callback table must match the > +/// type parameter `T` used for the stored `RegistrationData<T>`. > +/// > +/// The PM registration must keep the stored registration data alive and pinned > +/// until runtime PM is disabled and all in-flight PM callbacks have completed. > +macro_rules! define_pm_callback { > + (@parse_desc $name:ident) => { define_pm_callback!(@default $name); }; > + (@default $name:ident) => { > + paste!( > + /// Generated runtime PM callback. > + /// > + /// # Safety > + /// > + /// `dev` must be a valid `struct device *` supplied by the PM core for a device > + /// whose runtime PM callbacks and PM registration data were both created > + /// for `T`. > + unsafe extern "C" fn [<$name _callback>]<'a, T:PMOps>( > + dev: *mut bindings::device > + ) -> core::ffi::c_int > + where > + <T as PMOps>::DeviceType: 'a > + { > + runtime_pm_callback::<T, _>(dev,T::$name).map(|_| 0).unwrap_or_else(|e| e.to_errno()) > + } > + ); > + > + }; > +} > + > +/// SAFETY: > +/// bindings::dev_pm_ops is #[repr(C)], implements Default > +/// and the struct itself is all nullable function pointers. > +/// There is no padding and all zero bit-pattern is valid > +/// > +const PMOPS_NONE: bindings::dev_pm_ops = > + unsafe { core::mem::MaybeUninit::<bindings::dev_pm_ops>::zeroed().assume_init() }; > + > +/// Runtime PM callbacks implemented by a driver. > +/// > +/// Defines the [`PMOps`] trait and its corresponding [`bindings::dev_pm_ops`]. > +/// > +/// Each generated C callback recovers the Rust bus device from the raw > +/// `struct device *`, borrows the registered runtime PM payload, and delegates > +/// to the matching [`PMOps`] trait method. > +/// > +/// # Safety > +/// > +/// `PMContext::<T>::PM_OPS` must only be installed for devices whose concrete > +/// bus type is `T::DeviceType`, and whose runtime PM registration data was > +/// installed by [`Registration::new`] for the same `T`. > +/// > +macro_rules! define_pm_ops { > + ($($name:ident $( : $desc:tt )? ),+ $(,)?) => { > + $( define_pm_callback!( @parse_desc $name $( $desc )?); )+ > + define_pm_ops!(@common $( $name ), +); > + }; > + > + (@common $( $name:ident),+ ) => { > + /// Runtime PM callbacks implemented by a driver > + #[vtable] > + pub trait PMOps: Sized > + { > + /// Type of a bus device > + type DeviceType: AsBusDevice<device::Bound>; > + /// Type of the data associated with a PM transitions: > + type RuntimePayloadType: Send; > + > + $( > + #[allow(missing_docs)] > + // Callback-specific docs are provided by the generated `dev_pm_ops` > + // contract on `PMOps`. > + > + fn $name<'a>( > + _dev: &'a Self::DeviceType, > + _payload: Option<Self::RuntimePayloadType>, > + ) -> Result<Option<Self::RuntimePayloadType>, (Option<Self::RuntimePayloadType>, Error)> { > + build_error!(VTABLE_DEFAULT_ERROR) > + } > + )+ > + } > + paste!( > + impl<'a, T:PMOps> PMContext<'a, T> { > + /// Driver-provided runtime PM operations. > + /// > + /// A driver implements this trait to handle runtime PM > + /// transitions for its device type. > + /// > + /// Each callback receives the device and the current payload. > + /// On success, it returns the payload to keep for the next > + /// transition. On failure, it returns the payload together > + /// with the error so the previous, or otherwise sane state > + /// can be preserved. > + pub const PM_OPS: bindings::dev_pm_ops = bindings::dev_pm_ops { > + $( [<$name>]: if T::[<HAS_ $name:upper>] { > + Some([<$name _callback>]::<T>) > + } else { > + None > + }, )+ > + ..PMOPS_NONE > + }; > + } > + ); > + } > +} > + > +/// RAII guard for ongoing runtime PM payload transition. > +/// > +/// For most of the callbacks this guard is not necessairly needed as > +/// the callbacks themselves are being serialized by the runtime PM C code. > +/// Still, some like runtime_idle are exempt ftom that. > +#[allow(unused)] > +struct PayloadGuard<'a> { > + busy: &'a Atomic<bool>, > +} > + > +impl Drop for PayloadGuard<'_> { > + fn drop(&mut self) { > + self.busy.store(false, ordering::Release); > + } > +} > + > +struct PMPayload<P> { > + in_flight: Atomic<bool>, > + inner: UnsafeCell<Option<P>>, > +} > + > +impl<P> PMPayload<P> { > + /// Attempts to acquire exclusive access to the runtime PM payload. > + /// > + /// Returns `EBUSY` if another runtime PM callback is already transitioning the > + /// payload. > + fn acquire(&self) -> Result<PayloadGuard<'_>> { > + self.in_flight > + .cmpxchg(false, true, ordering::Acquire) > + .map_err(|_| EBUSY)?; > + Ok(PayloadGuard { > + busy: &self.in_flight, > + }) > + } > + > + /// Runs a runtime PM transition with exclusive access to the stored payload. > + /// > + /// This method acquires the in-flight guard, temporarily takes the payload out > + /// of storage The closure must return the payload that should be stored > + /// for the next transition. > + /// > + /// On success, the returned payload replaces the previous payload. On failure, > + /// the closure returns the payload together with the error, and that payload is > + /// restored before the error is propagated. > + /// > + /// Returns `EBUSY` if another runtime PM transition is already in progress. > + fn transition( > + &self, > + f: impl FnOnce(Option<P>) -> Result<Option<P>, (Option<P>, Error)>, > + ) -> Result { > + let _guard = self.acquire()?; > + // SAFETY: Holding `_guard` means this callback successfully changed > + // `in_flight` from false to true. No other caller can hold a `PayloadGuard` > + // until `_guard` is dropped, so this function has exclusive access to `inner`. > + let slot = unsafe { &mut *self.inner.get() }; > + > + let payload = slot.take(); > + > + match f(payload) { > + Ok(new_payload) => { > + *slot = new_payload; > + Ok(()) > + } > + Err((old_payload, err)) => { > + *slot = old_payload; > + Err(err) > + } > + } > + } > +} > + > +// SAFETY: Although PMPayload's `inner` is an `UnsafeCell`, it is only accessed > +// after `in_flight` has been acquired. The atomic flag serializes all mutable > +// access to the payload, and `PayloadGuard` clears the flag when the access ends. > +unsafe impl<P: Send> Sync for PMPayload<P> {} > + > +struct PMContextInner<'a, T: PMOps> { > + dev: &'a device::Device<device::Bound>, > + /// Optional driver-selected runtime PM request PMProfiles. > + /// > + /// Set of runtime PM predefined PMProfiles that can be used by the driver > + /// when requesting a PM transition. This might be useful when a driver > + /// has several different PM usage patterns. > + /// See [PMProfile] for more details. > + profiles: KVec<PMProfile>, > + /// Set of PM config options applied for associated device. > + configs: KVec<PMConfig>, > + _marker: PhantomData<T>, > +} > + > +/// Runtime PM context tied to a device. > +pub struct PMContext<'a, T: PMOps> { > + // Preferably, PMContext could be shared via borrowed reference over > + // a pm Registraion's lifetime but that bares complications on its own > + // when the context needs to be shared across different Registration types. > + inner: Arc<PMContextInner<'a, T>>, > +} > + > +impl<'a, T: PMOps> PMContext<'a, T> { > + /// Enable runtime PM > + pub fn enable(&self, state: RuntimePMState) -> Result { > + Self::apply_config(self.inner.dev, &self.inner.configs); > + match state { > + RuntimePMState::RESUMED => Request::mark_active(self.inner.dev), > + RuntimePMState::SUSPENDED => Request::mark_suspended(self.inner.dev), > + _ => Err(EINVAL), > + }?; > + Request::runtime_enable(self.inner.dev); > + Ok(()) > + } > + /// Disable runtime PM > + pub fn disable(&self) -> Result { > + Self::apply_config(self.inner.dev, &[PMConfig::AutoSuspend(false)]); > + Request::runtime_disable(self.inner.dev); > + Ok(()) > + } > + > + /// Returns whether the runtime PM state is active. > + #[inline] > + pub fn active(&self) -> bool { > + Request::active(self.inner.dev) > + } > + > + /// Returns whether the runtime PM state is suspended. > + #[inline] > + pub fn suspended(&self) -> bool { > + Request::suspended(self.inner.dev) > + } > + > + /// Creates a `ResumeScope` for the given PMProfile. > + #[inline] > + pub fn resume(&self, profile: PMProfile) -> Result<ResumeScope<'a>> { > + ResumeScope::new(self.inner.dev, profile.0) > + } > + > + /// Creates an `AwakeScope` for the given PMProfile. > + /// Note that for ASYNC request this does not guarantee > + /// the device has been resumed at the time this funtion returns. > + #[inline] > + pub fn get(&self, profile: PMProfile) -> Result<AwakeScope<'a>> { > + AwakeScope::new(self.inner.dev, profile.0 | Mode::ACQUIRE) > + } > + > + /// Creates a `RetainScope` for this device. > + pub fn hold(&self) -> Result<RetainScope<'a>> { > + RetainScope::new(self.inner.dev) > + } > + > + /// Creates a `RetainScope` for an active device. > + pub fn try_hold_active(&self) -> Result<RetainScope<'a>> { > + RetainScope::try_new(self.inner.dev) > + } > + > + /// Runs a closure while holding a `ResumeScope`. > + pub fn with_resume<R>(&self, profile: PMProfile, f: impl FnOnce() -> Result<R>) -> Result<R> { > + if profile.0.includes(Mode::ASYNC) { > + return Err(EINVAL); > + } > + let _scope = self.resume(profile)?; > + f() > + } > + /// Runs a closure while holding an `AwakeScope`. > + pub fn with_get<R>(&self, profile: PMProfile, f: impl FnOnce() -> Result<R>) -> Result<R> { > + if profile.0.includes(Mode::ASYNC) { > + return Err(EINVAL); > + } > + let _scope = self.get(profile)?; > + f() > + } > + > + /// Runs a closure while holding a `RetainScope`. > + pub fn with_hold<R>(&self, f: impl FnOnce() -> Result<R>) -> Result<R> { > + let _scope = self.hold()?; > + f() > + } > + > + /// Applies runtime PM configuration options. > + /// > + /// Options are applied in the order provided. The currently supported > + /// options do not report per-option failures. > + fn apply_config(dev: &device::Device<device::Bound>, opts: &[PMConfig]) { > + #[cfg(not(CONFIG_PM))] > + let _ = opts; > + let _ = dev; > + #[cfg(CONFIG_PM)] > + for opt in opts { > + match opt { > + // SAFETY: `self.dev` is a valid `&ARef<Device>`, so the underlying `Device` is > + // guaranteed to be alive and `as_raw()` yields a valid pointer for the > + // duration of this call. > + PMConfig::IgnoreChildren(v) => unsafe { > + bindings::pm_suspend_ignore_children(dev.as_raw(), *v) > + }, > + // SAFETY: `self.dev` is a valid `&ARef<Device>`, so the underlying `Device` is > + // guaranteed to be alive and `as_raw()` yields a valid pointer for the > + // duration of this call. > + PMConfig::NoCallbacks => unsafe { bindings::pm_runtime_no_callbacks(dev.as_raw()) }, > + // SAFETY: `self.dev` is a valid `&ARef<Device>`, so the underlying `Device` is > + // guaranteed to be alive and `as_raw()` yields a valid pointer for the > + // duration of this call. > + PMConfig::IrqSafe => unsafe { bindings::pm_runtime_irq_safe(dev.as_raw()) }, > + // SAFETY: `self.dev` is a valid `&ARef<Device>`, so the underlying `Device` is > + // guaranteed to be alive and `as_raw()` yields a valid pointer for the > + // duration of this call. > + PMConfig::AutoSuspend(v) => unsafe { > + bindings::__pm_runtime_use_autosuspend(dev.as_raw(), *v); > + }, > + // SAFETY: `self.dev` is a valid `&ARef<Device>`, so the underlying `Device` is > + // guaranteed to be alive and `as_raw()` yields a valid pointer for the > + // duration of this call. > + PMConfig::AutoSuspendDelay(v) => unsafe { > + bindings::pm_runtime_set_autosuspend_delay(dev.as_raw(), *v as i32); > + bindings::__pm_runtime_use_autosuspend(dev.as_raw(), true); > + }, > + } > + } > + } > + > + /// Get a borrowed reference to PM profiles asociated wiht the PM context > + pub fn profiles(&self) -> &[PMProfile] { > + &self.inner.profiles > + } > + > + /// Get a borrowed reference to PM configs asociated wiht the PM context > + pub fn configs(&self) -> &[PMConfig] { > + &self.inner.configs > + } > +} > + > +// Preferably, PMContext could be shared via borrowed reference over > +// a pm Registraion's lifetime but that bares complications on its own > +// when the context needs to be shared across different Registration types. > +impl<T: PMOps> Clone for PMContext<'_, T> { > + fn clone(&self) -> Self { > + Self { > + inner: self.inner.clone(), > + } > + } > +} > + > +define_pm_ops!( > + // PM state change > + runtime_suspend, > + runtime_resume, > +); > + > +/// Runtime PM request PMProfile. > +pub struct PMProfile(Mode); > + > +impl PMProfile { > + /// Creates a PMProfile with default SYNC mode set. > + pub const fn new() -> Self { > + Self(Mode::SYNC) > + } > + /// /Enables async PM operations for this PMProfile. > + pub const fn r#async(self) -> Self { > + Self(mode!(self.0, Mode::ASYNC)) > + } > + /// Use autosuspend > + pub const fn auto(self) -> Self { > + Self(mode!(self.0, Mode::AUTO)) > + } > + /// Requests idle handling for this PMProfile. > + pub const fn idle(self) -> Self { > + Self(mode!(self.0, Mode::IDLE)) > + } > + /// Do not wait for concurrent requests to finish. > + pub const fn nowait(self) -> Self { > + Self(mode!(self.0, Mode::NOWAIT)) > + } > +} > + > +impl Default for PMProfile { > + fn default() -> Self { > + Self::new() > + } > +} > + > +/// Configuration knobs for runtime PM. > +pub enum PMConfig { > + /// Ignore child devices when suspending. > + IgnoreChildren(bool), > + /// Disable runtime PM callbacks. > + NoCallbacks, > + /// Mark device as IRQ-safe for runtime PM. > + IrqSafe, > + /// Enable or disable autosuspend. > + AutoSuspend(bool), > + /// Set autosuspend delay (milliseconds). > + AutoSuspendDelay(u32), > +} > + > +/// Runtime PM data stored within the `struct device_private' during > +/// runtime PM registration. > +/// > +/// The data is associated with PM transitions and it's conceptually owned > +/// by the Registration itself. > +/// > +#[repr(C)] > +#[pin_data] > +struct RegistrationData<'a, T: PMOps> { > + #[pin] > + data: PMPayload<T::RuntimePayloadType>, > + _marker: PhantomData<&'a mut ()>, > +} > + > +/// Runtime PM registration for a device. > +/// > +/// A `Registration` installs the runtime PM payload used by the > +/// generated [`PMOps`] callbacks and owns the corresponding teardown. > +/// > +/// Dropping the registration disables runtime PM, waits for in-flight runtime PM > +/// callbacks to complete, and then removes the stored registration data. > +pub struct Registration<'a, T: PMOps> { > + ctx: PMContext<'a, T>, > +} > + > +impl<'a, T: PMOps> Registration<'a, T> { > + /// Creates a runtime PM registration for `dev`. > + /// > + /// The provided profiles and configuration are stored in the associated > + /// [`PMContext`]. The optional `payload` is stored as a Registration data > + /// and is used to service PM transitions. > + /// > + /// The device must use the callback table generated for the same `T`. > + pub fn new( > + dev: &'a device::Device<device::Core<'_>>, > + profiles: Option<KVec<PMProfile>>, > + configs: Option<KVec<PMConfig>>, > + payload: Option<T::RuntimePayloadType>, > + ) -> Result<Self> { > + let payload = KBox::pin_init( > + RegistrationData::<T> { > + data: PMPayload { > + in_flight: Atomic::new(false), > + inner: UnsafeCell::new(payload), > + }, > + _marker: PhantomData, > + }, > + GFP_KERNEL, > + )?; > + > + let inner_ctx = Arc::new( > + PMContextInner { > + dev, > + profiles: profiles.unwrap_or_default(), > + configs: configs.unwrap_or_default(), > + _marker: PhantomData, > + }, > + GFP_KERNEL, > + )?; > + > + // SAFETY: `dev` is a live `Device<Core>`, so its raw `struct device` pointer is > + // valid for the duration of this call. The payload allocation is converted into > + // a foreign pointer and owned by this `Registration` until `Drop` clears > + // `rust_private` and reconstructs the `KBox`. > + unsafe { > + let ptr = (*(*dev.as_raw()).p).rust_private; > + if !ptr.is_null() { > + return Err(EBUSY); > + } > + (*(*dev.as_raw()).p).rust_private = payload.into_foreign(); > + } > + > + Ok(Self { > + ctx: PMContext { inner: inner_ctx }, > + }) > + } > + /// Returns the runtime PM context associated with this registration. > + pub fn ctx(&self) -> &PMContext<'a, T> { > + &self.ctx > + } > +} > + > +impl<'a, T: PMOps> Drop for Registration<'a, T> { > + fn drop(&mut self) { > + // SAFETY: `self.ctx.inner.dev` is the device this registration was > + // created for. Runtime PM is disabled first, and `pm_runtime_barrier` > + // waits for pending runtime PM work/callbacks before the callback data > + // is removed below. > + > + unsafe { > + bindings::__pm_runtime_disable(self.ctx.inner.dev.as_raw(), true); Same here, is this safe to call even if it's not enabled? Registration can drop even when pm runtime is not enabled, right? > + bindings::pm_runtime_barrier(self.ctx.inner.dev.as_raw()); > + } You should not combine multiple unsafe calls in single unsafe block I think. > + // SAFETY: The pointer, if non-null, was stored by `Registration::new` > + // using `Pin<KBox<RegistrationData<T>>>::into_foreign`. Runtime PM has > + // been disabled and drained above, so generated callbacks can no longer > + // borrow this data. Clearing `rust_private` prevents later lookup, and > + // `from_foreign` reconstructs the owning allocation so it is dropped. > + unsafe { > + let ptr = (*(*self.ctx.inner.dev.as_raw()).p).rust_private; > + > + if !ptr.is_null() { > + (*(*self.ctx.inner.dev.as_raw()).p).rust_private = core::ptr::null_mut(); > + Pin::<KBox<RegistrationData<'_, T>>>::from_foreign(ptr); > + } > + } > + } > +} > -- > 2.43.0 >