Skip to content

Commit 9d0346a

Browse files
committed
rust: add tracepoint support
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]>
1 parent 38720c0 commit 9d0346a

File tree

5 files changed

+129
-0
lines changed

5 files changed

+129
-0
lines changed

rust/bindings/bindings_helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/refcount.h>
1616
#include <linux/sched.h>
1717
#include <linux/slab.h>
18+
#include <linux/tracepoint.h>
1819
#include <linux/wait.h>
1920
#include <linux/workqueue.h>
2021

rust/bindings/lib.rs

+15
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,18 @@ mod bindings_helper {
4848
}
4949

5050
pub use bindings_raw::*;
51+
52+
/// Rust version of the C macro `rcu_dereference_raw`.
53+
///
54+
/// The rust helper only works with void pointers, but this wrapper method makes it work with any
55+
/// pointer type using pointer casts.
56+
///
57+
/// # Safety
58+
///
59+
/// This method has the same safety requirements as the C macro of the same name.
60+
#[inline(always)]
61+
pub unsafe fn rcu_dereference_raw<T>(p: *const *mut T) -> *mut T {
62+
// SAFETY: This helper calls into the C macro, so the caller promises to uphold the safety
63+
// requirements.
64+
unsafe { __rcu_dereference_raw(p as *mut *mut _) as *mut T }
65+
}

rust/helpers.c

+18
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,24 @@ rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
165165
}
166166
EXPORT_SYMBOL_GPL(rust_helper_krealloc);
167167

168+
void rust_helper_preempt_enable_notrace(void)
169+
{
170+
preempt_enable_notrace();
171+
}
172+
EXPORT_SYMBOL_GPL(rust_helper_preempt_enable_notrace);
173+
174+
void rust_helper_preempt_disable_notrace(void)
175+
{
176+
preempt_disable_notrace();
177+
}
178+
EXPORT_SYMBOL_GPL(rust_helper_preempt_disable_notrace);
179+
180+
void *rust_helper___rcu_dereference_raw(void **p)
181+
{
182+
return rcu_dereference_raw(p);
183+
}
184+
EXPORT_SYMBOL_GPL(rust_helper___rcu_dereference_raw);
185+
168186
/*
169187
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
170188
* use it in contexts where Rust expects a `usize` like slice (array) indices.

rust/kernel/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub mod str;
4646
pub mod sync;
4747
pub mod task;
4848
pub mod time;
49+
pub mod tracepoint;
4950
pub mod types;
5051
pub mod workqueue;
5152

rust/kernel/tracepoint.rs

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
// Copyright (C) 2024 Google LLC.
4+
5+
//! Logic for tracepoints.
6+
7+
/// Declare the Rust entry point for a tracepoint.
8+
#[macro_export]
9+
macro_rules! declare_trace {
10+
($($(#[$attr:meta])* $pub:vis fn $name:ident($($argname:ident : $argtyp:ty),* $(,)?);)*) => {$(
11+
$( #[$attr] )*
12+
#[inline(always)]
13+
$pub unsafe fn $name($($argname : $argtyp),*) {
14+
#[cfg(CONFIG_TRACEPOINTS)]
15+
{
16+
use $crate::bindings::*;
17+
18+
// SAFETY: This macro only compiles if $name is a real tracepoint, and if it is a
19+
// real tracepoint, then it is okay to query the static key.
20+
let should_trace = unsafe {
21+
$crate::macros::paste! {
22+
$crate::static_key::static_key_false!(
23+
[< __tracepoint_ $name >],
24+
$crate::bindings::tracepoint,
25+
key
26+
)
27+
}
28+
};
29+
30+
if should_trace {
31+
// TODO: cpu_online(raw_smp_processor_id())
32+
let cond = true;
33+
$crate::tracepoint::do_trace!($name($($argname : $argtyp),*), cond);
34+
}
35+
}
36+
37+
#[cfg(not(CONFIG_TRACEPOINTS))]
38+
{
39+
// If tracepoints are disabled, insert a trivial use of each argument
40+
// to avoid unused argument warnings.
41+
$( let _unused = $argname; )*
42+
}
43+
}
44+
)*}
45+
}
46+
47+
#[doc(hidden)]
48+
#[macro_export]
49+
macro_rules! do_trace {
50+
($name:ident($($argname:ident : $argtyp:ty),* $(,)?), $cond:expr) => {{
51+
if !$cond {
52+
return;
53+
}
54+
55+
// SAFETY: This call is balanced with the call below.
56+
unsafe { $crate::bindings::preempt_disable_notrace() };
57+
58+
// SAFETY: This calls the tracepoint with the provided arguments. The caller of the Rust
59+
// wrapper guarantees that this is okay.
60+
#[cfg(CONFIG_HAVE_STATIC_CALL)]
61+
unsafe {
62+
let it_func_ptr: *mut $crate::bindings::tracepoint_func =
63+
$crate::bindings::rcu_dereference_raw(
64+
::core::ptr::addr_of!(
65+
$crate::macros::concat_idents!(__tracepoint_, $name).funcs
66+
)
67+
);
68+
69+
if !it_func_ptr.is_null() {
70+
let __data = (*it_func_ptr).data;
71+
$crate::macros::paste! {
72+
$crate::static_call::static_call! {
73+
[< tp_func_ $name >] (__data, $($argname),*)
74+
};
75+
}
76+
}
77+
}
78+
79+
// SAFETY: This calls the tracepoint with the provided arguments. The caller of the Rust
80+
// wrapper guarantees that this is okay.
81+
#[cfg(not(CONFIG_HAVE_STATIC_CALL))]
82+
unsafe {
83+
$crate::macros::concat_idents!(__traceiter_, $name)(
84+
::core::ptr::null_mut(),
85+
$($argname),*
86+
);
87+
}
88+
89+
// SAFETY: This call is balanced with the call above.
90+
unsafe { $crate::bindings::preempt_enable_notrace() };
91+
}}
92+
}
93+
94+
pub use {declare_trace, do_trace};

0 commit comments

Comments
 (0)