Both utrace and ptrace can want the same thread to be quiescent, in this
case its state is TASK_TRACED | TASK_UTRACED. And this also means that
this task must not run unless both utrace and ptrace resume it.
Change wake_up_quiescent(p, state) to do "p->state &= ~state" and return
false unless there is no more "quiescent" bits in task->state.
Signed-off-by: Oleg Nesterov <oleg(a)redhat.com>
---
kernel/signal.c | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/kernel/signal.c b/kernel/signal.c
index ba46eab..e06f795 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -701,11 +701,26 @@ void signal_wake_up(struct task_struct *t, int resume)
kick_process(t);
}
+#define STATE_QUIESCENT (__TASK_STOPPED | __TASK_TRACED | __TASK_UTRACED)
/*
* wakes up the STOPPED/TRACED task, must be called with ->siglock held.
*/
int wake_up_quiescent(struct task_struct *p, unsigned int state)
{
+ unsigned int quiescent = (p->state & STATE_QUIESCENT);
+
+ WARN_ON(state & ~(STATE_QUIESCENT | TASK_INTERRUPTIBLE));
+
+ if (quiescent) {
+ state &= ~TASK_INTERRUPTIBLE;
+ if ((quiescent & ~state) != 0) {
+ p->state &= ~state;
+ WARN_ON(!(p->state & STATE_QUIESCENT));
+ WARN_ON(!(p->state & TASK_WAKEKILL));
+ return 0;
+ }
+ }
+
return wake_up_state(p, state);
}
--
1.5.5.1