Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental tracepoint support #1084

Open
wants to merge 3 commits into
base: rust-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
3 changes: 3 additions & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ pub mod net;
pub mod prelude;
pub mod print;
mod static_assert;
pub mod static_call;
pub mod static_key;
#[doc(hidden)]
pub mod std_vendor;
pub mod str;
pub mod sync;
pub mod task;
pub mod time;
pub mod tracepoint;
pub mod types;
pub mod workqueue;

Expand Down
90 changes: 90 additions & 0 deletions rust/kernel/static_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0

// Copyright (C) 2024 Google LLC.

//! Logic for static calls.

#[macro_export]
#[doc(hidden)]
macro_rules! ty_underscore_for {
($arg:expr) => {
_
};
}

#[doc(hidden)]
#[repr(transparent)]
pub struct AddressableStaticCallKey {
Darksonn marked this conversation as resolved.
Show resolved Hide resolved
_ptr: *const bindings::static_call_key,
}
unsafe impl Sync for AddressableStaticCallKey {}
impl AddressableStaticCallKey {
pub const fn new(ptr: *const bindings::static_call_key) -> Self {
Self { _ptr: ptr }
}
}

#[cfg(CONFIG_HAVE_STATIC_CALL)]
#[doc(hidden)]
#[macro_export]
macro_rules! _static_call {
($name:ident($($args:expr),* $(,)?)) => {{
// Symbol mangling will give this symbol a unique name.
#[cfg(CONFIG_HAVE_STATIC_CALL_INLINE)]
#[link_section = ".discard.addressable"]
#[used]
static __ADDRESSABLE: $crate::static_call::AddressableStaticCallKey = unsafe {
$crate::static_call::AddressableStaticCallKey::new(::core::ptr::addr_of!(
$crate::macros::paste! { $crate::bindings:: [<__SCK__ $name >]; }
))
};

let fn_ptr: unsafe extern "C" fn($($crate::static_call::ty_underscore_for!($args)),*) -> _ =
$crate::macros::paste! { $crate::bindings:: [<__SCT__ $name >]; };
(fn_ptr)($($args),*)
}};
}

#[cfg(not(CONFIG_HAVE_STATIC_CALL))]
#[doc(hidden)]
#[macro_export]
macro_rules! _static_call {
($name:ident($($args:expr),* $(,)?)) => {{
let void_ptr_fn: *mut ::core::ffi::c_void = $crate::macros::paste! { $crate::bindings:: [<__SCK__ $name >]; }.func;

let fn_ptr: unsafe extern "C" fn($($crate::static_call::ty_underscore_for!($args)),*) -> _ = if true {
::core::mem::transmute(void_ptr_fn)
} else {
// This is dead code, but it influences type inference on `fn_ptr` so that we transmute
// the function pointer to the right type.
$crate::macros::paste! { $crate::bindings:: [<__SCT__ $name >]; }
};

(fn_ptr)($($args),*)
}};
}

/// Statically call a global function.
///
/// # Safety
///
/// This macro will call the provided function. It is up to the caller to uphold the safety
/// guarantees of the function.
///
/// # Examples
///
/// ```ignore
/// fn call_static() {
/// unsafe {
/// static_call! { your_static_call() };
/// }
/// }
/// ```
#[macro_export]
macro_rules! static_call {
// Forward to the real implementation. Separated like this so that we don't have to duplicate
// the documentation.
($($args:tt)*) => { $crate::static_call::_static_call! { $($args)* } };
}

pub use {_static_call, static_call, ty_underscore_for};
150 changes: 150 additions & 0 deletions rust/kernel/static_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0
Darksonn marked this conversation as resolved.
Show resolved Hide resolved

// Copyright (C) 2024 Google LLC.

//! Logic for static keys.

use crate::bindings::*;

