aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-06-14 05:20:14 -0400
committerOleg Nesterov <oleg@redhat.com>2011-06-16 15:41:52 -0400
commit73ddff2bee159ffb580bd24faf625cd5e628f5ec (patch)
tree218cf5101b67c98ef99814e59706976d3ad245c2 /kernel
parentdd1d6772692316fe35094085c5e4d9a370ad3462 (diff)
job control: introduce JOBCTL_TRAP_STOP and use it for group stop trap
do_signal_stop() implemented both normal group stop and trap for group stop while ptraced. This approach has been enough but scheduled changes require trap mechanism which can be used in more generic manner and using group stop trap for generic trap site simplifies both userland visible interface and implementation. This patch adds a new jobctl flag - JOBCTL_TRAP_STOP. When set, it triggers a trap site, which behaves like group stop trap, in get_signal_to_deliver() after checking for pending signals. While ptraced, do_signal_stop() doesn't stop itself. It initiates group stop if requested and schedules JOBCTL_TRAP_STOP and returns. The caller - get_signal_to_deliver() - is responsible for checking whether TRAP_STOP is pending afterwards and handling it. ptrace_attach() is updated to use JOBCTL_TRAP_STOP instead of JOBCTL_STOP_PENDING and __ptrace_unlink() to clear all pending trap bits and TRAPPING so that TRAP_STOP and future trap bits don't linger after detach. While at it, add proper function comment to do_signal_stop() and make it return bool. -v2: __ptrace_unlink() updated to clear JOBCTL_TRAP_MASK and TRAPPING instead of JOBCTL_PENDING_MASK. This avoids accidentally clearing JOBCTL_STOP_CONSUME. Spotted by Oleg. -v3: do_signal_stop() updated to return %false without dropping siglock while ptraced and TRAP_STOP check moved inside for(;;) loop after group stop participation. This avoids unnecessary relocking and also will help avoiding unnecessary traps by consuming group stop before handling pending traps. -v4: Jobctl trap handling moved into a separate function - do_jobctl_trap(). Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/ptrace.c12
-rw-r--r--kernel/signal.c90
2 files changed, 70 insertions, 32 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 7f05f3a1267b..45a8a4c5d8b2 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -83,6 +83,13 @@ void __ptrace_unlink(struct task_struct *child)
83 spin_lock(&child->sighand->siglock); 83 spin_lock(&child->sighand->siglock);
84 84
85 /* 85 /*
86 * Clear all pending traps and TRAPPING. TRAPPING should be
87 * cleared regardless of JOBCTL_STOP_PENDING. Do it explicitly.
88 */
89 task_clear_jobctl_pending(child, JOBCTL_TRAP_MASK);
90 task_clear_jobctl_trapping(child);
91
92 /*
86 * Reinstate JOBCTL_STOP_PENDING if group stop is in effect and 93 * Reinstate JOBCTL_STOP_PENDING if group stop is in effect and
87 * @child isn't dead. 94 * @child isn't dead.
88 */ 95 */
@@ -246,7 +253,7 @@ static int ptrace_attach(struct task_struct *task)
246 spin_lock(&task->sighand->siglock); 253 spin_lock(&task->sighand->siglock);
247 254
248 /* 255 /*
249 * If the task is already STOPPED, set JOBCTL_STOP_PENDING and 256 * If the task is already STOPPED, set JOBCTL_TRAP_STOP and
250 * TRAPPING, and kick it so that it transits to TRACED. TRAPPING 257 * TRAPPING, and kick it so that it transits to TRACED. TRAPPING
251 * will be cleared if the child completes the transition or any 258 * will be cleared if the child completes the transition or any
252 * event which clears the group stop states happens. We'll wait 259 * event which clears the group stop states happens. We'll wait
@@ -263,8 +270,7 @@ static int ptrace_attach(struct task_struct *task)
263 * in and out of STOPPED are protected by siglock. 270 * in and out of STOPPED are protected by siglock.
264 */ 271 */
265 if (task_is_stopped(task) && 272 if (task_is_stopped(task) &&
266 task_set_jobctl_pending(task, 273 task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))
267 JOBCTL_STOP_PENDING | JOBCTL_TRAPPING))
268 signal_wake_up(task, 1); 274 signal_wake_up(task, 1);
269 275
270 spin_unlock(&task->sighand->siglock); 276 spin_unlock(&task->sighand->siglock);
diff --git a/kernel/signal.c b/kernel/signal.c
index c99b8b5c0be7..b5f55ca1f43f 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -266,7 +266,7 @@ bool task_set_jobctl_pending(struct task_struct *task, unsigned int mask)
266 * CONTEXT: 266 * CONTEXT:
267 * Must be called with @task->sighand->siglock held. 267 * Must be called with @task->sighand->siglock held.
268 */ 268 */
269static void task_clear_jobctl_trapping(struct task_struct *task) 269void task_clear_jobctl_trapping(struct task_struct *task)
270{ 270{
271 if (unlikely(task->jobctl & JOBCTL_TRAPPING)) { 271 if (unlikely(task->jobctl & JOBCTL_TRAPPING)) {
272 task->jobctl &= ~JOBCTL_TRAPPING; 272 task->jobctl &= ~JOBCTL_TRAPPING;
@@ -1790,13 +1790,16 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
1790 /* 1790 /*
1791 * If @why is CLD_STOPPED, we're trapping to participate in a group 1791 * If @why is CLD_STOPPED, we're trapping to participate in a group
1792 * stop. Do the bookkeeping. Note that if SIGCONT was delievered 1792 * stop. Do the bookkeeping. Note that if SIGCONT was delievered
1793 * while siglock was released for the arch hook, PENDING could be 1793 * across siglock relocks since INTERRUPT was scheduled, PENDING
1794 * clear now. We act as if SIGCONT is received after TASK_TRACED 1794 * could be clear now. We act as if SIGCONT is received after
1795 * is entered - ignore it. 1795 * TASK_TRACED is entered - ignore it.
1796 */ 1796 */
1797 if (why == CLD_STOPPED && (current->jobctl & JOBCTL_STOP_PENDING)) 1797 if (why == CLD_STOPPED && (current->jobctl & JOBCTL_STOP_PENDING))
1798 gstop_done = task_participate_group_stop(current); 1798 gstop_done = task_participate_group_stop(current);
1799 1799
1800 /* any trap clears pending STOP trap */
1801 task_clear_jobctl_pending(current, JOBCTL_TRAP_STOP);
1802
1800 /* entering a trap, clear TRAPPING */ 1803 /* entering a trap, clear TRAPPING */
1801 task_clear_jobctl_trapping(current); 1804 task_clear_jobctl_trapping(current);
1802 1805
@@ -1888,13 +1891,30 @@ void ptrace_notify(int exit_code)
1888 spin_unlock_irq(&current->sighand->siglock); 1891 spin_unlock_irq(&current->sighand->siglock);
1889} 1892}
1890 1893
1891/* 1894/**
1892 * This performs the stopping for SIGSTOP and other stop signals. 1895 * do_signal_stop - handle group stop for SIGSTOP and other stop signals
1893 * We have to stop all threads in the thread group. 1896 * @signr: signr causing group stop if initiating
1894 * Returns non-zero if we've actually stopped and released the siglock. 1897 *
1895 * Returns zero if we didn't stop and still hold the siglock. 1898 * If %JOBCTL_STOP_PENDING is not set yet, initiate group stop with @signr
1899 * and participate in it. If already set, participate in the existing
1900 * group stop. If participated in a group stop (and thus slept), %true is
1901 * returned with siglock released.
1902 *
1903 * If ptraced, this function doesn't handle stop itself. Instead,
1904 * %JOBCTL_TRAP_STOP is scheduled and %false is returned with siglock
1905 * untouched. The caller must ensure that INTERRUPT trap handling takes
1906 * places afterwards.
1907 *
1908 * CONTEXT:
1909 * Must be called with @current->sighand->siglock held, which is released
1910 * on %true return.
1911 *
1912 * RETURNS:
1913 * %false if group stop is already cancelled or ptrace trap is scheduled.
1914 * %true if participated in group stop.
1896 */ 1915 */
1897static int do_signal_stop(int signr) 1916static bool do_signal_stop(int signr)
1917 __releases(&current->sighand->siglock)
1898{ 1918{
1899 struct signal_struct *sig = current->signal; 1919 struct signal_struct *sig = current->signal;
1900 1920
@@ -1907,7 +1927,7 @@ static int do_signal_stop(int signr)
1907 1927
1908 if (!likely(current->jobctl & JOBCTL_STOP_DEQUEUED) || 1928 if (!likely(current->jobctl & JOBCTL_STOP_DEQUEUED) ||
1909 unlikely(signal_group_exit(sig))) 1929 unlikely(signal_group_exit(sig)))
1910 return 0; 1930 return false;
1911 /* 1931 /*
1912 * There is no group stop already in progress. We must 1932 * There is no group stop already in progress. We must
1913 * initiate one now. 1933 * initiate one now.
@@ -1951,7 +1971,7 @@ static int do_signal_stop(int signr)
1951 } 1971 }
1952 } 1972 }
1953 } 1973 }
1954retry: 1974
1955 if (likely(!task_ptrace(current))) { 1975 if (likely(!task_ptrace(current))) {
1956 int notify = 0; 1976 int notify = 0;
1957 1977
@@ -1983,27 +2003,33 @@ retry:
1983 2003
1984 /* Now we don't run again until woken by SIGCONT or SIGKILL */ 2004 /* Now we don't run again until woken by SIGCONT or SIGKILL */
1985 schedule(); 2005 schedule();
1986 2006 return true;
1987 spin_lock_irq(&current->sighand->siglock);
1988 } else { 2007 } else {
1989 ptrace_stop(current->jobctl & JOBCTL_STOP_SIGMASK, 2008 /*
1990 CLD_STOPPED, 0, NULL); 2009 * While ptraced, group stop is handled by STOP trap.
1991 current->exit_code = 0; 2010 * Schedule it and let the caller deal with it.
1992 } 2011 */
1993 2012 task_set_jobctl_pending(current, JOBCTL_TRAP_STOP);
1994 /* 2013 return false;
1995 * JOBCTL_STOP_PENDING could be set if another group stop has
1996 * started since being woken up or ptrace wants us to transit
1997 * between TASK_STOPPED and TRACED. Retry group stop.
1998 */
1999 if (current->jobctl & JOBCTL_STOP_PENDING) {
2000 WARN_ON_ONCE(!(current->jobctl & JOBCTL_STOP_SIGMASK));
2001 goto retry;
2002 } 2014 }
2015}
2003 2016
2004 spin_unlock_irq(&current->sighand->siglock); 2017/**
2018 * do_jobctl_trap - take care of ptrace jobctl traps
2019 *
2020 * It is currently used only to trap for group stop while ptraced.
2021 *
2022 * CONTEXT:
2023 * Must be called with @current->sighand->siglock held, which may be
2024 * released and re-acquired before returning with intervening sleep.
2025 */
2026static void do_jobctl_trap(void)
2027{
2028 int signr = current->jobctl & JOBCTL_STOP_SIGMASK;
2005 2029
2006 return 1; 2030 WARN_ON_ONCE(!signr);
2031 ptrace_stop(signr, CLD_STOPPED, 0, NULL);
2032 current->exit_code = 0;
2007} 2033}
2008 2034
2009static int ptrace_signal(int signr, siginfo_t *info, 2035static int ptrace_signal(int signr, siginfo_t *info,
@@ -2110,6 +2136,12 @@ relock:
2110 do_signal_stop(0)) 2136 do_signal_stop(0))
2111 goto relock; 2137 goto relock;
2112 2138
2139 if (unlikely(current->jobctl & JOBCTL_TRAP_MASK)) {
2140 do_jobctl_trap();
2141 spin_unlock_irq(&sighand->siglock);
2142 goto relock;
2143 }
2144
2113 signr = dequeue_signal(current, &current->blocked, info); 2145 signr = dequeue_signal(current, &current->blocked, info);
2114 2146
2115 if (!signr) 2147 if (!signr)