Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f95576841d |
@@ -94,10 +94,16 @@ impl LocalApic {
|
||||
self.setup_error_int();
|
||||
//self.setup_timer();
|
||||
|
||||
let apic_id = self.id();
|
||||
PercpuBlock::current()
|
||||
.misc_arch_info
|
||||
.apic_id_opt
|
||||
.set(Some(self.id()));
|
||||
.set(Some(apic_id));
|
||||
|
||||
crate::cpu_topology::record_cpu(
|
||||
PercpuBlock::current().cpu_id,
|
||||
apic_id.get(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,22 +137,14 @@ pub unsafe fn mwait_loop(eax_hint: u32, ecx_hint: u32) {
|
||||
/// is, break on any interrupt".
|
||||
pub unsafe fn idle_loop() {
|
||||
let max_substate = cpuid_max_mwait_substate();
|
||||
let percpu = crate::percpu::PercpuBlock::current();
|
||||
if max_substate == 0 {
|
||||
// No MWAIT support. Land in C1 via hlt. This matches the
|
||||
// pre-MWAIT behavior of `enable_and_halt` and is safe on
|
||||
// every x86 CPU since the original Pentium.
|
||||
percpu.stats.record_hlt_entry();
|
||||
enable_and_halt();
|
||||
} else {
|
||||
// MWAIT supported. Enter the deepest substate, break on any
|
||||
// interrupt (ecx=0).
|
||||
//
|
||||
// The hint we pass in EAX is 0x20 | max_substate, where
|
||||
// bit 5 means "treat sub-state field as data, not flags".
|
||||
// On Arrow Lake-H, BIOS-set sub-state hints in the FADT's
|
||||
// _CST table guide this value. The kernel doesn't pick
|
||||
// the state — that's the BIOS/firmware's job.
|
||||
let eax_hint: u32 = 0x20 | (max_substate as u32);
|
||||
enable_and_halt(); // interrupts must be enabled first
|
||||
percpu.stats.record_mwait_entry();
|
||||
enable_and_halt();
|
||||
mwait_loop(eax_hint, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ pub struct CpuStats {
|
||||
state: AtomicU8,
|
||||
pub context_switches: AtomicU64,
|
||||
pub steals: AtomicU64,
|
||||
hlt_entries: AtomicU64,
|
||||
mwait_entries: AtomicU64,
|
||||
}
|
||||
|
||||
impl CpuStats {
|
||||
@@ -49,6 +51,8 @@ impl CpuStats {
|
||||
state: AtomicU8::new(0),
|
||||
context_switches: AtomicU64::new(0),
|
||||
steals: AtomicU64::new(0),
|
||||
hlt_entries: AtomicU64::new(0),
|
||||
mwait_entries: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +65,8 @@ pub struct CpuStatsData {
|
||||
pub irq: u64,
|
||||
pub context_switches: u64,
|
||||
pub steals: u64,
|
||||
pub hlt_entries: u64,
|
||||
pub mwait_entries: u64,
|
||||
}
|
||||
|
||||
impl CpuStats {
|
||||
@@ -103,6 +109,16 @@ impl CpuStats {
|
||||
IRQ_COUNT[irq as usize].fetch_add(1, Ordering::Relaxed);
|
||||
self.irq.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn record_hlt_entry(&self) {
|
||||
self.hlt_entries.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn record_mwait_entry(&self) {
|
||||
self.mwait_entries.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CpuStatsData {
|
||||
@@ -125,6 +141,8 @@ impl From<&CpuStats> for CpuStatsData {
|
||||
irq: val.irq.load(Ordering::Relaxed),
|
||||
context_switches: val.context_switches.load(Ordering::Relaxed),
|
||||
steals: val.steals.load(Ordering::Relaxed),
|
||||
hlt_entries: val.hlt_entries.load(Ordering::Relaxed),
|
||||
mwait_entries: val.mwait_entries.load(Ordering::Relaxed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
use alloc::vec::Vec;
|
||||
use crate::cpu_set::{LogicalCpuId, MAX_CPU_COUNT};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CpuTopologyEntry {
|
||||
pub apic_id: u32,
|
||||
pub core_id: u32,
|
||||
pub thread_id: u32,
|
||||
pub threads_per_core: u32,
|
||||
}
|
||||
|
||||
static APIC_IDS: [AtomicU32; MAX_CPU_COUNT as usize] =
|
||||
[const { AtomicU32::new(u32::MAX) }; MAX_CPU_COUNT as usize];
|
||||
|
||||
static CORE_IDS: [AtomicU32; MAX_CPU_COUNT as usize] =
|
||||
[const { AtomicU32::new(u32::MAX) }; MAX_CPU_COUNT as usize];
|
||||
|
||||
static THREADS_PER_CORE: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub fn record_cpu(cpu_id: LogicalCpuId, apic_id: u32) {
|
||||
let idx = cpu_id.get() as usize;
|
||||
if idx >= MAX_CPU_COUNT as usize {
|
||||
return;
|
||||
}
|
||||
|
||||
let (core_id, thread_id, threads_per_core) = detect_topology(apic_id);
|
||||
let core_id_combined = (core_id << 16) | (thread_id & 0xFFFF);
|
||||
|
||||
APIC_IDS[idx].store(apic_id, Ordering::Release);
|
||||
CORE_IDS[idx].store(core_id_combined, Ordering::Release);
|
||||
|
||||
let prev = THREADS_PER_CORE.load(Ordering::Acquire);
|
||||
if prev == 0 || threads_per_core > prev {
|
||||
THREADS_PER_CORE.store(threads_per_core, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_entry(cpu_id: LogicalCpuId) -> Option<CpuTopologyEntry> {
|
||||
let idx = cpu_id.get() as usize;
|
||||
if idx >= MAX_CPU_COUNT as usize {
|
||||
return None;
|
||||
}
|
||||
|
||||
let apic_id = APIC_IDS[idx].load(Ordering::Acquire);
|
||||
if apic_id == u32::MAX {
|
||||
return None;
|
||||
}
|
||||
|
||||
let core_combined = CORE_IDS[idx].load(Ordering::Acquire);
|
||||
if core_combined == u32::MAX {
|
||||
return None;
|
||||
}
|
||||
|
||||
let core_id = core_combined >> 16;
|
||||
let thread_id = core_combined & 0xFFFF;
|
||||
let threads_per_core = THREADS_PER_CORE.load(Ordering::Acquire).max(1);
|
||||
|
||||
Some(CpuTopologyEntry {
|
||||
apic_id,
|
||||
core_id,
|
||||
thread_id,
|
||||
threads_per_core,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_entries() -> Vec<(LogicalCpuId, CpuTopologyEntry)> {
|
||||
let count = crate::cpu_count();
|
||||
let mut result = Vec::new();
|
||||
for i in 0..count {
|
||||
let id = LogicalCpuId::new(i);
|
||||
if let Some(entry) = get_entry(id) {
|
||||
result.push((id, entry));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn detect_topology(initial_apic_id: u32) -> (u32, u32, u32) {
|
||||
use raw_cpuid::CpuId;
|
||||
|
||||
let cpuid = CpuId::new();
|
||||
|
||||
if let Some(topo) = cpuid.get_processor_topology_info() {
|
||||
let threads_per_core = topo.threads_per_core() as u32;
|
||||
let core_id = topo.core_id() as u32;
|
||||
if threads_per_core > 0 {
|
||||
let thread_id = initial_apic_id % threads_per_core;
|
||||
return (core_id, thread_id, threads_per_core);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(feature_info) = cpuid.get_feature_info() {
|
||||
let threads_per_core = feature_info.max_logical_processor_ids() as u32;
|
||||
if threads_per_core > 1 {
|
||||
let core_id = initial_apic_id / threads_per_core;
|
||||
let thread_id = initial_apic_id % threads_per_core;
|
||||
return (core_id, thread_id, threads_per_core);
|
||||
}
|
||||
}
|
||||
|
||||
(initial_apic_id, 0, 1)
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
fn detect_topology(initial_apic_id: u32) -> (u32, u32, u32) {
|
||||
(initial_apic_id, 0, 1)
|
||||
}
|
||||
@@ -51,6 +51,9 @@ mod cpu_set;
|
||||
/// Stats for the CPUs
|
||||
mod cpu_stats;
|
||||
|
||||
/// CPU topology detection (SMT/core mapping)
|
||||
mod cpu_topology;
|
||||
|
||||
/// Context management
|
||||
mod context;
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
use core::fmt::Write as _;
|
||||
|
||||
use crate::{
|
||||
percpu::get_all_stats,
|
||||
sync::CleanLockToken,
|
||||
syscall::error::Result,
|
||||
};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
|
||||
pub fn resource(_token: &mut CleanLockToken) -> Result<Vec<u8>> {
|
||||
let stats = get_all_stats();
|
||||
let mut out = String::new();
|
||||
let mut total_hlt: u64 = 0;
|
||||
let mut total_mwait: u64 = 0;
|
||||
|
||||
for (id, stat) in &stats {
|
||||
let _ = writeln!(
|
||||
&mut out,
|
||||
"cpu{} hlt_entries {} mwait_entries {}",
|
||||
id.get(),
|
||||
stat.hlt_entries,
|
||||
stat.mwait_entries,
|
||||
);
|
||||
|
||||
total_hlt += stat.hlt_entries;
|
||||
total_mwait += stat.mwait_entries;
|
||||
}
|
||||
|
||||
let _ = writeln!(
|
||||
&mut out,
|
||||
"total hlt_entries {} mwait_entries {}",
|
||||
total_hlt, total_mwait,
|
||||
);
|
||||
|
||||
Ok(out.into_bytes())
|
||||
}
|
||||
@@ -28,6 +28,7 @@ use super::{CallerCtx, HandleMap, KernelScheme, OpenResult, StrOrBytes};
|
||||
mod block;
|
||||
mod context;
|
||||
mod cpu;
|
||||
mod cstates;
|
||||
mod exe;
|
||||
mod fdstat;
|
||||
mod iostat;
|
||||
@@ -37,6 +38,7 @@ mod msr;
|
||||
mod sched;
|
||||
mod stat;
|
||||
mod syscall;
|
||||
mod topology;
|
||||
mod uname;
|
||||
|
||||
/// Extract the (cpu<<32 | msr) u64 handle stored in an MSR fd's
|
||||
@@ -104,6 +106,8 @@ const FILES: &[(&str, Kind)] = &[
|
||||
("block", Rd(block::resource)),
|
||||
("context", Rd(context::resource)),
|
||||
("cpu", Rd(cpu::resource)),
|
||||
("cstates", Rd(cstates::resource)),
|
||||
("topology", Rd(topology::resource)),
|
||||
#[cfg(feature = "sys_fdstat")]
|
||||
("fdstat", Rd(fdstat::resource)),
|
||||
("exe", Rd(exe::resource)),
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use core::fmt::Write as _;
|
||||
|
||||
use crate::{
|
||||
cpu_topology,
|
||||
sync::CleanLockToken,
|
||||
syscall::error::Result,
|
||||
};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
|
||||
pub fn resource(_token: &mut CleanLockToken) -> Result<Vec<u8>> {
|
||||
let entries = cpu_topology::all_entries();
|
||||
let mut out = String::new();
|
||||
|
||||
for (id, entry) in &entries {
|
||||
let _ = writeln!(
|
||||
&mut out,
|
||||
"cpu{} apic_id {} core_id {} thread_id {} threads_per_core {}",
|
||||
id.get(),
|
||||
entry.apic_id,
|
||||
entry.core_id,
|
||||
entry.thread_id,
|
||||
entry.threads_per_core,
|
||||
);
|
||||
}
|
||||
|
||||
if entries.is_empty() {
|
||||
let _ = writeln!(&mut out, "(topology not yet detected)");
|
||||
}
|
||||
|
||||
Ok(out.into_bytes())
|
||||
}
|
||||
Reference in New Issue
Block a user