summaryrefslogtreecommitdiff
path: root/rust/kernel/perf_event.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel/perf_event.rs')
-rw-r--r--rust/kernel/perf_event.rs315
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)
}
}