Skip to content

Commit 00bb9f4

Browse files
authored
Merge pull request #614 from wedsonaf/miscdev-context
rust: only require context when registering a `miscdev` device
2 parents e44ef5b + 4ab613e commit 00bb9f4

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)