signal: introduce kernel_signal_stop() to fix jffs2_garbage_collect_thread()
authorOleg Nesterov <oleg@redhat.com>
Sat, 7 Nov 2015 00:32:25 +0000 (16:32 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Nov 2015 01:50:42 +0000 (17:50 -0800)
jffs2_garbage_collect_thread() can race with SIGCONT and sleep in
TASK_STOPPED state after it was already sent. Add the new helper,
kernel_signal_stop(), which does this correctly.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Tejun Heo <tj@kernel.org>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/jffs2/background.c
include/linux/sched.h

index f3145fd..53cc735 100644 (file)
@@ -132,8 +132,7 @@ static int jffs2_garbage_collect_thread(void *_c)
                        case SIGSTOP:
                                jffs2_dbg(1, "%s(): SIGSTOP received\n",
                                          __func__);
-                               set_current_state(TASK_STOPPED);
-                               schedule();
+                               kernel_signal_stop();
                                break;
 
                        case SIGKILL:
index 3d54924..4069feb 100644 (file)
@@ -2475,6 +2475,16 @@ static inline int kernel_dequeue_signal(siginfo_t *info)
        return ret;
 }
 
+static inline void kernel_signal_stop(void)
+{
+       spin_lock_irq(&current->sighand->siglock);
+       if (current->jobctl & JOBCTL_STOP_DEQUEUED)
+               __set_current_state(TASK_STOPPED);
+       spin_unlock_irq(&current->sighand->siglock);
+
+       schedule();
+}
+
 extern void release_task(struct task_struct * p);
 extern int send_sig_info(int, struct siginfo *, struct task_struct *);
 extern int force_sigsegv(int, struct task_struct *);