Skip to content

Commit d716817

Browse files
committed
Implement array and string parameters
1 parent 58ab534 commit d716817

File tree

3 files changed

+407
-97
lines changed

3 files changed

+407
-97
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//! do so first instead of bypassing this crate.
1313
1414
#![no_std]
15-
#![feature(allocator_api, alloc_error_handler)]
15+
#![feature(allocator_api, alloc_error_handler, const_fn, const_mut_refs)]
1616
#![deny(clippy::complexity)]
1717
#![deny(clippy::correctness)]
1818
#![deny(clippy::perf)]

rust/kernel/module_param.rs

Lines changed: 190 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ use core::fmt::Write;
1515
///
1616
/// [`PAGE_SIZE`]: `crate::PAGE_SIZE`
1717
pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
18+
/// The `ModuleParam` will be used by the kernel module through this type.
19+
///
20+
/// This may differ from `Self` if, for example, `Self` needs to track
21+
/// ownership without exposing it or allocate extra space for other possible
22+
/// parameter values. See [`StringParam`] or [`ArrayParam`] for examples.
23+
type Value: ?Sized;
24+
1825
/// Whether the parameter is allowed to be set without an argument.
1926
///
2027
/// Setting this to `true` allows the parameter to be passed without an
@@ -27,7 +34,14 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
2734
/// `arg == None` indicates that the parameter was passed without an
2835
/// argument. If `NOARG_ALLOWED` is set to `false` then `arg` is guaranteed
2936
/// to always be `Some(_)`.
30-
fn try_from_param_arg(arg: Option<&[u8]>) -> Option<Self>;
37+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self>;
38+
39+
/// Get the current value of the parameter for use in the kernel module.
40+
///
41+
/// This function should not be used directly. Instead use the wrapper
42+
/// `read` which will be generated by [`module::module`].
43+
#[doc(hidden)]
44+
fn value(&self) -> &Self::Value;
3145

