|
5 | 5 | //! Utilities for working with `struct poll_table`. |
6 | 6 |
|
7 | 7 | use crate::{ |
| 8 | + alloc::AllocError, |
8 | 9 | bindings, |
9 | 10 | fs::File, |
10 | 11 | prelude::*, |
11 | 12 | sync::{CondVar, LockClassKey}, |
| 13 | + types::Opaque, // |
| 14 | +}; |
| 15 | +use core::{ |
| 16 | + marker::PhantomData, |
| 17 | + mem::ManuallyDrop, |
| 18 | + ops::Deref, // |
12 | 19 | }; |
13 | | -use core::{marker::PhantomData, ops::Deref}; |
14 | 20 |
|
15 | 21 | /// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class. |
16 | 22 | #[macro_export] |
@@ -66,6 +72,7 @@ impl<'a> PollTable<'a> { |
66 | 72 | /// |
67 | 73 | /// [`CondVar`]: crate::sync::CondVar |
68 | 74 | #[pin_data(PinnedDrop)] |
| 75 | +#[repr(transparent)] |
69 | 76 | pub struct PollCondVar { |
70 | 77 | #[pin] |
71 | 78 | inner: CondVar, |
@@ -104,3 +111,67 @@ impl PinnedDrop for PollCondVar { |
104 | 111 | unsafe { bindings::synchronize_rcu() }; |
105 | 112 | } |
106 | 113 | } |
| 114 | + |
| 115 | +/// A [`KBox<PollCondVar>`] that uses `kfree_rcu`. |
| 116 | +/// |
| 117 | +/// [`KBox<PollCondVar>`]: PollCondVar |
| 118 | +pub struct PollCondVarBox { |
| 119 | + inner: ManuallyDrop<Pin<KBox<PollCondVarBoxInner>>>, |
| 120 | +} |
| 121 | + |
| 122 | +#[pin_data] |
| 123 | +#[repr(C)] |
| 124 | +struct PollCondVarBoxInner { |
| 125 | + #[pin] |
| 126 | + inner: PollCondVar, |
| 127 | + rcu: Opaque<bindings::callback_head>, |
| 128 | +} |
| 129 | + |
| 130 | +// SAFETY: PollCondVar is Send |
| 131 | +unsafe impl Send for PollCondVarBoxInner {} |
| 132 | +// SAFETY: PollCondVar is Sync |
| 133 | +unsafe impl Sync for PollCondVarBoxInner {} |
| 134 | + |
| 135 | +impl PollCondVarBox { |
| 136 | + /// Constructs a new boxed [`PollCondVar`]. |
| 137 | + pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> Result<Self, AllocError> { |
| 138 | + let b = KBox::pin_init( |
| 139 | + pin_init!(PollCondVarBoxInner { |
| 140 | + inner <- PollCondVar::new(name, key), |
| 141 | + rcu: Opaque::uninit(), |
| 142 | + }), |
| 143 | + GFP_KERNEL, |
| 144 | + ) |
| 145 | + .map_err(|_| AllocError)?; |
| 146 | + |
| 147 | + Ok(PollCondVarBox { |
| 148 | + inner: ManuallyDrop::new(b), |
| 149 | + }) |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +impl Deref for PollCondVarBox { |
| 154 | + type Target = PollCondVar; |
| 155 | + fn deref(&self) -> &PollCondVar { |
| 156 | + &self.inner.inner |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +impl Drop for PollCondVarBox { |
| 161 | + #[inline] |
| 162 | + fn drop(&mut self) { |
| 163 | + // SAFETY: ManuallyDrop::take ok because not already taken. |
| 164 | + let boxed = unsafe { ManuallyDrop::take(&mut self.inner) }; |
| 165 | + |
| 166 | + // SAFETY: The code below frees the box without calling the actual destructor of the type, |
| 167 | + // but it's okay because it re-implements the destructor using `kfree_rcu()` in place of |
| 168 | + // `synchronize_rcu()`. |
| 169 | + let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(boxed) }); |
| 170 | + |
| 171 | + // SAFETY: The pointer points at a valid `wait_queue_head`. |
| 172 | + unsafe { bindings::__wake_up_pollfree((*ptr).inner.inner.wait_queue_head.get()) }; |
| 173 | + |
| 174 | + // SAFETY: This was allocated using `KBox::pin_init`, so it can be freed with `kvfree`. |
| 175 | + unsafe { bindings::kvfree_call_rcu((*ptr).rcu.get(), ptr.cast::<ffi::c_void>()) }; |
| 176 | + } |
| 177 | +} |
0 commit comments