Commit ee77f075921730b2b465880f9fd4367003bdab39
Committed by
Tejun Heo
1 parent
780006eac2
Exists in
master
and in
7 other branches
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 |
kernel/signal.c
... | ... | @@ -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 | /* |