Skip to content

Commit b2a5a9a

Browse files
committed
rust: poll: use kfree_rcu() for PollCondVar
Rust Binder currently uses PollCondVar, but it calls synchronize_rcu() in the destructor, which we would like to avoid. Add a variation of PollCondVar that kfree_rcu() instead. One could avoid the `rcu` field and allocate the rcu_head on drop using a fallback to synchronize_rcu() on ENOMEM. However, I'd prefer to avoid the potential for synchronize_rcu(), and Binder will only use this for a small fraction of processes, so even if it changes which kmalloc bucket it falls into, the extra memory is not a problem. Signed-off-by: Alice Ryhl <aliceryhl@google.com>
1 parent 0e77a93 commit b2a5a9a

1 file changed

Lines changed: 72 additions & 1 deletion

File tree

rust/kernel/sync/poll.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
55
//! Utilities for working with `struct poll_table`.
66
77
use crate::{
8+
alloc::AllocError,
89
bindings,
910
fs::File,
1011
prelude::*,
1112
sync::{CondVar, LockClassKey},
13+
types::Opaque, //
14+
};
15+
use core::{
16+
marker::PhantomData,
17+
mem::ManuallyDrop,
18+
ops::Deref, //
1219
};
13-
use core::{marker::PhantomData, ops::Deref};
1420

1521
/// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class.
1622
#[macro_export]
@@ -66,6 +72,7 @@ impl<'a> PollTable<'a> {
6672
///
6773
/// [`CondVar`]: crate::sync::CondVar
6874
#[pin_data(PinnedDrop)]
75+
#[repr(transparent)]
6976
pub struct PollCondVar {
7077
#[pin]
7178
inner: CondVar,
@@ -104,3 +111,67 @@ impl PinnedDrop for PollCondVar {
104111
unsafe { bindings::synchronize_rcu() };
105112
}
106113
}
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

Comments
 (0)