diff options
Diffstat (limited to 'rust/kernel/miscdevice.rs')
| -rw-r--r-- | rust/kernel/miscdevice.rs | 131 |
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. |
