job control: reorganize wait_task_stopped()
PATCH 1/1 job control: reorganize wait_task_stopped()
wait_task_stopped() tested task_stopped_code() without acquiring siglock and, if stop condition existed, called wait_task_stopped() and directly returned the result. This patch moves the initial task_stopped_code() testing into wait_task_stopped() and make wait_consider_task() fall through to wait_task_continue() on 0 return.
This is for the following two reasons.
* Because the initial task_stopped_code() test is done without acquiring siglock, it may race against SIGCONT generation. The stopped condition might have been replaced by continued state by the time wait_task_stopped() acquired siglock. This may lead to unexpected failure of WNOHANG waits.
This reorganization addresses this single race case but there are other cases - TASK_RUNNING -> TASK_STOPPED transition and EXIT_* transitions.
* Scheduled ptrace updates require changes to the initial test which would fit better inside wait_task_stopped().
---
Dropped the "it's not a bug fix" paragraph from description and added Reviewed-by.
Thanks.
kernel/exit.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-)
kernel/exit.c +23 -7
@@ -1377,11 +1377,23 @@ static int *task_stopped_code(struct tasreturn NULL ; } -/* -* Handle sys_wait4 work for one task in state TASK_STOPPED. We hold -* read_lock(&tasklist_lock) on entry. If we return zero, we still hold -* the lock and this task is uninteresting. If we return nonzero, we have -* released the lock and the system call should return. +/** +* wait_task_stopped - Wait for %TASK_STOPPED or %TASK_TRACED +* @wo: wait options +* @ptrace: is the wait for ptrace +* @p: task to wait for +* +* Handle sys_wait4() work for %p in state %TASK_STOPPED or %TASK_TRACED. +* +* CONTEXT: +* read_lock(&tasklist_lock), which is released if return value is +* non-zero. Also, grabs and releases @p->sighand->siglock. +* +* RETURNS: +* 0 if wait condition didn't exist and search for other wait conditions +* should continue. Non-zero return, -errno on failure and @p's pid on +* success, implies that tasklist_lock is released and wait condition +* search should terminate. */ static int wait_task_stopped (struct wait_opts *wo,int ptrace,struct task_struct *p)
@@ -1397,6 +1409,9 @@ static int wait_task_stopped(struct waitif (!ptrace && !(wo->wo_flags &WUNTRACED ))return 0 ; +if (!task_stopped_code (p, ptrace)) +return 0 ; + exit_code =0 ;spin_lock_irq (&p->sighand->siglock);
@@ -1607,8 +1622,9 @@ static int wait_consider_task(struct wai* Wait for stopped. Depending on @ptrace, different stopped state * is used and the two don't interact with each other. */ -if (task_stopped_code (p, ptrace)) -return wait_task_stopped (wo, ptrace, p); + ret =wait_task_stopped (wo, ptrace, p); +if (ret) +return ret;/* * Wait for continued. There's only one continued state and the