diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ddb5644d4fd900..d442f9ccfc2c43 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 40ddaee50d8bd8..48856761d6822e 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -48,3 +48,18 @@ mod bindings_helper { } pub use bindings_raw::*; + +/// Rust version of the C macro `rcu_dereference_raw`. +/// +/// The rust helper only works with void pointers, but this wrapper method makes it work with any +/// pointer type using pointer casts. +/// +/// # Safety +/// +/// This method has the same safety requirements as the C macro of the same name. +#[inline(always)] +pub unsafe fn rcu_dereference_raw(p: *const *mut T) -> *mut T { + // SAFETY: This helper calls into the C macro, so the caller promises to uphold the safety + // requirements. + unsafe { __rcu_dereference_raw(p as *mut *mut _) as *mut T } +} diff --git a/rust/helpers.c b/rust/helpers.c index 2c37a0f5d7a84f..3b0877eb7f366c 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -165,6 +165,24 @@ rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags) } EXPORT_SYMBOL_GPL(rust_helper_krealloc); +void rust_helper_preempt_enable_notrace(void) +{ + preempt_enable_notrace(); +} +EXPORT_SYMBOL_GPL(rust_helper_preempt_enable_notrace); + +void rust_helper_preempt_disable_notrace(void) +{ + preempt_disable_notrace(); +} +EXPORT_SYMBOL_GPL(rust_helper_preempt_disable_notrace); + +void *rust_helper___rcu_dereference_raw(void **p) +{ + return rcu_dereference_raw(p); +} +EXPORT_SYMBOL_GPL(rust_helper___rcu_dereference_raw); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 22e1fedd0774b3..3f3b280bb437a6 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -46,6 +46,7 @@ pub mod str; pub mod sync; pub mod task; pub mod time; +pub mod tracepoint; pub mod types; pub mod workqueue; diff --git a/rust/kernel/tracepoint.rs b/rust/kernel/tracepoint.rs new file mode 100644 index 00000000000000..6ce8bd43d2418d --- /dev/null +++ b/rust/kernel/tracepoint.rs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Logic for tracepoints. + +/// Declare the Rust entry point for a tracepoint. +#[macro_export] +macro_rules! declare_trace { + ($($(#[$attr:meta])* $pub:vis fn $name:ident($($argname:ident : $argtyp:ty),* $(,)?);)*) => {$( + $( #[$attr] )* + #[inline(always)] + $pub unsafe fn $name($($argname : $argtyp),*) { + #[cfg(CONFIG_TRACEPOINTS)] + { + use $crate::bindings::*; + + // SAFETY: This macro only compiles if $name is a real tracepoint, and if it is a + // real tracepoint, then it is okay to query the static key. + let should_trace = unsafe { + $crate::macros::paste! { + $crate::static_key::static_key_false!( + [< __tracepoint_ $name >], + $crate::bindings::tracepoint, + key + ) + } + }; + + if should_trace { + // TODO: cpu_online(raw_smp_processor_id()) + let cond = true; + $crate::tracepoint::do_trace!($name($($argname : $argtyp),*), cond); + } + } + + #[cfg(not(CONFIG_TRACEPOINTS))] + { + // If tracepoints are disabled, insert a trivial use of each argument + // to avoid unused argument warnings. + $( let _unused = $argname; )* + } + } + )*} +} + +#[doc(hidden)] +#[macro_export] +macro_rules! do_trace { + ($name:ident($($argname:ident : $argtyp:ty),* $(,)?), $cond:expr) => {{ + if !$cond { + return; + } + + // SAFETY: This call is balanced with the call below. + unsafe { $crate::bindings::preempt_disable_notrace() }; + + // SAFETY: This calls the tracepoint with the provided arguments. The caller of the Rust + // wrapper guarantees that this is okay. + #[cfg(CONFIG_HAVE_STATIC_CALL)] + unsafe { + let it_func_ptr: *mut $crate::bindings::tracepoint_func = + $crate::bindings::rcu_dereference_raw( + ::core::ptr::addr_of!( + $crate::macros::concat_idents!(__tracepoint_, $name).funcs + ) + ); + + if !it_func_ptr.is_null() { + let __data = (*it_func_ptr).data; + $crate::macros::paste! { + $crate::static_call::static_call! { + [< tp_func_ $name >] (__data, $($argname),*) + }; + } + } + } + + // SAFETY: This calls the tracepoint with the provided arguments. The caller of the Rust + // wrapper guarantees that this is okay. + #[cfg(not(CONFIG_HAVE_STATIC_CALL))] + unsafe { + $crate::macros::concat_idents!(__traceiter_, $name)( + ::core::ptr::null_mut(), + $($argname),* + ); + } + + // SAFETY: This call is balanced with the call above. + unsafe { $crate::bindings::preempt_enable_notrace() }; + }} +} + +pub use {declare_trace, do_trace};