Next Generation WASM Microkernel Operating System
at main 122 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}; 29 30use k23_cpu_local::collection::CpuLocal; 31 32/// Declares a new counter. 33#[macro_export] 34macro_rules! counter { 35 ($name:expr) => {{ 36 #[unsafe(link_section = concat!(".bss.kcounter.", $name))] 37 static ARENA: $crate::k23_cpu_local::CpuLocal<::core::sync::atomic::AtomicU64> = 38 $crate::cpu_local::CpuLocal::new(); 39 40 Counter::new(&ARENA, $name) 41 }}; 42} 43 44/// A kernel counter. 45pub struct Counter { 46 arena: &'static CpuLocal<AtomicU64>, 47 name: &'static str, 48} 49 50impl Counter { 51 #[doc(hidden)] 52 pub const fn new(arena: &'static CpuLocal<AtomicU64>, name: &'static str) -> Self { 53 Self { arena, name } 54 } 55 56 /// Increment the counter. 57 pub fn increment(&self, value: u64) { 58 self.arena 59 .get_or_default() 60 .fetch_add(value, Ordering::Relaxed); 61 } 62 63 /// Decrement the counter. 64 pub fn decrement(&self, value: u64) { 65 self.arena 66 .get_or_default() 67 .fetch_sub(value, Ordering::Relaxed); 68 } 69 70 /// Set the absolute value of the counter. 71 pub fn set(&self, value: u64) { 72 self.arena.get_or_default().store(value, Ordering::Relaxed); 73 } 74 75 /// Set the absolute value of the counter if the provided value is larger than the current value. 76 pub fn max(&self, value: u64) { 77 let _ = 78 self.arena 79 .get_or_default() 80 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| { 81 (old < value).then_some(value) 82 }); 83 } 84 85 /// Set the absolute value of the counter if the provided value is smaller than the current value. 86 pub fn min(&self, value: u64) { 87 let _ = 88 self.arena 89 .get_or_default() 90 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| { 91 (old > value).then_some(value) 92 }); 93 } 94 95 /// Get the counter value of the calling cpu, or `None` if the counter was never written to. 96 pub fn get(&self) -> Option<u64> { 97 Some(self.arena.get()?.load(Ordering::Relaxed)) 98 } 99 100 /// Return the sum of all counters across all cpus. 101 pub fn sum_across_all_cpus(&self) -> u64 { 102 self.arena.iter().map(|v| v.load(Ordering::Relaxed)).sum() 103 } 104 105 /// Return the largest value from across all CPUs. 106 pub fn max_across_all_cpus(&self) -> u64 { 107 self.arena 108 .iter() 109 .map(|v| v.load(Ordering::Relaxed)) 110 .max() 111 .unwrap() 112 } 113 114 /// Return the smallest value from across all CPUs. 115 pub fn min_across_all_cpus(&self) -> u64 { 116 self.arena 117 .iter() 118 .map(|v| v.load(Ordering::Relaxed)) 119 .min() 120 .unwrap() 121 } 122}