mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 12:21:22 -05:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"))?;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
8
rust/helpers/cpu.c
Normal 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();
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) };
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user