Skip to content

Commit 4bfae74

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 fbb7b1a commit 4bfae74

File tree

1 file changed

+130
-1
lines changed

1 file changed

+130
-1
lines changed

rust/kernel/device.rs

Lines changed: 130 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,122 @@ 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+
unsafe {
127+
$crate::device::Data::try_new(
128+
regs,
129+
res,
130+
gen,
131+
name,
132+
CLASS1.as_mut_ptr(),
133+
CLASS2.as_mut_ptr(),
134+
)
135+
}
136+
}};
137+
}
138+
139+
impl<Reg, Res, Gen> Data<Reg, Res, Gen> {
140+
/// Creates a new instance of `Data`.
141+
///
142+
/// It is recommended that the [`new_device_data`] macro be used as it automatically creates
143+
/// the lock classes.
144+
///
145+
/// # Safety
146+
///
147+
/// `key1` and `key2` must point to valid memory locations and remain valid until `self` is
148+
/// dropped.
149+
pub unsafe fn try_new(
150+
regs: Reg,
151+
res: Res,
152+
gen: Gen,
153+
name: &'static CStr,
154+
key1: *mut bindings::lock_class_key,
155+
key2: *mut bindings::lock_class_key,
156+
) -> Result<Pin<UniqueRef<Self>>> {
157+
let mut ret = Pin::from(UniqueRef::try_new(Self {
158+
// SAFETY: We call `RevocableMutex::init` below.
159+
regs: unsafe { RevocableMutex::new(regs) },
160+
res: Revocable::new(res),
161+
general: gen,
162+
})?);
163+
// SAFETY: `Data::regs` is pinned when `Data` is.
164+
let pinned = unsafe { ret.as_mut().map_unchecked_mut(|d| &mut d.regs) };
165+
166+
// SAFETY: The safety requirements of this function satisfy those of `RevocableMutex::init`.
167+
unsafe { pinned.init(name, key1, key2) };
168+
Ok(ret)
169+
}
170+
171+
/// Returns the resources if they're still available.
172+
pub fn resources(&self) -> Option<RevocableGuard<'_, Res>> {
173+
self.res.try_access()
174+
}
175+
176+
/// Returns the locked registrations if they're still available.
177+
pub fn registrations(&self) -> Option<RevocableMutexGuard<'_, Reg>> {
178+
self.regs.try_lock()
179+
}
180+
}
181+
182+
impl<Reg, Res, Gen> crate::driver::DeviceRemoval for Data<Reg, Res, Gen> {
183+
fn device_remove(&self) {
184+
// We revoke the registrations first so that resources are still available to them during
185+
// unregistration.
186+
self.regs.revoke();
187+
188+
// Release resources now. General data remains available.
189+
self.res.revoke();
190+
}
191+
}
192+
193+
impl<Reg, Res, Gen> Deref for Data<Reg, Res, Gen> {
194+
type Target = Gen;
195+
196+
fn deref(&self) -> &Gen {
197+
&self.general
198+
}
199+
}
200+
201+
impl<Reg, Res, Gen> DerefMut for Data<Reg, Res, Gen> {
202+
fn deref_mut(&mut self) -> &mut Gen {
203+
&mut self.general
204+
}
205+
}

0 commit comments

Comments
 (0)