binder: add zombie list to cleanup dead refs

Use same mechanism to cleanup dead refs as is used for
procs, nodes, and threads

Bug: 33250092 32225111
Change-Id: Ie21be9d902371a0bb85dca4aae75a3da255cdc84
Signed-off-by: Todd Kjos <tkjos@google.com>
This commit is contained in:
Todd Kjos
2016-12-08 10:43:00 -08:00
committed by Thierry Strudel
parent 902a22e61b
commit 005172f700

View File

@@ -319,6 +319,8 @@ struct binder_ref {
uint32_t desc;
atomic_t strong;
atomic_t weak;
bool is_zombie;
struct hlist_node zombie_ref;
struct binder_ref_death *death;
};
@@ -380,6 +382,7 @@ struct binder_proc {
struct binder_seq_node zombie_proc;
bool is_zombie;
struct hlist_head zombie_nodes;
struct hlist_head zombie_refs;
struct hlist_head zombie_threads;
struct binder_alloc alloc;
struct binder_context *context;
@@ -934,19 +937,20 @@ static void binder_delete_ref(struct binder_ref *ref, bool force)
ref->node->debug_id);
binder_proc_lock(ref->proc, __LINE__);
if (!force && ((atomic_read(&ref->strong) != 0) ||
(atomic_read(&ref->weak) != 0))) {
if (ref->is_zombie ||
(!force && ((atomic_read(&ref->strong) != 0) ||
(atomic_read(&ref->weak) != 0)))) {
/*
* This is not a forced deletion (eg not
* a release) and there is still a reference.
* Don't delete the ref.
* Multiple threads could observe the ref counts
* going to 0. The first one to get the lock will
* make it a zombie. Subsequent callers bail out
* here.
*/
binder_proc_unlock(ref->proc, __LINE__);
return;
}
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
ref->is_zombie = true;
binder_proc_unlock(ref->proc, __LINE__);
if (atomic_read(&ref->strong))
@@ -954,20 +958,28 @@ static void binder_delete_ref(struct binder_ref *ref, bool force)
binder_proc_lock(node_proc, __LINE__);
hlist_del(&ref->node_entry);
if (ref->node->is_zombie)
binder_queue_for_zombie_cleanup(node_proc);
binder_proc_unlock(node_proc, __LINE__);
binder_dec_node(ref->node, 0, 1);
binder_proc_lock(ref->proc, __LINE__);
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
if (ref->death) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%d delete ref %d desc %d has death notification\n",
ref->proc->pid, ref->debug_id, ref->desc);
binder_dequeue_work(&ref->death->work, __LINE__);
kfree(ref->death);
binder_stats_deleted(BINDER_STAT_DEATH);
if (ref->death->work.wlist)
binder_dequeue_work(&ref->death->work, __LINE__);
binder_stats_zombie(BINDER_STAT_DEATH);
}
kfree(ref);
binder_stats_deleted(BINDER_STAT_REF);
INIT_HLIST_NODE(&ref->zombie_ref);
hlist_add_head(&ref->zombie_ref, &ref->proc->zombie_refs);
binder_queue_for_zombie_cleanup(ref->proc);
binder_proc_unlock(ref->proc, __LINE__);
binder_stats_zombie(BINDER_STAT_REF);
}
static int binder_inc_ref(struct binder_ref *ref, int strong,
@@ -2145,7 +2157,7 @@ static int binder_thread_write(struct binder_proc *proc,
(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
ref = binder_get_ref_for_node(proc,
ctx_mgr_node);
if (ref->desc != target) {
if (ref && ref->desc != target) {
binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
proc->pid, thread->pid,
ref->desc);
@@ -3592,6 +3604,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
proc->requested_threads = 0;
proc->requested_threads_started = 0;
INIT_LIST_HEAD(&proc->zombie_proc.list_node);
INIT_HLIST_HEAD(&proc->zombie_refs);
INIT_HLIST_HEAD(&proc->zombie_nodes);
INIT_HLIST_HEAD(&proc->zombie_threads);
filp->private_data = proc;
@@ -3850,12 +3863,14 @@ static bool binder_proc_clear_zombies(struct binder_proc *proc)
struct binder_node *node;
struct hlist_node *tmp;
struct binder_thread *thread;
struct binder_ref *ref;
struct hlist_head nodes_to_free;
struct hlist_head threads_to_free;
struct hlist_head refs_to_free;
bool needs_requeue = false;
INIT_HLIST_HEAD(&nodes_to_free);
INIT_HLIST_HEAD(&threads_to_free);
INIT_HLIST_HEAD(&refs_to_free);
binder_proc_lock(proc, __LINE__);
if (!list_empty(&proc->zombie_proc.list_node)) {
@@ -3863,6 +3878,13 @@ static bool binder_proc_clear_zombies(struct binder_proc *proc)
binder_proc_unlock(proc, __LINE__);
return 0;
}
hlist_for_each_entry_safe(ref, tmp, &proc->zombie_refs, zombie_ref) {
hlist_del_init(&ref->zombie_ref);
hlist_add_head(&ref->zombie_ref, &refs_to_free);
}
if (!RB_EMPTY_ROOT(&proc->refs_by_desc))
needs_requeue = true;
hlist_for_each_entry_safe(node, tmp, &proc->zombie_nodes, dead_node)
if (hlist_empty(&node->refs)) {
hlist_del_init(&node->dead_node);
@@ -3900,6 +3922,17 @@ static bool binder_proc_clear_zombies(struct binder_proc *proc)
binder_stats_delete_zombie(BINDER_STAT_THREAD);
cleared_threads++;
}
hlist_for_each_entry_safe(ref, tmp, &refs_to_free, zombie_ref) {
hlist_del_init(&ref->zombie_ref);
if (ref->death) {
binder_dequeue_work(&ref->death->work, __LINE__);
kfree(ref->death);
binder_stats_delete_zombie(BINDER_STAT_DEATH);
}
kfree(ref);
binder_stats_delete_zombie(BINDER_STAT_REF);
}
return proc->is_zombie && !needs_requeue;
}
@@ -4301,16 +4334,18 @@ static void print_binder_proc_stats(struct seq_file *m,
struct rb_node *n;
struct binder_node *node;
struct binder_thread *thread;
struct binder_ref *ref;
int count, strong, weak;
int zombie_threads;
int zombie_nodes;
int zombie_refs;
seq_printf(m, "proc %d%s\n", proc->pid,
proc->is_zombie ? " (ZOMBIE)" : "");
seq_printf(m, "context %s\n", proc->context->name);
seq_printf(m, " cleared: procs=%d nodes=%d threads=%d\n",
cleared_procs, cleared_nodes, cleared_threads);
zombie_threads = zombie_nodes = count = 0;
zombie_threads = zombie_nodes = zombie_refs = count = 0;
binder_proc_lock(proc, __LINE__);
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
@@ -4336,12 +4371,15 @@ static void print_binder_proc_stats(struct seq_file *m,
hlist_for_each_entry(thread, &proc->zombie_threads,
zombie_thread)
zombie_threads++;
hlist_for_each_entry(ref, &proc->zombie_refs, zombie_ref)
zombie_refs++;
}
binder_proc_unlock(proc, __LINE__);
seq_printf(m, " active threads: %d\n", proc->active_thread_count);
seq_printf(m, " nodes: %d\n", count);
seq_printf(m, " zombie nodes: %d\n", zombie_nodes);
seq_printf(m, " zombie threads: %d\n", zombie_threads);
seq_printf(m, " zombie refs: %d\n", zombie_refs);
count = 0;
strong = 0;
weak = 0;