Skip to content

Commit

Permalink
rust: add device::Data.
Browse files Browse the repository at this point in the history
This allows access to registrations and io resources to be automatically
revoked when devices are removed, even if the ref count to their state
is non-zero.

This is the last piece needed by the PL061 driver.

Signed-off-by: Wedson Almeida Filho <[email protected]>
  • Loading branch information
wedsonaf committed Nov 30, 2021
1 parent e0734ef commit 9d7d482
Showing 1 changed file with 131 additions and 1 deletion.
132 changes: 131 additions & 1 deletion rust/kernel/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@
//!
//! C header: [`include/linux/device.h`](../../../../include/linux/device.h)

use crate::{bindings, str::CStr};
use crate::{
bindings,
revocable::{Revocable, RevocableGuard},
str::CStr,
sync::{NeedsLockClass, RevocableMutex, RevocableMutexGuard, UniqueRef},
Result,
};
use core::{
ops::{Deref, DerefMut},
pin::Pin,
};

/// A raw device.
///
Expand Down Expand Up @@ -74,3 +84,123 @@ impl Drop for Device {
unsafe { bindings::put_device(self.ptr) };
}
}

/// Device data.
///
/// When a device is removed (for whatever reason, for example, because the device was unplugged or
/// because the user decided to unbind the driver), the driver is given a chance to clean its state
/// up, and all io resources should ideally not be used anymore.
///
/// However, the device data is reference-counted because other subsystems hold pointers to it. So
/// some device state must be freed and not used anymore, while others must remain accessible.
///
/// This struct separates the device data into three categories:
/// 1. Registrations: are destroyed when the device is removed, but before the io resources
/// become inaccessible.
/// 2. Io resources: are available until the device is removed.
/// 3. General data: remain available as long as the ref count is nonzero.
///
/// This struct implements the `DeviceRemoval` trait so that it can clean resources up even if not
/// explicitly called by the device drivers.
pub struct Data<T, U, V> {
registrations: RevocableMutex<T>,
resources: Revocable<U>,
general: V,
}

/// Safely creates an new reference-counted instance of [`Data`].
#[doc(hidden)]
#[macro_export]
macro_rules! new_device_data {
($reg:expr, $res:expr, $gen:expr, $name:literal) => {{
static mut CLASS1: core::mem::MaybeUninit<$crate::bindings::lock_class_key> =
core::mem::MaybeUninit::uninit();
static mut CLASS2: core::mem::MaybeUninit<$crate::bindings::lock_class_key> =
core::mem::MaybeUninit::uninit();
let regs = $reg;
let res = $res;
let gen = $gen;
let name = $crate::c_str!($name);
// SAFETY: `CLASS1` and `CLASS2` are never used by Rust code directly; the C portion of the
// kernel may change it though.
unsafe {
$crate::device::Data::try_new(
regs,
res,
gen,
name,
CLASS1.as_mut_ptr(),
CLASS2.as_mut_ptr(),
)
}
}};
}

impl<T, U, V> Data<T, U, V> {
/// Creates a new instance of `Data`.
///
/// It is recommended that the [`new_device_data`] macro be used as it automatically creates
/// the lock classes.
///
/// # Safety
///
/// `key1` and `key2` must point to valid memory locations and remain valid until `self` is
/// dropped.
pub unsafe fn try_new(
registrations: T,
resources: U,
general: V,
name: &'static CStr,
key1: *mut bindings::lock_class_key,
key2: *mut bindings::lock_class_key,
) -> Result<Pin<UniqueRef<Self>>> {
let mut ret = Pin::from(UniqueRef::try_new(Self {
// SAFETY: We call `RevocableMutex::init` below.
registrations: unsafe { RevocableMutex::new(registrations) },
resources: Revocable::new(resources),
general,
})?);

// SAFETY: `Data::registrations` is pinned when `Data` is.
let pinned = unsafe { ret.as_mut().map_unchecked_mut(|d| &mut d.registrations) };

// SAFETY: The safety requirements of this function satisfy those of `RevocableMutex::init`.
unsafe { pinned.init(name, key1, key2) };
Ok(ret)
}

/// Returns the resources if they're still available.
pub fn resources(&self) -> Option<RevocableGuard<'_, U>> {
self.resources.try_access()
}

/// Returns the locked registrations if they're still available.
pub fn registrations(&self) -> Option<RevocableMutexGuard<'_, T>> {
self.registrations.try_lock()
}
}

impl<T, U, V> crate::driver::DeviceRemoval for Data<T, U, V> {
fn device_remove(&self) {
// We revoke the registrations first so that resources are still available to them during
// unregistration.
self.registrations.revoke();

// Release resources now. General data remains available.
self.resources.revoke();
}
}

impl<T, U, V> Deref for Data<T, U, V> {
type Target = V;

fn deref(&self) -> &V {
&self.general
}
}

impl<T, U, V> DerefMut for Data<T, U, V> {
fn deref_mut(&mut self) -> &mut V {
&mut self.general
}
}

0 comments on commit 9d7d482

Please sign in to comment.