diff options
Diffstat (limited to 'rust/kernel/perf_event.rs')
-rw-r--r-- | rust/kernel/perf_event.rs | 315 |
1 files changed, 157 insertions, 158 deletions
diff --git a/rust/kernel/perf_event.rs b/rust/kernel/perf_event.rs index 0326b5868afe..81fba3408503 100644 --- a/rust/kernel/perf_event.rs +++ b/rust/kernel/perf_event.rs @@ -5,7 +5,10 @@ //! //! C header: [`include/linux/perf_event.h`](srctree/include/linux/perf_event.h) +use crate::alloc::AllocError; +use crate::error::from_err_ptr; use crate::prelude::*; +use crate::types::ARef; use crate::{bindings, task::Task, types::Opaque}; /// Represents a type of performance event to monitor @@ -21,6 +24,7 @@ pub enum EventType { /// Hardware performance events that can be monitored #[derive(Debug, Clone, Copy)] +#[non_exhaustive] pub enum HardwareEvent { /// Total CPU cycles (both used and idle) /// Does not include cycles when the CPU is idle @@ -65,6 +69,7 @@ pub enum HardwareEvent { /// Software performance events that can be monitored #[derive(Debug, Clone, Copy)] +#[non_exhaustive] pub enum SoftwareEvent { /// CPU clock, a high-resolution per-CPU timer /// Measures time spent on this CPU in nanoseconds @@ -123,26 +128,56 @@ impl PerfEventAttr { } } -/// Overflow handler for perf events -#[repr(transparent)] -pub struct PerfOverflowHandler { - inner: Opaque<bindings::perf_overflow_handler_t>, +/// Wrapper for sample data +pub struct SampleData { + inner: Opaque<bindings::perf_sample_data>, } -impl Default for PerfOverflowHandler { - fn default() -> Self { - Self { - inner: Opaque::new(None), +impl SampleData { + /// Returns a reference to the underlying data + pub fn get(&self) -> Option<&bindings::perf_sample_data> { + let ptr = self.inner.get(); + if ptr.is_null() { + return None; + } + Some(unsafe { &*ptr }) + } +} + +/// Wrapper for the current register values when the overflow handler is called +pub struct Registers { + inner: Opaque<bindings::pt_regs>, +} + +impl Registers { + /// Returns a reference to the underlying data + pub fn get(&self) -> Option<&bindings::pt_regs> { + let ptr = self.inner.get(); + if ptr.is_null() { + return None; } + Some(unsafe { &*ptr }) } } +/// Handler function for overflow events + /// Perf Event wrapper #[repr(transparent)] pub struct PerfEvent { inner: *mut bindings::perf_event, } +/// Perf Event wrapper +#[repr(transparent)] +pub struct PerfEventRef { + inner: *const bindings::perf_event, +} + +// SAFETY: perf_event has internal locking for thread safety +unsafe impl Send for PerfEvent {} +unsafe impl Sync for PerfEvent {} + impl PerfEvent { /// Returns a raw pointer to the inner C struct. #[inline] @@ -175,6 +210,16 @@ impl PerfEvent { impl Drop for PerfEvent { fn drop(&mut self) { + if !self.inner.is_null() { + let context_ptr = + unsafe { *self.inner }.overflow_handler_context as *mut OverflowHandler; + if !context_ptr.is_null() { + let OverflowHandler { closure, dyn_fn } = unsafe { context_ptr.read() }; + let _ = dyn_fn; + unsafe { KBox::from_raw(closure) }; + } + } + unsafe { bindings::perf_event_release_kernel(self.inner); } @@ -195,7 +240,8 @@ pub struct EventBuilder { exclude_hv: bool, exclude_idle: bool, cpu: Option<i32>, - task: Option<Task>, + task: Option<ARef<Task>>, + overflow_handler: Option<OverflowHandler>, } /// Error type for performance event operations @@ -207,6 +253,14 @@ pub enum Error { InvalidCpu, /// Invalid task specified InvalidTask, + /// MemoryAllocation Error + Alloc(AllocError), +} + +impl From<AllocError> for Error { + fn from(value: AllocError) -> Self { + Self::Alloc(value) + } } // Implementation of From traits for event types @@ -266,6 +320,7 @@ impl EventBuilder { exclude_idle: false, cpu: None, task: None, + overflow_handler: None, } } @@ -338,11 +393,17 @@ impl EventBuilder { } /// Monitor events for a specific task (None for per-CPU mode) - pub fn task(mut self, task: Task) -> Self { + pub fn task(mut self, task: ARef<Task>) -> Self { self.task = Some(task); self } + /// Set handler for overflow events + pub fn on_overflow(mut self, handler: OverflowHandler) -> Self { + self.overflow_handler = Some(handler); + self + } + /// Build the perf event pub fn build(self) -> Result<PerfEvent, Error> { // Create the perf_event_attr structure @@ -388,7 +449,7 @@ impl EventBuilder { perf_event_attr, cpu, self.task, - PerfOverflowHandler::default(), + self.overflow_handler, ) .map_err(|_| Error::InvalidConfig)?; @@ -406,17 +467,21 @@ impl EventBuilder { pub fn perf_event_create_kernel_counter( perf_event_attr: PerfEventAttr, cpu: i32, - task: Option<Task>, - overflow: PerfOverflowHandler, + task: Option<ARef<Task>>, + overflow: Option<OverflowHandler>, ) -> Result<PerfEvent, Error> { + // Convert handler to C callback if provided // Create the perf event using kernel functions let raw_perf_event = unsafe { bindings::perf_event_create_kernel_counter( perf_event_attr.as_inner(), cpu, - task.map(|x| x.as_ptr()).unwrap_or(core::ptr::null_mut()), - *overflow.inner.get(), - core::ptr::null_mut(), + task.as_ref().map_or(core::ptr::null_mut(), |t| t.as_ptr()), + overflow.is_some().then_some(overflow_trampoline), + overflow.map_or(core::ptr::null_mut(), |x| { + KBox::into_raw(KBox::new(x, crate::alloc::flags::GFP_KERNEL).unwrap()) + as *mut crate::ffi::c_void + }), ) }; @@ -425,154 +490,88 @@ pub fn perf_event_create_kernel_counter( return Err(Error::InvalidConfig); } - let signed = isize::from_ne_bytes((raw_perf_event as usize).to_ne_bytes()); - if signed < 0 { - pr_err!("Encountered error during creation of perf event"); - let error = kernel::error::Error::from_errno(signed as i32); - pr_err!("Error: {error:?}"); - return Err(Error::InvalidConfig); - } - pr_err!("event_ptr:{:?}", raw_perf_event); - - Ok(PerfEvent { - inner: raw_perf_event, - }) -} - -unsafe impl Send for PerfEvent {} -unsafe impl Sync for PerfEvent {} - -#[cfg(test)] -mod tests { - use super::*; - use kernel::prelude::*; - - /// Test creating and configuring a hardware event - #[test] - fn test_hardware_event_builder() { - // Create a basic CPU cycles counter - let builder = EventBuilder::new(EventType::Hardware(HardwareEvent::CpuCycles)) - .sample_period(10000) // Sample every 10000 cycles - .disabled() // Start disabled - .exclude_kernel() // Don't count kernel activity - .exclude_idle(); // Don't count when idle - - let event = builder.build(); - assert!(event.is_ok(), "Failed to create hardware event"); - - if let Ok(event) = event { - // Event starts disabled, so enable it - event.enable(); - - // Wait a bit for some cycles to accumulate - core::hint::spin_loop(); - - // Read the counter - let count = event.read(); - assert!(count > 0, "CPU cycle counter should be non-zero"); - - // Disable the event - event.disable(); - - // Read again - value should be stable - let count2 = event.read(); - assert_eq!(count, count2, "Counter should not increase when disabled"); + let result = from_err_ptr(raw_perf_event); + match result { + Err(e) => { + pr_err!("Encountered error during creation of perf event"); + pr_err!("Error: {e:?}"); + Err(Error::InvalidConfig) } + Ok(raw_event) => Ok(PerfEvent { inner: raw_event }), } +} - /// Test creating and configuring a software event - #[test] - fn test_software_event_builder() { - // Create a context switch counter - let builder = EventBuilder::new(EventType::Software(SoftwareEvent::ContextSwitches)) - .sample_freq(100) // 100 samples per second - .inherit() // Child tasks inherit this event - .exclude_user(); // Don't count user-space switches - - let event = builder.build(); - assert!(event.is_ok(), "Failed to create software event"); - - if let Ok(event) = event { - event.enable(); - - // Do something that might cause context switches - for _ in 0..1000 { - core::hint::spin_loop(); - } - - let switches = event.read(); - event.disable(); - - // We should have seen at least one context switch - assert!(switches > 0, "Should detect at least one context switch"); - } +unsafe extern "C" fn overflow_trampoline( + perf_event: *mut bindings::perf_event, + sample_data: *mut bindings::perf_sample_data, + registers: *mut bindings::pt_regs, +) { + if perf_event.is_null() { + return; } - - /// Test raw event configuration - #[test] - fn test_raw_event_builder() { - // Use a raw configuration (platform specific) - let raw_config = 0x0011; // Example raw event code - let builder = EventBuilder::new(EventType::Raw(raw_config)) - .pinned() // Must stay on PMU - .exclusive(); // Only this group on PMU - - let event = builder.build(); - assert!(event.is_ok(), "Failed to create raw event"); + let context_ptr = unsafe { *perf_event }.overflow_handler_context as *mut OverflowHandler; + if context_ptr.is_null() { + return; } - - /// Test error cases - #[test] - fn test_event_builder_errors() { - // Test invalid CPU - let builder = EventBuilder::new(EventType::Hardware(HardwareEvent::CpuCycles)).cpu(99999); // Assuming this is an invalid CPU number - assert!(builder.build().is_err(), "Should fail with invalid CPU"); - - // Test mutually exclusive options - let builder = EventBuilder::new(EventType::Hardware(HardwareEvent::CpuCycles)) - .sample_period(1000) - .sample_freq(100); // This should override period - let event = builder.build().unwrap(); - - // Read the actual configuration to verify freq was used instead of period - let attr_ptr = event.as_ptr(); - unsafe { - let attr = &*attr_ptr; - assert!( - unsafe { attr.attr.freq() } != 0, - "Frequency setting should be used" - ); - } + let context = &mut unsafe { context_ptr.read() }; + overflow_wrapper(perf_event, sample_data, registers, &mut *context.dyn_fn); +} +fn overflow_wrapper( + perf_event: *mut bindings::perf_event, + sample_data: *mut bindings::perf_sample_data, + registers: *mut bindings::pt_regs, + mut handler: impl FnMut(&PerfEventRef, &mut SampleData, &mut Registers), +) { + if perf_event.is_null() || sample_data.is_null() || registers.is_null() { + return; } + handler( + &PerfEventRef { inner: perf_event }, + &mut SampleData { + inner: Opaque::new(unsafe { *sample_data }), + }, + &mut Registers { + inner: Opaque::new(unsafe { *registers }), + }, + ) +} +fn into_dyn( + handler: impl Fn(&PerfEventRef, &mut SampleData, &mut Registers) + Send + Sync + 'static, +) -> Result<OverflowHandler, Error> { + let b = KBox::new(handler, crate::alloc::flags::GFP_KERNEL)?; + let b = Box::leak(b); + let b_ptr = (b as *mut _) as *mut crate::ffi::c_void; + let c = b as &'static mut (dyn FnMut(&PerfEventRef, &mut SampleData, &mut Registers) + + Send + + Sync + + 'static); + + Ok(OverflowHandler { + closure: b_ptr, + dyn_fn: c, + }) +} +/// Workaround for the missing support of using KBox as a fat pointer +pub struct OverflowHandler { + closure: *mut crate::ffi::c_void, + dyn_fn: &'static mut (dyn FnMut(&PerfEventRef, &mut SampleData, &mut Registers) + + Send + + Sync + + 'static), +} - /// Test overflow handler - #[test] - fn test_overflow_handler() { - static mut OVERFLOW_COUNT: u32 = 0; - - // Create an event that should overflow quickly - let builder = - EventBuilder::new(EventType::Hardware(HardwareEvent::CpuCycles)).sample_period(100); // Very small period to trigger overflows - - let event = builder.build(); - assert!( - event.is_ok(), - "Failed to create event with overflow handler" - ); - - if let Ok(event) = event { - event.enable(); - - // Cause some CPU activity - for _ in 0..1000 { - core::hint::spin_loop(); - } - - event.disable(); - - unsafe { - assert!(OVERFLOW_COUNT > 0, "Should have triggered some overflows"); - } - } +impl OverflowHandler { + /// Constructs a new overflow handler callback which is run when a performance counter overflows. + /// + /// # Safety + /// The callback function is run in an NMI context: + /// - Handler must be interrupt-safe + /// - Handler must not block + /// - Handler must not alloc + /// - Handler must not panic + pub unsafe fn new( + handler: impl Fn(&PerfEventRef, &mut SampleData, &mut Registers) + Send + Sync + 'static, + ) -> Result<Self, Error> { + into_dyn(handler) } } |