Skip to content

Commit 8d19ae8

Browse files
committed
Share context with file instances based on registration.
1 parent 4f0e492 commit 8d19ae8

File tree

5 files changed

+154
-58
lines changed

5 files changed

+154
-58
lines changed

drivers/char/rust_example.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use core::pin::Pin;
1111
use kernel::prelude::*;
1212
use kernel::{
1313
chrdev, condvar_init, cstr,
14-
file_operations::FileOperations,
14+
file_operations::{FileOpener, FileOperations},
1515
miscdev, mutex_init, spinlock_init,
1616
sync::{CondVar, Mutex, SpinLock},
1717
};
@@ -53,15 +53,17 @@ module! {
5353

5454
struct RustFile;
5555

56+
impl FileOpener<()> for RustFile {
57+
fn open(_ctx: &()) -> KernelResult<Self::Wrapper> {
58+
println!("rust file was opened!");
59+
Ok(Box::try_new(Self)?)
60+
}
61+
}
62+
5663
impl FileOperations for RustFile {
5764
type Wrapper = Box<Self>;
5865

5966
kernel::declare_file_operations!();
60-
61-
fn open() -> KernelResult<Self::Wrapper> {
62-
println!("rust file was opened!");
63-
Ok(Box::try_new(Self)?)
64-
}
6567
}
6668

6769
struct RustExample {
@@ -150,7 +152,7 @@ impl KernelModule for RustExample {
150152

151153
Ok(RustExample {
152154
message: "on the heap!".to_owned(),
153-
_dev: miscdev::Registration::new_pinned::<RustFile>(cstr!("rust_miscdev"), None)?,
155+
_dev: miscdev::Registration::new_pinned::<RustFile>(cstr!("rust_miscdev"), None, ())?,
154156
_chrdev: chrdev_reg,
155157
})
156158
}

rust/kernel/chrdev.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl<const N: usize> Registration<{ N }> {
7878
/// Registers a character device.
7979
///
8080
/// You may call this once per device type, up to `N` times.
81-
pub fn register<T: file_operations::FileOperations>(self: Pin<&mut Self>) -> KernelResult {
81+
pub fn register<T: file_operations::FileOpener<()>>(self: Pin<&mut Self>) -> KernelResult {
8282
// SAFETY: We must ensure that we never move out of `this`.
8383
let this = unsafe { self.get_unchecked_mut() };
8484
if this.inner.is_none() {
@@ -112,7 +112,10 @@ impl<const N: usize> Registration<{ N }> {
112112
// SAFETY: Calling unsafe functions and manipulating `MaybeUninit`
113113
// pointer.
114114
unsafe {
115-
bindings::cdev_init(cdev, &file_operations::FileOperationsVtable::<T>::VTABLE);
115+
bindings::cdev_init(
116+
cdev,
117+
&file_operations::FileOperationsVtable::<Self, T>::VTABLE,
118+
);
116119
(*cdev).owner = this.this_module.0;
117120
let rc = bindings::cdev_add(cdev, inner.dev + inner.used as bindings::dev_t, 1);
118121
if rc != 0 {
@@ -124,6 +127,17 @@ impl<const N: usize> Registration<{ N }> {
124127
}
125128
}
126129

130+
impl<const N: usize> file_operations::FileOpenAdapter for Registration<{ N }> {
131+
type Arg = ();
132+
133+
unsafe fn convert(
134+
_inode: *mut bindings::inode,
135+
_file: *mut bindings::file,
136+
) -> *const Self::Arg {
137+
&()
138+
}
139+
}
140+
127141
// SAFETY: `Registration` does not expose any of its state across threads
128142
// (it is fine for multiple threads to have a shared reference to it).
129143
unsafe impl<const N: usize> Sync for Registration<{ N }> {}

rust/kernel/file_operations.rs

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,13 @@ macro_rules! from_kernel_result {
8181
}};
8282
}
8383

84-
unsafe extern "C" fn open_callback<T: FileOperations>(
85-
_inode: *mut bindings::inode,
84+
unsafe extern "C" fn open_callback<A: FileOpenAdapter, T: FileOpener<A::Arg>>(
85+
inode: *mut bindings::inode,
8686
file: *mut bindings::file,
8787
) -> c_types::c_int {
8888
from_kernel_result! {
89-
let ptr = T::open()?.into_pointer();
89+
let arg = A::convert(inode, file);
90+
let ptr = T::open(&*arg)?.into_pointer();
9091
(*file).private_data = ptr as *mut c_types::c_void;
9192
Ok(0)
9293
}
@@ -199,11 +200,11 @@ unsafe extern "C" fn fsync_callback<T: FileOperations>(
199200
}
200201
}
201202

202-
pub(crate) struct FileOperationsVtable<T>(marker::PhantomData<T>);
203+
pub(crate) struct FileOperationsVtable<A, T>(marker::PhantomData<A>, marker::PhantomData<T>);
203204

204-
impl<T: FileOperations> FileOperationsVtable<T> {
205+
impl<A: FileOpenAdapter, T: FileOpener<A::Arg>> FileOperationsVtable<A, T> {
205206
pub(crate) const VTABLE: bindings::file_operations = bindings::file_operations {
206-
open: Some(open_callback::<T>),
207+
open: Some(open_callback::<A, T>),
207208
release: Some(release_callback::<T>),
208209
read: if T::TO_USE.read {
209210
Some(read_callback::<T>)
@@ -410,6 +411,39 @@ impl IoctlCommand {
410411
}
411412
}
412413

414+
/// Trait for extracting file open arguments from kernel data structures.
415+
///
416+
/// This is meant to be implemented by registration managers.
417+
pub trait FileOpenAdapter {
418+
/// The type of argument this adpter extracts.
419+
type Arg;
420+
421+
/// Converts untyped data stored in [`struct inode`] and [`struct file`] (when [`struct
422+
/// file_operations::open`] is called) into the given type. For example, for `miscdev`
423+
/// devices, a pointer to the registered [`struct miscdev`] is stored in [`struct
424+
/// file::private_data`].
425+
///
426+
/// # Safety
427+
///
428+
/// This function must be called only when [`struct file_operations::open`] is being called for
429+
/// a file that was registered by the implementer.
430+
unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file)
431+
-> *const Self::Arg;
432+
}
433+
434+
/// Trait for implementers of kernel files.
435+
///
436+
/// In addition to the methods in [`FileOperations`], implementers must also provide
437+
/// [`FileOpener::open`] with a customised argument. This allows a single implementation of
438+
/// [`FileOperations`] to be used for different types of registrations, for example, `miscdev` and
439+
/// `chrdev`.
440+
pub trait FileOpener<T: ?Sized>: FileOperations {
441+
/// Creates a new instance of this file.
442+
///
443+
/// Corresponds to the `open` function pointer in `struct file_operations`.
444+
fn open(context: &T) -> KernelResult<Self::Wrapper>;
445+
}
446+
413447
/// Corresponds to the kernel's `struct file_operations`.
414448
///
415449
/// You implement this trait whenever you would create a `struct file_operations`.
@@ -423,11 +457,6 @@ pub trait FileOperations: Sync + Sized {
423457
/// The pointer type that will be used to hold ourselves.
424458
type Wrapper: PointerWrapper<Self>;
425459

426-
/// Creates a new instance of this file.
427-
///
428-
/// Corresponds to the `open` function pointer in `struct file_operations`.
429-
fn open() -> KernelResult<Self::Wrapper>;
430-
431460
/// Cleans up after the last reference to the file goes away.
432461
///
433462
/// Note that the object is moved, so it will be freed automatically unless the implementation
@@ -502,7 +531,7 @@ impl<T> PointerWrapper<T> for Box<T> {
502531
}
503532

504533
unsafe fn from_pointer(ptr: *const T) -> Self {
505-
Box::<T>::from_raw(ptr as _)
534+
Box::from_raw(ptr as _)
506535
}
507536
}
508537

@@ -512,6 +541,6 @@ impl<T> PointerWrapper<T> for Arc<T> {
512541
}
513542

514543
unsafe fn from_pointer(ptr: *const T) -> Self {
515-
Arc::<T>::from_raw(ptr)
544+
Arc::from_raw(ptr)
516545
}
517546
}

rust/kernel/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,5 +140,50 @@ fn panic(_info: &PanicInfo) -> ! {
140140
}
141141
}
142142

143+
/// Calculates the offset of a field from the beginning of the struct it belongs to.
144+
#[macro_export]
145+
macro_rules! offset_of {
146+
($type:ty, $($f:tt)*) => {{
147+
let tmp = core::mem::MaybeUninit::<$type>::uninit();
148+
let ptr = tmp.as_ptr();
149+
#[allow(unused_unsafe)]
150+
let dev = unsafe { core::ptr::addr_of!((*ptr).$($f)*) as *const u8 };
151+
#[allow(unused_unsafe)]
152+
unsafe { dev.offset_from(ptr as *const u8) }
153+
}}
154+
}
155+
156+
/// Produces a pointer to an object from a pointer to one of its fields.
157+
///
158+
/// # Safety
159+
///
160+
/// Callers must ensure that the pointer to the field is in fact a pointer to the specified field,
161+
/// as opposed to a pointer to another object of the same type.
162+
///
163+
/// # Example
164+
///
165+
///```
166+
/// struct Test {
167+
/// a: u64,
168+
/// b: u32,
169+
/// }
170+
///
171+
/// fn test() {
172+
/// let test = Test{a: 10, b: 20};
173+
/// let b_ptr = &test.b;
174+
/// let test_alias = unsafe { container_of!(b_ptr, Test, b) };
175+
/// // This prints `true`.
176+
/// println!("{}", core::ptr::eq(&test, test_alias));
177+
/// }
178+
///```
179+
///
180+
#[macro_export]
181+
macro_rules! container_of {
182+
($ptr:expr, $type:ty, $($f:tt)*) => {{
183+
let offset = $crate::offset_of!($type, $($f)*);
184+
($ptr as *const _ as *const u8).offset(-offset) as *const $type
185+
}}
186+
}
187+
143188
#[global_allocator]
144189
static ALLOCATOR: allocator::KernelAllocator = allocator::KernelAllocator;

rust/kernel/miscdev.rs

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,91 +7,97 @@
77
//! Reference: <https://www.kernel.org/doc/html/latest/driver-api/misc_devices.html>
88
99
use crate::error::{Error, KernelResult};
10-
use crate::file_operations::{FileOperations, FileOperationsVtable};
10+
use crate::file_operations::{FileOpenAdapter, FileOpener, FileOperationsVtable};
1111
use crate::{bindings, c_types, CStr};
1212
use alloc::boxed::Box;
1313
use core::marker::PhantomPinned;
1414
use core::pin::Pin;
1515

1616
/// A registration of a miscellaneous device.
17-
pub struct Registration {
18-
mdev: Option<bindings::miscdevice>,
17+
pub struct Registration<T: Sync = ()> {
18+
registered: bool,
19+
mdev: bindings::miscdevice,
1920
_pin: PhantomPinned,
21+
22+
/// Context initialised on construction and made available to all file instances on
23+
/// [`FileOpener::open`].
24+
pub context: T,
2025
}
2126

22-
impl Registration {
27+
// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it
28+
// is safe to pass `&Registration` to multiple threads because it offers no interior mutability,
29+
// except maybe through [`Registration::context`], but it is itself [`Sync`].
30+
unsafe impl<T: Sync> Sync for Registration<T> {}
31+
32+
impl<T: Sync> Registration<T> {
2333
/// Creates a new [`Registration`] but does not register it yet.
2434
///
2535
/// It is allowed to move.
26-
pub fn new() -> Self {
36+
pub fn new(context: T) -> Self {
2737
Self {
28-
mdev: None,
38+
registered: false,
39+
mdev: bindings::miscdevice::default(),
2940
_pin: PhantomPinned,
41+
context,
3042
}
3143
}
3244

3345
/// Registers a miscellaneous device.
3446
///
3547
/// Returns a pinned heap-allocated representation of the registration.
36-
pub fn new_pinned<T: FileOperations>(
48+
pub fn new_pinned<F: FileOpener<T>>(
3749
name: CStr<'static>,
3850
minor: Option<i32>,
51+
state: T,
3952
) -> KernelResult<Pin<Box<Self>>> {
40-
let mut r = Pin::from(Box::try_new(Self::new())?);
41-
r.as_mut().register::<T>(name, minor)?;
53+
let mut r = Pin::from(Box::try_new(Self::new(state))?);
54+
r.as_mut().register::<F>(name, minor)?;
4255
Ok(r)
4356
}
4457

4558
/// Registers a miscellaneous device with the rest of the kernel.
4659
///
47-
/// It must be pinned because the memory block that represents the
48-
/// registration is self-referential. If a minor is not given, the kernel
49-
/// allocates a new one if possible.
50-
pub fn register<T: FileOperations>(
60+
/// It must be pinned because the memory block that represents the registration is
61+
/// self-referential. If a minor is not given, the kernel allocates a new one if possible.
62+
pub fn register<F: FileOpener<T>>(
5163
self: Pin<&mut Self>,
5264
name: CStr<'static>,
5365
minor: Option<i32>,
5466
) -> KernelResult {
5567
// SAFETY: We must ensure that we never move out of `this`.
5668
let this = unsafe { self.get_unchecked_mut() };
57-
if this.mdev.is_some() {
69+
if this.registered {
5870
// Already registered.
5971
return Err(Error::EINVAL);
6072
}
6173

62-
this.mdev = Some(bindings::miscdevice::default());
63-
let dev = this.mdev.as_mut().unwrap();
64-
dev.fops = &FileOperationsVtable::<T>::VTABLE;
65-
dev.name = name.as_ptr() as *const c_types::c_char;
66-
dev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);
67-
let ret = unsafe { bindings::misc_register(dev) };
74+
this.mdev.fops = &FileOperationsVtable::<Self, F>::VTABLE;
75+
this.mdev.name = name.as_ptr() as *const c_types::c_char;
76+
this.mdev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);
77+
78+
let ret = unsafe { bindings::misc_register(&mut this.mdev) };
6879
if ret < 0 {
69-
this.mdev = None;
7080
return Err(Error::from_kernel_errno(ret));
7181
}
82+
this.registered = true;
7283
Ok(())
7384
}
7485
}
7586

76-
impl Default for Registration {
77-
fn default() -> Self {
78-
Self::new()
87+
impl<T: Sync> FileOpenAdapter for Registration<T> {
88+
type Arg = T;
89+
90+
unsafe fn convert(_inode: *mut bindings::inode, file: *mut bindings::file) -> *const Self::Arg {
91+
let reg = crate::container_of!((*file).private_data, Self, mdev);
92+
&(*reg).context
7993
}
8094
}
8195

82-
// SAFETY: The only method is `register()`, which requires a (pinned) mutable
83-
// `Registration`, so it is safe to pass `&Registration` to multiple threads
84-
// because it offers no interior mutability.
85-
unsafe impl Sync for Registration {}
86-
87-
impl Drop for Registration {
88-
/// Removes the registration from the kernel if it has completed
89-
/// successfully before.
96+
impl<T: Sync> Drop for Registration<T> {
97+
/// Removes the registration from the kernel if it has completed successfully before.
9098
fn drop(&mut self) {
91-
if let Some(ref mut dev) = self.mdev {
92-
unsafe {
93-
bindings::misc_deregister(dev);
94-
}
99+
if self.registered {
100+
unsafe { bindings::misc_deregister(&mut self.mdev) }
95101
}
96102
}
97103
}

0 commit comments

Comments
 (0)