Skip to content

Commit a467357

Browse files
Andreas Hindborgjannau
authored andcommitted
rust: add parameter support to the module! macro
Add support for module parameters to the `module!` macro. Implement read only support for integer types without `sysfs` support. Acked-by: Petr Pavlu <petr.pavlu@suse.com> # from modules perspective Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
1 parent bdf8b86 commit a467357

6 files changed

Lines changed: 461 additions & 18 deletions

File tree

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub mod jump_label;
5757
pub mod kunit;
5858
pub mod list;
5959
pub mod miscdevice;
60+
pub mod module_param;
6061
#[cfg(CONFIG_NET)]
6162
pub mod net;
6263
pub mod of;

rust/kernel/module_param.rs

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Support for module parameters.
4+
//!
5+
//! C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
6+
7+
use crate::prelude::*;
8+
use crate::str::BStr;
9+
10+
/// Newtype to make `bindings::kernel_param` [`Sync`].
11+
#[repr(transparent)]
12+
#[doc(hidden)]
13+
pub struct RacyKernelParam(pub ::kernel::bindings::kernel_param);
14+
15+
// SAFETY: C kernel handles serializing access to this type. We never access it
16+
// from Rust module.
17+
unsafe impl Sync for RacyKernelParam {}
18+
19+
/// Types that can be used for module parameters.
20+
pub trait ModuleParam: Sized {
21+
/// The [`ModuleParam`] will be used by the kernel module through this type.
22+
///
23+
/// This may differ from `Self` if, for example, `Self` needs to track
24+
/// ownership without exposing it or allocate extra space for other possible
25+
/// parameter values.
26+
// This is required to support string parameters in the future.
27+
type Value: ?Sized;
28+
29+
/// Parse a parameter argument into the parameter value.
30+
///
31+
/// `Err(_)` should be returned when parsing of the argument fails.
32+
///
33+
/// Parameters passed at boot time will be set before [`kmalloc`] is
34+
/// available (even if the module is loaded at a later time). However, in
35+
/// this case, the argument buffer will be valid for the entire lifetime of
36+
/// the kernel. So implementations of this method which need to allocate
37+
/// should first check that the allocator is available (with
38+
/// [`crate::bindings::slab_is_available`]) and when it is not available
39+
/// provide an alternative implementation which doesn't allocate. In cases
40+
/// where the allocator is not available it is safe to save references to
41+
/// `arg` in `Self`, but in other cases a copy should be made.
42+
///
43+
/// [`kmalloc`]: srctree/include/linux/slab.h
44+
fn try_from_param_arg(arg: &'static BStr) -> Result<Self>;
45+
}
46+
47+
/// Set the module parameter from a string.
48+
///
49+
/// Used to set the parameter value at kernel initialization, when loading
50+
/// the module or when set through `sysfs`.
51+
///
52+
/// See `struct kernel_param_ops.set`.
53+
///
54+
/// # Safety
55+
///
56+
/// - If `val` is non-null then it must point to a valid null-terminated string.
57+
/// The `arg` field of `param` must be an instance of `T`.
58+
/// - `param.arg` must be a pointer to valid `*mut T` as set up by the
59+
/// [`module!`] macro.
60+
///
61+
/// # Invariants
62+
///
63+
/// Currently, we only support read-only parameters that are not readable
64+
/// from `sysfs`. Thus, this function is only called at kernel
65+
/// initialization time, or at module load time, and we have exclusive
66+
/// access to the parameter for the duration of the function.
67+
///
68+
/// [`module!`]: macros::module
69+
unsafe extern "C" fn set_param<T>(
70+
val: *const kernel::ffi::c_char,
71+
param: *const crate::bindings::kernel_param,
72+
) -> core::ffi::c_int
73+
where
74+
T: ModuleParam,
75+
{
76+
// NOTE: If we start supporting arguments without values, val _is_ allowed
77+
// to be null here.
78+
if val.is_null() {
79+
// TODO: Use pr_warn_once available.
80+
crate::pr_warn!("Null pointer passed to `module_param::set_param`");
81+
return EINVAL.to_errno();
82+
}
83+
84+
// SAFETY: By function safety requirement, val is non-null and
85+
// null-terminated. By C API contract, `val` is live and valid for reads
86+
// for the duration of this function.
87+
let arg = unsafe { CStr::from_char_ptr(val) };
88+
89+
crate::error::from_result(|| {
90+
let new_value = T::try_from_param_arg(arg)?;
91+
92+
// SAFETY: `param` is guaranteed to be valid by C API contract
93+
// and `arg` is guaranteed to point to an instance of `T`.
94+
let old_value = unsafe { (*param).__bindgen_anon_1.arg as *mut T };
95+
96+
// SAFETY: `old_value` is valid for writes, as we have exclusive
97+
// access. `old_value` is pointing to an initialized static, and
98+
// so it is properly initialized.
99+
unsafe { core::ptr::replace(old_value, new_value) };
100+
Ok(0)
101+
})
102+
}
103+
104+
/// Drop the parameter.
105+
///
106+
/// Called when unloading a module.
107+
///
108+
/// # Safety
109+
///
110+
/// The `arg` field of `param` must be an initialized instance of `T`.
111+
unsafe extern "C" fn free<T>(arg: *mut core::ffi::c_void)
112+
where
113+
T: ModuleParam,
114+
{
115+
// SAFETY: By function safety requirement, `arg` is an initialized
116+
// instance of `T`. By C API contract, `arg` will not be used after
117+
// this function returns.
118+
unsafe { core::ptr::drop_in_place(arg as *mut T) };
119+
}
120+
121+
macro_rules! impl_int_module_param {
122+
($ty:ident) => {
123+
impl ModuleParam for $ty {
124+
type Value = $ty;
125+
126+
fn try_from_param_arg(arg: &'static BStr) -> Result<Self> {
127+
<$ty as crate::str::parse_int::ParseInt>::from_str(arg)
128+
}
129+
}
130+
};
131+
}
132+
133+
impl_int_module_param!(i8);
134+
impl_int_module_param!(u8);
135+
impl_int_module_param!(i16);
136+
impl_int_module_param!(u16);
137+
impl_int_module_param!(i32);
138+
impl_int_module_param!(u32);
139+
impl_int_module_param!(i64);
140+
impl_int_module_param!(u64);
141+
impl_int_module_param!(isize);
142+
impl_int_module_param!(usize);
143+
144+
/// A wrapper for kernel parameters.
145+
///
146+
/// This type is instantiated by the [`module!`] macro when module parameters are
147+
/// defined. You should never need to instantiate this type directly.
148+
///
149+
/// Note: This type is `pub` because it is used by module crates to access
150+
/// parameter values.
151+
#[repr(transparent)]
152+
pub struct ModuleParamAccess<T> {
153+
data: core::cell::UnsafeCell<T>,
154+
}
155+
156+
// SAFETY: We only create shared references to the contents of this container,
157+
// so if `T` is `Sync`, so is `ModuleParamAccess`.
158+
unsafe impl<T: Sync> Sync for ModuleParamAccess<T> {}
159+
160+
impl<T> ModuleParamAccess<T> {
161+
#[doc(hidden)]
162+
pub const fn new(value: T) -> Self {
163+
Self {
164+
data: core::cell::UnsafeCell::new(value),
165+
}
166+
}
167+
168+
/// Get a shared reference to the parameter value.
169+
// Note: When sysfs access to parameters are enabled, we have to pass in a
170+
// held lock guard here.
171+
pub fn get(&self) -> &T {
172+
// SAFETY: As we only support read only parameters with no sysfs
173+
// exposure, the kernel will not touch the parameter data after module
174+
// initialization.
175+
unsafe { &*self.data.get() }
176+
}
177+
178+
/// Get a mutable pointer to the parameter value.
179+
pub const fn as_mut_ptr(&self) -> *mut T {
180+
self.data.get()
181+
}
182+
}
183+
184+
#[doc(hidden)]
185+
#[macro_export]
186+
/// Generate a static [`kernel_param_ops`](srctree/include/linux/moduleparam.h) struct.
187+
///
188+
/// # Examples
189+
///
190+
/// ```ignore
191+
/// make_param_ops!(
192+
/// /// Documentation for new param ops.
193+
/// PARAM_OPS_MYTYPE, // Name for the static.
194+
/// MyType // A type which implements [`ModuleParam`].
195+
/// );
196+
/// ```
197+
macro_rules! make_param_ops {
198+
($ops:ident, $ty:ty) => {
199+
///
200+
/// Static [`kernel_param_ops`](srctree/include/linux/moduleparam.h)
201+
/// struct generated by `make_param_ops`
202+
#[doc = concat!("for [`", stringify!($ty), "`].")]
203+
pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops {
204+
flags: 0,
205+
set: Some(set_param::<$ty>),
206+
get: None,
207+
free: Some(free::<$ty>),
208+
};
209+
};
210+
}
211+
212+
make_param_ops!(PARAM_OPS_I8, i8);
213+
make_param_ops!(PARAM_OPS_U8, u8);
214+
make_param_ops!(PARAM_OPS_I16, i16);
215+
make_param_ops!(PARAM_OPS_U16, u16);
216+
make_param_ops!(PARAM_OPS_I32, i32);
217+
make_param_ops!(PARAM_OPS_U32, u32);
218+
make_param_ops!(PARAM_OPS_I64, i64);
219+
make_param_ops!(PARAM_OPS_U64, u64);
220+
make_param_ops!(PARAM_OPS_ISIZE, isize);
221+
make_param_ops!(PARAM_OPS_USIZE, usize);

rust/macros/helpers.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
1010
}
1111
}
1212

