Skip to content

Commit 37ee4f8

Browse files
committed
Share context with file instances based on registration.
1 parent dc7dead commit 37ee4f8

File tree

4 files changed

+89
-25
lines changed

4 files changed

+89
-25
lines changed

drivers/char/rust_example.rs

Lines changed: 8 additions & 6 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 {

rust/kernel/chrdev.rs

Lines changed: 20 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,12 @@ 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>::build());
115+
bindings::cdev_init(
116+
cdev,
117+
// SAFETY: The adapter doesn't retrieve any state yet, so it's compatible with any
118+
// registration.
119+
file_operations::FileOperationsVtable::<Self, T>::build(),
120+
);
116121
(*cdev).owner = this.this_module.0;
117122
let rc = bindings::cdev_add(cdev, inner.dev + inner.used as bindings::dev_t, 1);
118123
if rc != 0 {
@@ -124,6 +129,19 @@ impl<const N: usize> Registration<{ N }> {
124129
}
125130
}
126131

132+
impl<const N: usize> file_operations::FileOpenAdapter for Registration<{ N }> {
133+
type Arg = ();
134+
135+
unsafe fn convert(
136+
_inode: *mut bindings::inode,
137+
_file: *mut bindings::file,
138+
) -> *const Self::Arg {
139+
// TODO: Update the SAFETY comment on the call to `FileOperationsVTable::build` above once
140+
// this is updated to retrieve state.
141+
&()
142+
}
143+
}
144+
127145
// SAFETY: `Registration` does not expose any of its state across threads
128146
// (it is fine for multiple threads to have a shared reference to it).
129147
unsafe impl<const N: usize> Sync for Registration<{ N }> {}

rust/kernel/file_operations.rs

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

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

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

203-
impl<T: FileOperations> FileOperationsVtable<T> {
204+
impl<A: FileOpenAdapter, T: FileOpener<A::Arg>> FileOperationsVtable<A, T> {
204205
const VTABLE: bindings::file_operations = bindings::file_operations {
205-
open: Some(open_callback::<T>),
206+
open: Some(open_callback::<A, T>),
206207
release: Some(release_callback::<T>),
207208
read: if T::TO_USE.read {
208209
Some(read_callback::<T>)
@@ -262,7 +263,11 @@ impl<T: FileOperations> FileOperationsVtable<T> {
262263
};
263264

264265
/// Builds an instance of [`struct file_operations`].
265-
pub(crate) const fn build() -> &'static bindings::file_operations {
266+
///
267+
/// # Safety
268+
///
269+
/// The caller must ensure that the adapter is compatible with the way the device is registered.
270+
pub(crate) const unsafe fn build() -> &'static bindings::file_operations {
266271
&Self::VTABLE
267272
}
268273
}
@@ -400,6 +405,39 @@ impl IoctlCommand {
400405
}
401406
}
402407

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

417-
/// Creates a new instance of this file.
418-
///
419-
/// Corresponds to the `open` function pointer in `struct file_operations`.
420-
fn open() -> KernelResult<Self::Wrapper>;
421-
422455
/// Cleans up after the last reference to the file goes away.
423456
///
424457
/// Note that the object is moved, so it will be freed automatically unless the implementation

rust/kernel/miscdev.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
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;
@@ -19,7 +19,8 @@ pub struct Registration<T: Sync = ()> {
1919
mdev: bindings::miscdevice,
2020
_pin: PhantomPinned,
2121

22-
/// Context initialised on construction.
22+
/// Context initialised on construction and made available to all file instances on
23+
/// [`FileOpener::open`].
2324
pub context: T,
2425
}
2526

@@ -39,7 +40,7 @@ impl<T: Sync> Registration<T> {
3940
/// Registers a miscellaneous device.
4041
///
4142
/// Returns a pinned heap-allocated representation of the registration.
42-
pub fn new_pinned<F: FileOperations>(
43+
pub fn new_pinned<F: FileOpener<T>>(
4344
name: CStr<'static>,
4445
minor: Option<i32>,
4546
state: T,
@@ -53,7 +54,7 @@ impl<T: Sync> Registration<T> {
5354
///
5455
/// It must be pinned because the memory block that represents the registration is
5556
/// self-referential. If a minor is not given, the kernel allocates a new one if possible.
56-
pub fn register<F: FileOperations>(
57+
pub fn register<F: FileOpener<T>>(
5758
self: Pin<&mut Self>,
5859
name: CStr<'static>,
5960
minor: Option<i32>,
@@ -65,7 +66,8 @@ impl<T: Sync> Registration<T> {
6566
return Err(Error::EINVAL);
6667
}
6768

68-
this.mdev.fops = FileOperationsVtable::<F>::build();
69+
// SAFETY: The adapter is compatible with `misc_register`.
70+
this.mdev.fops = unsafe { FileOperationsVtable::<Self, F>::build() };
6971
this.mdev.name = name.as_ptr() as *const c_types::c_char;
7072
this.mdev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);
7173

@@ -78,6 +80,15 @@ impl<T: Sync> Registration<T> {
7880
}
7981
}
8082

83+
impl<T: Sync> FileOpenAdapter for Registration<T> {
84+
type Arg = T;
85+
86+
unsafe fn convert(_inode: *mut bindings::inode, file: *mut bindings::file) -> *const Self::Arg {
87+
let reg = crate::container_of!((*file).private_data, Self, mdev);
88+
&(*reg).context
89+
}
90+
}
91+
8192
// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it
8293
// is safe to pass `&Registration` to multiple threads because it offers no interior mutability,
8394
// except maybe through [`Registration::context`], but it is itself [`Sync`].

0 commit comments

Comments
 (0)