Merge tag 'pm-6.16-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management fixes from Rafael Wysocki:
 "These fix the cpupower utility installation, fix up the recently added
  Rust abstractions for cpufreq and OPP, restore the x86 update
  eliminating mwait_play_dead_cpuid_hint() that has been reverted during
  the 6.16 merge window along with preventing the failure caused by it
  from happening, and clean up mwait_idle_with_hints() usage in
  intel_idle:

   - Implement CpuId Rust abstraction and use it to fix doctest failure
     related to the recently introduced cpumask abstraction (Viresh
     Kumar)

   - Do minor cleanups in the `# Safety` sections for cpufreq
     abstractions added recently (Viresh Kumar)

   - Unbreak cpupower systemd service units installation on some systems
     by adding a unitdir variable for specifying the location to install
     them (Francesco Poli)

   - Eliminate mwait_play_dead_cpuid_hint() again after reverting its
     elimination during the 6.16 merge window due to a problem with
     handling "dead" SMT siblings, but this time prevent leaving them in
     C1 after initialization by taking them online and back offline when
     a proper cpuidle driver for the platform has been registered
     (Rafael Wysocki)

   - Update data types of variables passed as arguments to
     mwait_idle_with_hints() to match the function definition after
     recent changes (Uros Bizjak)"

* tag 'pm-6.16-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  rust: cpu: Add CpuId::current() to retrieve current CPU ID
  rust: Use CpuId in place of raw CPU numbers
  rust: cpu: Introduce CpuId abstraction
  intel_idle: Update arguments of mwait_idle_with_hints()
  cpufreq: Convert `/// SAFETY` lines to `# Safety` sections
  cpupower: split unitdir from libdir in Makefile
  Reapply "x86/smp: Eliminate mwait_play_dead_cpuid_hint()"
  ACPI: processor: Rescan "dead" SMT siblings during initialization
  intel_idle: Rescan "dead" SMT siblings during initialization
  x86/smp: PM/hibernate: Split arch_resume_nosmt()
  intel_idle: Use subsys_initcall_sync() for initialization
This commit is contained in:
Linus Torvalds
2025-06-13 13:27:41 -07:00
16 changed files with 367 additions and 132 deletions

View File

@@ -6255,6 +6255,7 @@ F: include/linux/cpuhotplug.h
F: include/linux/smpboot.h
F: kernel/cpu.c
F: kernel/smpboot.*
F: rust/helper/cpu.c
F: rust/kernel/cpu.rs
CPU IDLE TIME MANAGEMENT FRAMEWORK

View File

@@ -299,3 +299,27 @@ struct smp_ops smp_ops = {
.send_call_func_single_ipi = native_send_call_func_single_ipi,
};
EXPORT_SYMBOL_GPL(smp_ops);
int arch_cpu_rescan_dead_smt_siblings(void)
{
enum cpuhp_smt_control old = cpu_smt_control;
int ret;
/*
* If SMT has been disabled and SMT siblings are in HLT, bring them back
* online and offline them again so that they end up in MWAIT proper.
*
* Called with hotplug enabled.
*/
if (old != CPU_SMT_DISABLED && old != CPU_SMT_FORCE_DISABLED)
return 0;
ret = cpuhp_smt_enable();
if (ret)
return ret;
ret = cpuhp_smt_disable(old);
return ret;
}
EXPORT_SYMBOL_GPL(arch_cpu_rescan_dead_smt_siblings);

View File