13+
pub(crate) fn try_sign(it: &mut token_stream::IntoIter) -> Option<char> {
14+
let peek = it.clone().next();
15+
match peek {
16+
Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => {
17+
let _ = it.next();
18+
Some(punct.as_char())
19+
}
20+
_ => None,
21+
}
22+
}
23+
1324
pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
1425
if let Some(TokenTree::Literal(literal)) = it.next() {
1526
Some(literal.to_string())
@@ -107,6 +118,20 @@ pub(crate) struct Generics {
107118
pub(crate) ty_generics: Vec<TokenTree>,
108119
}
109120

121+
/// Parse a token stream of the form `expected_name: "value",` and return the
122+
/// string in the position of "value".
123+
///
124+
/// # Panics
125+
///
126+
/// - On parse error.
127+
pub(crate) fn expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
128+
assert_eq!(expect_ident(it), expected_name);
129+
assert_eq!(expect_punct(it), ':');
130+
let string = expect_string(it);
131+
assert_eq!(expect_punct(it), ',');
132+
string
133+
}
134+
110135
/// Parses the given `TokenStream` into `Generics` and the rest.
111136
///
112137
/// The generics are not present in the rest, but a where clause might remain.

rust/macros/lib.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ use proc_macro::TokenStream;
2424
/// The `type` argument should be a type which implements the [`Module`]
2525
/// trait. Also accepts various forms of kernel metadata.
2626
///
27+
/// The `params` field describe module parameters. Each entry has the form
28+
///
29+
/// ```ignore
30+
/// parameter_name: type {
31+
/// default: default_value,
32+
/// description: "Description",
33+
/// }
34+
/// ```
35+
///
36+
/// `type` may be one of
37+
///
38+
/// - [`i8`]
39+
/// - [`u8`]
40+
/// - [`i8`]
41+
/// - [`u8`]
42+
/// - [`i16`]
43+
/// - [`u16`]
44+
/// - [`i32`]
45+
/// - [`u32`]
46+
/// - [`i64`]
47+
/// - [`u64`]
48+
/// - [`isize`]
49+
/// - [`usize`]
50+
///
2751
/// C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
2852
///
2953
/// [`Module`]: ../kernel/trait.Module.html
@@ -40,6 +64,12 @@ use proc_macro::TokenStream;
4064
/// description: "My very own kernel module!",
4165
/// license: "GPL",
4266
/// alias: ["alternate_module_name"],
67+
/// params: {
68+
/// my_parameter: i64 {
69+
/// default: 1,
70+
/// description: "This parameter has a default of 1",
71+
/// },
72+
/// },
4373
/// }
4474
///
4575
/// struct MyModule(i32);
@@ -48,6 +78,7 @@ use proc_macro::TokenStream;
4878
/// fn init(_module: &'static ThisModule) -> Result<Self> {
4979
/// let foo: i32 = 42;
5080
/// pr_info!("I contain: {}\n", foo);
81+
/// pr_info!("i32 param is: {}\n", module_parameters::my_parameter.read());
5182
/// Ok(Self(foo))
5283
/// }
5384
/// }

0 commit comments

Comments
 (0)