diff options
-rw-r--r-- | include/linux/sched.h | 6 | ||||
-rw-r--r-- | kernel/signal.c | 62 |
2 files changed, 60 insertions, 8 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index 4b601be3dace..85f51042c2b8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1260,6 +1260,7 @@ struct task_struct { | |||
1260 | int exit_state; | 1260 | int exit_state; |
1261 | int exit_code, exit_signal; | 1261 | int exit_code, exit_signal; |
1262 | int pdeath_signal; /* The signal sent when the parent dies */ | 1262 | int pdeath_signal; /* The signal sent when the parent dies */ |
1263 | unsigned int group_stop; /* GROUP_STOP_*, siglock protected */ | ||
1263 | /* ??? */ | 1264 | /* ??? */ |
1264 | unsigned int personality; | 1265 | unsigned int personality; |
1265 | unsigned did_exec:1; | 1266 | unsigned did_exec:1; |
@@ -1771,6 +1772,11 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * | |||
1771 | #define tsk_used_math(p) ((p)->flags & PF_USED_MATH) | 1772 | #define tsk_used_math(p) ((p)->flags & PF_USED_MATH) |
1772 | #define used_math() tsk_used_math(current) | 1773 | #define used_math() tsk_used_math(current) |
1773 | 1774 | ||
1775 | /* | ||
1776 | * task->group_stop flags | ||
1777 | */ | ||
1778 | #define GROUP_STOP_CONSUME (1 << 17) /* consume group stop count */ | ||
1779 | |||
1774 | #ifdef CONFIG_PREEMPT_RCU | 1780 | #ifdef CONFIG_PREEMPT_RCU |
1775 | 1781 | ||
1776 | #define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */ | 1782 | #define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */ |
diff --git a/kernel/signal.c b/kernel/signal.c index 95ac42dc3bcb..ecb20089eaff 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -223,6 +223,52 @@ static inline void print_dropped_signal(int sig) | |||
223 | current->comm, current->pid, sig); | 223 | current->comm, current->pid, sig); |
224 | } | 224 | } |
225 | 225 | ||
226 | /** | ||
227 | * task_clear_group_stop_pending - clear pending group stop | ||
228 | * @task: target task | ||
229 | * | ||
230 | * Clear group stop states for @task. | ||
231 | * | ||
232 | * CONTEXT: | ||
233 | * Must be called with @task->sighand->siglock held. | ||
234 | */ | ||
235 | static void task_clear_group_stop_pending(struct task_struct *task) | ||
236 | { | ||
237 | task->group_stop &= ~GROUP_STOP_CONSUME; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * task_participate_group_stop - participate in a group stop | ||
242 | * @task: task participating in a group stop | ||
243 | * | ||
244 | * @task is participating in a group stop. Group stop states are cleared | ||
245 | * and the group stop count is consumed if %GROUP_STOP_CONSUME was set. If | ||
246 | * the consumption completes the group stop, the appropriate %SIGNAL_* | ||
247 | * flags are set. | ||
248 | * | ||
249 | * CONTEXT: | ||
250 | * Must be called with @task->sighand->siglock held. | ||
251 | */ | ||
252 | static bool task_participate_group_stop(struct task_struct *task) | ||
253 | { | ||
254 | struct signal_struct *sig = task->signal; | ||
255 | bool consume = task->group_stop & GROUP_STOP_CONSUME; | ||
256 | |||
257 | task_clear_group_stop_pending(task); | ||
258 | |||
259 | if (!consume) | ||
260 | return false; | ||
261 | |||
262 | if (!WARN_ON_ONCE(sig->group_stop_count == 0)) | ||
263 | sig->group_stop_count--; | ||
264 | |||
265 | if (!sig->group_stop_count) { | ||
266 | sig->flags = SIGNAL_STOP_STOPPED; | ||
267 | return true; | ||
268 | } | ||
269 | return false; | ||
270 | } | ||
271 | |||
226 | /* | 272 | /* |
227 | * allocate a new signal queue record | 273 | * allocate a new signal queue record |
228 | * - this may be called without locks if and only if t == current, otherwise an | 274 | * - this may be called without locks if and only if t == current, otherwise an |
@@ -1645,7 +1691,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) | |||
1645 | * we must participate in the bookkeeping. | 1691 | * we must participate in the bookkeeping. |
1646 | */ | 1692 | */ |
1647 | if (current->signal->group_stop_count > 0) | 1693 | if (current->signal->group_stop_count > 0) |
1648 | --current->signal->group_stop_count; | 1694 | task_participate_group_stop(current); |
1649 | 1695 | ||
1650 | current->last_siginfo = info; | 1696 | current->last_siginfo = info; |
1651 | current->exit_code = exit_code; | 1697 | current->exit_code = exit_code; |
@@ -1730,6 +1776,7 @@ static int do_signal_stop(int signr) | |||
1730 | int notify = 0; | 1776 | int notify = 0; |
1731 | 1777 | ||
1732 | if (!sig->group_stop_count) { | 1778 | if (!sig->group_stop_count) { |
1779 | unsigned int gstop = GROUP_STOP_CONSUME; | ||
1733 | struct task_struct *t; | 1780 | struct task_struct *t; |
1734 | 1781 | ||
1735 | if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) || | 1782 | if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) || |
@@ -1741,6 +1788,7 @@ static int do_signal_stop(int signr) | |||
1741 | */ | 1788 | */ |
1742 | sig->group_exit_code = signr; | 1789 | sig->group_exit_code = signr; |
1743 | 1790 | ||
1791 | current->group_stop = gstop; | ||
1744 | sig->group_stop_count = 1; | 1792 | sig->group_stop_count = 1; |
1745 | for (t = next_thread(current); t != current; t = next_thread(t)) | 1793 | for (t = next_thread(current); t != current; t = next_thread(t)) |
1746 | /* | 1794 | /* |
@@ -1750,19 +1798,19 @@ static int do_signal_stop(int signr) | |||
1750 | */ | 1798 | */ |
1751 | if (!(t->flags & PF_EXITING) && | 1799 | if (!(t->flags & PF_EXITING) && |
1752 | !task_is_stopped_or_traced(t)) { | 1800 | !task_is_stopped_or_traced(t)) { |
1801 | t->group_stop = gstop; | ||
1753 | sig->group_stop_count++; | 1802 | sig->group_stop_count++; |
1754 | signal_wake_up(t, 0); | 1803 | signal_wake_up(t, 0); |
1755 | } | 1804 | } else |
1805 | task_clear_group_stop_pending(t); | ||
1756 | } | 1806 | } |
1757 | /* | 1807 | /* |
1758 | * If there are no other threads in the group, or if there is | 1808 | * If there are no other threads in the group, or if there is |
1759 | * a group stop in progress and we are the last to stop, report | 1809 | * a group stop in progress and we are the last to stop, report |
1760 | * to the parent. When ptraced, every thread reports itself. | 1810 | * to the parent. When ptraced, every thread reports itself. |
1761 | */ | 1811 | */ |
1762 | if (!--sig->group_stop_count) { | 1812 | if (task_participate_group_stop(current)) |
1763 | sig->flags = SIGNAL_STOP_STOPPED; | ||
1764 | notify = CLD_STOPPED; | 1813 | notify = CLD_STOPPED; |
1765 | } | ||
1766 | if (task_ptrace(current)) | 1814 | if (task_ptrace(current)) |
1767 | notify = CLD_STOPPED; | 1815 | notify = CLD_STOPPED; |
1768 | 1816 | ||
@@ -2026,10 +2074,8 @@ void exit_signals(struct task_struct *tsk) | |||
2026 | recalc_sigpending_and_wake(t); | 2074 | recalc_sigpending_and_wake(t); |
2027 | 2075 | ||
2028 | if (unlikely(tsk->signal->group_stop_count) && | 2076 | if (unlikely(tsk->signal->group_stop_count) && |
2029 | !--tsk->signal->group_stop_count) { | 2077 | task_participate_group_stop(tsk)) |
2030 | tsk->signal->flags = SIGNAL_STOP_STOPPED; | ||
2031 | group_stop = CLD_STOPPED; | 2078 | group_stop = CLD_STOPPED; |
2032 | } | ||
2033 | out: | 2079 | out: |
2034 | spin_unlock_irq(&tsk->sighand->siglock); | 2080 | spin_unlock_irq(&tsk->sighand->siglock); |
2035 | 2081 | ||