Skip to content

Commit 5cd4b93

Browse files
Darksonnfbq
authored andcommitted
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 <aliceryhl@google.com> Link: https://lore.kernel.org/r/20231206-alice-file-v2-7-af617c0d9d94@google.com [ boqun: Removes unused POLLFREE definition ]
1 parent 7069c6e commit 5cd4b93

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/file.h>
1414
#include <linux/fs.h>
1515
#include <linux/pid_namespace.h>
16+
#include <linux/poll.h>
1617
#include <linux/security.h>
1718
#include <linux/slab.h>
1819
#include <linux/refcount.h>

rust/kernel/sync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod arc;
1111
mod condvar;
1212
pub mod lock;
1313
mod locked_by;
14+
pub mod poll;
1415

1516
pub use arc::{Arc, ArcBorrow, UniqueArc};
1617
pub use condvar::CondVar;

rust/kernel/sync/poll.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Utilities for working with `struct poll_table`.
4+
5+
use crate::{
6+
bindings,
7+
file::File,
8+
prelude::*,
9+
sync::{CondVar, LockClassKey},
10+
types::Opaque,
11+
};
12+
use core::ops::Deref;
13+
14+
/// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class.
15+
#[macro_export]
16+
macro_rules! new_poll_condvar {
17+
($($name:literal)?) => {
18+
$crate::file::PollCondVar::new($crate::optional_name!($($name)?), $crate::static_lock_class!())
19+
};
20+
}
21+
22+
/// Wraps the kernel's `struct poll_table`.
23+
#[repr(transparent)]
24+
pub struct PollTable(Opaque<bindings::poll_table>);
25+
26+
impl PollTable {
27+
/// Creates a reference to a [`PollTable`] from a valid pointer.
28+
///
29+
/// # Safety
30+
///
31+
/// The caller must ensure that for the duration of 'a, the pointer will point at a valid poll
32+
/// table, and that it is only accessed via the returned reference.
33+
pub unsafe fn from_ptr<'a>(ptr: *mut bindings::poll_table) -> &'a mut PollTable {
34+
// SAFETY: The safety requirements guarantee the validity of the dereference, while the
35+
// `PollTable` type being transparent makes the cast ok.
36+
unsafe { &mut *ptr.cast() }
37+
}
38+
39+
fn get_qproc(&self) -> bindings::poll_queue_proc {
40+
let ptr = self.0.get();
41+
// SAFETY: The `ptr` is valid because it originates from a reference, and the `_qproc`
42+
// field is not modified concurrently with this call since we have an immutable reference.
43+
unsafe { (*ptr)._qproc }
44+
}
45+
46+
/// Register this [`PollTable`] with the provided [`PollCondVar`], so that it can be notified
47+
/// using the condition variable.
48+
pub fn register_wait(&mut self, file: &File, cv: &PollCondVar) {
49+
if let Some(qproc) = self.get_qproc() {
50+
// SAFETY: The pointers to `self` and `file` are valid because they are references.
51+
//
52+
// Before the wait list is destroyed, the destructor of `PollCondVar` will clear
53+
// everything in the wait list, so the wait list is not used after it is freed.
54+
unsafe { qproc(file.as_ptr() as _, cv.wait_list.get(), self.0.get()) };
55+
}
56+
}
57+
}
58+
59+
/// A wrapper around [`CondVar`] that makes it usable with [`PollTable`].
60+
///
61+
/// # Invariant
62+
///
63+
/// If `needs_synchronize_rcu` is false, then there is nothing registered with `register_wait`.
64+
///
65+
/// [`CondVar`]: crate::sync::CondVar
66+
#[pin_data(PinnedDrop)]
67+
pub struct PollCondVar {
68+
#[pin]
69+
inner: CondVar,
70+
}
71+
72+
impl PollCondVar {
73+
/// Constructs a new condvar initialiser.
74+
pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> {
75+
pin_init!(Self {
76+
inner <- CondVar::new(name, key),
77+
})
78+
}
79+
}
80+
81+
// Make the `CondVar` methods callable on `PollCondVar`.
82+
impl Deref for PollCondVar {
83+
type Target = CondVar;
84+
85+
fn deref(&self) -> &CondVar {
86+
&self.inner
87+
}
88+
}
89+
90+
#[pinned_drop]
91+
impl PinnedDrop for PollCondVar {
92+
fn drop(self: Pin<&mut Self>) {
93+
// Clear anything registered using `register_wait`.
94+
//
95+
// SAFETY: The pointer points at a valid wait list.
96+
unsafe { bindings::__wake_up_pollfree(self.inner.wait_list.get()) };
97+
98+
// Wait for epoll items to be properly removed.
99+
//
100+
// SAFETY: Just an FFI call.
101+
unsafe { bindings::synchronize_rcu() };
102+
}
103+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy