summaryrefslogtreecommitdiff
path: root/rust/kernel/miscdevice.rs
diff options
context:
space:
mode:
authorAlice Ryhl <aliceryhl@google.com>2026-10-13 12:29:02 +0000
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-10-13 15:57:43 +0200
commitba9d2c7ea7f8fd02726241e9b93a2a31d30c77ea (patch)
treeabe50d2c9e7f05a65b462ca53bce564581282e0b /rust/kernel/miscdevice.rs
parent0ada43e79a5f23154c5146c3eaf595c9c1146976 (diff)
downloadlinux-ba9d2c7ea7f8fd02726241e9b93a2a31d30c77ea.tar.gz
linux-ba9d2c7ea7f8fd02726241e9b93a2a31d30c77ea.zip
rust: miscdevice: add registration data to MiscDevice
Currently when fds for a miscdevice are opened, the only "global" data they are given access to is the MiscDeviceRegistration value. However, this value doesn't let you store any user-provided data, so there is no way for different fds from the same miscdevice to interact with each other. Thus, let the user specify a type to be stored in the MiscDeviceRegistration in which the user can store whichever data they would like. The intended user of this patch is Rust course material. MiscDevice is a nice and relatively easy to use API for someone's first driver, and being able to persist data from fd to fd allows the student to interact with their driver using 'cat' and 'echo', even though each call opens a new fd. Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20261013-miscdevice-data-v2-1-d727496ed705@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'rust/kernel/miscdevice.rs')
-rw-r--r--rust/kernel/miscdevice.rs100
1 files changed, 68 insertions, 32 deletions
diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index 8d4b719bd83f..ecc142a1b7ea 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -10,6 +10,7 @@
use crate::{
bindings,
+ container_of,
device::Device,
error::{
to_result,
@@ -32,7 +33,10 @@ use crate::{
Opaque, //
}, //
};
-use core::marker::PhantomData;
+use core::{
+ marker::PhantomData,
+ ops::Deref, //
+};
/// Options for creating a misc device.
#[derive(Copy, Clone)]
@@ -63,37 +67,56 @@ impl MiscDeviceOptions {
/// - Deregistration occurs exactly once in [`Drop`] via `misc_deregister()`.
/// - `inner` wraps a valid, pinned `miscdevice` created using
/// [`MiscDeviceOptions::into_raw`].
-#[repr(transparent)]
+#[repr(C)]
#[pin_data(PinnedDrop)]
pub struct MiscDeviceRegistration<T> {
#[pin]
inner: Opaque<bindings::miscdevice>,
- _t: PhantomData<T>,
+ #[pin]
+ data: T,
}
// SAFETY: It is allowed to call `misc_deregister` on a different thread from where you called
-// `misc_register`.
-unsafe impl<T> Send for MiscDeviceRegistration<T> {}
+// `misc_register`, and `T` is `Send`.
+unsafe impl<T: Send> Send for MiscDeviceRegistration<T> {}
// SAFETY: All `&self` methods on this type are written to ensure that it is safe to call them in
-// parallel.
-unsafe impl<T> Sync for MiscDeviceRegistration<T> {}
+// parallel, and `T` is `Sync`.
+unsafe impl<T: Sync> Sync for MiscDeviceRegistration<T> {}
impl<T: MiscDevice> MiscDeviceRegistration<T> {
/// Register a misc device.
- pub fn register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error> {
+ ///
+ /// # Safety
+ ///
+ /// The caller must not `mem::forget()` the returned [`MiscDeviceRegistration`] or otherwise
+ /// prevent its [`Drop`] implementation from running.
+ pub unsafe fn register<'a>(
+ opts: MiscDeviceOptions,
+ data: impl PinInit<T, Error> + 'a,
+ ) -> impl PinInit<Self, Error> + 'a
+ where
+ T: 'a,
+ {
try_pin_init!(Self {
+ data <- data,
inner <- Opaque::try_ffi_init(move |slot: *mut bindings::miscdevice| {
// SAFETY: The initializer can write to the provided `slot`.
unsafe { slot.write(opts.into_raw::<T>()) };
// SAFETY: We just wrote the misc device options to the slot. The miscdevice will
// get unregistered before `slot` is deallocated because the memory is pinned and
- // the destructor of this type deallocates the memory.
+ // the caller guarantees that the destructor of this type will run.
+ //
+ // The `data` field is already initialized and is only dropped after
+ // `misc_deregister` in `PinnedDrop::drop`. Since `T: Sync` and the caller
+ // guarantees that `Drop` will run before `T` becomes invalid, it is sound for the
+ // `open` callback to access `&MiscDeviceRegistration<T>` until `misc_deregister`
+ // returns.
+ //
// INVARIANT: If this returns `Ok(())`, then the `slot` will contain a registered
// misc device.
to_result(unsafe { bindings::misc_register(slot) })
}),
- _t: PhantomData,
})
}
@@ -113,6 +136,14 @@ impl<T: MiscDevice> MiscDeviceRegistration<T> {
}
}
+impl<T> Deref for MiscDeviceRegistration<T> {
+ type Target = T;
+ #[inline]
+ fn deref(&self) -> &T {
+ &self.data
+ }
+}
+
#[pinned_drop]
impl<T> PinnedDrop for MiscDeviceRegistration<T> {
fn drop(self: Pin<&mut Self>) {
@@ -121,19 +152,23 @@ impl<T> PinnedDrop for MiscDeviceRegistration<T> {
}
}
-/// Trait implemented by the private data of an open misc device.
+/// Trait implemented by the data stored in a [`MiscDeviceRegistration`].
#[vtable]
-pub trait MiscDevice: Sized {
- /// What kind of pointer should `Self` be wrapped in.
- type Ptr: ForeignOwnable + Send + Sync;
+pub trait MiscDevice: Sized + Sync {
+ /// The type of the data stored in each file for this miscdevice.
+ ///
+ /// This type must be pointer-sized, so it will usually be a [`KBox`] or [`Arc`].
+ ///
+ /// [`Arc`]: crate::sync::Arc
+ type File: ForeignOwnable + Send + Sync + 'static;
/// Called when the misc device is opened.
///
/// The returned pointer will be stored as the private data for the file.
- fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>;
+ fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::File>;
/// Called when the misc device is released.
- fn release(device: Self::Ptr, _file: &File) {
+ fn release(device: Self::File, _file: &File) {
drop(device);
}
@@ -146,7 +181,7 @@ pub trait MiscDevice: Sized {
/// error, the kernel will complete initialization of the VMA according to the properties of
/// `vma`.
fn mmap(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_vma: &VmaNew,
) -> Result {
@@ -154,12 +189,12 @@ pub trait MiscDevice: Sized {
}
/// Read from this miscdevice.
- fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterDest<'_>) -> Result<usize> {
+ fn read_iter(_kiocb: Kiocb<'_, Self::File>, _iov: &mut IovIterDest<'_>) -> Result<usize> {
build_error!(VTABLE_DEFAULT_ERROR)
}
/// Write to this miscdevice.
- fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterSource<'_>) -> Result<usize> {
+ fn write_iter(_kiocb: Kiocb<'_, Self::File>, _iov: &mut IovIterSource<'_>) -> Result<usize> {
build_error!(VTABLE_DEFAULT_ERROR)
}
@@ -169,7 +204,7 @@ pub trait MiscDevice: Sized {
///
/// [`kernel::ioctl`]: mod@crate::ioctl
fn ioctl(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
@@ -186,7 +221,7 @@ pub trait MiscDevice: Sized {
/// provided, then `compat_ptr_ioctl` will be used instead.
#[cfg(CONFIG_COMPAT)]
fn compat_ioctl(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
@@ -196,7 +231,7 @@ pub trait MiscDevice: Sized {
/// Show info for this fd.
fn show_fdinfo(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_m: &SeqFile,
_file: &File,
) {
@@ -220,13 +255,14 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
}
// SAFETY: The open call of a file can access the private data.
- let misc_ptr = unsafe { (*raw_file).private_data };
+ let misc_ptr = Opaque::cast_from(unsafe { (*raw_file).private_data }.cast());
// SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
- // associated `struct miscdevice` before calling into this method. Furthermore,
- // `misc_open()` ensures that the miscdevice can't be unregistered and freed during this
- // call to `fops_open`.
- let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
+ // associated `struct miscdevice` (which is the `inner` field of a valid
+ // `MiscDeviceRegistration<T>`) before calling into this method. Furthermore, `misc_open()`
+ // ensures that the miscdevice can't be unregistered and freed during this call to
+ // `fops_open`, and `T: Sync` allows sharing `&MiscDeviceRegistration<T>` across threads.
+ let misc = unsafe { &*container_of!(misc_ptr, MiscDeviceRegistration<T>, inner) };
// SAFETY:
// * This underlying file is valid for (much longer than) the duration of `T::open`.
@@ -257,7 +293,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: The release call of a file owns the private data.
- let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
+ let ptr = unsafe { <T::File as ForeignOwnable>::from_foreign(private) };
// SAFETY:
// * The file is valid for the duration of this call.
@@ -320,7 +356,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
// SAFETY: This is a Rust Miscdevice, so we call `into_foreign` in `open` and
// `from_foreign` in `release`, and `fops_mmap` is guaranteed to be called between those
// two operations.
- let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private.cast()) };
+ let device = unsafe { <T::File as ForeignOwnable>::borrow(private) };
// SAFETY: The caller provides a vma that is undergoing initial VMA setup.
let area = unsafe { VmaNew::from_raw(vma) };
// SAFETY:
@@ -341,7 +377,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
// SAFETY: The ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
- let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
+ let device = unsafe { <T::File as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
@@ -366,7 +402,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
// SAFETY: The compat ioctl call of a file can access the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
- let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
+ let device = unsafe { <T::File as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
@@ -387,7 +423,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
- let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
+ let device = unsafe { <T::File as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.