diff options
| author | Oleg Nesterov <oleg@redhat.com> | 2010-08-10 21:03:07 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-11 11:59:19 -0400 |
| commit | c7e49c1488ab20342eaaf38f1ca35a207f4c051d (patch) | |
| tree | 43876a69cafbb49ce86f71ba5db88018ca7cc036 /kernel | |
| parent | 13d7e3a2dba6a79589ed34dc0b9114d7b5ff9eab (diff) | |
ptrace: optimize exit_ptrace() for the likely case
exit_ptrace() takes tasklist_lock unconditionally. We need this lock to
avoid the race with ptrace_traceme(), it acts as a barrier.
Change its caller, forget_original_parent(), to call exit_ptrace() under
tasklist_lock. Change exit_ptrace() to drop and reacquire this lock if
needed.
This allows us to add the fastpath list_empty(ptraced) check. In the
likely no-tracees case exit_ptrace() just returns and we avoid the lock()
+ unlock() sequence.
"Zhang, Yanmin" <yanmin_zhang@linux.intel.com> suggested to add this
check, and he reports that this change adds about 11% improvement in some
tests.
Suggested-and-tested-by: "Zhang, Yanmin" <yanmin_zhang@linux.intel.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/exit.c | 7 | ||||
| -rw-r--r-- | kernel/ptrace.c | 12 |
2 files changed, 14 insertions, 5 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index ceffc67b564a..671ed56e0a49 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
| @@ -771,9 +771,12 @@ static void forget_original_parent(struct task_struct *father) | |||
| 771 | struct task_struct *p, *n, *reaper; | 771 | struct task_struct *p, *n, *reaper; |
| 772 | LIST_HEAD(dead_children); | 772 | LIST_HEAD(dead_children); |
| 773 | 773 | ||
| 774 | exit_ptrace(father); | ||
| 775 | |||
| 776 | write_lock_irq(&tasklist_lock); | 774 | write_lock_irq(&tasklist_lock); |
| 775 | /* | ||
| 776 | * Note that exit_ptrace() and find_new_reaper() might | ||
| 777 | * drop tasklist_lock and reacquire it. | ||
| 778 | */ | ||
| 779 | exit_ptrace(father); | ||
| 777 | reaper = find_new_reaper(father); | 780 | reaper = find_new_reaper(father); |
| 778 | 781 | ||
| 779 | list_for_each_entry_safe(p, n, &father->children, sibling) { | 782 | list_for_each_entry_safe(p, n, &father->children, sibling) { |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 74a3d693c196..f34d798ef4a2 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
| @@ -324,26 +324,32 @@ int ptrace_detach(struct task_struct *child, unsigned int data) | |||
| 324 | } | 324 | } |
| 325 | 325 | ||
| 326 | /* | 326 | /* |
| 327 | * Detach all tasks we were using ptrace on. | 327 | * Detach all tasks we were using ptrace on. Called with tasklist held |
| 328 | * for writing, and returns with it held too. But note it can release | ||
| 329 | * and reacquire the lock. | ||
| 328 | */ | 330 | */ |
| 329 | void exit_ptrace(struct task_struct *tracer) | 331 | void exit_ptrace(struct task_struct *tracer) |
| 330 | { | 332 | { |
| 331 | struct task_struct *p, *n; | 333 | struct task_struct *p, *n; |
| 332 | LIST_HEAD(ptrace_dead); | 334 | LIST_HEAD(ptrace_dead); |
| 333 | 335 | ||
| 334 | write_lock_irq(&tasklist_lock); | 336 | if (likely(list_empty(&tracer->ptraced))) |
| 337 | return; | ||
| 338 | |||
| 335 | list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) { | 339 | list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) { |
| 336 | if (__ptrace_detach(tracer, p)) | 340 | if (__ptrace_detach(tracer, p)) |
| 337 | list_add(&p->ptrace_entry, &ptrace_dead); | 341 | list_add(&p->ptrace_entry, &ptrace_dead); |
| 338 | } | 342 | } |
| 339 | write_unlock_irq(&tasklist_lock); | ||
| 340 | 343 | ||
| 344 | write_unlock_irq(&tasklist_lock); | ||
| 341 | BUG_ON(!list_empty(&tracer->ptraced)); | 345 | BUG_ON(!list_empty(&tracer->ptraced)); |
| 342 | 346 | ||
| 343 | list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) { | 347 | list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) { |
| 344 | list_del_init(&p->ptrace_entry); | 348 | list_del_init(&p->ptrace_entry); |
| 345 | release_task(p); | 349 | release_task(p); |
| 346 | } | 350 | } |
| 351 | |||
| 352 | write_lock_irq(&tasklist_lock); | ||
| 347 | } | 353 | } |
| 348 | 354 | ||
| 349 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) | 355 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) |
