diff options
Diffstat (limited to 'drivers/android')
-rw-r--r-- | drivers/android/binder.c | 181 |
1 files changed, 127 insertions, 54 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 55a44c0b3b20..8f2031c52ea4 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -970,7 +970,20 @@ static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc, } } -static void binder_wakeup_proc_ilocked(struct binder_proc *proc, bool sync) +/** + * binder_select_thread_ilocked() - selects a thread for doing proc work. + * @proc: process to select a thread from + * + * Note that calling this function moves the thread off the waiting_threads + * list, so it can only be woken up by the caller of this function, or a + * signal. Therefore, callers *should* always wake up the thread this function + * returns. + * + * Return: If there's a thread currently waiting for process work, + * returns that thread. Otherwise returns NULL. + */ +static struct binder_thread * +binder_select_thread_ilocked(struct binder_proc *proc) { struct binder_thread *thread; @@ -979,8 +992,35 @@ static void binder_wakeup_proc_ilocked(struct binder_proc *proc, bool sync) struct binder_thread, waiting_thread_node); - if (thread) { + if (thread) list_del_init(&thread->waiting_thread_node); + + return thread; +} + +/** + * binder_wakeup_thread_ilocked() - wakes up a thread for doing proc work. + * @proc: process to wake up a thread in + * @thread: specific thread to wake-up (may be NULL) + * @sync: whether to do a synchronous wake-up + * + * This function wakes up a thread in the @proc process. + * The caller may provide a specific thread to wake-up in + * the @thread parameter. If @thread is NULL, this function + * will wake up threads that have called poll(). + * + * Note that for this function to work as expected, callers + * should first call binder_select_thread() to find a thread + * to handle the work (if they don't have a thread already), + * and pass the result into the @thread parameter. + */ +static void binder_wakeup_thread_ilocked(struct binder_proc *proc, + struct binder_thread *thread, + bool sync) +{ + BUG_ON(!spin_is_locked(&proc->inner_lock)); + + if (thread) { if (sync) wake_up_interruptible_sync(&thread->wait); else @@ -1004,6 +1044,13 @@ static void binder_wakeup_proc_ilocked(struct binder_proc *proc, bool sync) binder_wakeup_poll_threads_ilocked(proc, sync); } +static void binder_wakeup_proc_ilocked(struct binder_proc *proc) +{ + struct binder_thread *thread = binder_select_thread_ilocked(proc); + + binder_wakeup_thread_ilocked(proc, thread, /* sync = */false); +} + static void binder_set_nice(long nice) { long min_nice; @@ -1222,7 +1269,7 @@ static bool binder_dec_node_nilocked(struct binder_node *node, if (proc && (node->has_strong_ref || node->has_weak_ref)) { if (list_empty(&node->work.entry)) { binder_enqueue_work_ilocked(&node->work, &proc->todo); - binder_wakeup_proc_ilocked(proc, false); + binder_wakeup_proc_ilocked(proc); } } else { if (hlist_empty(&node->refs) && !node->local_strong_refs && @@ -2468,6 +2515,73 @@ static int binder_fixup_parent(struct binder_transaction *t, return 0; } +/** + * binder_proc_transaction() - sends a transaction to a process and wakes it up + * @t: transaction to send + * @proc: process to send the transaction to + * @thread: thread in @proc to send the transaction to (may be NULL) + * + * This function queues a transaction to the specified process. It will try + * to find a thread in the target process to handle the transaction and + * wake it up. If no thread is found, the work is queued to the proc + * waitqueue. + * + * If the @thread parameter is not NULL, the transaction is always queued + * to the waitlist of that specific thread. + * + * Return: true if the transactions was successfully queued + * false if the target process or thread is dead + */ +static bool binder_proc_transaction(struct binder_transaction *t, + struct binder_proc *proc, + struct binder_thread *thread) +{ + struct list_head *target_list = NULL; + struct binder_node *node = t->buffer->target_node; + bool oneway = !!(t->flags & TF_ONE_WAY); + bool wakeup = true; + + BUG_ON(!node); + binder_node_lock(node); + if (oneway) { + BUG_ON(thread); + if (node->has_async_transaction) { + target_list = &node->async_todo; + wakeup = false; + } else { + node->has_async_transaction = 1; + } + } + + binder_inner_proc_lock(proc); + + if (proc->is_dead || (thread && thread->is_dead)) { + binder_inner_proc_unlock(proc); + binder_node_unlock(node); + return false; + } + + if (!thread && !target_list) + thread = binder_select_thread_ilocked(proc); + + if (thread) + target_list = &thread->todo; + else if (!target_list) + target_list = &proc->todo; + else + BUG_ON(target_list != &node->async_todo); + + binder_enqueue_work_ilocked(&t->work, target_list); + + if (wakeup) + binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); + + binder_inner_proc_unlock(proc); + binder_node_unlock(node); + + return true; +} + static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply, @@ -2482,7 +2596,6 @@ static void binder_transaction(struct binder_proc *proc, struct binder_proc *target_proc = NULL; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; - struct list_head *target_list; struct binder_transaction *in_reply_to = NULL; struct binder_transaction_log_entry *e; uint32_t return_error = 0; @@ -2492,7 +2605,6 @@ static void binder_transaction(struct binder_proc *proc, binder_size_t last_fixup_min_off = 0; struct binder_context *context = proc->context; int t_debug_id = atomic_inc_return(&binder_last_id); - bool wakeup_for_proc_work = false; e = binder_transaction_log_add(&binder_transaction_log); e->debug_id = t_debug_id; @@ -2653,13 +2765,8 @@ static void binder_transaction(struct binder_proc *proc, } binder_inner_proc_unlock(proc); } - if (target_thread) { + if (target_thread) e->to_thread = target_thread->pid; - target_list = &target_thread->todo; - } else { - target_list = &target_proc->todo; - wakeup_for_proc_work = true; - } e->to_proc = target_proc->pid; /* TODO: reuse incoming transaction for reply */ @@ -2938,8 +3045,9 @@ static void binder_transaction(struct binder_proc *proc, } BUG_ON(t->buffer->async_transaction != 0); binder_pop_transaction_ilocked(target_thread, in_reply_to); - binder_enqueue_work_ilocked(&t->work, target_list); + binder_enqueue_work_ilocked(&t->work, &target_thread->todo); binder_inner_proc_unlock(target_proc); + wake_up_interruptible_sync(&target_thread->wait); binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); @@ -2948,49 +3056,17 @@ static void binder_transaction(struct binder_proc *proc, t->from_parent = thread->transaction_stack; thread->transaction_stack = t; binder_inner_proc_unlock(proc); - binder_inner_proc_lock(target_proc); - if (target_proc->is_dead || - (target_thread && target_thread->is_dead)) { - binder_inner_proc_unlock(target_proc); + if (!binder_proc_transaction(t, target_proc, target_thread)) { binder_inner_proc_lock(proc); binder_pop_transaction_ilocked(thread, t); binder_inner_proc_unlock(proc); goto err_dead_proc_or_thread; } - binder_enqueue_work_ilocked(&t->work, target_list); - binder_inner_proc_unlock(target_proc); } else { BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); - binder_node_lock(target_node); - if (target_node->has_async_transaction) { - target_list = &target_node->async_todo; - wakeup_for_proc_work = false; - } else - target_node->has_async_transaction = 1; - /* - * Test/set of has_async_transaction - * must be atomic with enqueue on - * async_todo - */ - binder_inner_proc_lock(target_proc); - if (target_proc->is_dead || - (target_thread && target_thread->is_dead)) { - binder_inner_proc_unlock(target_proc); - binder_node_unlock(target_node); + if (!binder_proc_transaction(t, target_proc, NULL)) goto err_dead_proc_or_thread; - } - binder_enqueue_work_ilocked(&t->work, target_list); - binder_inner_proc_unlock(target_proc); - binder_node_unlock(target_node); - } - if (target_thread) { - wake_up_interruptible_sync(&target_thread->wait); - } else if (wakeup_for_proc_work) { - binder_inner_proc_lock(target_proc); - binder_wakeup_proc_ilocked(target_proc, - !(tr->flags & TF_ONE_WAY)); - binder_inner_proc_unlock(target_proc); } if (target_thread) binder_thread_dec_tmpref(target_thread); @@ -3435,8 +3511,7 @@ static int binder_thread_write(struct binder_proc *proc, &ref->death->work, &proc->todo); binder_wakeup_proc_ilocked( - proc, - false); + proc); binder_inner_proc_unlock(proc); } } @@ -3473,8 +3548,7 @@ static int binder_thread_write(struct binder_proc *proc, &death->work, &proc->todo); binder_wakeup_proc_ilocked( - proc, - false); + proc); } } else { BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER); @@ -3529,8 +3603,7 @@ static int binder_thread_write(struct binder_proc *proc, binder_enqueue_work_ilocked( &death->work, &proc->todo); - binder_wakeup_proc_ilocked( - proc, false); + binder_wakeup_proc_ilocked(proc); } } binder_inner_proc_unlock(proc); @@ -4248,7 +4321,7 @@ static int binder_ioctl_write_read(struct file *filp, trace_binder_read_done(ret); binder_inner_proc_lock(proc); if (!binder_worklist_empty_ilocked(&proc->todo)) - binder_wakeup_proc_ilocked(proc, false); + binder_wakeup_proc_ilocked(proc); binder_inner_proc_unlock(proc); if (ret < 0) { if (copy_to_user(ubuf, &bwr, sizeof(bwr))) @@ -4620,7 +4693,7 @@ static int binder_node_release(struct binder_node *node, int refs) ref->death->work.type = BINDER_WORK_DEAD_BINDER; binder_enqueue_work_ilocked(&ref->death->work, &ref->proc->todo); - binder_wakeup_proc_ilocked(ref->proc, false); + binder_wakeup_proc_ilocked(ref->proc); binder_inner_proc_unlock(ref->proc); } |