signal: Turn SIGNAL_STOP_DEQUEUED into GROUP_STOP_DEQUEUED
authorOleg Nesterov <oleg@redhat.com>
Fri, 1 Apr 2011 18:12:38 +0000 (20:12 +0200)
committerTejun Heo <tj@kernel.org>
Mon, 4 Apr 2011 00:11:05 +0000 (02:11 +0200)
This patch moves SIGNAL_STOP_DEQUEUED from signal_struct->flags to
task_struct->group_stop, and thus makes it per-thread.

Like SIGNAL_STOP_DEQUEUED, GROUP_STOP_DEQUEUED can be false-positive
after return from get_signal_to_deliver(), this is fine. The only
purpose of this bit is: we can drop ->siglock after __dequeue_signal()
returns the sig_kernel_stop() signal and before we call
do_signal_stop(), in this case we must not miss SIGCONT if it comes in
between.

But, unlike SIGNAL_STOP_DEQUEUED, GROUP_STOP_DEQUEUED can not be
false-positive in do_signal_stop() if multiple threads dequeue the
sig_kernel_stop() signal at the same time.

Consider two threads T1 and T2, SIGTTIN has a hanlder.

- T1 dequeues SIGTSTP and sets SIGNAL_STOP_DEQUEUED, then
  it drops ->siglock

- SIGCONT comes and clears SIGNAL_STOP_DEQUEUED, SIGTSTP
  should be cancelled.

- T2 dequeues SIGTTIN and sets SIGNAL_STOP_DEQUEUED again.
  Since we have a handler we should not stop, T2 returns
  to usermode to run the handler.

- T1 continues, calls do_signal_stop() and wrongly starts
  the group stop because SIGNAL_STOP_DEQUEUED was restored
  in between.

With or without this change:

- we need to do something with ptrace_signal() which can
  return SIGSTOP, but this needs another discussion

- SIGSTOP can be lost if it races with the mt exec, will
  be fixed later.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
include/linux/sched.h
kernel/signal.c

index 456d80ed3b784e0638b6b9265b14103f72b607cc..8cef82d4cf7719f5502ef63f541ca6c408e989e2 100644 (file)
@@ -652,9 +652,8 @@ struct signal_struct {
  * Bits in flags field of signal_struct.
  */
 #define SIGNAL_STOP_STOPPED    0x00000001 /* job control stop in effect */
-#define SIGNAL_STOP_DEQUEUED   0x00000002 /* stop signal dequeued */
-#define SIGNAL_STOP_CONTINUED  0x00000004 /* SIGCONT since WCONTINUED reap */
-#define SIGNAL_GROUP_EXIT      0x00000008 /* group exit in progress */
+#define SIGNAL_STOP_CONTINUED  0x00000002 /* SIGCONT since WCONTINUED reap */
+#define SIGNAL_GROUP_EXIT      0x00000004 /* group exit in progress */
 /*
  * Pending notifications to parent.
  */
@@ -1779,6 +1778,7 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *
 #define GROUP_STOP_PENDING     (1 << 16) /* task should stop for group stop */
 #define GROUP_STOP_CONSUME     (1 << 17) /* consume group stop count */
 #define GROUP_STOP_TRAPPING    (1 << 18) /* switching from STOPPED to TRACED */
+#define GROUP_STOP_DEQUEUED    (1 << 19) /* stop signal dequeued */
 
 extern void task_clear_group_stop_pending(struct task_struct *task);
 
index e9abc69dc0d89f258eb08035205a4289c3b4056c..4f7312b49b2d3815e00a2c3251995ed89ca3e8a8 100644 (file)
@@ -254,7 +254,8 @@ static void task_clear_group_stop_trapping(struct task_struct *task)
  */
 void task_clear_group_stop_pending(struct task_struct *task)
 {
-       task->group_stop &= ~(GROUP_STOP_PENDING | GROUP_STOP_CONSUME);
+       task->group_stop &= ~(GROUP_STOP_PENDING | GROUP_STOP_CONSUME |
+                             GROUP_STOP_DEQUEUED);
 }
 
 /**
@@ -602,7 +603,7 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
                 * is to alert stop-signal processing code when another
                 * processor has come along and cleared the flag.
                 */
-               tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
+               current->group_stop |= GROUP_STOP_DEQUEUED;
        }
        if ((info->si_code & __SI_MASK) == __SI_TIMER && info->si_sys_private) {
                /*
@@ -821,13 +822,6 @@ static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns)
                        signal->flags = why | SIGNAL_STOP_CONTINUED;
                        signal->group_stop_count = 0;
                        signal->group_exit_code = 0;
-               } else {
-                       /*
-                        * We are not stopped, but there could be a stop
-                        * signal in the middle of being processed after
-                        * being removed from the queue.  Clear that too.
-                        */
-                       signal->flags &= ~SIGNAL_STOP_DEQUEUED;
                }
        }
 
@@ -1855,7 +1849,7 @@ static int do_signal_stop(int signr)
                /* signr will be recorded in task->group_stop for retries */
                WARN_ON_ONCE(signr & ~GROUP_STOP_SIGMASK);
 
-               if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) ||
+               if (!likely(current->group_stop & GROUP_STOP_DEQUEUED) ||
                    unlikely(signal_group_exit(sig)))
                        return 0;
                /*