Next Generation WASM Microkernel Operating System
at trap_handler 121 lines 3.9 kB view raw
1// Copyright 2025 Jonas Kruckenberg 2// 3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5// http://opensource.org/licenses/MIT>, at your option. This file may not be 6// copied, modified, or distributed except according to those terms. 7//! Kernel counters 8//! 9//! Kernel counters are per-cpu, unsigned integer counters that facilitate diagnostics across the 10//! whole kernel. Questions like "how many times has X happened over N seconds?", "has X ever happened?" 11//! can be answered using this API. 12//! 13//! Counters are declared in their respective modules like so: 14//! ```rust 15//! use crate::metrics::{Counter, counter}; 16//! 17//! static TEST_CNT: Counter = counter!("test-event"); 18//! 19//! fn some_function() { 20//! TEST_CNT.increment(1); 21//! } 22//! ``` 23//! 24//! Kernel counters are always per-cpu, which means each cpu keeps an individual counter. Methods 25//! on `Counter` can be used to sum events across cpus or even get the maximum or minimum value across 26//! cpus. 27 28use core::sync::atomic::{AtomicU64, Ordering}; 29use cpu_local::collection::CpuLocal; 30 31/// Declares a new counter. 32#[macro_export] 33macro_rules! counter { 34 ($name:expr) => {{ 35 #[unsafe(link_section = concat!(".bss.kcounter.", $name))] 36 static ARENA: $crate::cpu_local::CpuLocal<::core::sync::atomic::AtomicU64> = 37 $crate::cpu_local::CpuLocal::new(); 38 39 Counter::new(&ARENA, $name) 40 }}; 41} 42 43/// A kernel counter. 44pub struct Counter { 45 arena: &'static CpuLocal<AtomicU64>, 46 name: &'static str, 47} 48 49impl Counter { 50 #[doc(hidden)] 51 pub const fn new(arena: &'static CpuLocal<AtomicU64>, name: &'static str) -> Self { 52 Self { arena, name } 53 } 54 55 /// Increment the counter. 56 pub fn increment(&self, value: u64) { 57 self.arena 58 .get_or_default() 59 .fetch_add(value, Ordering::Relaxed); 60 } 61 62 /// Decrement the counter. 63 pub fn decrement(&self, value: u64) { 64 self.arena 65 .get_or_default() 66 .fetch_sub(value, Ordering::Relaxed); 67 } 68 69 /// Set the absolute value of the counter. 70 pub fn set(&self, value: u64) { 71 self.arena.get_or_default().store(value, Ordering::Relaxed); 72 } 73 74 /// Set the absolute value of the counter if the provided value is larger than the current value. 75 pub fn max(&self, value: u64) { 76 let _ = 77 self.arena 78 .get_or_default() 79 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| { 80 (old < value).then_some(value) 81 }); 82 } 83 84 /// Set the absolute value of the counter if the provided value is smaller than the current value. 85 pub fn min(&self, value: u64) { 86 let _ = 87 self.arena 88 .get_or_default() 89 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| { 90 (old > value).then_some(value) 91 }); 92 } 93 94 /// Get the counter value of the calling cpu, or `None` if the counter was never written to. 95 pub fn get(&self) -> Option<u64> { 96 Some(self.arena.get()?.load(Ordering::Relaxed)) 97 } 98 99 /// Return the sum of all counters across all cpus. 100 pub fn sum_across_all_cpus(&self) -> u64 { 101 self.arena.iter().map(|v| v.load(Ordering::Relaxed)).sum() 102 } 103 104 /// Return the largest value from across all CPUs. 105 pub fn max_across_all_cpus(&self) -> u64 { 106 self.arena 107 .iter() 108 .map(|v| v.load(Ordering::Relaxed)) 109 .max() 110 .unwrap() 111 } 112 113 /// Return the smallest value from across all CPUs. 114 pub fn min_across_all_cpus(&self) -> u64 { 115 self.arena 116 .iter() 117 .map(|v| v.load(Ordering::Relaxed)) 118 .min() 119 .unwrap() 120 } 121}