Initialize the driver's private data directly on `PrivateData`.
Introduce `OpenGuard` for resource cleanup.
Signed-off-by: Markus Probst markus.probst@posteo.de --- rust/kernel/serdev.rs | 131 ++++++++++++++++++++------------------------------ 1 file changed, 52 insertions(+), 79 deletions(-)
diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs index c16d6593a8d2..3f9165e16776 100644 --- a/rust/kernel/serdev.rs +++ b/rust/kernel/serdev.rs @@ -17,16 +17,12 @@ prelude::*, sync::aref::AlwaysRefCounted, time::Jiffies, - types::{ - Opaque, - ScopeGuard, // - }, // + types::Opaque, // };
use core::{ - cell::UnsafeCell, marker::PhantomData, - mem::{offset_of, MaybeUninit}, + mem::offset_of, ptr::NonNull, // };
@@ -92,24 +88,36 @@ unsafe fn unregister(sdrv: &OpaqueSelf::DriverType) { } }
+struct OpenGuard<'bound> { + sdev: &'bound Devicedevice::Bound, +} + +impl Drop for OpenGuard<'_> { + fn drop(&mut self) { + // SAFETY: + // - `self.sdev.as_raw()` is guaranteed to be a pointer to a valid + // `struct serdev_device`. + // - The existence of self proves that the device is open. + unsafe { bindings::serdev_device_close(self.sdev.as_raw()) }; + } +} + #[doc(hidden)] -#[pin_data(PinnedDrop)] +#[pin_data] pub struct PrivateData<'bound, T: Driver> { - sdev: &'bound Devicedevice::Bound, #[pin] - driver: UnsafeCell<MaybeUninit<T::Data<'bound>>>, - open: UnsafeCell<bool>, + driver: T::Data<'bound>, + open: OpenGuard<'bound>, }
-#[pinned_drop] -impl<T: Driver> PinnedDrop for PrivateData<'_, T> { - fn drop(self: Pin<&mut Self>) { - // SAFETY: We have exclusive access to `self.open`. - if unsafe { *self.open.get() } { - // SAFETY: `self.sdev.as_raw()` is guaranteed to be a pointer to a valid - // `struct serdev_device`. - unsafe { bindings::serdev_device_close(self.sdev.as_raw()) }; - } +impl<'bound, T: Driver> PrivateData<'bound, T> { + #[inline] + fn driver_data(self: Pin<&Self>) -> Pin<&T::Data<'bound>> { + // SAFETY: We treat the result as pinned. + let inner = unsafe { Pin::into_inner_unchecked(self) }; + + // SAFETY: `self.driver` is pinned. + unsafe { Pin::new_unchecked(&inner.driver) } } }
@@ -134,46 +142,30 @@ extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi:
from_result(|| { sdev.as_ref().set_drvdata(try_pin_init!(PrivateData::<T> { - sdev: &**sdev, - driver: MaybeUninit::<T::Data<'_>>::zeroed().into(), - open: false.into(), + open: { + // SAFETY: + // - `sdev.as_raw()` is guaranteed to be a valid pointer to + // `serdev_device`. + // - It is safe to call before open. + unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) }; + + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to + // `serdev_device`. + unsafe { bindings::serdev_device_pause_rx(sdev.as_raw()) }; + + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to + // `serdev_device`. + to_result(unsafe { bindings::serdev_device_open(sdev.as_raw()) })?; + + OpenGuard { sdev } + }, + driver <- T::probe(sdev, info), }))?; - // SAFETY: We just set drvdata to `PrivateData<'_, T>`. - let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() }; - let private_data = ScopeGuard::new_with_data(private_data, |_| { - // SAFETY: We just set drvdata to `PrivateData<'_, T>`. - drop(unsafe { sdev.as_ref().drvdata_obtain::<PrivateData<'_, T>>() }); - }); - // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`. - unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
// SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`. - unsafe { bindings::serdev_device_pause_rx(sdev.as_raw()) }; - - // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer - // to a `serdev_device`. - to_result(unsafe { bindings::serdev_device_open(sdev.as_raw()) })?; - - // SAFETY: We have exclusive access to `private_data.open`. - unsafe { *private_data.open.get() = true }; - - let data = T::probe(sdev, info); + unsafe { bindings::serdev_device_resume_rx(sdev.as_raw()) };
- // SAFETY: We have exclusive access to `private_data.driver`. - let driver = unsafe { &mut *private_data.driver.get() }; - // SAFETY: - // - `driver.as_mut_ptr()` is a valid pointer to uninitialized data. - // - `private_data.driver` is pinned. - let result = unsafe { pin_init::raw_try_init(driver.as_mut_ptr(), data) }; - - result.map(|()| { - private_data.dismiss(); - - // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`. - unsafe { bindings::serdev_device_resume_rx(sdev.as_raw()) }; - - 0 - }) + Ok(0) }) }
@@ -189,21 +181,10 @@ extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) { // and stored a `Pin<KBox<PrivateData<'_, T>>>`. let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
- // SAFETY: No one has exclusive access to `private_data.driver`. - let data = unsafe { &*private_data.driver.get() }; - // SAFETY: - // - `private_data.driver` is pinned. - // - `remove_callback` is only ever called after a successful call to `probe_callback`, - // hence it's guaranteed that `private_data.driver` was initialized. - let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) }; - - T::unbind(sdev, data_pinned); + T::unbind(sdev, private_data.driver_data());
// SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`. unsafe { bindings::serdev_device_pause_rx(sdev.as_raw()) }; - - // SAFETY: We already established that `data` is guaranteed to be initialized. - unsafe { data.assume_init_drop() }; }
extern "C" fn receive_buf_callback( @@ -211,6 +192,9 @@ extern "C" fn receive_buf_callback( buf: *const u8, length: usize, ) -> usize { + // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`. + let buf = unsafe { core::slice::from_raw_parts(buf, length) }; + // SAFETY: The serial device bus only ever calls the receive buf callback with a valid // pointer to a `struct serdev_device`. // @@ -222,18 +206,7 @@ extern "C" fn receive_buf_callback( // and stored a `Pin<KBox<PrivateData<'_, T>>>`. let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
- // SAFETY: No one has exclusive access to `private_data.driver`. - let data = unsafe { &*private_data.driver.get() }; - // SAFETY: - // - `private_data.driver` is pinned. - // - `receive_buf_callback` is only ever called after a successful call to `probe_callback`, - // hence it's guaranteed that `private_data.driver` was initialized. - let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) }; - - // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`. - let buf = unsafe { core::slice::from_raw_parts(buf, length) }; - - T::receive(sdev, data_pinned, buf) + T::receive(sdev, private_data.driver_data(), buf) } }