Commit ee77f075921730b2b465880f9fd4367003bdab39

Authored by Oleg Nesterov
Committed by Tejun Heo
1 parent 780006eac2

signal: Turn SIGNAL_STOP_DEQUEUED into GROUP_STOP_DEQUEUED

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>

Showing 2 changed files with 7 additions and 13 deletions Side-by-side Diff

include/linux/sched.h
... ... @@ -652,9 +652,8 @@
652 652 * Bits in flags field of signal_struct.
653 653 */
654 654 #define SIGNAL_STOP_STOPPED 0x00000001 /* job control stop in effect */
655   -#define SIGNAL_STOP_DEQUEUED 0x00000002 /* stop signal dequeued */
656   -#define SIGNAL_STOP_CONTINUED 0x00000004 /* SIGCONT since WCONTINUED reap */
657   -#define SIGNAL_GROUP_EXIT 0x00000008 /* group exit in progress */
  655 +#define SIGNAL_STOP_CONTINUED 0x00000002 /* SIGCONT since WCONTINUED reap */
  656 +#define SIGNAL_GROUP_EXIT 0x00000004 /* group exit in progress */
658 657 /*
659 658 * Pending notifications to parent.
660 659 */
... ... @@ -1779,6 +1778,7 @@
1779 1778 #define GROUP_STOP_PENDING (1 << 16) /* task should stop for group stop */
1780 1779 #define GROUP_STOP_CONSUME (1 << 17) /* consume group stop count */
1781 1780 #define GROUP_STOP_TRAPPING (1 << 18) /* switching from STOPPED to TRACED */
  1781 +#define GROUP_STOP_DEQUEUED (1 << 19) /* stop signal dequeued */
1782 1782  
1783 1783 extern void task_clear_group_stop_pending(struct task_struct *task);
1784 1784  
... ... @@ -254,7 +254,8 @@
254 254 */
255 255 void task_clear_group_stop_pending(struct task_struct *task)
256 256 {
257   - task->group_stop &= ~(GROUP_STOP_PENDING | GROUP_STOP_CONSUME);
  257 + task->group_stop &= ~(GROUP_STOP_PENDING | GROUP_STOP_CONSUME |
  258 + GROUP_STOP_DEQUEUED);
258 259 }
259 260  
260 261 /**
... ... @@ -602,7 +603,7 @@
602 603 * is to alert stop-signal processing code when another
603 604 * processor has come along and cleared the flag.
604 605 */
605   - tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
  606 + current->group_stop |= GROUP_STOP_DEQUEUED;
606 607 }
607 608 if ((info->si_code & __SI_MASK) == __SI_TIMER && info->si_sys_private) {
608 609 /*
... ... @@ -821,13 +822,6 @@
821 822 signal->flags = why | SIGNAL_STOP_CONTINUED;
822 823 signal->group_stop_count = 0;
823 824 signal->group_exit_code = 0;
824   - } else {
825   - /*
826   - * We are not stopped, but there could be a stop
827   - * signal in the middle of being processed after
828   - * being removed from the queue. Clear that too.
829   - */
830   - signal->flags &= ~SIGNAL_STOP_DEQUEUED;
831 825 }
832 826 }
833 827  
... ... @@ -1855,7 +1849,7 @@
1855 1849 /* signr will be recorded in task->group_stop for retries */
1856 1850 WARN_ON_ONCE(signr & ~GROUP_STOP_SIGMASK);
1857 1851  
1858   - if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) ||
  1852 + if (!likely(current->group_stop & GROUP_STOP_DEQUEUED) ||
1859 1853 unlikely(signal_group_exit(sig)))
1860 1854 return 0;
1861 1855 /*