Skip to content

Commit bf694eb

Browse files
committed
rust_binder: move (e)poll wait queue to Process
Most processes do not use Rust Binder with epoll, so avoid paying the synchronize_rcu() cost in drop for those that don't need it. For those that do, we also manage to replace synchronize_rcu() with kfree_rcu(), though we introduce an extra allocation. In case the last ref to an Arc<Thread> is dropped outside of deferred_release(), this also ensures that synchronize_rcu() is not called in destructor of Arc<Thread> in other places. Theoretically that could lead to jank by making other syscalls slow, which would be problematic. Signed-off-by: Alice Ryhl <aliceryhl@google.com>
1 parent b2a5a9a commit bf694eb

4 files changed

Lines changed: 95 additions & 60 deletions

File tree

drivers/android/binder/node.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ impl Node {
538538
inner.oneway_todo.push_back(transaction);
539539
} else {
540540
inner.has_oneway_transaction = true;
541-
guard.push_work(transaction)?;
541+
guard.push_work(&self.owner, transaction)?;
542542
}
543543
Ok(())
544544
}
@@ -570,7 +570,7 @@ impl Node {
570570
let transaction = inner.oneway_todo.pop_front();
571571
inner.has_oneway_transaction = transaction.is_some();
572572
if let Some(transaction) = transaction {
573-
match guard.push_work(transaction) {
573+
match guard.push_work(&self.owner, transaction) {
574574
Ok(()) => {}
575575
Err((_err, work)) => {
576576
// Process is dead.

drivers/android/binder/process.rs

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ use kernel::{
3030
sync::{
3131
aref::ARef,
3232
lock::{spinlock::SpinLockBackend, Guard},
33-
Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SpinLock, UniqueArc,
33+
poll::PollCondVarBox,
34+
Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SetOnce, SpinLock, UniqueArc,
3435
},
3536
task::{Pid, Task},
3637
uaccess::{UserSlice, UserSliceReader},
@@ -172,39 +173,39 @@ impl ProcessInner {
172173
/// taken while holding the inner process lock.
173174
pub(crate) fn push_work(
174175
&mut self,
176+
proc: &Process,
175177
work: DLArc<dyn DeliverToRead>,
176178
) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> {
179+
let sync = work.should_sync_wakeup();
180+
177181
// Try to find a ready thread to which to push the work.
178182
if let Some(thread) = self.ready_threads.pop_front() {
179183
// Push to thread while holding state lock. This prevents the thread from giving up
180184
// (for example, because of a signal) when we're about to deliver work.
181-
match thread.push_work(work) {
185+
match thread.push_work_inner(work, sync) {
182186
PushWorkRes::Ok => Ok(()),
187+
PushWorkRes::OkNotifyPoll => {
188+
proc.notify_poll(sync);
189+
Ok(())
190+
}
183191
PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)),
184192
}
185193
} else if self.is_dead {
186194
Err((BinderError::new_dead(), work))
187195
} else {
188-
let sync = work.should_sync_wakeup();
189-
190196
// Didn't find a thread waiting for proc work; this can happen
191197
// in two scenarios:
192198
// 1. All threads are busy handling transactions
193199
// In that case, one of those threads should call back into
194200
// the kernel driver soon and pick up this work.
195201
// 2. Threads are using the (e)poll interface, in which case
196202
// they may be blocked on the waitqueue without having been
197-
// added to waiting_threads. For this case, we just iterate
198-
// over all threads not handling transaction work, and
199-
// wake them all up. We wake all because we don't know whether
200-
// a thread that called into (e)poll is handling non-binder
201-
// work currently.
203+
// added to waiting_threads. For this case, we wake it up
204+
// directly.
202205
self.work.push_back(work);
203206

204207
// Wake up polling threads, if any.
205-
for thread in self.threads.values() {
206-
thread.notify_if_poll_ready(sync);
207-
}
208+
proc.notify_poll(sync);
208209

209210
Ok(())
210211
}
@@ -227,11 +228,11 @@ impl ProcessInner {
227228

228229
// If we decided that we need to push work, push either to the process or to a thread if
229230
// one is specified.
230-
if let Some(node) = push {
231+
if let Some(pnode) = push {
231232
if let Some(thread) = othread {
232-
thread.push_work_deferred(node);
233+
thread.push_work_deferred(pnode);
233234
} else {
234-
let _ = self.push_work(node);
235+
let _ = self.push_work(&node.owner, pnode);
235236
// Nothing to do: `push_work` may fail if the process is dead, but that's ok as in
236237
// that case, it doesn't care about the notification.
237238
}
@@ -457,6 +458,12 @@ pub(crate) struct Process {
457458
#[pin]
458459
node_refs: SpinLock<ProcessNodeRefs>,
459460

461+
// Synchronizes `register_wait` calls to the `PollCondVarBox`.
462+
//
463+
// The `PollCondVarBox` is not stored here because synchronization is
464+
// done for `register_wait` only. Wakeups do not take this lock.
465+
poll: SetOnce<PollCondVarBox>,
466+
460467
// Work node for deferred work item.
461468
#[pin]
462469
defer_work: Work<Process>,
@@ -516,6 +523,7 @@ impl Process {
516523
defer_work <- kernel::new_work!("Process::defer_work"),
517524
links <- ListLinks::new(),
518525
stats: BinderStats::new(),
526+
poll: SetOnce::new(),
519527
}),
520528
GFP_KERNEL,
521529
)?;
@@ -715,7 +723,7 @@ impl Process {
715723

716724
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
717725
// If push_work fails, drop the work item outside the lock.
718-
let res = self.inner.lock().push_work(work);
726+
let res = self.inner.lock().push_work(self, work);
719727
match res {
720728
Ok(()) => Ok(()),
721729
Err((err, work)) => {
@@ -1018,7 +1026,7 @@ impl Process {
10181026
if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) {
10191027
if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) {
10201028
// This only fails if the process is dead.
1021-
let _ = inner.push_work(node);
1029+
let _ = inner.push_work(self, node);
10221030
}
10231031
}
10241032
Ok(())
@@ -1535,6 +1543,15 @@ impl Process {
15351543
}
15361544
}
15371545
}
1546+
1547+
pub(crate) fn notify_poll(&self, sync: bool) {
1548+
if let Some(poll) = self.poll.as_ref() {
1549+
if sync {
1550+
poll.notify_sync();
1551+
}
1552+
poll.notify_all();
1553+
}
1554+
}
15381555
}
15391556

15401557
fn get_frozen_status(data: UserSlice) -> Result {
@@ -1726,7 +1743,21 @@ impl Process {
17261743
table: PollTable<'_>,
17271744
) -> Result<u32> {
17281745
let thread = this.get_current_thread()?;
1729-
let (from_proc, mut mask) = thread.poll(file, table);
1746+
{
1747+
let poll = loop {
1748+
if let Some(poll) = this.poll.as_ref() {
1749+
break poll;
1750+
}
1751+
1752+
let poll = PollCondVarBox::new(c"Process::poll", kernel::static_lock_class!())?;
1753+
// Reuse our existing lock to synchronize callers initializing.
1754+
let _guard = this.node_refs.lock();
1755+
this.poll.populate(poll);
1756+
};
1757+
1758+
table.register_wait(file, poll);
1759+
}
1760+
let (from_proc, mut mask) = thread.poll()?;
17301761
if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
17311762
mask |= bindings::POLLIN;
17321763
}

drivers/android/binder/thread.rs

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99
1010
use kernel::{
1111
bindings,
12-
fs::{File, LocalFile},
12+
fs::LocalFile,
1313
list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc},
1414
prelude::*,
1515
security,
1616
seq_file::SeqFile,
1717
seq_print,
1818
sync::atomic::{ordering::Relaxed, Atomic},
19-
sync::poll::{PollCondVar, PollTable},
20-
sync::{aref::ARef, Arc, SpinLock},
19+
sync::{aref::ARef, Arc, CondVar, SpinLock},
2120
task::Task,
2221
uaccess::{UserPtr, UserSlice, UserSliceReader},
2322
uapi,
@@ -225,15 +224,18 @@ impl UnusedBufferSpace {
225224
}
226225
}
227226

227+
#[must_use]
228228
pub(crate) enum PushWorkRes {
229229
Ok,
230+
OkNotifyPoll,
230231
FailedDead(DLArc<dyn DeliverToRead>),
231232
}
232233

233234
impl PushWorkRes {
234235
fn is_ok(&self) -> bool {
235236
match self {
236237
PushWorkRes::Ok => true,
238+
PushWorkRes::OkNotifyPoll => true,
237239
PushWorkRes::FailedDead(_) => false,
238240
}
239241
}
@@ -310,27 +312,32 @@ impl InnerThread {
310312

311313
fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
312314
if self.is_dead {
313-
PushWorkRes::FailedDead(work)
315+
return PushWorkRes::FailedDead(work);
316+
}
317+
self.work_list.push_back(work);
318+
self.process_work_list = true;
319+
if self.looper_flags & LOOPER_POLL != 0 {
320+
PushWorkRes::OkNotifyPoll
314321
} else {
315-
self.work_list.push_back(work);
316-
self.process_work_list = true;
317322
PushWorkRes::Ok
318323
}
319324
}
320325

321-
fn push_reply_work(&mut self, code: u32) {
326+
fn push_reply_work(&mut self, code: u32) -> PushWorkRes {
322327
if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) {
323328
work.set_error_code(code);
324-
self.push_work(work);
329+
self.push_work(work)
325330
} else {
326331
pr_warn!("Thread reply work is already in use.");
332+
PushWorkRes::Ok
327333
}
328334
}
329335

330336
fn push_return_work(&mut self, reply: u32) {
331337
if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) {
332338
work.set_error_code(reply);
333-
self.push_work(work);
339+
// Not notifying: Reply to current thread.
340+
let _ = self.push_work(work);
334341
} else {
335342
pr_warn!("Thread return work is already in use.");
336343
}
@@ -422,7 +429,7 @@ pub(crate) struct Thread {
422429
#[pin]
423430
inner: SpinLock<InnerThread>,
424431
#[pin]
425-
work_condvar: PollCondVar,
432+
work_condvar: CondVar,
426433
/// Used to insert this thread into the process' `ready_threads` list.
427434
///
428435
/// INVARIANT: May never be used for any other list than the `self.process.ready_threads`.
@@ -453,7 +460,7 @@ impl Thread {
453460
process,
454461
task: ARef::from(&**kernel::current!()),
455462
inner <- kernel::new_spinlock!(inner, "Thread::inner"),
456-
work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"),
463+
work_condvar <- kernel::new_condvar!("Thread::work_condvar"),
457464
links <- ListLinks::new(),
458465
links_track <- AtomicTracker::new(),
459466
}),
@@ -624,7 +631,14 @@ impl Thread {
624631
/// Returns whether the item was successfully pushed. This can only fail if the thread is dead.
625632
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
626633
let sync = work.should_sync_wakeup();
634+
self.push_work_inner(work, sync)
635+
}
627636

637+
pub(crate) fn push_work_inner(
638+
&self,
639+
work: DLArc<dyn DeliverToRead>,
640+
sync: bool,
641+
) -> PushWorkRes {
628642
let res = self.inner.lock().push_work(work);
629643

630644
if res.is_ok() {
@@ -643,7 +657,8 @@ impl Thread {
643657
pub(crate) fn push_work_if_looper(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
644658
let mut inner = self.inner.lock();
645659
if inner.is_looper() && !inner.is_dead {
646-
inner.push_work(work);
660+
// Not notifying: Reply to current thread.
661+
let _ = inner.push_work(work);
647662
Ok(())
648663
} else {
649664
drop(inner);
@@ -1154,7 +1169,7 @@ impl Thread {
11541169
transaction.set_outstanding(&mut self.process.inner.lock());
11551170
}
11561171

1157-
{
1172+
let ret = {
11581173
let mut inner = self.inner.lock();
11591174
if !inner.pop_transaction_replied(transaction) {
11601175
return false;
@@ -1171,15 +1186,16 @@ impl Thread {
11711186
}
11721187

11731188
match reply {
1174-
Ok(work) => {
1175-
inner.push_work(work);
1176-
}
1189+
Ok(work) => inner.push_work(work),
11771190
Err(code) => inner.push_reply_work(code),
11781191
}
1179-
}
1192+
};
11801193

11811194
// Notify the thread now that we've released the inner lock.
11821195
self.work_condvar.notify_sync();
1196+
if matches!(ret, PushWorkRes::OkNotifyPoll) {
1197+
self.process.notify_poll(true);
1198+
}
11831199
false
11841200
}
11851201

@@ -1349,7 +1365,8 @@ impl Thread {
13491365
let process = orig.from.process.clone();
13501366
let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
13511367
let reply = Transaction::new_reply(self, process, info, allow_fds)?;
1352-
self.inner.lock().push_work(completion);
1368+
// Not notifying: Reply to current thread.
1369+
let _ = self.inner.lock().push_work(completion);
13531370
orig.from.deliver_reply(Ok(reply), &orig, None);
13541371
Ok(())
13551372
})()
@@ -1387,7 +1404,8 @@ impl Thread {
13871404
};
13881405
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
13891406
let completion = list_completion.clone_arc();
1390-
self.inner.lock().push_work(list_completion);
1407+
// Not notifying: Reply to current thread.
1408+
let _ = self.inner.lock().push_work(list_completion);
13911409
match transaction.submit(info) {
13921410
Ok(()) => Ok(()),
13931411
Err(err) => {
@@ -1589,10 +1607,9 @@ impl Thread {
15891607
ret
15901608
}
15911609

1592-
pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) {
1593-
table.register_wait(file, &self.work_condvar);
1610+
pub(crate) fn poll(&self) -> Result<(bool, u32)> {
15941611
let mut inner = self.inner.lock();
1595-
(inner.should_use_process_work_queue(), inner.poll())
1612+
Ok((inner.should_use_process_work_queue(), inner.poll()))
15961613
}
15971614

15981615
/// Make the call to `get_work` or `get_work_local` return immediately, if any.
@@ -1609,26 +1626,9 @@ impl Thread {
16091626
}
16101627
}
16111628

1612-
pub(crate) fn notify_if_poll_ready(&self, sync: bool) {
1613-
// Determine if we need to notify. This requires the lock.
1614-
let inner = self.inner.lock();
1615-
let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue();
1616-
drop(inner);
1617-
1618-
// Now that the lock is no longer held, notify the waiters if we have to.
1619-
if notify {
1620-
if sync {
1621-
self.work_condvar.notify_sync();
1622-
} else {
1623-
self.work_condvar.notify_one();
1624-
}
1625-
}
1626-
}
1627-
16281629
pub(crate) fn release(self: &Arc<Self>) {
16291630
self.inner.lock().is_dead = true;
16301631

1631-
//self.work_condvar.clear();
16321632
self.unwind_transaction_stack();
16331633

16341634
// Cancel all pending work items.

drivers/android/binder/transaction.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,15 @@ impl Transaction {
371371
crate::trace::trace_transaction(false, &self, Some(&thread.task));
372372
match thread.push_work(self) {
373373
PushWorkRes::Ok => Ok(()),
374+
PushWorkRes::OkNotifyPoll => {
375+
process.notify_poll(true);
376+
Ok(())
377+
}
374378
PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
375379
}
376380
} else {
377381
crate::trace::trace_transaction(false, &self, None);
378-
process_inner.push_work(self)
382+
process_inner.push_work(&process, self)
379383
};
380384
drop(process_inner);
381385

0 commit comments

Comments
 (0)