[PATCH RFC 07/20] rust_binder: add epoll support

From: Alice Ryhl
Date: Wed Nov 01 2023 - 14:03:04 EST


From: Wedson Almeida Filho <wedsonaf@xxxxxxxxx>

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

Signed-off-by: Wedson Almeida Filho <wedsonaf@xxxxxxxxx>
Co-developed-by: Alice Ryhl <aliceryhl@xxxxxxxxxx>
Signed-off-by: Alice Ryhl <aliceryhl@xxxxxxxxxx>
---
drivers/android/process.rs | 21 +++++++++++++++++----
drivers/android/thread.rs | 39 ++++++++++++++++++++++++++++++++++++---
2 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/drivers/android/process.rs b/drivers/android/process.rs
index 26dd9309fbee..2e8b0fc07756 100644
--- a/drivers/android/process.rs
+++ b/drivers/android/process.rs
@@ -122,8 +122,16 @@ pub(crate) fn push_work(
} else if self.is_dead {
Err((BinderError::new_dead(), work))
} else {
+ let sync = work.should_sync_wakeup();
+
// There are no ready threads. Push work to process queue.
self.work.push_back(work);
+
+ // Wake up polling threads, if any.
+ for thread in self.threads.values() {
+ thread.notify_if_poll_ready(sync);
+ }
+
Ok(())
}
}
@@ -897,11 +905,16 @@ pub(crate) fn mmap(
}

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_thread(kernel::current!().pid())?;
+ 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)
}
}

diff --git a/drivers/android/thread.rs b/drivers/android/thread.rs
index f34de7ad6e6f..159beebbd23e 100644
--- a/drivers/android/thread.rs
+++ b/drivers/android/thread.rs
@@ -7,6 +7,7 @@

use kernel::{
bindings,
+ file::{File, PollCondVar, PollTable},
io_buffer::{IoBufferReader, IoBufferWriter},
list::{
AtomicListArcTracker, HasListLinks, List, ListArc, ListArcSafe, ListItem, ListLinks,
@@ -14,7 +15,7 @@
},
prelude::*,
security,
- sync::{Arc, CondVar, SpinLock},
+ sync::{Arc, SpinLock},
types::Either,
user_ptr::{UserSlicePtr, UserSlicePtrWriter},
};
@@ -75,6 +76,7 @@ struct InnerThread {
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> {
@@ -159,6 +161,15 @@ fn is_looper(&self) -> bool {
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.
@@ -169,7 +180,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`.
@@ -201,7 +212,7 @@ pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {
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(),
}))
@@ -590,6 +601,12 @@ pub(crate) fn write_read(self: &Arc<Self>, data: UserSlicePtr, wait: bool) -> Re
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();
@@ -604,6 +621,22 @@ pub(crate) fn exit_looper(&self) {
}
}

+ 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;


--
2.42.0.820.g83a721a137-goog