#[doc(hidden)]
#[macro_export]
#[cfg(target_arch = "x86_64")]
#[cfg(not(CONFIG_HAVE_RUST_ASM_GOTO))]
macro_rules! _static_key_false {
($key:path, $keytyp:ty, $field:ident) => {{
let mut output = 1u32;

core::arch::asm!(
r#"
1: .byte 0x0f,0x1f,0x44,0x00,0x00

.pushsection __jump_table, "aw"
.balign 8
.long 1b - .
.long 3f - .
.quad {0} + {1} - .
.popsection

2: mov {2:e}, 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may want to use att_syntax ;-)

Copy link
Collaborator Author

@Darksonn Darksonn May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you show me how to do that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options(att_syntax), here is an example:

unsafe fn i32_xadd(v: *mut i32, mut i: i32) -> i32 {
    // SAFETY: Per function safety requirement, the address of `v` is valid for "xadd".
    unsafe {
        asm!(
            lock_instr!("xaddl {i:e}, ({v})"),
            i = inout(reg) i,
            v = in(reg) v,
            options(att_syntax, preserves_flags),
        );
    }

    i
}

in https://lore.kernel.org/rust-for-linux/20240322233838.868874-2-boqun.feng@gmail.com/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kernel developers will prefer that, yeah... :)

Sadly, specifying every single time that may be painful. Perhaps we could have our own asm! that expands to the actual one plus that option, if that is possible, or otherwise it would be nice to have a rustc flag to always use that by default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to our wishlist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A asm! wrapper should be fairly straightforward. The asm macro deliberately allows multiple options to simplify cases where people want to always add options(att_syntax). See rust-lang/rust#73227.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Gary, then we should probably do it soon if we are going to start adding more asm! blocks. I would still like to have a flag (or similar) nevertheless, to avoid extra macros and to match the C compilers.

3:
"#,
sym $key,
const ::core::mem::offset_of!($keytyp, $field),
Darksonn marked this conversation as resolved.
Show resolved Hide resolved
inout(reg) output,
);

output != 0
}};
}

#[doc(hidden)]
#[macro_export]
#[cfg(target_arch = "x86_64")]
#[cfg(CONFIG_HAVE_RUST_ASM_GOTO)]
macro_rules! _static_key_false {
($key:path, $keytyp:ty, $field:ident) => {'my_label: {
core::arch::asm!(
r#"
1: .byte 0x0f,0x1f,0x44,0x00,0x00

.pushsection __jump_table, "aw"
.balign 8
.long 1b - .
.long {0} - .
.quad {1} + {2} - .
.popsection
"#,
label {
break 'my_label true;
},
sym $key,
const ::core::mem::offset_of!($keytyp, $field),
);

break 'my_label false;
}};
}

#[doc(hidden)]
#[macro_export]
#[cfg(target_arch = "aarch64")]
#[cfg(not(CONFIG_HAVE_RUST_ASM_GOTO))]
macro_rules! _static_key_false {
($key:path, $keytyp:ty, $field:ident) => {{
let mut output = 1u32;

core::arch::asm!(
r#"
1: nop

.pushsection __jump_table, "aw"
.align 3
.long 1b - ., 3f - .
.quad {0} + {1} - .
.popsection

2: mov {2:w}, 0
3:
"#,
sym $key,
const ::core::mem::offset_of!($keytyp, $field),
inout(reg) output
);

output != 0
}};
}

#[doc(hidden)]
#[macro_export]
#[cfg(target_arch = "aarch64")]
#[cfg(CONFIG_HAVE_RUST_ASM_GOTO)]
macro_rules! _static_key_false {
($key:path, $keytyp:ty, $field:ident) => {'my_label: {
core::arch::asm!(
r#"
1: nop

.pushsection __jump_table, "aw"
.align 3
.long 1b - ., {0} - .
.quad {1} + {2} - .
.popsection
"#,
label {
break 'my_label true;
},
sym $key,
const ::core::mem::offset_of!($keytyp, $field),
);

break 'my_label false;
}};
}

/// Branch based on a static key.
///
/// Takes three arguments:
///
/// * `key` - the path to the static variable containing the `static_key`.
/// * `keytyp` - the type of `key`.
/// * `field` - the name of the field of `key` that contains the `static_key`.
#[macro_export]
macro_rules! static_key_false {
// Forward to the real implementation. Separated like this so that we don't have to duplicate
// the documentation.
($key:path, $keytyp:ty, $field:ident) => {{
// Assert that `$key` has type `$keytyp` and that `$key.$field` has type `static_key`.
//
// SAFETY: We know that `$key` is a static because otherwise the inline assembly will not
// compile. The raw pointers created in this block are in-bounds of `$key`.
static _TY_ASSERT: () = unsafe {
let key: *const $keytyp = ::core::ptr::addr_of!($key);
let _: *const $crate::bindings::static_key = ::core::ptr::addr_of!((*key).$field);
};

$crate::static_key::_static_key_false! { $key, $keytyp, $field }
}};
}

pub use {_static_key_false, static_key_false};
Loading