Re: [PATCH V10 13/15] rust: cpufreq: Extend abstractions for driver registration

From: Viresh Kumar
Date: Thu Apr 17 2025 - 04:29:24 EST


On 16-04-25, 12:59, Danilo Krummrich wrote:
> Anyways, that doesn't help for now. Unfortunately, I think you actually need to
> dynamically allocate it. There's no need to revert everything though. You can
> just allocate a new KBox from VTABLE, i.e.
>
> let vtable = KBox::new(Self::VTABLE, GFP_KERNEL)?;

Thanks. Here is the diff for this patch:

diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index e8f4e18002c9..8cb62641c64b 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -9,7 +9,6 @@
//! Reference: <https://docs.kernel.org/admin-guide/pm/cpufreq.html>

use crate::{
- alloc::AllocError,
clk::{Clk, Hertz},
cpumask,
device::Device,
@@ -22,16 +21,17 @@
};

use core::{
+ cell::UnsafeCell,
marker::PhantomData,
mem::MaybeUninit,
ops::{Deref, DerefMut},
pin::Pin,
- ptr::{self, NonNull},
+ ptr,
};

use macros::vtable;

-// Maximum length of CPU frequency driver's name.
+/// Maximum length of CPU frequency driver's name.
const CPUFREQ_NAME_LEN: usize = bindings::CPUFREQ_NAME_LEN as usize;

/// Default transition latency value in nanoseconds.
@@ -872,7 +872,7 @@ fn register_em(_policy: &mut Policy) {
/// data.generic_verify()
/// }
///
-/// fn target_index(policy: &mut cpufreq::Policy, index: u32) -> Result<()> {
+/// fn target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result<()> {
/// // Update CPU frequency
/// Ok(())
/// }
@@ -887,15 +887,15 @@ fn register_em(_policy: &mut Policy) {
/// }
/// ```
#[repr(transparent)]
-pub struct Registration<T: Driver>(NonNull<bindings::cpufreq_driver>, PhantomData<T>);
+pub struct Registration<T: Driver>(KBox<UnsafeCell<bindings::cpufreq_driver>>, PhantomData<T>);

-// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between threads
-// or CPUs, so it is safe to share it.
+/// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between threads
+/// or CPUs, so it is safe to share it.
unsafe impl<T: Driver> Sync for Registration<T> {}

#[allow(clippy::non_send_fields_in_send_ty)]
-// SAFETY: Registration with and unregistration from the cpufreq subsystem can happen from any
-// thread.
+/// SAFETY: Registration with and unregistration from the cpufreq subsystem can happen from any
+/// thread.
unsafe impl<T: Driver> Send for Registration<T> {}

impl<T: Driver> Registration<T> {
@@ -1020,16 +1020,14 @@ impl<T: Driver> Registration<T> {

/// Registers a CPU frequency driver with the cpufreq core.
pub fn new() -> Result<Self> {
- let drv: *const bindings::cpufreq_driver = &Self::VTABLE;
- let drv = drv.cast_mut();
+ // We can't use `&Self::VTABLE` directly because the cpufreq core modifies some fields in
+ // the C `struct cpufreq_driver`, which requires a mutable reference.
+ let mut drv = KBox::new(UnsafeCell::new(Self::VTABLE), GFP_KERNEL)?;

- // SAFETY: It is safe to register the driver with the cpufreq core in the kernel C code.
- to_result(unsafe { bindings::cpufreq_register_driver(drv) })?;
+ // SAFETY: `drv` is guaranteed to be valid for the lifetime of `Registration`.
+ to_result(unsafe { bindings::cpufreq_register_driver(drv.get_mut()) })?;

- Ok(Self(
- NonNull::new(drv.cast()).ok_or(AllocError)?,
- PhantomData,
- ))
+ Ok(Self(drv, PhantomData))
}

/// Same as [`Registration::new`], but does not return a [`Registration`] instance.
@@ -1037,16 +1035,15 @@ pub fn new() -> Result<Self> {
/// Instead the [`Registration`] is owned by [`Devres`] and will be revoked / dropped, once the
/// device is detached.
pub fn new_foreign_owned(dev: &Device) -> Result<()> {
- Devres::new_foreign_owned(dev, Self::new()?, GFP_KERNEL)?;
- Ok(())
+ Devres::new_foreign_owned(dev, Self::new()?, GFP_KERNEL)
}
}

-// CPU frequency driver callbacks.
+/// CPU frequency driver callbacks.
impl<T: Driver> Registration<T> {
- // Driver's `init` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `init` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn init_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
from_result(|| {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
@@ -1059,9 +1056,9 @@ extern "C" fn init_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::
})
}

- // Driver's `exit` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `exit` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn exit_callback(ptr: *mut bindings::cpufreq_policy) {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
// lifetime of `policy`.
@@ -1071,9 +1068,9 @@ extern "C" fn exit_callback(ptr: *mut bindings::cpufreq_policy) {
let _ = T::exit(policy, data);
}

- // Driver's `online` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `online` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn online_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
from_result(|| {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
@@ -1083,9 +1080,9 @@ extern "C" fn online_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi
})
}

- // Driver's `offline` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `offline` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn offline_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
from_result(|| {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
@@ -1095,9 +1092,9 @@ extern "C" fn offline_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ff
})
}

- // Driver's `suspend` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `suspend` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn suspend_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
from_result(|| {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
@@ -1107,9 +1104,9 @@ extern "C" fn suspend_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ff
})
}

- // Driver's `resume` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `resume` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn resume_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
from_result(|| {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
@@ -1119,9 +1116,9 @@ extern "C" fn resume_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi
})
}

