Commit e32cab8a authored by Stefan Lankes's avatar Stefan Lankes Committed by Stefan Lankes
Browse files

counts the interrupts and prints the results during shutdown

parent 77047547
......@@ -8,8 +8,10 @@
use crate::arch;
#[cfg(feature = "acpi")]
use crate::arch::x86_64::kernel::acpi;
use crate::arch::x86_64::kernel::irq::IrqStatistics;
#[cfg(not(test))]
use crate::arch::x86_64::kernel::smp_boot_code::SMP_BOOT_CODE;
use crate::arch::x86_64::kernel::IRQ_COUNTERS;
use crate::arch::x86_64::mm::paging::{BasePageSize, PageSize, PageTableEntryFlags};
use crate::arch::x86_64::mm::{paging, virtualmem};
use crate::collections::CachePadded;
......@@ -137,6 +139,7 @@ impl fmt::Display for IoApicRecord {
extern "x86-interrupt" fn tlb_flush_handler(_stack_frame: &mut irq::ExceptionStackFrame) {
debug!("Received TLB Flush Interrupt");
increment_irq_counter(TLB_FLUSH_INTERRUPT_NUMBER.into());
unsafe {
cr3_write(cr3());
}
......@@ -158,6 +161,7 @@ extern "x86-interrupt" fn spurious_interrupt_handler(stack_frame: &mut irq::Exce
extern "x86-interrupt" fn wakeup_handler(_stack_frame: &mut irq::ExceptionStackFrame) {
debug!("Received Wakeup Interrupt");
increment_irq_counter(WAKEUP_INTERRUPT_NUMBER.into());
let core_scheduler = core_scheduler();
core_scheduler.check_input();
eoi();
......@@ -270,6 +274,13 @@ pub extern "C" fn eoi() {
}
pub fn init() {
let boxed_irq = Box::new(IrqStatistics::new());
let boxed_irq_raw = Box::into_raw(boxed_irq);
unsafe {
IRQ_COUNTERS.insert(0, &(*boxed_irq_raw));
PERCORE.irq_statistics.set(boxed_irq_raw);
}
// Initialize an empty vector for the Local APIC IDs of all CPUs.
unsafe {
CPU_LOCAL_APIC_IDS = Some(Vec::new());
......@@ -300,6 +311,7 @@ pub fn init() {
// Set gates to ISRs for the APIC interrupts we are going to enable.
idt::set_gate(TLB_FLUSH_INTERRUPT_NUMBER, tlb_flush_handler as usize, 0);
irq::add_irq_name((TLB_FLUSH_INTERRUPT_NUMBER - 32).into(), "TLB flush");
idt::set_gate(ERROR_INTERRUPT_NUMBER, error_interrupt_handler as usize, 0);
idt::set_gate(
SPURIOUS_INTERRUPT_NUMBER,
......@@ -307,6 +319,7 @@ pub fn init() {
0,
);
idt::set_gate(WAKEUP_INTERRUPT_NUMBER, wakeup_handler as usize, 0);
irq::add_irq_name((WAKEUP_INTERRUPT_NUMBER - 32).into(), "Wakeup");
// Initialize interrupt handling over APIC.
// All interrupts of the PIC have already been masked, so it doesn't need to be disabled again.
......@@ -482,8 +495,14 @@ pub fn init_next_processor_variables(core_id: CoreId) {
// Allocate stack and PerCoreVariables structure for the CPU and pass the addresses.
// Keep the stack executable to possibly support dynamically generated code on the stack (see https://security.stackexchange.com/a/47825).
let stack = mm::allocate(KERNEL_STACK_SIZE, true);
let boxed_percore = Box::new(CachePadded::new(PerCoreInnerVariables::new(core_id)));
let mut boxed_percore = Box::new(CachePadded::new(PerCoreInnerVariables::new(core_id)));
let boxed_irq = Box::new(IrqStatistics::new());
let boxed_irq_raw = Box::into_raw(boxed_irq);
unsafe {
IRQ_COUNTERS.insert(core_id, &(*boxed_irq_raw));
boxed_percore.irq_statistics = PerCoreVariable::new(boxed_irq_raw);
core::ptr::write_volatile(&mut (*BOOT_INFO).current_stack_address, stack as u64);
core::ptr::write_volatile(
&mut (*BOOT_INFO).current_percore_address,
......
......@@ -12,9 +12,16 @@ use crate::arch::x86_64::kernel::percore::*;
use crate::arch::x86_64::kernel::processor;
use crate::arch::x86_64::mm::paging;
use crate::scheduler;
use crate::synch::spinlock::SpinlockIrqSave;
use crate::x86::bits64::rflags;
use crate::alloc::string::ToString;
use alloc::collections::BTreeMap;
use alloc::string::String;
use core::fmt;
static IRQ_NAMES: SpinlockIrqSave<BTreeMap<u32, String>> = SpinlockIrqSave::new(BTreeMap::new());
// Derived from Philipp Oppermann's blog
// => https://github.com/phil-opp/blog_os/blob/master/src/interrupts/mod.rs
/// Represents the exception stack frame pushed by the CPU on exception entry.
......@@ -197,9 +204,20 @@ pub extern "C" fn irq_install_handler(irq_number: u32, handler: usize) {
idt::set_gate((32 + irq_number) as u8, handler, 0);
}
pub fn add_irq_name(irq_number: u32, name: &'static str) {
debug!("Install handler for interrupt {}", irq_number);
IRQ_NAMES.lock().insert(32 + irq_number, name.to_string());
}
pub fn get_irq_name(irq_number: u32) -> Option<String> {
let name = IRQ_NAMES.lock().get(&irq_number)?.clone();
Some(name)
}
fn unhandled_interrupt(irq_number: u8) {
warn!("Receive unhandled interrupt {}", irq_number);
apic::eoi();
increment_irq_counter((32 + irq_number).into());
}
extern "x86-interrupt" fn unhandled_interrupt0(_stack_frame: &mut ExceptionStackFrame) {
......@@ -374,6 +392,8 @@ extern "x86-interrupt" fn device_not_available_exception(_stack_frame: &mut Exce
// We set the CR0_TASK_SWITCHED flag every time we switch to a task.
// This causes the "Device Not Available" Exception (int #7) to be thrown as soon as we use the FPU for the first time.
increment_irq_counter(7);
// Clear CR0_TASK_SWITCHED so this doesn't happen again before the next switch.
unsafe {
llvm_asm!("clts" :::: "volatile");
......@@ -470,3 +490,19 @@ extern "x86-interrupt" fn reserved_exception(stack_frame: &mut ExceptionStackFra
error!("Reserved Exception: {:#?}", stack_frame);
scheduler::abort();
}
#[derive(Clone, Copy)]
#[repr(align(64))]
pub struct IrqStatistics {
pub counters: [u64; 256],
}
impl IrqStatistics {
pub const fn new() -> Self {
IrqStatistics { counters: [0; 256] }
}
pub fn inc(&mut self, pos: usize) {
self.counters[pos] = self.counters[pos] + 1;
}
}
......@@ -35,11 +35,16 @@ pub mod virtio;
pub mod virtio_fs;
pub mod virtio_net;
use crate::arch::x86_64::kernel::irq::{get_irq_name, IrqStatistics};
use crate::arch::x86_64::kernel::percore::*;
use crate::arch::x86_64::kernel::serial::SerialPort;
use crate::environment;
use crate::kernel_message_buffer;
use crate::scheduler::CoreId;
use alloc::collections::BTreeMap;
use core::convert::TryInto;
#[cfg(feature = "newlib")]
use core::slice;
use core::{intrinsics, ptr};
......@@ -47,6 +52,9 @@ use x86::controlregs::{cr0, cr4};
const SERIAL_PORT_BAUDRATE: u32 = 115_200;
/// Map between Core ID and per-core scheduler
static mut IRQ_COUNTERS: BTreeMap<CoreId, &IrqStatistics> = BTreeMap::new();
#[repr(C)]
pub struct BootInfo {
magic_number: u32,
......@@ -374,3 +382,23 @@ fn finish_processor_init() {
let _ = intrinsics::atomic_xadd(&mut (*BOOT_INFO).cpu_online as *mut u32, 1);
}
}
pub fn print_statistics() {
info!("Number of interrupts");
unsafe {
for (core_id, irg_statistics) in IRQ_COUNTERS.iter() {
for (i, counter) in irg_statistics.counters.iter().enumerate() {
if *counter > 0 {
match get_irq_name(i.try_into().unwrap()) {
Some(name) => {
info!("[{}][{}]: {}", core_id, name, *counter);
}
_ => {
info!("[{}][{}]: {}", core_id, i, *counter);
}
}
}
}
}
}
}
......@@ -5,6 +5,7 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use crate::arch::x86_64::kernel::irq::IrqStatistics;
use crate::arch::x86_64::kernel::BOOT_INFO;
use crate::collections::CachePadded;
use crate::scheduler::{CoreId, PerCoreScheduler};
......@@ -25,6 +26,8 @@ pub struct PerCoreInnerVariables {
pub tss: PerCoreVariable<*mut TaskStateSegment>,
/// start address of the kernel stack
pub kernel_stack: PerCoreVariable<u64>,
/// Interface to the interrupt counters
pub irq_statistics: PerCoreVariable<*mut IrqStatistics>,
}
impl PerCoreInnerVariables {
......@@ -34,6 +37,7 @@ impl PerCoreInnerVariables {
scheduler: PerCoreVariable::new(ptr::null_mut() as *mut PerCoreScheduler),
tss: PerCoreVariable::new(ptr::null_mut() as *mut TaskStateSegment),
kernel_stack: PerCoreVariable::new(0),
irq_statistics: PerCoreVariable::new(ptr::null_mut() as *mut IrqStatistics),
}
}
}
......@@ -49,7 +53,7 @@ pub trait PerCoreVariableMethods<T> {
}
impl<T> PerCoreVariable<T> {
const fn new(value: T) -> Self {
pub const fn new(value: T) -> Self {
Self { data: value }
}
......@@ -131,6 +135,14 @@ pub fn set_core_scheduler(scheduler: *mut PerCoreScheduler) {
}
}
#[inline]
pub fn increment_irq_counter(irq_no: usize) {
unsafe {
let irq = &mut *PERCORE.irq_statistics.get();
irq.inc(irq_no);
}
}
pub fn init() {
unsafe {
// Store the address to the PerCoreVariables structure allocated for this core in GS.
......
......@@ -10,7 +10,7 @@ use crate::arch::x86_64::kernel::irq::*;
use crate::arch::x86_64::kernel::pci::{
self, get_network_driver, PciAdapter, PciClassCode, PciDriver, PciNetworkControllerSubclass,
};
use crate::arch::x86_64::kernel::percore::core_scheduler;
use crate::arch::x86_64::kernel::percore::{core_scheduler, increment_irq_counter};
use crate::arch::x86_64::kernel::virtio_fs;
use crate::arch::x86_64::kernel::virtio_net;
......@@ -846,13 +846,21 @@ pub fn init_virtio_device(adapter: &pci::PciAdapter) {
};
// Install interrupt handler
unsafe {
VIRTIO_IRQ_NO = adapter.irq;
}
irq_install_handler(adapter.irq as u32, virtio_irqhandler as usize);
add_irq_name(adapter.irq as u32, "virtio");
}
/// Specifies the interrupt number of the virtio device
static mut VIRTIO_IRQ_NO: u8 = 0;
#[cfg(target_arch = "x86_64")]
extern "x86-interrupt" fn virtio_irqhandler(_stack_frame: &mut ExceptionStackFrame) {
debug!("Receive virtio interrupt");
apic::eoi();
increment_irq_counter((32 + unsafe { VIRTIO_IRQ_NO }).into());
let check_scheduler = match get_network_driver() {
Some(driver) => driver.borrow_mut().handle_interrupt(),
......
......@@ -160,6 +160,9 @@ pub fn sys_netwait(millis: Option<u64>) {
}
pub fn __sys_shutdown(arg: i32) -> ! {
// print some performance statistics
crate::arch::kernel::print_statistics();
unsafe { SYS.shutdown(arg) }
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment