mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

Use the newly defined `CpuId` abstraction instead of raw CPU numbers.
This also fixes a doctest failure for configurations where `nr_cpu_ids <
4`.
The C `cpumask_{set|clear}_cpu()` APIs emit a warning when given an
invalid CPU number — but only if `CONFIG_DEBUG_PER_CPU_MAPS=y` is set.
Meanwhile, `cpumask_weight()` only considers CPUs up to `nr_cpu_ids`,
which can cause inconsistencies: a CPU number greater than `nr_cpu_ids`
may be set in the mask, yet the weight calculation won't reflect it.
This leads to doctest failures when `nr_cpu_ids < 4`, as the test tries
to set CPUs 2 and 3:
rust_doctest_kernel_cpumask_rs_0.location: rust/kernel/cpumask.rs:180
rust_doctest_kernel_cpumask_rs_0: ASSERTION FAILED at rust/kernel/cpumask.rs:190
Fixes: 8961b8cb30
("rust: cpumask: Add initial abstractions")
Reported-by: Miguel Ojeda <ojeda@kernel.org>
Closes: https://lore.kernel.org/rust-for-linux/CANiq72k3ozKkLMinTLQwvkyg9K=BeRxs1oYZSKhJHY-veEyZdg@mail.gmail.com/
Reported-by: Andreas Hindborg <a.hindborg@kernel.org>
Closes: https://lore.kernel.org/all/87qzzy3ric.fsf@kernel.org/
Suggested-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Boqun Feng <boqun.feng@gmail.com>
141 lines
4.0 KiB
Rust
141 lines
4.0 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Generic CPU definitions.
|
|
//!
|
|
//! C header: [`include/linux/cpu.h`](srctree/include/linux/cpu.h)
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
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
|
|
///
|
|
/// Reference counting is not implemented for the CPU device in the C code. When a CPU is
|
|
/// hot-unplugged, the corresponding CPU device is unregistered, but its associated memory
|
|
/// is not freed.
|
|
///
|
|
/// 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: CpuId) -> Result<&'static Device> {
|
|
// SAFETY: It is safe to call `get_cpu_device()` for any CPU.
|
|
let ptr = unsafe { bindings::get_cpu_device(u32::from(cpu)) };
|
|
if ptr.is_null() {
|
|
return Err(ENODEV);
|
|
}
|
|
|
|
// SAFETY: The pointer returned by `get_cpu_device()`, if not `NULL`, is a valid pointer to
|
|
// a `struct device` and is never freed by the C code.
|
|
Ok(unsafe { Device::as_ref(ptr) })
|
|
}
|