[PATCH 0/31] utrace for 3.1 kernel

2011-08-03 Thread Oleg Nesterov
On 08/02, Oleg Nesterov wrote:

 utrace patches for 3.1 kernel. Untested, will try to do some tests
 tomorrow.

I tried to test it a bit, seems to work. But see the new
[PATCH 31/31] utrace_resume: check irqs_disabled() to shut up lockdep.

The whole series is available in

git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc.git utrace-3.1

Oleg.



[PATCH 04/31] tracehooks: reintroduce tracehook_consider_fatal_signal()

2011-08-03 Thread Oleg Nesterov
Add the killed tracehook_consider_fatal_signal() back. It has multiple
callers and it is not easy add the necessary checks inline.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 arch/s390/kernel/traps.c  |4 ++--
 include/linux/tracehook.h |   22 ++
 kernel/signal.c   |4 ++--
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index ffabcd9..1018ab6 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -329,7 +329,7 @@ void __kprobes do_per_trap(struct pt_regs *regs)
 
if (notify_die(DIE_SSTEP, sstep, regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
return;
-   if (!current-ptrace)
+   if (!tracehook_consider_fatal_signal(current, SIGTRAP))
return;
info.si_signo = SIGTRAP;
info.si_errno = 0;
@@ -428,7 +428,7 @@ static void __kprobes illegal_op(struct pt_regs *regs, long 
pgm_int_code,
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
return;
if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
-   if (current-ptrace) {
+   if (tracehook_consider_fatal_signal(current, SIGTRAP)) {
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index 8cc28bc..ec2af67 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -161,6 +161,28 @@ static inline void tracehook_signal_handler(int sig, 
siginfo_t *info,
ptrace_notify(SIGTRAP);
 }
 
+/**
+ * tracehook_consider_fatal_signal - suppress special handling of fatal signal
+ * @task:  task receiving the signal
+ * @sig:   signal number being sent
+ *
+ * Return nonzero to prevent special handling of this termination signal.
+ * Normally handler for signal is %SIG_DFL.  It can be %SIG_IGN if @sig is
+ * ignored, in which case force_sig() is about to reset it to %SIG_DFL.
+ * When this returns zero, this signal might cause a quick termination
+ * that does not give the debugger a chance to intercept the signal.
+ *
+ * Called with or without @task-sighand-siglock held.
+ */
+static inline int tracehook_consider_fatal_signal(struct task_struct *task,
+ int sig)
+{
+   if (unlikely(task_utrace_flags(task)  (UTRACE_EVENT(SIGNAL_TERM) |
+   UTRACE_EVENT(SIGNAL_CORE
+   return 1;
+   return task-ptrace != 0;
+}
+
 #ifdef TIF_NOTIFY_RESUME
 /**
  * set_notify_resume - cause tracehook_notify_resume() to be called
diff --git a/kernel/signal.c b/kernel/signal.c
index 291c970..d7ef0da 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -494,7 +494,7 @@ int unhandled_signal(struct task_struct *tsk, int sig)
if (handler != SIG_IGN  handler != SIG_DFL)
return 0;
/* if ptraced, let the tracer determine */
-   return !tsk-ptrace;
+   return !tracehook_consider_fatal_signal(tsk, sig);
 }
 
 /*
@@ -982,7 +982,7 @@ static void complete_signal(int sig, struct task_struct *p, 
int group)
if (sig_fatal(p, sig) 
!(signal-flags  (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) 
!sigismember(t-real_blocked, sig) 
-   (sig == SIGKILL || !t-ptrace)) {
+   (sig == SIGKILL || !tracehook_consider_fatal_signal(t, sig))) {
/*
 * This signal will be fatal to the whole group.
 */
-- 
1.5.5.1




[PATCH 08/31] restore the DEATH/REAP utrace hooks

2011-08-03 Thread Oleg Nesterov
Restore the necessary hooks in release_task() and exit_notify(),
add the corresponding helpers into utrace.h.

Note: the @signal argument passed to -report_death() does not
match the previous behaviour. I think this shouldn't affect the
current users, and I bet nobody can really understand what this
magic argument should actually mean anyway.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/utrace.h |   22 ++
 kernel/exit.c  |4 
 2 files changed, 26 insertions(+), 0 deletions(-)

diff --git a/include/linux/utrace.h b/include/linux/utrace.h
index 9a2e2f4..cf13839 100644
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -697,4 +697,26 @@ static inline __must_check int utrace_barrier_pid(struct 
pid *pid,
 
 #endif /* CONFIG_UTRACE */
 
+static inline void utrace_release_task(struct task_struct *task)
+{
+   /* see utrace_add_engine() about this barrier */
+   smp_mb();
+   if (task_utrace_flags(task))
+   utrace_maybe_reap(task, task_utrace_struct(task), true);
+}
+
+static inline void utrace_exit_notify(struct task_struct *task,
+ int signal, int group_dead)
+{
+   /*
+* If utrace_set_events() was just called to enable
+* UTRACE_EVENT(DEATH), then we are obliged to call
+* utrace_report_death() and not miss it.  utrace_set_events()
+* checks @task-exit_state under tasklist_lock to synchronize
+* with exit_notify(), the caller.
+*/
+   if (task_utrace_flags(task)  _UTRACE_DEATH_EVENTS)
+   utrace_report_death(task, group_dead, signal);
+}
+
 #endif /* linux/utrace.h */
diff --git a/kernel/exit.c b/kernel/exit.c
index c1b0ab6..ba5ba22 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -168,6 +168,8 @@ void release_task(struct task_struct * p)
struct task_struct *leader;
int zap_leader;
 repeat:
+   utrace_release_task(p);
+
/* don't need to get the RCU readlock here - the process is dead and
 * can't be modifying its own credentials. But shut RCU-lockdep up */
rcu_read_lock();
@@ -860,6 +862,8 @@ static void exit_notify(struct task_struct *tsk, int 
group_dead)
wake_up_process(tsk-signal-group_exit_task);
write_unlock_irq(tasklist_lock);
 
+   utrace_exit_notify(tsk, autoreap ? -1 : SIGCHLD, group_dead);
+
/* If the process is dead, release it - nobody will wait for it */
if (autoreap)
release_task(tsk);
-- 
1.5.5.1




[PATCH 09/31] utrace: remove jobctl bits

2011-08-03 Thread Oleg Nesterov
- change utrace_get_signal() to check JOBCTL_STOP_PENDING instead of
  signal-group_stop_count. With the recent changes group_stop_count
  doesn't necessarily mean this task should participate in group stop.

- remove the participate in group stop code from utrace_wakeup() and
  utrace_stop(), this is no longer needed and wrong.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 kernel/utrace.c |   16 ++--
 1 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/kernel/utrace.c b/kernel/utrace.c
index 1e750ad..5d3974e 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -648,11 +648,7 @@ static void utrace_wakeup(struct task_struct *target, 
struct utrace *utrace)
 {
lockdep_assert_held(utrace-lock);
spin_lock_irq(target-sighand-siglock);
-   if (target-signal-flags  SIGNAL_STOP_STOPPED ||
-   target-signal-group_stop_count)
-   target-state = TASK_STOPPED;
-   else
-   wake_up_state(target, __TASK_TRACED);
+   wake_up_state(target, __TASK_TRACED);
spin_unlock_irq(target-sighand-siglock);
 }
 
@@ -805,14 +801,6 @@ relock:
 
__set_current_state(TASK_TRACED);
 
-   /*
-* If there is a group stop in progress,
-* we must participate in the bookkeeping.
-*/
-   if (unlikely(task-signal-group_stop_count) 
-   !--task-signal-group_stop_count)
-   task-signal-flags = SIGNAL_STOP_STOPPED;
-
spin_unlock_irq(task-sighand-siglock);
spin_unlock(utrace-lock);
 
@@ -2037,7 +2025,7 @@ int utrace_get_signal(struct task_struct *task, struct 
pt_regs *regs,
ka = NULL;
memset(return_ka, 0, sizeof *return_ka);
} else if (!(task-utrace_flags  UTRACE_EVENT_SIGNAL_ALL) ||
-  unlikely(task-signal-group_stop_count)) {
+  unlikely(task-jobctl  JOBCTL_STOP_PENDING)) {
/*
 * If no engine is interested in intercepting signals or
 * we must stop, let the caller just dequeue them normally
-- 
1.5.5.1




[PATCH 05/31] add utrace hooks into sig_ignored() and recalc_sigpending()

2011-08-03 Thread Oleg Nesterov
Add the necessary and somewhat special hooks into sig_ignored() and
recalc_sigpending(). Basically this restores _force_sigpending() and
_consider_ignored_signal() tracehook logic.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/utrace.h |2 ++
 kernel/signal.c|7 ++-
 2 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/include/linux/utrace.h b/include/linux/utrace.h
index f251efe..1b8da1c 100644
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -107,6 +107,8 @@ bool utrace_report_syscall_entry(struct pt_regs *);
 void utrace_report_syscall_exit(struct pt_regs *);
 void utrace_signal_handler(struct task_struct *, int);
 
+#define UTRACE_FLAG(task, ev)  (task_utrace_flags(task)  UTRACE_EVENT(ev))
+
 #ifndef CONFIG_UTRACE
 
 /*
diff --git a/kernel/signal.c b/kernel/signal.c
index d7ef0da..0f9af0b 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -87,7 +87,7 @@ static int sig_ignored(struct task_struct *t, int sig, int 
from_ancestor_ns)
/*
 * Tracers may want to know about even ignored signals.
 */
-   return !t-ptrace;
+   return !t-ptrace  !UTRACE_FLAG(t, SIGNAL_IGN);
 }
 
 /*
@@ -150,6 +150,11 @@ void recalc_sigpending_and_wake(struct task_struct *t)
 
 void recalc_sigpending(void)
 {
+   if (task_utrace_flags(current)  utrace_interrupt_pending()) {
+   set_thread_flag(TIF_SIGPENDING);
+   return;
+   }
+
if (!recalc_sigpending_tsk(current)  !freezing(current))
clear_thread_flag(TIF_SIGPENDING);
 
-- 
1.5.5.1




[PATCH 11/31] introduce wake_up_quiescent()

2011-08-03 Thread Oleg Nesterov
No functional changes. Add the new helper, wake_up_quiescent(task, state),
which simply returns wake_up_state(task, state). Change all callers which
do wake_up_state(STOPPED/TRACED) to use the new helper. ptrace_stop() is
a bit special, it does __set_current_state(RUNNING) in the very unlikely
case, change it as well.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/signal.h |2 ++
 kernel/ptrace.c|2 +-
 kernel/signal.c|   12 ++--
 kernel/utrace.c|2 +-
 4 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/include/linux/signal.h b/include/linux/signal.h
index a822300..2be3712 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -239,6 +239,8 @@ static inline int valid_signal(unsigned long sig)
 struct timespec;
 struct pt_regs;
 
+extern int wake_up_quiescent(struct task_struct *p, unsigned int state);
+
 extern int next_signal(struct sigpending *pending, sigset_t *mask);
 extern int do_send_sig_info(int sig, struct siginfo *info,
struct task_struct *p, bool group);
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 56b8fc1..4194664 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -621,7 +621,7 @@ static int ptrace_resume(struct task_struct *child, long 
request,
child-exit_code = data;
 
if (lock_task_sighand(child, flags)) {
-   wake_up_state(child, __TASK_TRACED);
+   wake_up_quiescent(child, __TASK_TRACED);
unlock_task_sighand(child, flags);
}
 
diff --git a/kernel/signal.c b/kernel/signal.c
index 71f5cca..3e8e0b1 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -702,6 +702,14 @@ void signal_wake_up(struct task_struct *t, int resume)
 }
 
 /*
+ * wakes up the STOPPED/TRACED task, must be called with -siglock held.
+ */
+int wake_up_quiescent(struct task_struct *p, unsigned int state)
+{
+   return wake_up_state(p, state);
+}
+
+/*
  * Remove signals in mask from the pending set and queue.
  * Returns 1 if any signals were found.
  *
@@ -888,7 +896,7 @@ static int prepare_signal(int sig, struct task_struct *p, 
int from_ancestor_ns)
task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING);
rm_from_queue(SIG_KERNEL_STOP_MASK, t-pending);
if (likely(!(t-ptrace  PT_SEIZED)))
-   wake_up_state(t, __TASK_STOPPED);
+   wake_up_quiescent(t, __TASK_STOPPED);
else
ptrace_trap_notify(t);
} while_each_thread(p, t);
@@ -1879,7 +1887,7 @@ static void ptrace_stop(int exit_code, int why, int 
clear_code, siginfo_t *info)
do_notify_parent_cldstop(current, false, why);
 
spin_lock_irq(current-sighand-siglock);
-   __set_current_state(TASK_RUNNING);
+   wake_up_quiescent(current, __TASK_TRACED);
spin_unlock_irq(current-sighand-siglock);
 
if (clear_code)
diff --git a/kernel/utrace.c b/kernel/utrace.c
index 5d3974e..cebc390 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -648,7 +648,7 @@ static void utrace_wakeup(struct task_struct *target, 
struct utrace *utrace)
 {
lockdep_assert_held(utrace-lock);
spin_lock_irq(target-sighand-siglock);
-   wake_up_state(target, __TASK_TRACED);
+   wake_up_quiescent(target, __TASK_TRACED);
spin_unlock_irq(target-sighand-siglock);
 }
 
-- 
1.5.5.1




[PATCH 16/31] reintroduce tracehook_finish_jctl() as utrace_end_stop()

2011-08-03 Thread Oleg Nesterov
utrace_finish_stop() is needed to avoid the races with SIGKILL which
wakes up UTRACED task, and thus it should be called every time after
the STOPPED/TRACED/UTRACED returns from schedule(), remember that
TASK_UTRACED can be added while the task is STOPPED/UTRACED.

- change do_signal_state() to call this helper right after schedule(),
  otherwise this logic is broken by the upstream changes

- now that utrace doesn't control TASK_TRACED bit, ptrace_stop() must
  call this helper too.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/utrace.h |   11 +++
 kernel/signal.c|5 +
 kernel/utrace.c|2 +-
 3 files changed, 17 insertions(+), 1 deletions(-)

diff --git a/include/linux/utrace.h b/include/linux/utrace.h
index cf13839..0279c74 100644
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -719,4 +719,15 @@ static inline void utrace_exit_notify(struct task_struct 
*task,
utrace_report_death(task, group_dead, signal);
 }
 
+/**
+ * utrace_end_stop - report about return from STOPPED/TRACED
+ *
+ * This is called by do_signal_stop() and ptrace_stop after wakeup.
+ */
+static inline void utrace_end_stop(void)
+{
+   if (task_utrace_flags(current))
+   utrace_finish_stop();
+}
+
 #endif /* linux/utrace.h */
diff --git a/kernel/signal.c b/kernel/signal.c
index 0dc6abb..a625309 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1895,6 +1895,8 @@ static void ptrace_stop(int exit_code, int why, int 
clear_code, siginfo_t *info)
read_unlock(tasklist_lock);
}
 
+   utrace_end_stop();
+
/*
 * While in TASK_TRACED, we were considered frozen enough.
 * Now that we woke up, it's crucial if we're supposed to be
@@ -2059,6 +2061,9 @@ static bool do_signal_stop(int signr)
 
/* Now we don't run again until woken by SIGCONT or SIGKILL */
schedule();
+
+   utrace_end_stop();
+
return true;
} else {
/*
diff --git a/kernel/utrace.c b/kernel/utrace.c
index 2097103..d41b982 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -741,7 +741,7 @@ static bool utrace_reset(struct task_struct *task, struct 
utrace *utrace)
 void utrace_finish_stop(void)
 {
/*
-* If we were task_is_traced() and then SIGKILL'ed, make
+* If we were task_is_utraced() and then SIGKILL'ed, make
 * sure we do nothing until the tracer drops utrace-lock.
 */
if (unlikely(__fatal_signal_pending(current))) {
-- 
1.5.5.1




[PATCH 15/31] utrace: use TASK_UTRACED instead of TASK_TRACED

2011-08-03 Thread Oleg Nesterov
Change utrace.c to use TASK_UTRACED instead of TASK_TRACED.

- utrace_stop/utrace_wakeup: simply use the new state

- utrace_do_stop: do not clear STOPPED/TRACED, but add the new
  __TASK_UTRACED bit to state the fact that both ptrace and utrace
  want this task to be stopped

- naturally, do not use task_is_traced() to check if this task was
  stopped by utrace, use the new task_is_utraced() helper which
  checks __TASK_UTRACED.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 kernel/utrace.c |   26 ++
 1 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/kernel/utrace.c b/kernel/utrace.c
index cebc390..2097103 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -462,6 +462,8 @@ static void put_detached_list(struct list_head *list)
  */
 #define ENGINE_STOP(1UL  _UTRACE_NEVENTS)
 
+#define task_is_utraced(task)  ((task-state  __TASK_UTRACED) != 0)
+
 static void mark_engine_wants_stop(struct task_struct *task,
   struct utrace_engine *engine)
 {
@@ -576,7 +578,7 @@ int utrace_set_events(struct task_struct *target,
 
ret = 0;
if ((old_flags  ~events)  target != current 
-   !task_is_stopped_or_traced(target)  !target-exit_state) {
+   !task_is_utraced(target)  !target-exit_state) {
/*
 * This barrier ensures that our engine-flags changes
 * have hit before we examine utrace-reporting,
@@ -623,21 +625,21 @@ static void mark_engine_detached(struct utrace_engine 
*engine)
  */
 static bool utrace_do_stop(struct task_struct *target, struct utrace *utrace)
 {
-   if (task_is_stopped(target)) {
+   if (task_is_stopped_or_traced(target)) {
/*
 * Stopped is considered quiescent; when it wakes up, it will
 * go through utrace_finish_stop() before doing anything else.
 */
spin_lock_irq(target-sighand-siglock);
-   if (likely(task_is_stopped(target)))
-   __set_task_state(target, TASK_TRACED);
+   if (likely(task_is_stopped_or_traced(target)))
+   target-state |= TASK_UTRACED;
spin_unlock_irq(target-sighand-siglock);
} else if (utrace-resume  UTRACE_REPORT) {
utrace-resume = UTRACE_REPORT;
set_notify_resume(target);
}
 
-   return task_is_traced(target);
+   return task_is_utraced(target);
 }
 
 /*
@@ -648,7 +650,7 @@ static void utrace_wakeup(struct task_struct *target, 
struct utrace *utrace)
 {
lockdep_assert_held(utrace-lock);
spin_lock_irq(target-sighand-siglock);
-   wake_up_quiescent(target, __TASK_TRACED);
+   wake_up_quiescent(target, __TASK_UTRACED);
spin_unlock_irq(target-sighand-siglock);
 }
 
@@ -710,7 +712,7 @@ static bool utrace_reset(struct task_struct *task, struct 
utrace *utrace)
/*
 * If no more engines want it stopped, wake it up.
 */
-   if (task_is_traced(task)  !(flags  ENGINE_STOP)) {
+   if (task_is_utraced(task)  !(flags  ENGINE_STOP)) {
/*
 * It just resumes, so make sure single-step
 * is not left set.
@@ -749,7 +751,7 @@ void utrace_finish_stop(void)
 }
 
 /*
- * Perform %UTRACE_STOP, i.e. block in TASK_TRACED until woken up.
+ * Perform %UTRACE_STOP, i.e. block in TASK_UTRACED until woken up.
  * @task == current, @utrace == current-utrace, which is not locked.
  * Return true if we were woken up by SIGKILL even though some utrace
  * engine may still want us to stay stopped.
@@ -799,7 +801,7 @@ relock:
return;
}
 
-   __set_current_state(TASK_TRACED);
+   __set_current_state(TASK_UTRACED);
 
spin_unlock_irq(task-sighand-siglock);
spin_unlock(utrace-lock);
@@ -809,14 +811,14 @@ relock:
utrace_finish_stop();
 
/*
-* While in TASK_TRACED, we were considered frozen enough.
+* While in TASK_UTRACED, we were considered frozen enough.
 * Now that we woke up, it's crucial if we're supposed to be
 * frozen that we freeze now before running anything substantial.
 */
try_to_freeze();
 
/*
-* While we were in TASK_TRACED, complete_signal() considered
+* While we were in TASK_UTRACED, complete_signal() considered
 * us uninterested in signal wakeups.  Now make sure our
 * TIF_SIGPENDING state is correct for normal running.
 */
@@ -1087,7 +1089,7 @@ int utrace_control(struct task_struct *target,
if (unlikely(IS_ERR(utrace)))
return PTR_ERR(utrace);
 
-   reset = task_is_traced(target);
+   reset = task_is_utraced(target);
ret = 0;
 
/*
-- 
1.5.5.1




[PATCH 12/31] introduce ptrace_signal_wake_up()

2011-08-03 Thread Oleg Nesterov
Add the new helper, ptrace_signal_wake_up(), change ptrace.c/signal.c
to use it instead of signal_wake_up() to wake up a STOPPED/TRACED task.

The new helper does almost the same, except:

- it doesn't use the TASK_WAKEKILL bit to wake up the TRACED
  or STOPPED task, it uses __TASK_STOPPED | __TASK_TRACED
  explicitly. This is what ptrace actually wants, it should
  never wake up a TASK_KILLABLE task.

  This should be cleanuped upatream, signal_wake_up() should
  take the state as an argument, not a boolean. Until then
  we add a new static helper.

- it uses wake_up_quiescent() instead of wake_up_state().

Thereafter every change from STOPPED/TRACED to RUNNING is done via
wake_up_quiescent().

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/ptrace.h |1 +
 kernel/ptrace.c|   20 
 kernel/signal.c|2 +-
 3 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 800f113..6d9282a 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -113,6 +113,7 @@
 #include linux/compiler.h/* For unlikely.  */
 #include linux/sched.h   /* For struct task_struct.  */
 
+extern void ptrace_signal_wake_up(struct task_struct *p, int quiescent);
 
 extern long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data);
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 4194664..1a50090 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -25,6 +25,18 @@
 #include linux/hw_breakpoint.h
 #include linux/cn_proc.h
 
+void ptrace_signal_wake_up(struct task_struct *p, int quiescent)
+{
+   unsigned int state;
+
+   set_tsk_thread_flag(p, TIF_SIGPENDING);
+
+   state = TASK_INTERRUPTIBLE;
+   if (quiescent)
+   state |= (__TASK_STOPPED | __TASK_TRACED);
+   if (!wake_up_quiescent(p, state))
+   kick_process(p);
+}
 
 static int ptrace_trapping_sleep_fn(void *flags)
 {
@@ -106,7 +118,7 @@ void __ptrace_unlink(struct task_struct *child)
 * TASK_KILLABLE sleeps.
 */
if (child-jobctl  JOBCTL_STOP_PENDING || task_is_traced(child))
-   signal_wake_up(child, task_is_traced(child));
+   ptrace_signal_wake_up(child, task_is_traced(child));
 
spin_unlock(child-sighand-siglock);
 }
@@ -296,7 +308,7 @@ static int ptrace_attach(struct task_struct *task, long 
request,
 */
if (task_is_stopped(task) 
task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))
-   signal_wake_up(task, 1);
+   ptrace_signal_wake_up(task, 1);
 
spin_unlock(task-sighand-siglock);
 
@@ -731,7 +743,7 @@ int ptrace_request(struct task_struct *child, long request,
 * tracee into STOP.
 */
if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP)))
-   signal_wake_up(child, child-jobctl  JOBCTL_LISTENING);
+   ptrace_signal_wake_up(child, child-jobctl  
JOBCTL_LISTENING);
 
unlock_task_sighand(child, flags);
ret = 0;
@@ -760,7 +772,7 @@ int ptrace_request(struct task_struct *child, long request,
 * of this trap and now.  Trigger re-trap immediately.
 */
if (child-jobctl  JOBCTL_TRAP_NOTIFY)
-   signal_wake_up(child, true);
+   ptrace_signal_wake_up(child, true);
 
unlock_task_sighand(child, flags);
ret = 0;
diff --git a/kernel/signal.c b/kernel/signal.c
index 3e8e0b1..0dc6abb 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -854,7 +854,7 @@ static void ptrace_trap_notify(struct task_struct *t)
assert_spin_locked(t-sighand-siglock);
 
task_set_jobctl_pending(t, JOBCTL_TRAP_NOTIFY);
-   signal_wake_up(t, t-jobctl  JOBCTL_LISTENING);
+   ptrace_signal_wake_up(t, t-jobctl  JOBCTL_LISTENING);
 }
 
 /*
-- 
1.5.5.1




[PATCH 18/31] ptrace_stop: do not assume the task is running after wake_up_quiescent()

2011-08-03 Thread Oleg Nesterov
If ptrace_stop() sets TASK_TRACED and then detects we should not stop,
it can race with utrace_do_stop() which can see TASK_TRACED and add
TASK_UTRACED. In this case we should stop for utrace needs.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 kernel/signal.c |8 
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/kernel/signal.c b/kernel/signal.c
index 0d1675a..249760f 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1908,6 +1908,14 @@ static void ptrace_stop(int exit_code, int why, int 
clear_code, siginfo_t *info)
if (clear_code)
current-exit_code = 0;
read_unlock(tasklist_lock);
+
+   /*
+* It is possible that __TASK_UTRACED was added by utrace
+* while we were __TASK_TRACED and before we take -siglock
+* for wake_up_quiescent(), we need to block in this case.
+* Otherwise this is unnecessary but absolutely harmless.
+*/
+   schedule();
}
 
utrace_end_stop();
-- 
1.5.5.1




[PATCH 14/31] introduce TASK_UTRACED state

2011-08-03 Thread Oleg Nesterov
Introduce TASK_UTRACED state, will be used by utrace instead of TASK_TRACED.

Note: this state is reported as t (tracing stop) to the user-space to
avoid the confusion. IOW, it looks like TASK_TRACED in /proc/pid/status.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 fs/proc/array.c   |   11 ++-
 include/linux/sched.h |   20 +++-
 2 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/fs/proc/array.c b/fs/proc/array.c
index f0c0ea2..e0daec4 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -138,11 +138,12 @@ static const char * const task_state_array[] = {
D (disk sleep),   /*   2 */
T (stopped),  /*   4 */
t (tracing stop), /*   8 */
-   Z (zombie),   /*  16 */
-   X (dead), /*  32 */
-   x (dead), /*  64 */
-   K (wakekill), /* 128 */
-   W (waking),   /* 256 */
+   t (tracing stop), /*  16 (stopped by utrace) */
+   Z (zombie),   /*  32 */
+   X (dead), /*  64 */
+   x (dead), /* 128 */
+   K (wakekill), /* 256 */
+   W (waking),   /* 512 */
 };
 
 static inline const char *get_task_state(struct task_struct *tsk)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index c6d79af..f3f0a77 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -184,16 +184,17 @@ print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq 
*cfs_rq)
 #define TASK_UNINTERRUPTIBLE   2
 #define __TASK_STOPPED 4
 #define __TASK_TRACED  8
+#define __TASK_UTRACED 16
 /* in tsk-exit_state */
-#define EXIT_ZOMBIE16
-#define EXIT_DEAD  32
+#define EXIT_ZOMBIE32
+#define EXIT_DEAD  64
 /* in tsk-state again */
-#define TASK_DEAD  64
-#define TASK_WAKEKILL  128
-#define TASK_WAKING256
-#define TASK_STATE_MAX 512
+#define TASK_DEAD  128
+#define TASK_WAKEKILL  256
+#define TASK_WAKING512
+#define TASK_STATE_MAX 1024
 
-#define TASK_STATE_TO_CHAR_STR RSDTtZXxKW
+#define TASK_STATE_TO_CHAR_STR RSDTtUZXxKW
 
 extern char ___assert_task_state[1 - 2*!!(
sizeof(TASK_STATE_TO_CHAR_STR)-1 != ilog2(TASK_STATE_MAX)+1)];
@@ -202,15 +203,16 @@ extern char ___assert_task_state[1 - 2*!!(
 #define TASK_KILLABLE  (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
 #define TASK_STOPPED   (TASK_WAKEKILL | __TASK_STOPPED)
 #define TASK_TRACED(TASK_WAKEKILL | __TASK_TRACED)
+#define TASK_UTRACED   (TASK_WAKEKILL | __TASK_UTRACED)
 
 /* Convenience macros for the sake of wake_up */
 #define TASK_NORMAL(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
-#define TASK_ALL   (TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)
+#define TASK_ALL   (TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED | 
__TASK_UTRACED)
 
 /* get_task_state() */
 #define TASK_REPORT(TASK_RUNNING | TASK_INTERRUPTIBLE | \
 TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \
-__TASK_TRACED)
+__TASK_TRACED | __TASK_UTRACED)
 
 #define task_is_traced(task)   ((task-state  __TASK_TRACED) != 0)
 #define task_is_stopped(task)  ((task-state  __TASK_STOPPED) != 0)
-- 
1.5.5.1




[PATCH 19/31] get_signal_to_deliver: restore/restructure utrace/ptrace signal reporting

2011-08-03 Thread Oleg Nesterov
- Reintroduce tracehook_get_signal() as utrace_hook_signal().

- Change get_signal_to_deliver() to call utrace_hook_signal() first,
  before dequeue_signal()

- Always call ptrace_signal() if signal != SIGKILL, no matter whether
  this signal comes from utrace or not.

  Since this can change signr again, update struct k_sigaction *ka
  in this case.

IOW, roughly, ptrace acts as if it is the last attached engine, it
takes the final decision about the signal.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/utrace.h |   31 +++
 kernel/signal.c|   30 --
 2 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/include/linux/utrace.h b/include/linux/utrace.h
index 0279c74..63103e2 100644
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -730,4 +730,35 @@ static inline void utrace_end_stop(void)
utrace_finish_stop();
 }
 
+/**
+ * utrace_hook_signal - deliver synthetic signal to traced task
+ * @task:  @current
+ * @regs:  task_pt_regs(@current)
+ * @info:  details of synthetic signal
+ * @return_ka: sigaction for synthetic signal
+ *
+ * Return zero to check for a real pending signal normally.
+ * Return -1 after releasing the siglock to repeat the check.
+ * Return a signal number to induce an artificial signal delivery,
+ * setting *@info and *@return_ka to specify its details and behavior.
+ *
+ * The @return_ka-sa_handler value controls the disposition of the
+ * signal, no matter the signal number.  For %SIG_DFL, the return value
+ * is a representative signal to indicate the behavior (e.g. %SIGTERM
+ * for death, %SIGQUIT for core dump, %SIGSTOP for job control stop,
+ * %SIGTSTP for stop unless in an orphaned pgrp), but the signal number
+ * reported will be @info-si_signo instead.
+ *
+ * Called with @task-sighand-siglock held, before dequeuing pending signals.
+ */
+static inline int utrace_hook_signal(struct task_struct *task,
+  struct pt_regs *regs,
+  siginfo_t *info,
+  struct k_sigaction *return_ka)
+{
+   if (unlikely(task_utrace_flags(task)))
+   return utrace_get_signal(task, regs, info, return_ka);
+   return 0;
+}
+
 #endif /* linux/utrace.h */
diff --git a/kernel/signal.c b/kernel/signal.c
index 249760f..3c783d3 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2234,17 +2234,27 @@ relock:
for (;;) {
struct k_sigaction *ka;
 
-   if (unlikely(current-jobctl  JOBCTL_STOP_PENDING) 
-   do_signal_stop(0))
+   signr = utrace_hook_signal(current, regs, info, return_ka);
+   if (unlikely(signr  0))
goto relock;
 
-   if (unlikely(current-jobctl  JOBCTL_TRAP_MASK)) {
-   do_jobctl_trap();
-   spin_unlock_irq(sighand-siglock);
-   goto relock;
-   }
+   if (unlikely(signr != 0))
+   ka = return_ka;
+   else {
+   if (unlikely(current-jobctl  JOBCTL_STOP_PENDING) 
+   do_signal_stop(0))
+   goto relock;
 
-   signr = dequeue_signal(current, current-blocked, info);
+   if (unlikely(current-jobctl  JOBCTL_TRAP_MASK)) {
+   do_jobctl_trap();
+   spin_unlock_irq(sighand-siglock);
+   goto relock;
+   }
+
+   signr = dequeue_signal(current, current-blocked, 
info);
+
+   ka = sighand-action[signr-1];
+   }
 
if (!signr)
break; /* will return 0 */
@@ -2254,9 +2264,9 @@ relock:
  regs, cookie);
if (!signr)
continue;
-   }
 
-   ka = sighand-action[signr-1];
+   ka = sighand-action[signr-1];
+   }
 
/* Trace actually delivered signals. */
trace_signal_deliver(signr, info, ka);
-- 
1.5.5.1




[PATCH 20/31] utrace_get_signal: s/JOBCTL_STOP_PENDING/JOBCTL_PENDING_MASK/

2011-08-03 Thread Oleg Nesterov
utrace_get_signal() checks JOBCTL_STOP_PENDING to detect the
case when we should not try to dequeue the signal but should
try to participate in the group-stop.

With the recent changes this is not enough, everything which
contrbutes to recalc_sigpending_tsk() should be respected.

Check JOBCTL_PENDING_MASK instead. This matches the
JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK code in the caller,
get_signal_to_deliver(). Note that this code won't run if
utrace_get_signal() returns signr  0.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 kernel/utrace.c |2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/kernel/utrace.c b/kernel/utrace.c
index d41b982..0bb0a06 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -2027,7 +2027,7 @@ int utrace_get_signal(struct task_struct *task, struct 
pt_regs *regs,
ka = NULL;
memset(return_ka, 0, sizeof *return_ka);
} else if (!(task-utrace_flags  UTRACE_EVENT_SIGNAL_ALL) ||
-  unlikely(task-jobctl  JOBCTL_STOP_PENDING)) {
+  unlikely(task-jobctl  JOBCTL_PENDING_MASK)) {
/*
 * If no engine is interested in intercepting signals or
 * we must stop, let the caller just dequeue them normally
-- 
1.5.5.1




[PATCH 17/31] teach wake_up_quiescent() to do selective wake_up

2011-08-03 Thread Oleg Nesterov
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 o...@redhat.com
---
 kernel/signal.c |   15 +++
 1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/kernel/signal.c b/kernel/signal.c
index a625309..0d1675a 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




[PATCH 22/31] introduce PT_SYSCALL_TRACE flag

2011-08-03 Thread Oleg Nesterov
Currently tracehooks assume that if the ptraced task has
TIF_SYSCALL_TRACE set, the tracee should report the syscall.
This is not true, this thread flag can be set by utrace.

Add the new internal ptrace flag, PT_SYSCALL_TRACE. Change
ptrace_set_syscall_trace() to set/clear this bit along with
TIF_SYSCALL_TRACE, change ptrace_report_syscall() to check
this flag instead of PT_PTRACED.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/ptrace.h|3 +++
 include/linux/tracehook.h |2 +-
 kernel/ptrace.c   |7 +--
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 6d9282a..c10f610 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -104,6 +104,8 @@
 
 #define PT_TRACE_MASK  0x03f4
 
+#define PT_SYSCALL_TRACE   0x0002
+
 /* single stepping state bits (used on ARM and PA-RISC) */
 #define PT_SINGLESTEP_BIT  31
 #define PT_SINGLESTEP  (1PT_SINGLESTEP_BIT)
@@ -227,6 +229,7 @@ static inline void ptrace_init_task(struct task_struct 
*child, bool ptrace)
 
if (unlikely(ptrace)  current-ptrace) {
child-ptrace = current-ptrace;
+   child-ptrace = ~PT_SYSCALL_TRACE;
__ptrace_link(child, current-parent);
 
if (child-ptrace  PT_SEIZED)
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index ec2af67..eb9fe30 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -59,7 +59,7 @@ static inline void ptrace_report_syscall(struct pt_regs *regs)
 {
int ptrace = current-ptrace;
 
-   if (!(ptrace  PT_PTRACED))
+   if (!(ptrace  PT_SYSCALL_TRACE))
return;
 
ptrace_notify(SIGTRAP | ((ptrace  PT_TRACESYSGOOD) ? 0x80 : 0));
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index dc2ad34..7deb292 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -40,10 +40,13 @@ void ptrace_signal_wake_up(struct task_struct *p, int 
quiescent)
 
 static void ptrace_set_syscall_trace(struct task_struct *p, bool on)
 {
-   if (on)
+   if (on) {
+   p-ptrace |= PT_SYSCALL_TRACE;
set_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
-   else
+   } else {
+   p-ptrace = ~PT_SYSCALL_TRACE;
clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
+   }
 }
 
 static int ptrace_trapping_sleep_fn(void *flags)
-- 
1.5.5.1




[PATCH 21/31] introduce ptrace_set_syscall_trace()

2011-08-03 Thread Oleg Nesterov
No functional changes. Add the new helper, ptrace_set_syscall_trace(),
which should be used to set/clear TIF_SYSCALL_TRACE in ptrace code.
Currently it does nothing more.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 kernel/ptrace.c |   15 ++-
 1 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 1a50090..dc2ad34 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -38,6 +38,14 @@ void ptrace_signal_wake_up(struct task_struct *p, int 
quiescent)
kick_process(p);
 }
 
+static void ptrace_set_syscall_trace(struct task_struct *p, bool on)
+{
+   if (on)
+   set_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
+   else
+   clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
+}
+
 static int ptrace_trapping_sleep_fn(void *flags)
 {
schedule();
@@ -418,7 +426,7 @@ static int ptrace_detach(struct task_struct *child, 
unsigned int data)
 
/* Architecture-specific hardware disable .. */
ptrace_disable(child);
-   clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+   ptrace_set_syscall_trace(child, false);
 
write_lock_irq(tasklist_lock);
/*
@@ -606,10 +614,7 @@ static int ptrace_resume(struct task_struct *child, long 
request,
if (!valid_signal(data))
return -EIO;
 
-   if (request == PTRACE_SYSCALL)
-   set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-   else
-   clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+   ptrace_set_syscall_trace(child, request == PTRACE_SYSCALL);
 
 #ifdef TIF_SYSCALL_EMU
if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
-- 
1.5.5.1




[PATCH 23/31] utrace: don't clear TIF_SYSCALL_TRACE if it is needed by ptrace

2011-08-03 Thread Oleg Nesterov
TIF_SYSCALL_TRACE should be cleared only if both ptrace and utrace do
not want it, change utrace_reset() to check PT_SYSCALL_TRACE before
clear_tsk_thread_flag(TIF_SYSCALL_TRACE).

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 kernel/utrace.c |1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/kernel/utrace.c b/kernel/utrace.c
index 0bb0a06..bebf6de 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -697,6 +697,7 @@ static bool utrace_reset(struct task_struct *task, struct 
utrace *utrace)
BUG_ON(utrace-death);
flags = UTRACE_EVENT(REAP);
} else if (!(flags  UTRACE_EVENT_SYSCALL) 
+  !(task-ptrace  PT_SYSCALL_TRACE) 
   test_tsk_thread_flag(task, TIF_SYSCALL_TRACE)) {
clear_tsk_thread_flag(task, TIF_SYSCALL_TRACE);
}
-- 
1.5.5.1




[PATCH 02/31] utrace: add utrace_init_task/utrace_free_task calls

2011-08-03 Thread Oleg Nesterov
Add the necessary copy_process()-utrace_init_task() and
free_task()-utrace_free_task() calls.

Originally this was the part of utrace core patch, but since
tracehooks are dying it doesn't make sense to reintroduce them.
Instead, just call the utrace_ helpers directly. This is fine
even without CONFIG_UTRACE, gcc is smart enough to optimize out
the code in free_task().

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 kernel/fork.c |5 +
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/kernel/fork.c b/kernel/fork.c
index e7ceaca..a9891da 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -66,6 +66,7 @@
 #include linux/user-return-notifier.h
 #include linux/oom.h
 #include linux/khugepaged.h
+#include linux/utrace.h
 
 #include asm/pgtable.h
 #include asm/pgalloc.h
@@ -167,6 +168,8 @@ void free_task(struct task_struct *tsk)
free_thread_info(tsk-stack);
rt_mutex_debug_task_free(tsk);
ftrace_graph_exit_task(tsk);
+   if (task_utrace_struct(tsk))
+   utrace_free_task(tsk);
free_task_struct(tsk);
 }
 EXPORT_SYMBOL(free_task);
@@ -1096,6 +1099,8 @@ static struct task_struct *copy_process(unsigned long 
clone_flags,
if (!p)
goto fork_out;
 
+   utrace_init_task(p);
+
ftrace_graph_init_task(p);
 
rt_mutex_init_task(p);
-- 
1.5.5.1




[PATCH 24/31] introduce task_utrace_lock/task_utrace_unlock

2011-08-03 Thread Oleg Nesterov
- Add task_utrace_lock(task). It simply takes task-utrace-lock if
  this task was ever utraced. Otherwise it takes task_lock(), this
  serializes with utrace_attach_task()-utrace_task_alloc(). In both
  case the caller can be sure it can't race with anything which needs
  utrace-lock.

- Add task_utrace_unlock(task), it releases the corresponding lock.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/utrace.h |9 +
 kernel/utrace.c|   26 ++
 2 files changed, 35 insertions(+), 0 deletions(-)

diff --git a/include/linux/utrace.h b/include/linux/utrace.h
index 63103e2..f37373b 100644
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -117,6 +117,12 @@ void utrace_signal_handler(struct task_struct *, int);
 
 #ifndef CONFIG_UTRACE
 
+static inline void task_utrace_lock(struct task_struct *task)
+{
+}
+static inline void task_utrace_unlock(struct task_struct *task)
+{
+}
 /*
  * linux/tracehook.h uses these accessors to avoid #ifdef CONFIG_UTRACE.
  */
@@ -139,6 +145,9 @@ static inline void task_utrace_proc_status(struct seq_file 
*m,
 
 #else  /* CONFIG_UTRACE */
 
+extern void task_utrace_lock(struct task_struct *task);
+extern void task_utrace_unlock(struct task_struct *task);
+
 static inline unsigned long task_utrace_flags(struct task_struct *task)
 {
return task-utrace_flags;
diff --git a/kernel/utrace.c b/kernel/utrace.c
index bebf6de..960dd9e 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -79,6 +79,32 @@ static int __init utrace_init(void)
 }
 module_init(utrace_init);
 
+void task_utrace_lock(struct task_struct *task)
+{
+   struct utrace *utrace = task_utrace_struct(task);
+
+   if (!utrace) {
+   task_lock(task);
+   utrace = task_utrace_struct(task);
+   if (!utrace)
+   return;
+
+   task_unlock(task);
+   }
+
+   spin_lock(utrace-lock);
+}
+
+void task_utrace_unlock(struct task_struct *task)
+{
+   struct utrace *utrace = task_utrace_struct(task);
+
+   if (utrace)
+   spin_unlock(utrace-lock);
+   else
+   task_unlock(task);
+}
+
 /*
  * Set up @task.utrace for the first time.  We can have races
  * between two utrace_attach_task() calls here.  The task_lock()
-- 
1.5.5.1




[PATCH 27/31] utrace: finish_resume_report: don't do user_xxx_step() if ptrace_wants_step()

2011-08-03 Thread Oleg Nesterov
finish_resume_report() should not enable/disable the stepping if
ptrace_wants_step() == T. If ptrace wants block_step while utrace
wants single_step we could promote the stepping, but I do not
think this really makes sense.

Unless the tracee is killed this can't race with ptrace, this is
called by the tracee itself. If it is killed we do not care.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/tracehook.h |8 
 kernel/utrace.c   |9 ++---
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index 21c8ca2..b6812d4 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -104,8 +104,8 @@ static inline __must_check int 
tracehook_report_syscall_entry(
return 0;
 }
 
-#define ptrace_wants_step()\
-   (current-ptrace  (PT_SINGLE_STEP | PT_SINGLE_BLOCK))
+#define ptrace_wants_step(task)\
+   ((task)-ptrace  (PT_SINGLE_STEP | PT_SINGLE_BLOCK))
 
 /**
  * tracehook_report_syscall_exit - task has just finished a system call
@@ -129,7 +129,7 @@ static inline void tracehook_report_syscall_exit(struct 
pt_regs *regs, int step)
if (task_utrace_flags(current)  UTRACE_EVENT(SYSCALL_EXIT))
utrace_report_syscall_exit(regs);
 
-   if (step  ptrace_wants_step()) {
+   if (step  ptrace_wants_step(current)) {
siginfo_t info;
user_single_step_siginfo(current, regs, info);
force_sig_info(SIGTRAP, info, current);
@@ -160,7 +160,7 @@ static inline void tracehook_signal_handler(int sig, 
siginfo_t *info,
 {
if (task_utrace_flags(current))
utrace_signal_handler(current, stepping);
-   if (stepping  ptrace_wants_step())
+   if (stepping  ptrace_wants_step(current))
ptrace_notify(SIGTRAP);
 }
 
diff --git a/kernel/utrace.c b/kernel/utrace.c
index 960dd9e..05e8532 100644
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -1829,7 +1829,8 @@ static void finish_resume_report(struct task_struct *task,
 
case UTRACE_BLOCKSTEP:
if (likely(arch_has_block_step())) {
-   user_enable_block_step(task);
+   if (!ptrace_wants_step(task))
+   user_enable_block_step(task);
break;
}
 
@@ -1842,7 +1843,8 @@ static void finish_resume_report(struct task_struct *task,
 
case UTRACE_SINGLESTEP:
if (likely(arch_has_single_step())) {
-   user_enable_single_step(task);
+   if (!ptrace_wants_step(task))
+   user_enable_single_step(task);
} else {
/*
 * This means some callback is to blame for failing
@@ -1857,7 +1859,8 @@ static void finish_resume_report(struct task_struct *task,
case UTRACE_REPORT:
case UTRACE_RESUME:
default:
-   user_disable_single_step(task);
+   if (!ptrace_wants_step(task))
+   user_disable_single_step(task);
break;
}
 }
-- 
1.5.5.1




[PATCH 30/31] ptrace_report_syscall: check TIF_SYSCALL_EMU

2011-08-03 Thread Oleg Nesterov
4d16a64 introduce PT_SYSCALL_TRACE flag breaks PTRACE_SYSEMU
which doesn't set PT_SYSCALL_TRACE.

Change ptrace_report_syscall() to check TIF_SYSCALL_EMU as well.
This can't conflict with utrace, this flag can only be set by
ptrace.

Signed-off-by: Oleg Nesterov o...@redhat.com
---
 include/linux/tracehook.h |2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index b6812d4..90ca578 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -59,7 +59,7 @@ static inline void ptrace_report_syscall(struct pt_regs *regs)
 {
int ptrace = current-ptrace;
 
-   if (!(ptrace  PT_SYSCALL_TRACE))
+   if (!(ptrace  PT_SYSCALL_TRACE)  !test_thread_flag(TIF_SYSCALL_EMU))
return;
 
ptrace_notify(SIGTRAP | ((ptrace  PT_TRACESYSGOOD) ? 0x80 : 0));
-- 
1.5.5.1