Skip to content

rust: add device::Data. #570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 30, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) => {{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is the potential for people to mess up the order of the fields. Maybe do

let data = new_device_data! {
    registration: reg,
    resource: res,
    general: gen,
    name: "foo",
};

? With a potential shorthand when the variable name matches the field name just like with struct.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The types are different and need to be spelled out elsewhere (as opposed to being inferred) because this is usually the Data type of a Driver trait. Here's an example of it being used: https://github.com/Rust-for-Linux/linux/pull/571/files#diff-0b4ec144d8f0479a497eca5a2e4ab8c1009b0debcdb00ece09f79e5baa8faac9R272

IOW, if people mess up the order, they'll get a compilation error.

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
}
}