[PATCH 1/3] signal: Make signal_wake_up() take @sig_type insteadof @resume

From: Tejun Heo
Date: Tue Mar 29 2011 - 10:46:14 EST


signal_wake_up() currently takes boolean @resume which indicates that
the target task should be woken up with %TASK_WAKEKILL. Replace the
argument with @sig_type and use %SIGKILL to indicate %TASK_WAKEKILL
wakeups.

This is to prepare for adding more signal wake up types. All users
are converted to use %SIGKILL instead of 1 and this patch doesn't
cause any behavior difference.

While at it, convert to docbook function comment.

Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
---
Hello, guys.

These three patches implement fix for the SIGCONT notification corner
case Oleg pointed out in the following thread.

http://thread.gmane.org/gmane.linux.kernel/1116692/focus=1117000

In the process, signal_wake_up() is beefed up to handle most of child
wake up paths in signal and ptrace and I think the resulting code is
easier to comprehend and slightly less error-prone.

It may call in child into signal delivery path where it doesn't need
to but such cases are by no means common and I don't think there's any
performance implication. Please read the patch description on the
third patch for details.

Oleg, what do you think?

Thanks.

fs/exec.c | 2 +-
include/linux/sched.h | 2 +-
kernel/ptrace.c | 8 +++---
kernel/signal.c | 66 ++++++++++++++++++++++++++++++++-----------------
4 files changed, 49 insertions(+), 29 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 8328beb..63e726d 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1662,7 +1662,7 @@ static int zap_process(struct task_struct *start, int exit_code)
task_clear_group_stop_pending(t);
if (t != current && t->mm) {
sigaddset(&t->pending.signal, SIGKILL);
- signal_wake_up(t, 1);
+ signal_wake_up(t, SIGKILL);
nr++;
}
} while_each_thread(start, t);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 456d80e..4c30c00 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2488,7 +2488,7 @@ static inline void thread_group_cputime_init(struct signal_struct *sig)
extern void recalc_sigpending_and_wake(struct task_struct *t);
extern void recalc_sigpending(void);

-extern void signal_wake_up(struct task_struct *t, int resume_stopped);
+extern void signal_wake_up(struct task_struct *t, int sig_type);

/*
* Wrappers for p->thread_info->cpu access. No-op on UP.
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 4348586..ec8cce6 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -86,12 +86,12 @@ void __ptrace_unlink(struct task_struct *child)

/*
* If transition to TASK_STOPPED is pending or in TASK_TRACED, kick
- * @child in the butt. Note that @resume should be used iff @child
- * is in TASK_TRACED; otherwise, we might unduly disrupt
+ * @child in the butt. Note that %SIGKILL wake up should be used
+ * iff @child is in TASK_TRACED; otherwise, we might unduly disrupt
* TASK_KILLABLE sleeps.
*/
if (child->group_stop & GROUP_STOP_PENDING || task_is_traced(child))
- signal_wake_up(child, task_is_traced(child));
+ signal_wake_up(child, task_is_traced(child) ? SIGKILL : 0);

spin_unlock(&child->sighand->siglock);
}
@@ -243,7 +243,7 @@ static int ptrace_attach(struct task_struct *task)
*/
if (task_is_stopped(task)) {
task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING;
- signal_wake_up(task, 1);
+ signal_wake_up(task, SIGKILL);
wait_trap = true;
}

diff --git a/kernel/signal.c b/kernel/signal.c
index f799a05..837070c 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -618,33 +618,53 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
return signr;
}

-/*
- * Tell a process that it has a new active signal..
+/**
+ * signal_wake_up - tell a task that it has a new active signal
+ * @t: the target task
+ * @sig_type: the type of the new signal (0 or SIGKILL)
+ *
+ * This function makes sure that @t is woken up and/or brought into signal
+ * delivery path as necessary accodring to @sig_type. @sig_type can be one
+ * of the followings.
+ *
+ * 0 Normal signal delivery. If @t is executing in userland it
+ * should be brought in to deliver the signal. When @t is in
+ * kernel, wake it up iff it's in interruptible sleep.
*
- * NOTE! we rely on the previous spin_lock to
- * lock interrupts for us! We can only be called with
- * "siglock" held, and the local interrupt must
- * have been disabled when that got acquired!
+ * %SIGKILL @t is being killed. In addition to the usual kicking,
+ * interrupt KILLABLE, STOPPED and TRACED sleeps using
+ * %TASK_WAKEKILL.
*
- * No need to set need_resched since signal event passing
- * goes through ->blocked
+ * CONTEXT:
+ * Must be called with @t->sighand->siglock held.
*/
-void signal_wake_up(struct task_struct *t, int resume)
+void signal_wake_up(struct task_struct *t, int sig_type)
{
- unsigned int mask;
+ unsigned int uninitialized_var(mask);

set_tsk_thread_flag(t, TIF_SIGPENDING);

- /*
- * For SIGKILL, we want to wake it up in the stopped/traced/killable
- * case. We don't check t->state here because there is a race with it
- * executing another processor and just now entering stopped state.
- * By using wake_up_state, we ensure the process will wake up and
- * handle its death signal.
- */
- mask = TASK_INTERRUPTIBLE;
- if (resume)
- mask |= TASK_WAKEKILL;
+ switch (sig_type) {
+ case 0:
+ mask = TASK_INTERRUPTIBLE;
+ break;
+
+ case SIGKILL:
+ /*
+ * For SIGKILL, we want to wake it up in the stopped /
+ * traced / killable case. We don't check t->state here
+ * because there is a race with it executing another
+ * processor and just now entering stopped state. By using
+ * wake_up_state, we ensure the process will wake up and
+ * handle its death signal.
+ */
+ mask |= TASK_INTERRUPTIBLE | TASK_WAKEKILL;
+ break;
+
+ default:
+ BUG();
+ }
+
if (!wake_up_state(t, mask))
kick_process(t);
}
@@ -941,7 +961,7 @@ static void complete_signal(int sig, struct task_struct *p, int group)
do {
task_clear_group_stop_pending(t);
sigaddset(&t->pending.signal, SIGKILL);
- signal_wake_up(t, 1);
+ signal_wake_up(t, SIGKILL);
} while_each_thread(p, t);
return;
}
@@ -951,7 +971,7 @@ static void complete_signal(int sig, struct task_struct *p, int group)
* The signal is already in the shared-pending queue.
* Tell the chosen thread to wake up and dequeue it.
*/
- signal_wake_up(t, sig == SIGKILL);
+ signal_wake_up(t, sig == SIGKILL ? SIGKILL : 0);
return;
}

@@ -1180,7 +1200,7 @@ int zap_other_threads(struct task_struct *p)
if (t->exit_state)
continue;
sigaddset(&t->pending.signal, SIGKILL);
- signal_wake_up(t, 1);
+ signal_wake_up(t, SIGKILL);
}

return count;
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/