@@ -1244,6 +1244,10 @@ void play_dead_common(void)
local_irq_disable();
}
/*
* We need to flush the caches before going to sleep, lest we have
* dirty data in our caches when we come back up.
*/
void __noreturn mwait_play_dead(unsigned int eax_hint)
{
struct mwait_cpu_dead *md = this_cpu_ptr(&mwait_cpu_dead);
@@ -1289,50 +1293,6 @@ void __noreturn mwait_play_dead(unsigned int eax_hint)
}
}
/*
* We need to flush the caches before going to sleep, lest we have
* dirty data in our caches when we come back up.
*/
static inline void mwait_play_dead_cpuid_hint(void)
{
unsigned int eax, ebx, ecx, edx;
unsigned int highest_cstate = 0;
unsigned int highest_subcstate = 0;
int i;
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
return;
if (!this_cpu_has(X86_FEATURE_MWAIT))
return;
if (!this_cpu_has(X86_FEATURE_CLFLUSH))
return;
eax = CPUID_LEAF_MWAIT;
ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
/*
* eax will be 0 if EDX enumeration is not valid.
* Initialized below to cstate, sub_cstate value when EDX is valid.
*/
if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED)) {
eax = 0;
} else {
edx >>= MWAIT_SUBSTATE_SIZE;
for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
if (edx & MWAIT_SUBSTATE_MASK) {
highest_cstate = i;
highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
}
}
eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
(highest_subcstate - 1);
}
mwait_play_dead(eax);
}
/*
* Kick all "offline" CPUs out of mwait on kexec(). See comment in
* mwait_play_dead().
@@ -1383,9 +1343,9 @@ void native_play_dead(void)
play_dead_common();
tboot_shutdown(TB_SHUTDOWN_WFS);
mwait_play_dead_cpuid_hint();
if (cpuidle_play_dead())
hlt_play_dead();
/* Below returns only on error. */
cpuidle_play_dead();
hlt_play_dead();
}
#else /* ... !CONFIG_HOTPLUG_CPU */

View File

@@ -192,7 +192,8 @@ int relocate_restore_code(void)
int arch_resume_nosmt(void)
{
int ret = 0;
int ret;
/*
* We reached this while coming out of hibernation. This means
* that SMT siblings are sleeping in hlt, as mwait is not safe
@@ -206,18 +207,10 @@ int arch_resume_nosmt(void)
* Called with hotplug disabled.
*/
cpu_hotplug_enable();
if (cpu_smt_control == CPU_SMT_DISABLED ||
cpu_smt_control == CPU_SMT_FORCE_DISABLED) {
enum cpuhp_smt_control old = cpu_smt_control;
ret = cpuhp_smt_enable();
if (ret)
goto out;
ret = cpuhp_smt_disable(old);
if (ret)
goto out;
}
out:
ret = arch_cpu_rescan_dead_smt_siblings();
cpu_hotplug_disable();
return ret;
}

View File

@@ -175,6 +175,12 @@ bool processor_physically_present(acpi_handle handle);
static inline void acpi_early_processor_control_setup(void) {}
#endif
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
void acpi_idle_rescan_dead_smt_siblings(void);
#else
static inline void acpi_idle_rescan_dead_smt_siblings(void) {}
#endif
/* --------------------------------------------------------------------------
Embedded Controller
-------------------------------------------------------------------------- */

View File

@@ -279,6 +279,9 @@ static int __init acpi_processor_driver_init(void)
* after acpi_cppc_processor_probe() has been called for all online CPUs
*/
acpi_processor_init_invariance_cppc();
acpi_idle_rescan_dead_smt_siblings();
return 0;
err:
driver_unregister(&acpi_processor_driver);

View File

