Skip to content

Commit 5ddd62f

Browse files
committed
rust: add device::Data.
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]>
1 parent 6c9714f commit 5ddd62f

File tree

1 file changed

+131
-1
lines changed

1 file changed

+131
-1
lines changed

rust/kernel/device.rs

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,17 @@
44
//!
55
//! C header: [`include/linux/device.h`](../../../../include/linux/device.h)
66
7-
use crate::{bindings, str::CStr};
7+
use crate::{
8+
bindings,
9+
revocable::{Revocable, RevocableGuard},
10+
str::CStr,
11+
sync::{NeedsLockClass, RevocableMutex, RevocableMutexGuard, UniqueRef},
12+
Result,
13+
};
14+
use core::{
15+
ops::{Deref, DerefMut},
16+
pin::Pin,
17+
};
818

919
/// A raw device.
1020
///
@@ -74,3 +84,123 @@ impl Drop for Device {
7484
unsafe { bindings::put_device(self.ptr) };
7585
}
7686
}
87+
88+
/// Device data.
89+
///
90+
/// When a device is removed (for whatever reason, for example, because the device was unplugged or
91+
/// because the user decided to remove the driver), the driver is given a chance to clean its state
92+
/// up, and all io resources should ideally not be used anymore.
93+
///
94+
/// However, the device data is reference-counted because other subsystems hold pointers to it. So
95+
/// some device state must be freed and not used anymore, while others must remain accessible.
96+
///
97+
/// This struct separates the device data into three categories:
98+
/// 1. Io resources: are available until the device is removed.
99+
/// 2. Registrations: are destroyed on when the device is removed, but before the io resources
100+
/// become inaccessible.
101+
/// 3. General data: remain available as long as the ref count is nonzero.
102+
///
103+
/// This struct implements the `DeviceRemoval` trait so that it can clean resources up even if not
104+
/// explicitly called by the device drivers.
105+
pub struct Data<Reg, Res, Gen> {
106+
regs: RevocableMutex<Reg>,
107+
res: Revocable<Res>,
108+
general: Gen,
109+
}
110+
111+
/// Safely creates an new reference-counted instance of [`Data`].
112+
#[doc(hidden)]
113+
#[macro_export]
114+
macro_rules! new_device_data {
115+
($reg:expr, $res:expr, $gen:expr, $name:literal) => {{
116+
static mut CLASS1: core::mem::MaybeUninit<$crate::bindings::lock_class_key> =
117+
core::mem::MaybeUninit::uninit();
118+
static mut CLASS2: core::mem::MaybeUninit<$crate::bindings::lock_class_key> =
119+
core::mem::MaybeUninit::uninit();
120+
let regs = $reg;
121+
let res = $res;
122+
let gen = $gen;
123+
let name = $crate::c_str!($name);
124+
// SAFETY: `CLASS1` and `CLASS2` are never used by Rust code directly; the C portion of the
125+
// kernel may change it though.
126+
#[allow(unused_unsafe)]
127+
unsafe {
128+
$crate::device::Data::try_new(
129+
regs,
130+
res,
131+
gen,
132+
name,
133+
CLASS1.as_mut_ptr(),
134+
CLASS2.as_mut_ptr(),
135+
)
136+
}
137+
}};
138+
}
139+
140+
impl<Reg, Res, Gen> Data<Reg, Res, Gen> {
141+
/// Creates a new instance of `Data`.
142+
///
143+
/// It is recommended that the [`new_device_data`] macro be used as it automatically creates
144+
/// the lock classes.
145+
///
146+
/// # Safety
147+
///
148+
/// `key1` and `key2` must point to valid memory locations and remain valid until `self` is
149+
/// dropped.
150+
pub unsafe fn try_new(
151+
regs: Reg,
152+
res: Res,
153+
gen: Gen,
154+
name: &'static CStr,
155+
key1: *mut bindings::lock_class_key,
156+
key2: *mut bindings::lock_class_key,
157+
) -> Result<Pin<UniqueRef<Self>>> {
158+
let mut ret = Pin::from(UniqueRef::try_new(Self {
159+
// SAFETY: We call `RevocableMutex::init` below.
160+
regs: unsafe { RevocableMutex::new(regs) },
161+
res: Revocable::new(res),
162+
general: gen,
163+
})?);
164+
// SAFETY: `Data::regs` is pinned when `Data` is.
165+
let pinned = unsafe { ret.as_mut().map_unchecked_mut(|d| &mut d.regs) };
166+
167+
// SAFETY: The safety requirements of this function satisfy those of `RevocableMutex::init`.
168+
unsafe { pinned.init(name, key1, key2) };
169+
Ok(ret)
170+
}
171+
172+
/// Returns the resources if they're still available.
173+
pub fn resources(&self) -> Option<RevocableGuard<'_, Res>> {
174+
self.res.try_access()
175+
}
176+
177+
/// Returns the locked registrations if they're still available.
178+
pub fn registrations(&self) -> Option<RevocableMutexGuard<'_, Reg>> {
179+
self.regs.try_lock()
180+
}
181+
}
182+
183+
impl<Reg, Res, Gen> crate::driver::DeviceRemoval for Data<Reg, Res, Gen> {
184+
fn device_remove(&self) {
185+
// We revoke the registrations first so that resources are still available to them during
186+
// unregistration.
187+
self.regs.revoke();
188+
189+
// Release resources now. General data remains available.
190+
self.res.revoke();
191+
}
192+
}
193+
194+
impl<Reg, Res, Gen> Deref for Data<Reg, Res, Gen> {
195+
type Target = Gen;
196+
197+
fn deref(&self) -> &Gen {
198+
&self.general
199+
}
200+
}
201+
202+
impl<Reg, Res, Gen> DerefMut for Data<Reg, Res, Gen> {
203+
fn deref_mut(&mut self) -> &mut Gen {
204+
&mut self.general
205+
}
206+
}

0 commit comments

Comments
 (0)