// SPDX-License-Identifier: GPL-2.012//! Generic CPU definitions.3//!4//! C header: [`include/linux/cpu.h`](srctree/include/linux/cpu.h)56use crate::{bindings, device::Device, error::Result, prelude::ENODEV};78/// Returns the maximum number of possible CPUs in the current system configuration.9#[inline]10pub fn nr_cpu_ids() -> u32 {11#[cfg(any(NR_CPUS_1, CONFIG_FORCE_NR_CPUS))]12{13bindings::NR_CPUS14}1516#[cfg(not(any(NR_CPUS_1, CONFIG_FORCE_NR_CPUS)))]17// SAFETY: `nr_cpu_ids` is a valid global provided by the kernel.18unsafe {19bindings::nr_cpu_ids20}21}2223/// The CPU ID.24///25/// Represents a CPU identifier as a wrapper around an [`u32`].26///27/// # Invariants28///29/// The CPU ID lies within the range `[0, nr_cpu_ids())`.30///31/// # Examples32///33/// ```34/// use kernel::cpu::CpuId;35///36/// let cpu = 0;37///38/// // SAFETY: 0 is always a valid CPU number.39/// let id = unsafe { CpuId::from_u32_unchecked(cpu) };40///41/// assert_eq!(id.as_u32(), cpu);42/// assert!(CpuId::from_i32(0).is_some());43/// assert!(CpuId::from_i32(-1).is_none());44/// ```45#[derive(Copy, Clone, PartialEq, Eq, Debug)]46pub struct CpuId(u32);4748impl CpuId {49/// Creates a new [`CpuId`] from the given `id` without checking bounds.50///51/// # Safety52///53/// The caller must ensure that `id` is a valid CPU ID (i.e., `0 <= id < nr_cpu_ids()`).54#[inline]55pub unsafe fn from_i32_unchecked(id: i32) -> Self {56debug_assert!(id >= 0);57debug_assert!((id as u32) < nr_cpu_ids());5859// INVARIANT: The function safety guarantees `id` is a valid CPU id.60Self(id as u32)61}6263/// Creates a new [`CpuId`] from the given `id`, checking that it is valid.64pub fn from_i32(id: i32) -> Option<Self> {65if id < 0 || id as u32 >= nr_cpu_ids() {66None67} else {68// INVARIANT: `id` has just been checked as a valid CPU ID.69Some(Self(id as u32))70}71}7273/// Creates a new [`CpuId`] from the given `id` without checking bounds.74///75/// # Safety76///77/// The caller must ensure that `id` is a valid CPU ID (i.e., `0 <= id < nr_cpu_ids()`).78#[inline]79pub unsafe fn from_u32_unchecked(id: u32) -> Self {80debug_assert!(id < nr_cpu_ids());8182// Ensure the `id` fits in an [`i32`] as it's also representable that way.83debug_assert!(id <= i32::MAX as u32);8485// INVARIANT: The function safety guarantees `id` is a valid CPU id.86Self(id)87}8889/// Creates a new [`CpuId`] from the given `id`, checking that it is valid.90pub fn from_u32(id: u32) -> Option<Self> {91if id >= nr_cpu_ids() {92None93} else {94// INVARIANT: `id` has just been checked as a valid CPU ID.95Some(Self(id))96}97}9899/// Returns CPU number.100#[inline]101pub fn as_u32(&self) -> u32 {102self.0103}104105/// Returns the ID of the CPU the code is currently running on.106///107/// The returned value is considered unstable because it may change108/// unexpectedly due to preemption or CPU migration. It should only be109/// used when the context ensures that the task remains on the same CPU110/// or the users could use a stale (yet valid) CPU ID.111#[inline]112pub fn current() -> Self {113// SAFETY: raw_smp_processor_id() always returns a valid CPU ID.114unsafe { Self::from_u32_unchecked(bindings::raw_smp_processor_id()) }115}116}117118impl From<CpuId> for u32 {119fn from(id: CpuId) -> Self {120id.as_u32()121}122}123124impl From<CpuId> for i32 {125fn from(id: CpuId) -> Self {126id.as_u32() as i32127}128}129130/// Creates a new instance of CPU's device.131///132/// # Safety133///134/// Reference counting is not implemented for the CPU device in the C code. When a CPU is135/// hot-unplugged, the corresponding CPU device is unregistered, but its associated memory136/// is not freed.137///138/// Callers must ensure that the CPU device is not used after it has been unregistered.139/// This can be achieved, for example, by registering a CPU hotplug notifier and removing140/// any references to the CPU device within the notifier's callback.141pub unsafe fn from_cpu(cpu: CpuId) -> Result<&'static Device> {142// SAFETY: It is safe to call `get_cpu_device()` for any CPU.143let ptr = unsafe { bindings::get_cpu_device(u32::from(cpu)) };144if ptr.is_null() {145return Err(ENODEV);146}147148// SAFETY: The pointer returned by `get_cpu_device()`, if not `NULL`, is a valid pointer to149// a `struct device` and is never freed by the C code.150Ok(unsafe { Device::from_raw(ptr) })151}152153154