aboutsummaryrefslogtreecommitdiff
path: root/rust/kernel/miscdevice.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel/miscdevice.rs')
-rw-r--r--rust/kernel/miscdevice.rs131
1 files changed, 79 insertions, 52 deletions
diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index 8d4b719bd83f..900676ea6bbc 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -10,11 +10,9 @@
use crate::{
bindings,
+ container_of,
device::Device,
- error::{
- to_result,
- VTABLE_DEFAULT_ERROR, //
- },
+ error::to_result,
fs::{
File,
Kiocb, //
@@ -32,7 +30,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 +64,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 +133,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 +149,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);
}
@@ -145,37 +177,33 @@ pub trait MiscDevice: Sized {
/// the VMA initialization by calling methods of `vma`. If the function does not return an
/// error, the kernel will complete initialization of the VMA according to the properties of
/// `vma`.
+ #[optional]
fn mmap(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_vma: &VmaNew,
- ) -> Result {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ ) -> Result;
/// Read from this miscdevice.
- fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterDest<'_>) -> Result<usize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ #[optional]
+ fn read_iter(_kiocb: Kiocb<'_, Self::File>, _iov: &mut IovIterDest<'_>) -> Result<usize>;
/// Write to this miscdevice.
- fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterSource<'_>) -> Result<usize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ #[optional]
+ fn write_iter(_kiocb: Kiocb<'_, Self::File>, _iov: &mut IovIterSource<'_>) -> Result<usize>;
/// Handler for ioctls.
///
/// The `cmd` argument is usually manipulated using the utilities in [`kernel::ioctl`].
///
/// [`kernel::ioctl`]: mod@crate::ioctl
+ #[optional]
fn ioctl(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
- ) -> Result<isize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ ) -> Result<isize>;
/// Handler for ioctls.
///
@@ -185,23 +213,21 @@ pub trait MiscDevice: Sized {
/// that have different layout on 32-bit and 64-bit userspace. If no implementation is
/// provided, then `compat_ptr_ioctl` will be used instead.
#[cfg(CONFIG_COMPAT)]
+ #[optional]
fn compat_ioctl(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
- ) -> Result<isize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ ) -> Result<isize>;
/// Show info for this fd.
+ #[optional]
fn show_fdinfo(
- _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+ _device: <Self::File as ForeignOwnable>::Borrowed<'_>,
_m: &SeqFile,
_file: &File,
- ) {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ );
}
/// A vtable for the file operations of a Rust miscdevice.
@@ -220,13 +246,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 +284,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 +347,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 +368,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 +393,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 +414,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.