3246
/// Set the module parameter from a string.
3347
///
@@ -161,13 +175,18 @@ impl_parse_int!(usize);
161175
macro_rules! impl_module_param {
162176
($ty:ident) => {
163177
impl ModuleParam for $ty {
178+
type Value = $ty;
164179
const NOARG_ALLOWED: bool = false;
165180

166-
fn try_from_param_arg(arg: Option<&[u8]>) -> Option<Self> {
181+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
167182
let bytes = arg?;
168183
let utf8 = core::str::from_utf8(bytes).ok()?;
169184
<$ty as crate::module_param::ParseInt>::from_str(utf8)
170185
}
186+
187+
fn value(&self) -> &Self::Value {
188+
self
189+
}
171190
}
172191
};
173192
}
@@ -184,27 +203,27 @@ macro_rules! impl_module_param {
184203
/// );
185204
/// ```
186205
macro_rules! make_param_ops {
187-
($ops:ident, $ty:ident) => {
188-
make_param_ops!(
206+
($ops:ident, $ty:ty) => {
207+
$crate::make_param_ops!(
189208
#[doc=""]
190209
$ops,
191210
$ty
192211
);
193212
};
194-
($(#[$meta:meta])* $ops:ident, $ty:ident) => {
213+
($(#[$meta:meta])* $ops:ident, $ty:ty) => {
195214
$(#[$meta])*
196215
///
197216
/// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h)
198217
/// struct generated by [`make_param_ops`].
199-
pub static $ops: crate::bindings::kernel_param_ops = crate::bindings::kernel_param_ops {
200-
flags: if <$ty as crate::module_param::ModuleParam>::NOARG_ALLOWED {
201-
crate::bindings::KERNEL_PARAM_OPS_FL_NOARG
218+
pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops {
219+
flags: if <$ty as $crate::module_param::ModuleParam>::NOARG_ALLOWED {
220+
$crate::bindings::KERNEL_PARAM_OPS_FL_NOARG
202221
} else {
203222
0
204223
},
205-
set: Some(<$ty as crate::module_param::ModuleParam>::set_param),
206-
get: Some(<$ty as crate::module_param::ModuleParam>::get_param),
207-
free: Some(<$ty as crate::module_param::ModuleParam>::free),
224+
set: Some(<$ty as $crate::module_param::ModuleParam>::set_param),
225+
get: Some(<$ty as $crate::module_param::ModuleParam>::get_param),
226+
free: Some(<$ty as $crate::module_param::ModuleParam>::free),
208227
};
209228
};
210229
}
@@ -282,16 +301,21 @@ make_param_ops!(
282301
);
283302

284303
impl ModuleParam for bool {
304+
type Value = bool;
285305
const NOARG_ALLOWED: bool = true;
286306

287-
fn try_from_param_arg(arg: Option<&[u8]>) -> Option<Self> {
307+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
288308
match arg {
289309
None => Some(true),
290310
Some(b"y") | Some(b"Y") | Some(b"1") | Some(b"true") => Some(true),
291311
Some(b"n") | Some(b"N") | Some(b"0") | Some(b"false") => Some(false),
292312
_ => None,
293313
}
294314
}
315+
316+
fn value(&self) -> &Self::Value {
317+
self
318+
}
295319
}
296320

297321
make_param_ops!(
@@ -300,3 +324,157 @@ make_param_ops!(
300324
PARAM_OPS_BOOL,
301325
bool
302326
);
327+
328+
/// An array of at __most__ `N` values.
329+
///
330+
/// # Invariant
331+
///
332+
/// The first `self.used` elements of `self.values` are initialized.
333+
pub struct ArrayParam<T, const N: usize> {
334+
values: [core::mem::MaybeUninit<T>; N],
335+
used: usize,
336+
}
337+
338+
impl<T, const N: usize> ArrayParam<T, { N }> {
339+
fn values(&self) -> &[T] {
340+
// SAFETY: The invariant maintained by `ArrayParam` allows us to cast
341+
// the first `self.used` elements to `T`.
342+
unsafe {
343+
&*(&self.values[0..self.used] as *const [core::mem::MaybeUninit<T>] as *const [T])
344+
}
345+
}
346+
}
347+
348+
impl<T: Copy, const N: usize> ArrayParam<T, { N }> {
349+
const fn new() -> Self {
350+
// INVARIANT: The first `self.used` elements of `self.values` are
351+
// initialized.
352+
ArrayParam {
353+
values: [core::mem::MaybeUninit::uninit(); N],
354+
used: 0,
355+
}
356+
}
357+
358+
const fn push(&mut self, val: T) {
359+
if self.used < N {
360+
// INVARIANT: The first `self.used` elements of `self.values` are
361+
// initialized.
362+
self.values[self.used] = core::mem::MaybeUninit::new(val);
363+
self.used += 1;
364+
}
365+
}
366+
367+
/// Create an instance of `ArrayParam` initialized with `vals`.
368+
///
369+
/// This function is only meant to be used in the [`module::module`] macro.
370+
#[doc(hidden)]
371+
pub const fn create(vals: &[T]) -> Self {
372+
let mut result = ArrayParam::new();
373+
let mut i = 0;
374+
while i < vals.len() {
375+
result.push(vals[i]);
376+
i += 1;
377+
}
378+
result
379+
}
380+
}
381+
382+
impl<T: core::fmt::Display, const N: usize> core::fmt::Display for ArrayParam<T, { N }> {
383+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
384+
for val in self.values() {
385+
write!(f, "{},", val)?;
386+
}
387+
Ok(())
388+
}
389+
}
390+
391+
impl<T: Copy + core::fmt::Display + ModuleParam, const N: usize> ModuleParam
392+
for ArrayParam<T, { N }>
393+
{
394+
type Value = [T];
395+
const NOARG_ALLOWED: bool = false;
396+
397+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
398+
arg.and_then(|args| {
399+
let mut result = Self::new();
400+
for arg in args.split(|b| *b == b',') {
401+
result.push(T::try_from_param_arg(Some(arg))?);
402+
}
403+
Some(result)
404+
})
405+
}
406+
407+
fn value(&self) -> &Self::Value {
408+
self.values()
409+
}
410+
}
411+
412+
/// A C-style string parameter.
413+
///
414+
/// The Rust version of the [`charp`] parameter. This type is meant to be
415+
/// used by the [`module::module`] macro, not handled directly. Instead use the
416+
/// `read` method generated by that macro.
417+
///
418+
/// [`charp`]: ../../../include/linux/moduleparam.h
419+
pub enum StringParam {
420+
/// A borrowed parameter value.
421+
///
422+
/// Either the default value (which is static in the module) or borrowed
423+
/// from the original argument buffer used to set the value.
424+
Ref(&'static [u8]),
425+
426+
/// A value that was allocated when the parameter was set.
427+
///
428+
/// The value needs to be freed when the parameter is reset or the module is
429+
/// unloaded.
430+
Owned(alloc::vec::Vec<u8>),
431+
}
432+
433+
impl StringParam {
434+
fn bytes(&self) -> &[u8] {
435+
match self {
436+
StringParam::Ref(bytes) => *bytes,
437+
StringParam::Owned(vec) => &vec[..],
438+
}
439+
}
440+
}
441+
442+
impl core::fmt::Display for StringParam {
443+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
444+
let bytes = self.bytes();
445+
match core::str::from_utf8(bytes) {
446+
Ok(utf8) => write!(f, "{}", utf8),
447+
Err(_) => write!(f, "{:?}", bytes),
448+
}
449+
}
450+
}
451+
452+
impl ModuleParam for StringParam {
453+
type Value = [u8];
454+
const NOARG_ALLOWED: bool = false;
455+
456+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
457+
// SAFETY: It is always safe to call [`slab_is_available`](../../../include/linux/slab.h).
458+
let slab_available = unsafe { crate::bindings::slab_is_available() };
459+
arg.map(|arg| {
460+
if slab_available {
461+
let mut vec = alloc::vec::Vec::new();
462+
vec.extend_from_slice(arg);
463+
StringParam::Owned(vec)
464+
} else {
465+
StringParam::Ref(arg)
466+
}
467+
})
468+
}
469+
470+
fn value(&self) -> &Self::Value {
471+
self.bytes()
472+
}
473+
}
474+
475+
make_param_ops!(
476+
/// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
477+
/// for [`StringParam`].
478+
PARAM_OPS_STR,
479+
StringParam
480+
);

0 commit comments

Comments
 (0)