aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Kjos <tkjos@google.com>2019-10-30 12:36:47 -0700
committerTodd Kjos <tkjos@google.com>2019-11-04 16:53:27 +0000
commitaefd2d632eb0914f22fc6149f52eb0c6c148ce35 (patch)
tree0878ed6fc8c65518126865c097d31b9b061c5187
parenta30de0e015a2d22f6c8e633086044a16c6fd98d3 (diff)
ANDROID: binder: fix sleeping from invalid function caused by RT inheritanceASB-2019-11-05_mainline
When changing a thread's scheduling priority, binder calls sched_setscheduler_nocheck() while holding the node lock and proc inner lock. This was safe until v5.3 when a change was introduced where cpuset_read_lock() is called in this path which can sleep: commit 710da3c8ea7df ("sched/core: Prevent race condition between cpuset and __sched_setscheduler()"). Refactored binder_proc_transaction() to avoid holding a lock when calling binder_transaction_priority(). Bug: 143627611 Change-Id: I405c76b4813777905090ccc33e4f048b37700068 Fixes: e00eb41c0c7be ("ANDROID: binder: add support for RT prio inheritance.") Signed-off-by: Todd Kjos <tkjos@google.com>
-rw-r--r--drivers/android/binder.c55
1 files changed, 46 insertions, 9 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 1face45e0e49..bc431dbf94f5 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -2911,19 +2911,50 @@ static bool binder_proc_transaction(struct binder_transaction *t,
struct binder_priority node_prio;
bool oneway = !!(t->flags & TF_ONE_WAY);
bool pending_async = false;
+ bool retry = false;
BUG_ON(!node);
- binder_node_lock(node);
+
+set_thread_prio:
node_prio.prio = node->min_priority;
node_prio.sched_policy = node->sched_policy;
+ if (thread) {
+ /*
+ * Priority must be set outside of lock, but must be
+ * done before enqueuing the transaction.
+ */
+ binder_transaction_priority(thread->task, t, node_prio,
+ node->inherit_rt);
+ }
+
+retry_after_prio_restore:
+ binder_node_lock(node);
if (oneway) {
- BUG_ON(thread);
+ BUG_ON(!retry && thread);
if (node->has_async_transaction) {
pending_async = true;
} else {
node->has_async_transaction = true;
}
+ if (thread && pending_async) {
+ /*
+ * The node state has changed since we selected
+ * the thread. Return the thread to the
+ * waiting_threads list. We have to drop
+ * the node lock to restore priority so we
+ * have to re-check the node state.
+ */
+ binder_node_unlock(node);
+ binder_restore_priority(thread->task,
+ proc->default_priority);
+ binder_inner_proc_lock(proc);
+ list_add(&thread->waiting_thread_node,
+ &proc->waiting_threads);
+ binder_inner_proc_unlock(proc);
+ thread = NULL;
+ goto retry_after_prio_restore;
+ }
}
binder_inner_proc_lock(proc);
@@ -2934,18 +2965,24 @@ static bool binder_proc_transaction(struct binder_transaction *t,
return false;
}
- if (!thread && !pending_async)
+ if (!thread && !pending_async) {
thread = binder_select_thread_ilocked(proc);
+ if (thread) {
+ if (oneway)
+ node->has_async_transaction = false;
+ binder_inner_proc_unlock(proc);
+ binder_node_unlock(node);
+ retry = true;
+ goto set_thread_prio;
+ }
+ }
- if (thread) {
- binder_transaction_priority(thread->task, t, node_prio,
- node->inherit_rt);
+ if (thread)
binder_enqueue_thread_work_ilocked(thread, &t->work);
- } else if (!pending_async) {
+ else if (!pending_async)
binder_enqueue_work_ilocked(&t->work, &proc->todo);
- } else {
+ else
binder_enqueue_work_ilocked(&t->work, &node->async_todo);
- }
if (!pending_async)
binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);