ANDROID: rust_binder: add epoll support

This adds epoll integration, allowing you to get an epoll notification
when an incoming transaction arrives.

Link: https://lore.kernel.org/rust-for-linux/20231101-rust-binder-v1-7-08ba9197f637@google.com/
Change-Id: I42e874205f4c7901d6914ac014c7e4deadaa2b88
Co-developed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Bug: 278052745
This commit is contained in:
Wedson Almeida Filho 2023-07-04 11:11:56 +00:00 committed by Alice Ryhl
parent bb1d504151
commit 98cf4f1b76
2 changed files with 66 additions and 8 deletions

View File

@ -125,8 +125,27 @@ impl ProcessInner {
} else if self.is_dead {
Err((BinderError::new_dead(), work))
} else {
// There are no ready threads. Push work to process queue.
let sync = work.should_sync_wakeup();
// Didn't find a thread waiting for proc work; this can happen
// in two scenarios:
// 1. All threads are busy handling transactions
// In that case, one of those threads should call back into
// the kernel driver soon and pick up this work.
// 2. Threads are using the (e)poll interface, in which case
// they may be blocked on the waitqueue without having been
// added to waiting_threads. For this case, we just iterate
// over all threads not handling transaction work, and
// wake them all up. We wake all because we don't know whether
// a thread that called into (e)poll is handling non-binder
// work currently.
self.work.push_back(work);
// Wake up polling threads, if any.
for thread in self.threads.values() {
thread.notify_if_poll_ready(sync);
}
Ok(())
}
}
@ -1000,11 +1019,16 @@ impl Process {
}
pub(crate) fn poll(
_this: ArcBorrow<'_, Process>,
_file: &File,
_table: &mut PollTable,
this: ArcBorrow<'_, Process>,
file: &File,
table: &mut PollTable,
) -> Result<u32> {
Err(EINVAL)
let thread = this.get_current_thread()?;
let (from_proc, mut mask) = thread.poll(file, table);
if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
mask |= bindings::POLLIN;
}
Ok(mask)
}
}

View File

@ -9,13 +9,15 @@
use kernel::{
bindings,
file::File,
list::{
AtomicListArcTracker, HasListLinks, List, ListArc, ListArcSafe, ListItem, ListLinks,
TryNewListArc,
},
prelude::*,
security,
sync::{Arc, CondVar, SpinLock},
sync::poll::{PollCondVar, PollTable},
sync::{Arc, SpinLock},
types::Either,
uaccess::{UserSlice, UserSliceWriter},
};
@ -76,6 +78,7 @@ const LOOPER_EXITED: u32 = 0x04;
const LOOPER_INVALID: u32 = 0x08;
const LOOPER_WAITING: u32 = 0x10;
const LOOPER_WAITING_PROC: u32 = 0x20;
const LOOPER_POLL: u32 = 0x40;
impl InnerThread {
fn new() -> Result<Self> {
@ -162,6 +165,15 @@ impl InnerThread {
fn should_use_process_work_queue(&self) -> bool {
!self.process_work_list && self.is_looper()
}
fn poll(&mut self) -> u32 {
self.looper_flags |= LOOPER_POLL;
if self.process_work_list || self.looper_need_return {
bindings::POLLIN
} else {
0
}
}
}
/// This represents a thread that's used with binder.
@ -172,7 +184,7 @@ pub(crate) struct Thread {
#[pin]
inner: SpinLock<InnerThread>,
#[pin]
work_condvar: CondVar,
work_condvar: PollCondVar,
/// Used to insert this thread into the process' `ready_threads` list.
///
/// INVARIANT: May never be used for any other list than the `self.process.ready_threads`.
@ -204,7 +216,7 @@ impl Thread {
id,
process,
inner <- kernel::new_spinlock!(inner, "Thread::inner"),
work_condvar <- kernel::new_condvar!("Thread::work_condvar"),
work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"),
links <- ListLinks::new(),
links_track <- AtomicListArcTracker::new(),
}))
@ -623,6 +635,12 @@ impl Thread {
ret
}
pub(crate) fn poll(&self, file: &File, table: &mut PollTable) -> (bool, u32) {
table.register_wait(file, &self.work_condvar);
let mut inner = self.inner.lock();
(inner.should_use_process_work_queue(), inner.poll())
}
/// Make the call to `get_work` or `get_work_local` return immediately, if any.
pub(crate) fn exit_looper(&self) {
let mut inner = self.inner.lock();
@ -637,6 +655,22 @@ impl Thread {
}
}
pub(crate) fn notify_if_poll_ready(&self, sync: bool) {
// Determine if we need to notify. This requires the lock.
let inner = self.inner.lock();
let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue();
drop(inner);
// Now that the lock is no longer held, notify the waiters if we have to.
if notify {
if sync {
self.work_condvar.notify_sync();
} else {
self.work_condvar.notify_one();
}
}
}
pub(crate) fn release(self: &Arc<Self>) {
self.inner.lock().is_dead = true;