@@ -24,6 +24,8 @@
#include <acpi/processor.h>
#include <linux/context_tracking.h>
#include "internal.h"
/*
* Include the apic definitions for x86 to have the APIC timer related defines
* available also for UP (on SMP it gets magically included via linux/smp.h).
@@ -55,6 +57,12 @@ struct cpuidle_driver acpi_idle_driver = {
};
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
void acpi_idle_rescan_dead_smt_siblings(void)
{
if (cpuidle_get_driver() == &acpi_idle_driver)
arch_cpu_rescan_dead_smt_siblings();
}
static
DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], acpi_cstate);

View File

@@ -26,9 +26,9 @@ fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
}
/// Finds supply name for the CPU from DT.
fn find_supply_names(dev: &Device, cpu: u32) -> Option<KVec<CString>> {
fn find_supply_names(dev: &Device, cpu: cpu::CpuId) -> Option<KVec<CString>> {
// Try "cpu0" for older DTs, fallback to "cpu".
let name = (cpu == 0)
let name = (cpu.as_u32() == 0)
.then(|| find_supply_name_exact(dev, "cpu0"))
.flatten()
.or_else(|| find_supply_name_exact(dev, "cpu"))?;

View File

@@ -152,8 +152,8 @@ static __always_inline int __intel_idle(struct cpuidle_device *dev,
int index, bool irqoff)
{
struct cpuidle_state *state = &drv->states[index];
unsigned long eax = flg2MWAIT(state->flags);
unsigned long ecx = 1*irqoff; /* break on interrupt flag */
unsigned int eax = flg2MWAIT(state->flags);
unsigned int ecx = 1*irqoff; /* break on interrupt flag */
mwait_idle_with_hints(eax, ecx);
@@ -226,9 +226,9 @@ static __cpuidle int intel_idle_xstate(struct cpuidle_device *dev,
static __cpuidle int intel_idle_s2idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
unsigned long ecx = 1; /* break on interrupt flag */
struct cpuidle_state *state = &drv->states[index];
unsigned long eax = flg2MWAIT(state->flags);
unsigned int eax = flg2MWAIT(state->flags);
unsigned int ecx = 1; /* break on interrupt flag */
if (state->flags & CPUIDLE_FLAG_INIT_XSTATE)
fpu_idle_fpregs();
@@ -2507,6 +2507,8 @@ static int __init intel_idle_init(void)
pr_debug("Local APIC timer is reliable in %s\n",
boot_cpu_has(X86_FEATURE_ARAT) ? "all C-states" : "C1");
arch_cpu_rescan_dead_smt_siblings();
return 0;
hp_setup_fail:
@@ -2518,7 +2520,7 @@ static int __init intel_idle_init(void)
return retval;
}
device_initcall(intel_idle_init);
subsys_initcall_sync(intel_idle_init);
/*
* We are not really modular, but we used to support that. Meaning we also

View File

@@ -120,6 +120,7 @@ extern void cpu_maps_update_begin(void);
extern void cpu_maps_update_done(void);
int bringup_hibernate_cpu(unsigned int sleep_cpu);
void bringup_nonboot_cpus(unsigned int max_cpus);
int arch_cpu_rescan_dead_smt_siblings(void);
#else /* CONFIG_SMP */
#define cpuhp_tasks_frozen 0
@@ -134,6 +135,8 @@ static inline void cpu_maps_update_done(void)
static inline int add_cpu(unsigned int cpu) { return 0;}
static inline int arch_cpu_rescan_dead_smt_siblings(void) { return 0; }
#endif /* CONFIG_SMP */
extern const struct bus_type cpu_subsys;

8
rust/helpers/cpu.c Normal file
View File

@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/smp.h>
unsigned int rust_helper_raw_smp_processor_id(void)
{
return raw_smp_processor_id();
}

View File

@@ -13,6 +13,7 @@
#include "build_assert.c"
#include "build_bug.c"
#include "clk.c"
#include "cpu.c"
#include "cpufreq.c"
#include "cpumask.c"
#include "cred.c"

View File

