forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 435
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rust: file: add abstraction for
poll_table
The existing `CondVar` abstraction is a wrapper around `wait_list`, but it does not support all use-cases of the C `wait_list` type. To be specific, a `CondVar` cannot be registered with a `struct poll_table`. This limitation has the advantage that you do not need to call `synchronize_rcu` when destroying a `CondVar`. However, we need the ability to register a `poll_table` with a `wait_list` in Rust Binder. To enable this, introduce a type called `PollCondVar`, which is like `CondVar` except that you can register a `poll_table`. We also introduce `PollTable`, which is a safe wrapper around `poll_table` that is intended to be used with `PollCondVar`. The destructor of `PollCondVar` unconditionally calls `synchronize_rcu` to ensure that the removal of epoll waiters has fully completed before the `wait_list` is destroyed. That said, `synchronize_rcu` is rather expensive and is not needed in all cases: If we have never registered a `poll_table` with the `wait_list`, then we don't need to call `synchronize_rcu`. (And this is a common case in Binder - not all processes use Binder with epoll.) The current implementation does not account for this, but if we find that it is necessary to improve this, a future patch could change store a boolean next to the `wait_list` to keep track of whether a `poll_table` has ever been registered. Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ boqun: Removes unused POLLFREE definition ]
- Loading branch information
Showing
3 changed files
with
105 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
//! Utilities for working with `struct poll_table`. | ||
use crate::{ | ||
bindings, | ||
file::File, | ||
prelude::*, | ||
sync::{CondVar, LockClassKey}, | ||
types::Opaque, | ||
}; | ||
use core::ops::Deref; | ||
|
||
/// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class. | ||
#[macro_export] | ||
macro_rules! new_poll_condvar { | ||
($($name:literal)?) => { | ||
$crate::file::PollCondVar::new($crate::optional_name!($($name)?), $crate::static_lock_class!()) | ||
}; | ||
} | ||
|
||
/// Wraps the kernel's `struct poll_table`. | ||
#[repr(transparent)] | ||
pub struct PollTable(Opaque<bindings::poll_table>); | ||
|
||
impl PollTable { | ||
/// Creates a reference to a [`PollTable`] from a valid pointer. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The caller must ensure that for the duration of 'a, the pointer will point at a valid poll | ||
/// table, and that it is only accessed via the returned reference. | ||
pub unsafe fn from_ptr<'a>(ptr: *mut bindings::poll_table) -> &'a mut PollTable { | ||
// SAFETY: The safety requirements guarantee the validity of the dereference, while the | ||
// `PollTable` type being transparent makes the cast ok. | ||
unsafe { &mut *ptr.cast() } | ||
} | ||
|
||
fn get_qproc(&self) -> bindings::poll_queue_proc { | ||
let ptr = self.0.get(); | ||
// SAFETY: The `ptr` is valid because it originates from a reference, and the `_qproc` | ||
// field is not modified concurrently with this call since we have an immutable reference. | ||
unsafe { (*ptr)._qproc } | ||
} | ||
|
||
/// Register this [`PollTable`] with the provided [`PollCondVar`], so that it can be notified | ||
/// using the condition variable. | ||
pub fn register_wait(&mut self, file: &File, cv: &PollCondVar) { | ||
if let Some(qproc) = self.get_qproc() { | ||
// SAFETY: The pointers to `self` and `file` are valid because they are references. | ||
// | ||
// Before the wait list is destroyed, the destructor of `PollCondVar` will clear | ||
// everything in the wait list, so the wait list is not used after it is freed. | ||
unsafe { qproc(file.as_ptr() as _, cv.wait_list.get(), self.0.get()) }; | ||
} | ||
} | ||
} | ||
|
||
/// A wrapper around [`CondVar`] that makes it usable with [`PollTable`]. | ||
/// | ||
/// # Invariant | ||
/// | ||
/// If `needs_synchronize_rcu` is false, then there is nothing registered with `register_wait`. | ||
/// | ||
/// [`CondVar`]: crate::sync::CondVar | ||
#[pin_data(PinnedDrop)] | ||
pub struct PollCondVar { | ||
#[pin] | ||
inner: CondVar, | ||
} | ||
|
||
impl PollCondVar { | ||
/// Constructs a new condvar initialiser. | ||
pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> { | ||
pin_init!(Self { | ||
inner <- CondVar::new(name, key), | ||
}) | ||
} | ||
} | ||
|
||
// Make the `CondVar` methods callable on `PollCondVar`. | ||
impl Deref for PollCondVar { | ||
type Target = CondVar; | ||
|
||
fn deref(&self) -> &CondVar { | ||
&self.inner | ||
} | ||
} | ||
|
||
#[pinned_drop] | ||
impl PinnedDrop for PollCondVar { | ||
fn drop(self: Pin<&mut Self>) { | ||
// Clear anything registered using `register_wait`. | ||
// | ||
// SAFETY: The pointer points at a valid wait list. | ||
unsafe { bindings::__wake_up_pollfree(self.inner.wait_list.get()) }; | ||
|
||
// Wait for epoll items to be properly removed. | ||
// | ||
// SAFETY: Just an FFI call. | ||
unsafe { bindings::synchronize_rcu() }; | ||
} | ||
} |