Skip to content

Commit

Permalink
rust: add tracepoint support
Browse files Browse the repository at this point in the history
Make it possible to have Rust code call into tracepoints defined by C
code. It is still required that the tracepoint is declared in a C
header, and that this header is included in the input to bindgen.

Signed-off-by: Alice Ryhl <[email protected]>
  • Loading branch information
Darksonn committed Jun 6, 2024
1 parent 38720c0 commit 9d0346a
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 0 deletions.
1 change: 1 addition & 0 deletions rust/bindings/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/refcount.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/tracepoint.h>
#include <linux/wait.h>
#include <linux/workqueue.h>

Expand Down
15 changes: 15 additions & 0 deletions rust/bindings/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(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 }
}
18 changes: 18 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
94 changes: 94 additions & 0 deletions rust/kernel/tracepoint.rs
Original file line number Diff line number Diff line change
@@ -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};

0 comments on commit 9d0346a

Please sign in to comment.