Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f95576841d | |||
| 88b661fb18 | |||
| c46d3a90eb | |||
| c6a5b7a1ad | |||
| e812356cf0 |
@@ -1,3 +1,6 @@
|
||||
target
|
||||
/config.toml
|
||||
.gitlab-ci-local/
|
||||
kernel
|
||||
kernel.all
|
||||
kernel.sym
|
||||
|
||||
@@ -31,8 +31,8 @@ TARGET_SPEC=$(RUST_TARGET_PATH)/$(ARCH)-unknown-kernel.json
|
||||
KERNEL_CARGO_FEATURES?=
|
||||
|
||||
$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f)
|
||||
cd $(SOURCE) && RUSTUP_TOOLCHAIN=nightly-2025-10-03 cargo rustc \
|
||||
-Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem \
|
||||
cd $(SOURCE) && RUSTUP_TOOLCHAIN=nightly-2026-04-11 RUSTFLAGS="-Zunstable-options" cargo rustc \
|
||||
-Z json-target-spec -Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem \
|
||||
--bin kernel \
|
||||
--manifest-path "$(MANIFEST)" \
|
||||
--target "$(TARGET_SPEC)" \
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2025-10-03"
|
||||
channel = "nightly-2026-04-11"
|
||||
components = ["rust-src"]
|
||||
|
||||
@@ -69,21 +69,41 @@ impl LocalApic {
|
||||
}
|
||||
}
|
||||
|
||||
/// LAPIC Spurious Interrupt Vector.
|
||||
///
|
||||
/// Must not overlap with exception vectors (0–31) or generic IRQ vectors
|
||||
/// (32–254). Vector 255 is the conventional choice per Intel SDM Vol 3
|
||||
/// § 10.9 ("Spurious Interrupt").
|
||||
///
|
||||
/// The previous value was 0x00 (divide-error exception), meaning any LAPIC
|
||||
/// spurious interrupt was misrouted to the divide-by-zero handler — a
|
||||
/// latent bug that causes undefined behaviour on real hardware.
|
||||
const SPURIOUS_VECTOR: u32 = 0xFF;
|
||||
|
||||
/// Spurious Interrupt Vector Register value: vector | bit 8 (APIC enable).
|
||||
const SVR_VALUE: u32 = Self::SPURIOUS_VECTOR | 0x100;
|
||||
|
||||
unsafe fn init_ap(&mut self) {
|
||||
unsafe {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_APIC_BASE, rdmsr(IA32_APIC_BASE) | (1 << 10));
|
||||
wrmsr(IA32_X2APIC_SIVR, 0x100);
|
||||
wrmsr(IA32_X2APIC_SIVR, u64::from(Self::SVR_VALUE));
|
||||
} else {
|
||||
self.write(0xF0, 0x100);
|
||||
self.write(0xF0, Self::SVR_VALUE);
|
||||
}
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,10 @@ fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt, backup_stack_end: usize) {
|
||||
});
|
||||
}
|
||||
|
||||
// Override vector 0xFF (255) with the LAPIC spurious interrupt handler.
|
||||
// The generic stub loop above fills 32..=255, so this must come after.
|
||||
current_idt[0xFF].set_func(irq::lapic_spurious);
|
||||
|
||||
// reserve bits 31:0, i.e. the first 32 interrupts, which are reserved for exceptions
|
||||
*current_reservations[0].get_mut() |= 0x0000_0000_FFFF_FFFF;
|
||||
|
||||
@@ -251,6 +255,7 @@ fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt, backup_stack_end: usize) {
|
||||
idt.set_reserved_mut(IpiKind::Switch as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Tlb as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Pit as u8, true);
|
||||
idt.set_reserved_mut(0xFF, true);
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@ pub enum IrqMethod {
|
||||
|
||||
static SPURIOUS_COUNT_IRQ7: AtomicUsize = AtomicUsize::new(0);
|
||||
static SPURIOUS_COUNT_IRQ15: AtomicUsize = AtomicUsize::new(0);
|
||||
static SPURIOUS_COUNT_LAPIC: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub fn spurious_count_irq7() -> usize {
|
||||
SPURIOUS_COUNT_IRQ7.load(Ordering::Relaxed)
|
||||
@@ -33,20 +34,21 @@ pub fn spurious_count_irq7() -> usize {
|
||||
pub fn spurious_count_irq15() -> usize {
|
||||
SPURIOUS_COUNT_IRQ15.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn spurious_count_lapic() -> usize {
|
||||
SPURIOUS_COUNT_LAPIC.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn spurious_count() -> usize {
|
||||
spurious_count_irq7() + spurious_count_irq15()
|
||||
spurious_count_irq7() + spurious_count_irq15() + spurious_count_lapic()
|
||||
}
|
||||
pub fn spurious_irq_resource(_token: &mut CleanLockToken) -> syscall::Result<Vec<u8>> {
|
||||
match irq_method() {
|
||||
IrqMethod::Apic => Ok(Vec::from(&b"(not implemented for APIC yet)"[..])),
|
||||
IrqMethod::Pic => Ok(format!(
|
||||
"{}\tIRQ7\n{}\tIRQ15\n{}\ttotal\n",
|
||||
spurious_count_irq7(),
|
||||
spurious_count_irq15(),
|
||||
spurious_count()
|
||||
)
|
||||
.into_bytes()),
|
||||
}
|
||||
Ok(format!(
|
||||
"{}\tIRQ7\n{}\tIRQ15\n{}\tLAPIC\n{}\ttotal\n",
|
||||
spurious_count_irq7(),
|
||||
spurious_count_irq15(),
|
||||
spurious_count_lapic(),
|
||||
spurious_count()
|
||||
)
|
||||
.into_bytes())
|
||||
}
|
||||
|
||||
static IRQ_METHOD: AtomicUsize = AtomicUsize::new(IrqMethod::Pic as usize);
|
||||
@@ -320,6 +322,10 @@ interrupt!(lapic_error, || {
|
||||
unsafe { lapic_eoi() };
|
||||
});
|
||||
|
||||
interrupt!(lapic_spurious, || {
|
||||
SPURIOUS_COUNT_LAPIC.fetch_add(1, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
interrupt_error!(generic_irq, |_stack, code| {
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -127,8 +127,11 @@ pub fn run_contexts(token: LockToken<'_, L0>) -> MutexGuard<'_, L1, RunContextDa
|
||||
|
||||
fn least_loaded_cpu() -> LogicalCpuId {
|
||||
let current_cpu = crate::cpu_id();
|
||||
let topo = crate::numa::topology();
|
||||
let mut best_cpu = current_cpu;
|
||||
let mut best_depth = usize::MAX;
|
||||
let mut best_local_cpu = current_cpu;
|
||||
let mut best_local_depth = usize::MAX;
|
||||
|
||||
for raw_id in 0..crate::cpu_count() {
|
||||
let cpu_id = LogicalCpuId::new(raw_id);
|
||||
@@ -144,9 +147,18 @@ fn least_loaded_cpu() -> LogicalCpuId {
|
||||
best_depth = depth;
|
||||
best_cpu = cpu_id;
|
||||
}
|
||||
|
||||
if topo.same_node(current_cpu, cpu_id) && depth < best_local_depth {
|
||||
best_local_depth = depth;
|
||||
best_local_cpu = cpu_id;
|
||||
}
|
||||
}
|
||||
|
||||
best_cpu
|
||||
if best_local_depth < usize::MAX && best_local_depth <= best_depth + 1 {
|
||||
best_local_cpu
|
||||
} else {
|
||||
best_cpu
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(token: &mut CleanLockToken) {
|
||||
|
||||
+13
-1
@@ -74,7 +74,19 @@ pub fn signal_handler(token: &mut CleanLockToken) {
|
||||
pub fn excp_handler(excp: syscall::Exception) {
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
|
||||
let current = context::current();
|
||||
let Some(current) = context::try_current() else {
|
||||
let kind = excp.kind;
|
||||
let code = excp.code;
|
||||
let address = excp.address;
|
||||
info!(
|
||||
"excp_handler: no current context (early boot), CPU {}, kind {}, code {}, address {:#x}",
|
||||
crate::cpu_id(),
|
||||
kind,
|
||||
code,
|
||||
address
|
||||
);
|
||||
panic!("unhandled exception during early boot (no context)");
|
||||
};
|
||||
|
||||
let context = current.write(token.token());
|
||||
|
||||
|
||||
+31
-2
@@ -162,6 +162,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
||||
|
||||
let percpu = PercpuBlock::current();
|
||||
cpu_stats::add_context_switch();
|
||||
percpu.stats.context_switches.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
//set PIT Interrupt counter to 0, giving each process same amount of PIT ticks
|
||||
percpu.switch_internals.pit_ticks.set(0);
|
||||
@@ -399,7 +400,9 @@ fn steal_work(
|
||||
continue;
|
||||
};
|
||||
|
||||
let victim_idle = victim.switch_internals.idle_context();
|
||||
let Some(victim_idle) = victim.switch_internals.try_idle_context() else {
|
||||
continue;
|
||||
};
|
||||
let mut victim_lock = SchedQueuesLock::new(&victim.sched);
|
||||
let victim_queues = unsafe { victim_lock.queues_mut() };
|
||||
|
||||
@@ -422,6 +425,9 @@ fn steal_work(
|
||||
if let UpdateResult::CanSwitch = sw {
|
||||
assign_context_to_cpu(&mut context_guard, cpu_id);
|
||||
SCHED_STEAL_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
if let Some(thief) = get_percpu_block(cpu_id) {
|
||||
thief.stats.steals.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
return Some(context_guard);
|
||||
}
|
||||
|
||||
@@ -469,7 +475,9 @@ fn migrate_one_context(
|
||||
return false;
|
||||
};
|
||||
|
||||
let source_idle = source.switch_internals.idle_context();
|
||||
let Some(source_idle) = source.switch_internals.try_idle_context() else {
|
||||
return false;
|
||||
};
|
||||
let moved = {
|
||||
let mut source_lock = SchedQueuesLock::new(&source.sched);
|
||||
let source_queues = unsafe { source_lock.queues_mut() };
|
||||
@@ -691,6 +699,10 @@ fn pick_next_from_queues(
|
||||
let mut vruntime = guard.vruntime;
|
||||
if guard.last_cpu == Some(cpu_id) {
|
||||
vruntime = vruntime.saturating_sub(vruntime / 8);
|
||||
} else if let Some(last_cpu) = guard.last_cpu {
|
||||
if crate::numa::topology().same_node(cpu_id, last_cpu) {
|
||||
vruntime = vruntime.saturating_sub(vruntime / 16);
|
||||
}
|
||||
}
|
||||
drop(guard);
|
||||
if vruntime < min_vruntime {
|
||||
@@ -866,6 +878,10 @@ fn pick_next_from_global_queues(
|
||||
let mut vruntime = guard.vruntime;
|
||||
if guard.last_cpu == Some(cpu_id) {
|
||||
vruntime = vruntime.saturating_sub(vruntime / 8);
|
||||
} else if let Some(last_cpu) = guard.last_cpu {
|
||||
if crate::numa::topology().same_node(cpu_id, last_cpu) {
|
||||
vruntime = vruntime.saturating_sub(vruntime / 16);
|
||||
}
|
||||
}
|
||||
drop(guard);
|
||||
if vruntime < min_vruntime {
|
||||
@@ -1174,4 +1190,17 @@ impl ContextSwitchPercpu {
|
||||
.expect("no idle context present"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Retrieves the current idle context if it has been initialized.
|
||||
///
|
||||
/// This is the fallible variant of [`idle_context`], intended for
|
||||
/// cross-CPU paths (`steal_work`, `migrate_one_context`) that may
|
||||
/// access a PercpuBlock whose `context::init()` has not run yet
|
||||
/// during AP bring-up.
|
||||
///
|
||||
/// # Returns
|
||||
/// `Some(Arc<ContextLock>)` if the idle context is set, `None` otherwise.
|
||||
pub fn try_idle_context(&self) -> Option<Arc<ContextLock>> {
|
||||
self.idle_ctxt.borrow().as_ref().map(Arc::clone)
|
||||
}
|
||||
}
|
||||
|
||||
+26
-11
@@ -28,18 +28,16 @@ pub enum CpuState {
|
||||
/// Statistics for the CPUs.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CpuStats {
|
||||
/// Number of ticks spent on userspace contexts
|
||||
user: AtomicU64,
|
||||
/// Number of ticks spent on Niced userspace contexts
|
||||
nice: AtomicU64,
|
||||
/// Number of ticks spent on kernel contexts
|
||||
kernel: AtomicU64,
|
||||
/// Number of ticks spent idle
|
||||
idle: AtomicU64,
|
||||
/// Number of times the CPU handled an interrupt
|
||||
irq: AtomicU64,
|
||||
/// Current state of the CPU
|
||||
state: AtomicU8,
|
||||
pub context_switches: AtomicU64,
|
||||
pub steals: AtomicU64,
|
||||
hlt_entries: AtomicU64,
|
||||
mwait_entries: AtomicU64,
|
||||
}
|
||||
|
||||
impl CpuStats {
|
||||
@@ -51,21 +49,24 @@ impl CpuStats {
|
||||
idle: AtomicU64::new(0),
|
||||
irq: AtomicU64::new(0),
|
||||
state: AtomicU8::new(0),
|
||||
context_switches: AtomicU64::new(0),
|
||||
steals: AtomicU64::new(0),
|
||||
hlt_entries: AtomicU64::new(0),
|
||||
mwait_entries: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CpuStatsData {
|
||||
/// Number of ticks spent on userspace contexts
|
||||
pub user: u64,
|
||||
/// Number of ticks spent on Niced userspace contexts
|
||||
pub nice: u64,
|
||||
/// Number of ticks spent on kernel contexts
|
||||
pub kernel: u64,
|
||||
/// Number of ticks spent idle
|
||||
pub idle: u64,
|
||||
/// Number of times the CPU handled an interrupt
|
||||
pub irq: u64,
|
||||
pub context_switches: u64,
|
||||
pub steals: u64,
|
||||
pub hlt_entries: u64,
|
||||
pub mwait_entries: u64,
|
||||
}
|
||||
|
||||
impl CpuStats {
|
||||
@@ -108,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 {
|
||||
@@ -128,6 +139,10 @@ impl From<&CpuStats> for CpuStatsData {
|
||||
kernel: val.kernel.load(Ordering::Relaxed),
|
||||
idle: val.idle.load(Ordering::Relaxed),
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -70,6 +73,8 @@ mod log;
|
||||
/// Memory management
|
||||
mod memory;
|
||||
|
||||
mod numa;
|
||||
|
||||
/// Panic
|
||||
mod panic;
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const MAX_NUMA_NODES: usize = 8;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct NumaHint {
|
||||
pub node_id: u8,
|
||||
pub cpus: LogicalCpuSet,
|
||||
|
||||
@@ -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,14 +28,17 @@ use super::{CallerCtx, HandleMap, KernelScheme, OpenResult, StrOrBytes};
|
||||
mod block;
|
||||
mod context;
|
||||
mod cpu;
|
||||
mod cstates;
|
||||
mod exe;
|
||||
mod fdstat;
|
||||
mod iostat;
|
||||
mod irq;
|
||||
mod log;
|
||||
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
|
||||
@@ -103,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)),
|
||||
@@ -115,6 +120,7 @@ const FILES: &[(&str, Kind)] = &[
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
("spurious_irq", Rd(interrupt::irq::spurious_irq_resource)),
|
||||
("stat", Rd(stat::resource)),
|
||||
("sched", Rd(sched::resource)),
|
||||
// Disabled because the debugger is inherently unsafe and probably will break the system.
|
||||
/*
|
||||
("trigger_debugger", Rd(|token| unsafe {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
use core::fmt::Write as _;
|
||||
|
||||
use crate::{
|
||||
percpu::{get_all_stats, get_percpu_block},
|
||||
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_switches: u64 = 0;
|
||||
let mut total_steals: u64 = 0;
|
||||
|
||||
for (id, stat) in &stats {
|
||||
let queue_depth = get_percpu_block(*id)
|
||||
.map(|p| {
|
||||
p.sched.take_lock();
|
||||
let d = unsafe { p.sched.queues().iter().map(|q| q.len()).sum::<usize>() };
|
||||
p.sched.release_lock();
|
||||
d
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
let _ = writeln!(
|
||||
&mut out,
|
||||
"cpu{} switches {} steals {} queue_depth {}",
|
||||
id.get(),
|
||||
stat.context_switches,
|
||||
stat.steals,
|
||||
queue_depth,
|
||||
);
|
||||
|
||||
total_switches += stat.context_switches;
|
||||
total_steals += stat.steals;
|
||||
}
|
||||
|
||||
let _ = writeln!(
|
||||
&mut out,
|
||||
"total switches {} steals {}",
|
||||
total_switches, total_steals,
|
||||
);
|
||||
|
||||
Ok(out.into_bytes())
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
+2
-1
@@ -159,7 +159,8 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
|
||||
//Initialize the first context, stored in kernel/src/context/mod.rs
|
||||
context::init(&mut token);
|
||||
|
||||
//Initialize global schemes, such as `acpi:`.
|
||||
crate::numa::init_default();
|
||||
|
||||
scheme::init_globals();
|
||||
|
||||
debug!("BSP: {} CPUs", crate::cpu_count());
|
||||
|
||||
Reference in New Issue
Block a user