@@ -6,6 +6,127 @@
use crate::{bindings, device::Device, error::Result, prelude::ENODEV};
/// Returns the maximum number of possible CPUs in the current system configuration.
#[inline]
pub fn nr_cpu_ids() -> u32 {
#[cfg(any(NR_CPUS_1, CONFIG_FORCE_NR_CPUS))]
{
bindings::NR_CPUS
}
#[cfg(not(any(NR_CPUS_1, CONFIG_FORCE_NR_CPUS)))]
// SAFETY: `nr_cpu_ids` is a valid global provided by the kernel.
unsafe {
bindings::nr_cpu_ids
}
}
/// The CPU ID.
///
/// Represents a CPU identifier as a wrapper around an [`u32`].
///
/// # Invariants
///
/// The CPU ID lies within the range `[0, nr_cpu_ids())`.
///
/// # Examples
///
/// ```
/// use kernel::cpu::CpuId;
///
/// let cpu = 0;
///
/// // SAFETY: 0 is always a valid CPU number.
/// let id = unsafe { CpuId::from_u32_unchecked(cpu) };
///
/// assert_eq!(id.as_u32(), cpu);
/// assert!(CpuId::from_i32(0).is_some());
/// assert!(CpuId::from_i32(-1).is_none());
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct CpuId(u32);
impl CpuId {
/// Creates a new [`CpuId`] from the given `id` without checking bounds.
///
/// # Safety
///
/// The caller must ensure that `id` is a valid CPU ID (i.e., `0 <= id < nr_cpu_ids()`).
#[inline]
pub unsafe fn from_i32_unchecked(id: i32) -> Self {
debug_assert!(id >= 0);
debug_assert!((id as u32) < nr_cpu_ids());
// INVARIANT: The function safety guarantees `id` is a valid CPU id.
Self(id as u32)
}
/// Creates a new [`CpuId`] from the given `id`, checking that it is valid.
pub fn from_i32(id: i32) -> Option<Self> {
if id < 0 || id as u32 >= nr_cpu_ids() {
None
} else {
// INVARIANT: `id` has just been checked as a valid CPU ID.
Some(Self(id as u32))
}
}
/// Creates a new [`CpuId`] from the given `id` without checking bounds.
///
/// # Safety
///
/// The caller must ensure that `id` is a valid CPU ID (i.e., `0 <= id < nr_cpu_ids()`).
#[inline]
pub unsafe fn from_u32_unchecked(id: u32) -> Self {
debug_assert!(id < nr_cpu_ids());
// Ensure the `id` fits in an [`i32`] as it's also representable that way.
debug_assert!(id <= i32::MAX as u32);
// INVARIANT: The function safety guarantees `id` is a valid CPU id.
Self(id)
}
/// Creates a new [`CpuId`] from the given `id`, checking that it is valid.
pub fn from_u32(id: u32) -> Option<Self> {
if id >= nr_cpu_ids() {
None
} else {
// INVARIANT: `id` has just been checked as a valid CPU ID.
Some(Self(id))
}
}
/// Returns CPU number.
#[inline]
pub fn as_u32(&self) -> u32 {
self.0
}
/// Returns the ID of the CPU the code is currently running on.
///
/// The returned value is considered unstable because it may change
/// unexpectedly due to preemption or CPU migration. It should only be
/// used when the context ensures that the task remains on the same CPU
/// or the users could use a stale (yet valid) CPU ID.
pub fn current() -> Self {
// SAFETY: raw_smp_processor_id() always returns a valid CPU ID.
unsafe { Self::from_u32_unchecked(bindings::raw_smp_processor_id()) }
}
}
impl From<CpuId> for u32 {
fn from(id: CpuId) -> Self {
id.as_u32()
}
}
impl From<CpuId> for i32 {
fn from(id: CpuId) -> Self {
id.as_u32() as i32
}
}
/// Creates a new instance of CPU's device.
///
/// # Safety
@@ -17,9 +138,9 @@
/// Callers must ensure that the CPU device is not used after it has been unregistered.
/// This can be achieved, for example, by registering a CPU hotplug notifier and removing
/// any references to the CPU device within the notifier's callback.
pub unsafe fn from_cpu(cpu: u32) -> Result<&'static Device> {
pub unsafe fn from_cpu(cpu: CpuId) -> Result<&'static Device> {
// SAFETY: It is safe to call `get_cpu_device()` for any CPU.
let ptr = unsafe { bindings::get_cpu_device(cpu) };
let ptr = unsafe { bindings::get_cpu_device(u32::from(cpu)) };
if ptr.is_null() {
return Err(ENODEV);
}

View File