- // Driver's `ready` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `ready` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn ready_callback(ptr: *mut bindings::cpufreq_policy) {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
// lifetime of `policy`.
@@ -1129,9 +1126,9 @@ extern "C" fn ready_callback(ptr: *mut bindings::cpufreq_policy) {
T::ready(policy);
}

- // Driver's `verify` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `verify` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn verify_callback(ptr: *mut bindings::cpufreq_policy_data) -> kernel::ffi::c_int {
from_result(|| {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
@@ -1141,9 +1138,9 @@ extern "C" fn verify_callback(ptr: *mut bindings::cpufreq_policy_data) -> kernel
})
}

- // Driver's `setpolicy` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `setpolicy` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn setpolicy_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
from_result(|| {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
@@ -1153,9 +1150,9 @@ extern "C" fn setpolicy_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::
})
}

- // Driver's `target` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `target` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn target_callback(
ptr: *mut bindings::cpufreq_policy,
target_freq: u32,
@@ -1169,9 +1166,9 @@ extern "C" fn target_callback(
})
}

- // Driver's `target_index` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `target_index` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn target_index_callback(
ptr: *mut bindings::cpufreq_policy,
index: u32,
@@ -1180,13 +1177,18 @@ extern "C" fn target_index_callback(
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
// lifetime of `policy`.
let policy = unsafe { Policy::from_raw_mut(ptr) };
+
+ // SAFETY: The C code guarantees that `index` corresponds to a valid entry in the
+ // frequency table.
+ let index = unsafe { TableIndex::new(index as usize) };
+
T::target_index(policy, index).map(|()| 0)
})
}

- // Driver's `fast_switch` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `fast_switch` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn fast_switch_callback(
ptr: *mut bindings::cpufreq_policy,
target_freq: u32,
@@ -1197,7 +1199,7 @@ extern "C" fn fast_switch_callback(
T::fast_switch(policy, target_freq)
}

- // Driver's `adjust_perf` callback.
+ /// Driver's `adjust_perf` callback.
extern "C" fn adjust_perf_callback(
cpu: u32,
min_perf: usize,
@@ -1209,9 +1211,9 @@ extern "C" fn adjust_perf_callback(
}
}

- // Driver's `get_intermediate` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `get_intermediate` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn get_intermediate_callback(
ptr: *mut bindings::cpufreq_policy,
index: u32,
@@ -1219,12 +1221,17 @@ extern "C" fn get_intermediate_callback(
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
// lifetime of `policy`.
let policy = unsafe { Policy::from_raw_mut(ptr) };
+
+ // SAFETY: The C code guarantees that `index` corresponds to a valid entry in the
+ // frequency table.
+ let index = unsafe { TableIndex::new(index as usize) };
+
T::get_intermediate(policy, index)
}

- // Driver's `target_intermediate` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `target_intermediate` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn target_intermediate_callback(
ptr: *mut bindings::cpufreq_policy,
index: u32,
@@ -1233,25 +1240,30 @@ extern "C" fn target_intermediate_callback(
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
// lifetime of `policy`.
let policy = unsafe { Policy::from_raw_mut(ptr) };
+
+ // SAFETY: The C code guarantees that `index` corresponds to a valid entry in the
+ // frequency table.
+ let index = unsafe { TableIndex::new(index as usize) };
+
T::target_intermediate(policy, index).map(|()| 0)
})
}

- // Driver's `get` callback.
+ /// Driver's `get` callback.
extern "C" fn get_callback(cpu: u32) -> kernel::ffi::c_uint {
PolicyCpu::from_cpu(cpu).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f))
}

- // Driver's `update_limit` callback.
+ /// Driver's `update_limit` callback.
extern "C" fn update_limits_callback(cpu: u32) {
if let Ok(mut policy) = PolicyCpu::from_cpu(cpu) {
T::update_limits(&mut policy);
}
}

- // Driver's `bios_limit` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `bios_limit` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn bios_limit_callback(cpu: i32, limit: *mut u32) -> kernel::ffi::c_int {
from_result(|| {
let mut policy = PolicyCpu::from_cpu(cpu as u32)?;
@@ -1261,9 +1273,9 @@ extern "C" fn bios_limit_callback(cpu: i32, limit: *mut u32) -> kernel::ffi::c_i
})
}

- // Driver's `set_boost` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `set_boost` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn set_boost_callback(
ptr: *mut bindings::cpufreq_policy,
state: i32,
@@ -1276,9 +1288,9 @@ extern "C" fn set_boost_callback(
})
}

- // Driver's `register_em` callback.
- //
- // SAFETY: Called from C. Inputs must be valid pointers.
+ /// Driver's `register_em` callback.
+ ///
+ /// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn register_em_callback(ptr: *mut bindings::cpufreq_policy) {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
// lifetime of `policy`.
@@ -1288,9 +1300,9 @@ extern "C" fn register_em_callback(ptr: *mut bindings::cpufreq_policy) {
}

impl<T: Driver> Drop for Registration<T> {
- // Removes the `Registration` from the kernel, if it has initialized successfully earlier.
+ /// Unregisters with the cpufreq core.
fn drop(&mut self) {
- // SAFETY: The driver was earlier registered from `new`.
- unsafe { bindings::cpufreq_unregister_driver(self.0.as_ptr()) };
+ // SAFETY: `self.0` is guaranteed to be valid for the lifetime of `Registration`.
+ unsafe { bindings::cpufreq_unregister_driver(self.0.get_mut()) };
}
}

--
viresh