blob: e36e5989a0b795dd888d25eab8334d95bbd54ad5 [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use crate::kernel::timer::Timer;
use crate::kernel::types::cpu_num_t;
use crate::vm::page_state::VmPageCounts;
use core::mem::offset_of;
use core::sync::atomic::AtomicU64;
use percpu_bindings as bindings;
/// An opaque type representing the C++ `TimerQueue` class.
#[repr(transparent)]
pub struct TimerQueue(pub bindings::TimerQueue);
/// An opaque type representing the C++ `CpuSearchSet` class.
#[repr(transparent)]
pub struct CpuSearchSet(pub bindings::CpuSearchSet);
/// An opaque type representing the C++ `Scheduler` class.
#[repr(transparent)]
pub struct Scheduler(pub bindings::Scheduler);
#[cfg(with_lock_dep)]
/// An opaque type representing the C++ `lockdep::ThreadLockState` class.
#[repr(transparent)]
pub struct ThreadLockState(pub bindings::lockdep_ThreadLockState);
/// An opaque type representing the C++ `ChainLockTransaction` class.
#[repr(C)]
pub struct ChainLockTransaction {
_private: [u8; 0],
}
/// Type alias for `guest_stats`.
pub type GuestStats = bindings::guest_stats;
#[allow(non_camel_case_types)]
pub type guest_stats = bindings::guest_stats;
/// Type alias for `cpu_stats`.
pub type CpuStats = bindings::cpu_stats;
#[allow(non_camel_case_types)]
pub type cpu_stats = bindings::cpu_stats;
/// An opaque type representing the C++ `IdlePowerThread` class.
#[repr(transparent)]
pub struct IdlePowerThread(pub bindings::IdlePowerThread);
/// An opaque type representing the C++ `DpcRunner` class.
#[repr(transparent)]
pub struct DpcRunner(pub bindings::DpcRunner);
/// An opaque type representing the C++ `StallAccumulator` class.
#[repr(transparent)]
pub struct StallAccumulator(pub bindings::StallAccumulator);
/// An opaque type representing the C++ `PlatformCpuResumeState` struct.
#[repr(transparent)]
pub struct PlatformCpuResumeState(pub bindings::PlatformCpuResumeState);
/// Rust representation of the C++ `struct percpu`.
#[repr(C)]
pub struct PerCpu {
/// ZST to force alignment to target arch cache lines.
_align: crate::arch_rs::CpuAlignMarker,
/// Each CPU maintains a per-cpu queue of timers.
pub timer_queue: TimerQueue,
/// per cpu search set
pub search_set: CpuSearchSet,
/// per cpu scheduler
pub scheduler: Scheduler,
#[cfg(with_lock_dep)]
/// state for runtime lock validation when in irq context
pub lock_state: ThreadLockState,
/// The state of the currently active chain-lock transaction if any, nullptr
/// otherwise.
pub active_cl_transaction: *mut ChainLockTransaction,
/// The chainlock conflict ID generator for this CPU. This field is used by
/// threads to know when to retry a ChainLockTransaction when a conflict is
/// encountered. See lib/kconcurrent for more details.
pub chain_lock_conflict_id: AtomicU64,
/// guest entry/exit statistics
pub gstats: GuestStats,
/// thread/cpu level statistics
pub stats: CpuStats,
/// per cpu idle/power thread
pub idle_power_thread: IdlePowerThread,
/// kernel counters arena
pub counters: *mut i64,
/// Each cpu maintains a DpcRunner.
pub dpc_runner: DpcRunner,
/// Page state counts are percpu because they change frequently and we don't want to pay for either
/// heavy synchronization, or transferring the counters between CPUs a lot. Using percpu relaxed
/// atomics we minimize the need for synchronization (no locks, interrupt or preemption disabling
/// needed), and avoid cache line bouncing the counters around different CPUs.
///
/// While it's OK for an observer to temporarily see incorrect values, the counts need to
/// eventually quiesce. It's important that we don't "drop" changes and that the values don't
/// drift over time.
///
/// When modifying it is not necessary to disable preemption, and can just use
/// percpu::GetCurrent()| and then modify the counts. In the unlikely event a CPU migration happens
/// this does not effect correctness, and paying for the rare cache line transfer is preferable to
/// consistently paying to avoid migration.
///
/// When reading, use |ForEachPreemptDisable|. Although it is not possible to guarantee a
/// consistent snapshot of these counters, it should be good enough for diagnostic uses.
pub vm_page_counts: VmPageCounts,
/// lockup_detector state.
///
/// Every active CPU wakes up periodically (even during periods of suspend-to-idle) to record a
/// heartbeat, as well as to check to see if any of its peers are showing signs of problems. The
/// lockup detector timer is the timer used for this.
///
/// This field is not a member of LockupDetectorState because Timer depends on
/// SpinLock, which depends on lockup_detector. By pulling it out of
/// LockupDetectorState we can inline performance critical lockup_detector
/// functions. See also gLockupDetectorPerCpuState.
pub lockup_detector_timer: Timer,
/// When thread sampling is enabled, we use this timer to periodically mark that the active thread
/// should be sampled when safe to do so.
pub sampling_timer: Timer,
/// The accumulated memory stall timers for this CPU.
pub memory_stall_accumulator: StallAccumulator,
/// See |PlatformResumeState| type declaration.
pub resume_state: PlatformCpuResumeState,
}
// One of the members of the PerCpu has a zero sized type, which is not FFI compliant, however we
// are separately validating the equivalence of the final layout of the C++ and Rust objects so
// this is okay.
#[allow(improper_ctypes)]
unsafe extern "C" {
// C++ name mangled form of `size_t percpu::processor_count_`
#[link_name = "_ZN6percpu16processor_count_E"]
/// Number of percpu entries.
static PROCESSOR_COUNT: usize;
// C++ name mangled form of `size_t percpu::processor_index_`
#[link_name = "_ZN6percpu16processor_index_E"]
/// Translates from CPU number to percpu instance. Some or all instances
/// of percpu may be discontiguous.
static PROCESSOR_INDEX: *const *const PerCpu;
}
// Compile-time layout assertions against C++ `struct percpu` via bindgen.
zr::static_assert!(size_of::<PerCpu>() == size_of::<bindings::percpu>());
zr::static_assert!(align_of::<PerCpu>() == align_of::<bindings::percpu>());
zr::static_assert!(offset_of!(PerCpu, timer_queue) == offset_of!(bindings::percpu, timer_queue));
zr::static_assert!(offset_of!(PerCpu, search_set) == offset_of!(bindings::percpu, search_set));
zr::static_assert!(offset_of!(PerCpu, scheduler) == offset_of!(bindings::percpu, scheduler));
#[cfg(with_lock_dep)]
zr::static_assert!(offset_of!(PerCpu, lock_state) == offset_of!(bindings::percpu, lock_state));
zr::static_assert!(
offset_of!(PerCpu, active_cl_transaction)
== offset_of!(bindings::percpu, active_cl_transaction)
);
zr::static_assert!(
offset_of!(PerCpu, chain_lock_conflict_id)
== offset_of!(bindings::percpu, chain_lock_conflict_id)
);
zr::static_assert!(offset_of!(PerCpu, gstats) == offset_of!(bindings::percpu, gstats));
zr::static_assert!(offset_of!(PerCpu, stats) == offset_of!(bindings::percpu, stats));
zr::static_assert!(
offset_of!(PerCpu, idle_power_thread) == offset_of!(bindings::percpu, idle_power_thread)
);
zr::static_assert!(offset_of!(PerCpu, counters) == offset_of!(bindings::percpu, counters));
zr::static_assert!(offset_of!(PerCpu, dpc_runner) == offset_of!(bindings::percpu, dpc_runner));
zr::static_assert!(
offset_of!(PerCpu, vm_page_counts) == offset_of!(bindings::percpu, vm_page_counts)
);
zr::static_assert!(
offset_of!(PerCpu, lockup_detector_timer)
== offset_of!(bindings::percpu, lockup_detector_timer)
);
zr::static_assert!(
offset_of!(PerCpu, sampling_timer) == offset_of!(bindings::percpu, sampling_timer)
);
zr::static_assert!(
offset_of!(PerCpu, memory_stall_accumulator)
== offset_of!(bindings::percpu, memory_stall_accumulator)
);
zr::static_assert!(offset_of!(PerCpu, resume_state) == offset_of!(bindings::percpu, resume_state));
// Compile-time layout assertions for member types.
zr::static_assert!(size_of::<TimerQueue>() == size_of::<bindings::TimerQueue>());
zr::static_assert!(align_of::<TimerQueue>() == align_of::<bindings::TimerQueue>());
zr::static_assert!(size_of::<CpuSearchSet>() == size_of::<bindings::CpuSearchSet>());
zr::static_assert!(align_of::<CpuSearchSet>() == align_of::<bindings::CpuSearchSet>());
zr::static_assert!(size_of::<Scheduler>() == size_of::<bindings::Scheduler>());
zr::static_assert!(align_of::<Scheduler>() == align_of::<bindings::Scheduler>());
#[cfg(with_lock_dep)]
zr::static_assert!(size_of::<ThreadLockState>() == size_of::<bindings::lockdep_ThreadLockState>());
#[cfg(with_lock_dep)]
zr::static_assert!(
align_of::<ThreadLockState>() == align_of::<bindings::lockdep_ThreadLockState>()
);
zr::static_assert!(size_of::<IdlePowerThread>() == size_of::<bindings::IdlePowerThread>());
zr::static_assert!(align_of::<IdlePowerThread>() == align_of::<bindings::IdlePowerThread>());
zr::static_assert!(size_of::<DpcRunner>() == size_of::<bindings::DpcRunner>());
zr::static_assert!(align_of::<DpcRunner>() == align_of::<bindings::DpcRunner>());
zr::static_assert!(size_of::<StallAccumulator>() == size_of::<bindings::StallAccumulator>());
zr::static_assert!(align_of::<StallAccumulator>() == align_of::<bindings::StallAccumulator>());
zr::static_assert!(
size_of::<PlatformCpuResumeState>() == size_of::<bindings::PlatformCpuResumeState>()
);
zr::static_assert!(
align_of::<PlatformCpuResumeState>() == align_of::<bindings::PlatformCpuResumeState>()
);
impl PerCpu {
/// Returns a reference to the percpu instance for given CPU number.
pub fn get(cpu_num: cpu_num_t) -> &'static Self {
debug_assert!(cpu_num < Self::processor_count() as cpu_num_t);
unsafe { &**PROCESSOR_INDEX.wrapping_add(cpu_num as usize) }
}
/// Returns a reference to the percpu instance for the calling CPU.
pub fn get_current() -> &'static Self {
unsafe { &*crate::arch_rs::get_curr_percpu() }
}
/// Returns the number of percpu instances.
pub fn processor_count() -> usize {
unsafe { PROCESSOR_COUNT }
}
/// Call |func| with the current CPU's percpu struct with preemption disabled.
pub fn with_current_preempt_disable<F: FnOnce(&Self)>(func: F) {
let _preempt_disable = crate::kernel::thread::AutoPreemptDisabler::new();
func(Self::get_current());
}
/// Call |func| once per CPU with each CPU's percpu struct with preemption disabled.
pub fn for_each_preempt_disable<F: FnMut(cpu_num_t, &Self)>(func: F) {
let _preempt_disable = crate::kernel::thread::AutoPreemptDisabler::new();
Self::for_each(func);
}
/// Call |func| once per CPU with each CPU's percpu struct.
pub fn for_each<F: FnMut(cpu_num_t, &Self)>(mut func: F) {
for cpu_num in 0..(Self::processor_count() as cpu_num_t) {
func(cpu_num, Self::get(cpu_num));
}
}
}