Change exit_ptrace() to use ptrace_do_detach() and kill now unused ptrace_utrace_exit().
This means we can't iterate over ->ptraced (the current code does this twice) under tasklist, change the code to pick tracees by one and do get/put on task_struct. With this patch, exit_ptrace() does ptrace_unlink() then UTRACE_DETACH. But currently this is not exactly correct too. Unlike ptrace_detach(), exit_ptrace() can be called when the tracee is running, if it calls utrace_stop() before UTRACE_DETACH we will notify ->real_parent. Hopefully this will be fixed when we change the ptrace-related kludge in utrace_stop(). --- kernel/ptrace.c | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) --- PU/kernel/ptrace.c~13_REWORK_EXIT_PTRACE 2009-08-22 20:08:23.000000000 +0200 +++ PU/kernel/ptrace.c 2009-08-22 20:31:26.000000000 +0200 @@ -751,7 +751,8 @@ static void ptrace_do_detach(struct task */ traced = tracee->ptrace; if (likely(traced)) { - tracee->exit_code = data; + if (valid_signal(data)) + tracee->exit_code = data; dead = __ptrace_detach(current, tracee); } write_unlock_irq(&tasklist_lock); @@ -776,37 +777,26 @@ int ptrace_detach(struct task_struct *ch return 0; } -static void ptrace_utrace_exit(struct task_struct *task) -{ - struct task_struct *child; - read_lock(&tasklist_lock); - list_for_each_entry(child, &task->ptraced, ptrace_entry) - ptrace_detach_task(child, 0); - read_unlock(&tasklist_lock); -} - /* * Detach all tasks we were using ptrace on. */ void exit_ptrace(struct task_struct *tracer) { - struct task_struct *p, *n; - LIST_HEAD(ptrace_dead); - - ptrace_utrace_exit(tracer); - - write_lock_irq(&tasklist_lock); - list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) { - if (__ptrace_detach(tracer, p)) - list_add(&p->ptrace_entry, &ptrace_dead); - } - write_unlock_irq(&tasklist_lock); + for (;;) { + struct task_struct *tracee = NULL; - BUG_ON(!list_empty(&tracer->ptraced)); + read_lock(&tasklist_lock); + if (!list_empty(&tracer->ptraced)) { + tracee = list_first_entry(&tracer->ptraced, + struct task_struct, ptrace_entry); + get_task_struct(tracee); + } + read_unlock(&tasklist_lock); + if (!tracee) + break; - list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) { - list_del_init(&p->ptrace_entry); - release_task(p); + ptrace_do_detach(tracee, -1); + put_task_struct(tracee); } }