@@ -10,6 +10,7 @@
use crate::{
clk::Hertz,
cpu::CpuId,
cpumask,
device::{Bound, Device},
devres::Devres,
@@ -465,8 +466,9 @@ fn as_mut_ref(&mut self) -> &mut bindings::cpufreq_policy {
/// Returns the primary CPU for the [`Policy`].
#[inline]
pub fn cpu(&self) -> u32 {
self.as_ref().cpu
pub fn cpu(&self) -> CpuId {
// SAFETY: The C API guarantees that `cpu` refers to a valid CPU number.
unsafe { CpuId::from_u32_unchecked(self.as_ref().cpu) }
}
/// Returns the minimum frequency for the [`Policy`].
@@ -525,7 +527,7 @@ pub fn generic_suspend(&mut self) -> Result {
#[inline]
pub fn generic_get(&self) -> Result<u32> {
// SAFETY: By the type invariant, the pointer stored in `self` is valid.
Ok(unsafe { bindings::cpufreq_generic_get(self.cpu()) })
Ok(unsafe { bindings::cpufreq_generic_get(u32::from(self.cpu())) })
}
/// Provides a wrapper to the register with energy model using the OPP core.
@@ -678,9 +680,9 @@ fn clear_data<T: ForeignOwnable>(&mut self) -> Option<T> {
struct PolicyCpu<'a>(&'a mut Policy);
impl<'a> PolicyCpu<'a> {
fn from_cpu(cpu: u32) -> Result<Self> {
fn from_cpu(cpu: CpuId) -> Result<Self> {
// SAFETY: It is safe to call `cpufreq_cpu_get` for any valid CPU.
let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(cpu) })?;
let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) })?;
Ok(Self(
// SAFETY: The `ptr` is guaranteed to be valid and remains valid for the lifetime of
@@ -1055,8 +1057,11 @@ pub fn new_foreign_owned(dev: &Device<Bound>) -> Result {
impl<T: Driver> Registration<T> {
/// 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 {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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
// lifetime of `policy`.
@@ -1070,8 +1075,11 @@ 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.
extern "C" fn exit_callback(ptr: *mut bindings::cpufreq_policy) {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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`.
let policy = unsafe { Policy::from_raw_mut(ptr) };
@@ -1082,8 +1090,11 @@ extern "C" fn exit_callback(ptr: *mut bindings::cpufreq_policy) {
/// 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 {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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
// lifetime of `policy`.
@@ -1094,8 +1105,13 @@ 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.
extern "C" fn offline_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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
// lifetime of `policy`.
@@ -1106,8 +1122,13 @@ 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.
extern "C" fn suspend_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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
// lifetime of `policy`.
@@ -1118,8 +1139,11 @@ 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.
extern "C" fn resume_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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
// lifetime of `policy`.
@@ -1130,8 +1154,11 @@ 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.
extern "C" fn ready_callback(ptr: *mut bindings::cpufreq_policy) {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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`.
let policy = unsafe { Policy::from_raw_mut(ptr) };
@@ -1140,8 +1167,13 @@ extern "C" fn ready_callback(ptr: *mut bindings::cpufreq_policy) {
/// 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 {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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
// lifetime of `policy`.
@@ -1152,8 +1184,13 @@ 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.
extern "C" fn setpolicy_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::ffi::c_int {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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
// lifetime of `policy`.
@@ -1164,8 +1201,11 @@ extern "C" fn setpolicy_callback(ptr: *mut bindings::cpufreq_policy) -> kernel::
/// Driver's `target` callback.
///
/// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn target_callback(
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn target_callback(
ptr: *mut bindings::cpufreq_policy,
target_freq: u32,
relation: u32,
@@ -1180,8 +1220,11 @@ extern "C" fn target_callback(
/// Driver's `target_index` callback.
///
/// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn target_index_callback(
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn target_index_callback(
ptr: *mut bindings::cpufreq_policy,
index: u32,
) -> kernel::ffi::c_int {
@@ -1200,8 +1243,11 @@ extern "C" fn target_index_callback(
/// Driver's `fast_switch` callback.
///
/// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn fast_switch_callback(
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn fast_switch_callback(
ptr: *mut bindings::cpufreq_policy,
target_freq: u32,
) -> kernel::ffi::c_uint {
@@ -1212,21 +1258,31 @@ extern "C" fn fast_switch_callback(
}
/// Driver's `adjust_perf` callback.
extern "C" fn adjust_perf_callback(
///
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
unsafe extern "C" fn adjust_perf_callback(
cpu: u32,
min_perf: usize,
target_perf: usize,
capacity: usize,
) {
if let Ok(mut policy) = PolicyCpu::from_cpu(cpu) {
// SAFETY: The C API guarantees that `cpu` refers to a valid CPU number.
let cpu_id = unsafe { CpuId::from_u32_unchecked(cpu) };
if let Ok(mut policy) = PolicyCpu::from_cpu(cpu_id) {
T::adjust_perf(&mut policy, min_perf, target_perf, capacity);
}
}
/// Driver's `get_intermediate` callback.
///
/// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn get_intermediate_callback(
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn get_intermediate_callback(
ptr: *mut bindings::cpufreq_policy,
index: u32,
) -> kernel::ffi::c_uint {
@@ -1243,8 +1299,11 @@ extern "C" fn get_intermediate_callback(
/// Driver's `target_intermediate` callback.
///
/// SAFETY: Called from C. Inputs must be valid pointers.
extern "C" fn target_intermediate_callback(
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn target_intermediate_callback(
ptr: *mut bindings::cpufreq_policy,
index: u32,
) -> kernel::ffi::c_int {
@@ -1262,12 +1321,24 @@ extern "C" fn target_intermediate_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))
///
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
unsafe extern "C" fn get_callback(cpu: u32) -> kernel::ffi::c_uint {
// SAFETY: The C API guarantees that `cpu` refers to a valid CPU number.
let cpu_id = unsafe { CpuId::from_u32_unchecked(cpu) };
PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f))
}
/// Driver's `update_limit` callback.
extern "C" fn update_limits_callback(ptr: *mut bindings::cpufreq_policy) {
///
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn update_limits_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`.
let policy = unsafe { Policy::from_raw_mut(ptr) };
@@ -1276,10 +1347,16 @@ extern "C" fn update_limits_callback(ptr: *mut bindings::cpufreq_policy) {
/// 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 {
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn bios_limit_callback(cpu: i32, limit: *mut u32) -> kernel::ffi::c_int {
// SAFETY: The C API guarantees that `cpu` refers to a valid CPU number.
let cpu_id = unsafe { CpuId::from_i32_unchecked(cpu) };
from_result(|| {
let mut policy = PolicyCpu::from_cpu(cpu as u32)?;
let mut policy = PolicyCpu::from_cpu(cpu_id)?;
// SAFETY: `limit` is guaranteed by the C code to be valid.
T::bios_limit(&mut policy, &mut (unsafe { *limit })).map(|()| 0)
@@ -1288,8 +1365,11 @@ 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.
extern "C" fn set_boost_callback(
/// # Safety
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe extern "C" fn set_boost_callback(
ptr: *mut bindings::cpufreq_policy,
state: i32,
) -> kernel::ffi::c_int {
@@ -1303,8 +1383,11 @@ extern "C" fn set_boost_callback(
/// 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
///
/// - This function may only be called from the cpufreq C infrastructure.
/// - The pointer arguments must be valid pointers.
unsafe 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`.
let policy = unsafe { Policy::from_raw_mut(ptr) };

View File

@@ -6,6 +6,7 @@
use crate::{
alloc::{AllocError, Flags},
cpu::CpuId,
prelude::*,
types::Opaque,
};
@@ -35,9 +36,10 @@
///
/// ```
/// use kernel::bindings;
/// use kernel::cpu::CpuId;
/// use kernel::cpumask::Cpumask;
///
/// fn set_clear_cpu(ptr: *mut bindings::cpumask, set_cpu: u32, clear_cpu: i32) {
/// fn set_clear_cpu(ptr: *mut bindings::cpumask, set_cpu: CpuId, clear_cpu: CpuId) {
/// // SAFETY: The `ptr` is valid for writing and remains valid for the lifetime of the
/// // returned reference.
/// let mask = unsafe { Cpumask::as_mut_ref(ptr) };
@@ -90,9 +92,9 @@ pub fn as_raw(&self) -> *mut bindings::cpumask {
/// This mismatches kernel naming convention and corresponds to the C
/// function `__cpumask_set_cpu()`.
#[inline]
pub fn set(&mut self, cpu: u32) {
pub fn set(&mut self, cpu: CpuId) {
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `__cpumask_set_cpu`.
unsafe { bindings::__cpumask_set_cpu(cpu, self.as_raw()) };
unsafe { bindings::__cpumask_set_cpu(u32::from(cpu), self.as_raw()) };
}
/// Clear `cpu` in the cpumask.
@@ -101,19 +103,19 @@ pub fn set(&mut self, cpu: u32) {
/// This mismatches kernel naming convention and corresponds to the C
/// function `__cpumask_clear_cpu()`.
#[inline]
pub fn clear(&mut self, cpu: i32) {
pub fn clear(&mut self, cpu: CpuId) {
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to
// `__cpumask_clear_cpu`.
unsafe { bindings::__cpumask_clear_cpu(cpu, self.as_raw()) };
unsafe { bindings::__cpumask_clear_cpu(i32::from(cpu), self.as_raw()) };
}
/// Test `cpu` in the cpumask.
///
/// Equivalent to the kernel's `cpumask_test_cpu` API.
#[inline]
pub fn test(&self, cpu: i32) -> bool {
pub fn test(&self, cpu: CpuId) -> bool {
// SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_test_cpu`.
unsafe { bindings::cpumask_test_cpu(cpu, self.as_raw()) }
unsafe { bindings::cpumask_test_cpu(i32::from(cpu), self.as_raw()) }
}
/// Set all CPUs in the cpumask.
@@ -178,21 +180,40 @@ pub fn copy(&self, dstp: &mut Self) {
/// The following example demonstrates how to create and update a [`CpumaskVar`].
///
/// ```
/// use kernel::cpu::CpuId;
/// use kernel::cpumask::CpumaskVar;
///
/// let mut mask = CpumaskVar::new_zero(GFP_KERNEL).unwrap();
///
/// assert!(mask.empty());
/// mask.set(2);
/// assert!(mask.test(2));
/// mask.set(3);
/// assert!(mask.test(3));
/// assert_eq!(mask.weight(), 2);
/// let mut count = 0;
///
/// let cpu2 = CpuId::from_u32(2);
/// if let Some(cpu) = cpu2 {
/// mask.set(cpu);
/// assert!(mask.test(cpu));
/// count += 1;
/// }
///
/// let cpu3 = CpuId::from_u32(3);
/// if let Some(cpu) = cpu3 {
/// mask.set(cpu);
/// assert!(mask.test(cpu));
/// count += 1;
/// }
///
/// assert_eq!(mask.weight(), count);
///
/// let mask2 = CpumaskVar::try_clone(&mask).unwrap();
/// assert!(mask2.test(2));
/// assert!(mask2.test(3));
/// assert_eq!(mask2.weight(), 2);
///
/// if let Some(cpu) = cpu2 {
/// assert!(mask2.test(cpu));
/// }
///
/// if let Some(cpu) = cpu3 {
/// assert!(mask2.test(cpu));
/// }
/// assert_eq!(mask2.weight(), count);
/// ```
pub struct CpumaskVar {
#[cfg(CONFIG_CPUMASK_OFFSTACK)]

View File

@@ -73,6 +73,7 @@ sbindir ?= /usr/sbin
mandir ?= /usr/man
libdir ?= /usr/lib
libexecdir ?= /usr/libexec
unitdir ?= /usr/lib/systemd/system
includedir ?= /usr/include
localedir ?= /usr/share/locale
docdir ?= /usr/share/doc/packages/cpupower
@@ -309,9 +310,9 @@ install-tools: $(OUTPUT)cpupower
$(INSTALL_DATA) cpupower-service.conf '$(DESTDIR)${confdir}'
$(INSTALL) -d $(DESTDIR)${libexecdir}
$(INSTALL_PROGRAM) cpupower.sh '$(DESTDIR)${libexecdir}/cpupower'
$(INSTALL) -d $(DESTDIR)${libdir}/systemd/system
sed 's|___CDIR___|${confdir}|; s|___LDIR___|${libexecdir}|' cpupower.service.in > '$(DESTDIR)${libdir}/systemd/system/cpupower.service'
$(SETPERM_DATA) '$(DESTDIR)${libdir}/systemd/system/cpupower.service'
$(INSTALL) -d $(DESTDIR)${unitdir}
sed 's|___CDIR___|${confdir}|; s|___LDIR___|${libexecdir}|' cpupower.service.in > '$(DESTDIR)${unitdir}/cpupower.service'
$(SETPERM_DATA) '$(DESTDIR)${unitdir}/cpupower.service'
install-man:
$(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1
@@ -348,7 +349,7 @@ uninstall:
- rm -f $(DESTDIR)${bindir}/utils/cpupower
- rm -f $(DESTDIR)${confdir}cpupower-service.conf
- rm -f $(DESTDIR)${libexecdir}/cpupower
- rm -f $(DESTDIR)${libdir}/systemd/system/cpupower.service
- rm -f $(DESTDIR)${unitdir}/cpupower.service
- rm -f $(DESTDIR)${mandir}/man1/cpupower.1
- rm -f $(DESTDIR)${mandir}/man1/cpupower-frequency-set.1
- rm -f $(DESTDIR)${mandir}/man1/cpupower-frequency-info.1