Skip to content

Commit 4ab613e

Browse files
committed
rust: only require context when registering a miscdev device
This way it's possible to create a registration instance without having the context yet, and later provide the context when actually registering. This is useful for cases when the registration is contained in the context data, for example, when registering a new `miscdev` from `probe` functions. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 7739ad0 commit 4ab613e

File tree

2 files changed

+46
-13
lines changed

2 files changed

+46
-13
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
const_mut_refs,
2121
doc_cfg,
2222
generic_associated_types,
23+
maybe_uninit_extra,
2324
ptr_metadata,
2425
receiver_trait,
2526
coerce_unsized,

rust/kernel/miscdev.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,34 @@ use crate::file_operations::{FileOpenAdapter, FileOpener, FileOperationsVtable};
1212
use crate::{str::CStr, KernelModule, ThisModule};
1313
use alloc::boxed::Box;
1414
use core::marker::{PhantomData, PhantomPinned};
15-
use core::pin::Pin;
15+
use core::{mem::MaybeUninit, pin::Pin};
1616

1717
/// A registration of a miscellaneous device.
18+
///
19+
/// # Invariants
20+
///
21+
/// `Context` is always initialised when `registered` is `true`, and not initialised otherwise.
1822
pub struct Registration<T: Sync = ()> {
1923
registered: bool,
2024
mdev: bindings::miscdevice,
2125
_pin: PhantomPinned,
2226

2327
/// Context initialised on construction and made available to all file instances on
2428
/// [`FileOpener::open`].
25-
pub context: T,
29+
open_data: MaybeUninit<T>,
2630
}
2731

2832
impl<T: Sync> Registration<T> {
2933
/// Creates a new [`Registration`] but does not register it yet.
3034
///
3135
/// It is allowed to move.
32-
pub fn new(context: T) -> Self {
36+
pub fn new() -> Self {
37+
// INVARIANT: `registered` is `false` and `open_data` is not initialised.
3338
Self {
3439
registered: false,
3540
mdev: bindings::miscdevice::default(),
3641
_pin: PhantomPinned,
37-
context,
42+
open_data: MaybeUninit::uninit(),
3843
}
3944
}
4045

@@ -44,10 +49,10 @@ impl<T: Sync> Registration<T> {
4449
pub fn new_pinned<F: FileOpener<T>>(
4550
name: &'static CStr,
4651
minor: Option<i32>,
47-
context: T,
52+
open_data: T,
4853
) -> Result<Pin<Box<Self>>> {
49-
let mut r = Pin::from(Box::try_new(Self::new(context))?);
50-
r.as_mut().register::<F>(name, minor)?;
54+
let mut r = Pin::from(Box::try_new(Self::new())?);
55+
r.as_mut().register::<F>(name, minor, open_data)?;
5156
Ok(r)
5257
}
5358

@@ -59,6 +64,7 @@ impl<T: Sync> Registration<T> {
5964
self: Pin<&mut Self>,
6065
name: &'static CStr,
6166
minor: Option<i32>,
67+
open_data: T,
6268
) -> Result {
6369
// SAFETY: We must ensure that we never move out of `this`.
6470
let this = unsafe { self.get_unchecked_mut() };
@@ -72,31 +78,51 @@ impl<T: Sync> Registration<T> {
7278
this.mdev.name = name.as_char_ptr();
7379
this.mdev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);
7480

81+
// We write to `open_data` here because as soon as `misc_register` succeeds, the file can be
82+
// opened, so we need `open_data` configured ahead of time.
83+
//
84+
// INVARIANT: `registered` is set to `true`, but `open_data` is also initialised.
85+
this.registered = true;
86+
this.open_data.write(open_data);
87+
7588
let ret = unsafe { bindings::misc_register(&mut this.mdev) };
7689
if ret < 0 {
90+
// INVARIANT: `registered` is set back to `false` and the `open_data` is destructued.
91+
this.registered = false;
92+
// SAFETY: `open_data` was initialised a few lines above.
93+
unsafe { this.open_data.assume_init_drop() };
7794
return Err(Error::from_kernel_errno(ret));
7895
}
79-
this.registered = true;
96+
8097
Ok(())
8198
}
8299
}
83100

101+
impl<T: Sync> Default for Registration<T> {
102+
fn default() -> Self {
103+
Self::new()
104+
}
105+
}
106+
84107
impl<T: Sync> FileOpenAdapter for Registration<T> {
85108
type Arg = T;
86109

87110
unsafe fn convert(_inode: *mut bindings::inode, file: *mut bindings::file) -> *const Self::Arg {
88111
// SAFETY: the caller must guarantee that `file` is valid.
89112
let reg = crate::container_of!(unsafe { (*file).private_data }, Self, mdev);
90-
unsafe { &(*reg).context }
113+
114+
// SAFETY: This function is only called while the misc device is still registered, so the
115+
// registration must be valid. Additionally, the type invariants guarantee that while the
116+
// miscdev is registered, `open_data` is initialised.
117+
unsafe { (*reg).open_data.as_ptr() }
91118
}
92119
}
93120

94121
// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it
95-
// is safe to pass `&Registration` to multiple threads because it offers no interior mutability,
96-
// except maybe through `Registration::context`, but it is itself `Sync`.
122+
// is safe to pass `&Registration` to multiple threads because it offers no interior mutability.
97123
unsafe impl<T: Sync> Sync for Registration<T> {}
98124

99-
// SAFETY: All functions work from any thread. So as long as the `Registration::context` is
125+
// SAFETY: All functions work from any thread. So as long as the `Registration::open_data` is
100126
// `Send`, so is `Registration<T>`. `T` needs to be `Sync` because it's a requirement of
101127
// `Registration<T>`.
102128
unsafe impl<T: Send + Sync> Send for Registration<T> {}
@@ -105,7 +131,13 @@ impl<T: Sync> Drop for Registration<T> {
105131
/// Removes the registration from the kernel if it has completed successfully before.
106132
fn drop(&mut self) {
107133
if self.registered {
108-
unsafe { bindings::misc_deregister(&mut self.mdev) }
134+
// SAFETY: `registered` being `true` indicates that a previous call to `misc_register`
135+
// succeeded.
136+
unsafe { bindings::misc_deregister(&mut self.mdev) };
137+
138+
// SAFETY: The type invariant guarantees that `open_data` is initialised when
139+
// `registered` is `true`.
140+
unsafe { self.open_data.assume_init_drop() };
109141
}
110142
}
111143
}

0 commit comments

Comments
 (0)