Ack!  While you were doing that, I was hacking up this patch.
I probably won't get to integrating your stuff tonight either.
So maybe you will look at this and merge them before I do.


Thanks,
Roland


diff --git a/kernel/utrace.c b/kernel/utrace.c
index 3f98f84..0000000 100644  
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -840,11 +840,16 @@ void utrace_finish_stop(void)
  * engine may still want us to stay stopped.
  */
 static void utrace_stop(struct task_struct *task, struct utrace *utrace,
-                       enum utrace_resume_action action)
+                       enum utrace_resume_action action, bool skip_notify)
 {
 relock:
        spin_lock(&utrace->lock);
 
+       /*
+        * We're stopping now, so this covers the pending vfork stop.
+        */
+       utrace->vfork_stop = 0;
+
        if (action < utrace->resume) {
                /*
                 * Ensure a reporting pass when we're resumed.
@@ -852,7 +857,7 @@ relock:
                utrace->resume = action;
                if (action == UTRACE_INTERRUPT)
                        set_thread_flag(TIF_SIGPENDING);
-               else
+               else if (action == UTRACE_REPORT || !skip_notify)
                        set_thread_flag(TIF_NOTIFY_RESUME);
        }
 
@@ -1522,6 +1527,77 @@ static const struct utrace_engine_ops *s
 }
 
 /*
+ * Apply a resume action, possibly after a stop.
+ * Returns true iff this resume action is requesting a report.
+ */
+static bool apply_resume_action(struct task_struct *task,
+                               struct utrace *utrace,
+                               enum utrace_resume_action action)
+{
+       switch (action) {
+       case UTRACE_INTERRUPT:
+               if (!signal_pending(task))
+                       set_tsk_thread_flag(task, TIF_SIGPENDING);
+               return true;
+
+       case UTRACE_BLOCKSTEP:
+               if (likely(arch_has_block_step())) {
+                       user_enable_block_step(task);
+                       break;
+               }
+
+               /*
+                * This means some callback is to blame for failing
+                * to check arch_has_block_step() itself.  Warn and
+                * then fall through to treat it as SINGLESTEP.
+                */
+               WARN_ON(1);
+
+       case UTRACE_SINGLESTEP:
+               if (likely(arch_has_single_step()))
+                       user_enable_single_step(task);
+               else
+                       /*
+                        * This means some callback is to blame for failing
+                        * to check arch_has_single_step() itself.  Spew
+                        * about it so the loser will fix his module.
+                        */
+                       WARN_ON(1);
+               break;
+
+       default:
+               user_disable_single_step(task);
+               return action == UTRACE_REPORT;
+       }
+
+       return false;
+}
+
+/*
+ * Finish the last reporting pass before returning to user mode,
+ * This is also used at syscall entry, where we both stop and set
+ * arch single-step state as if returning to user.  Returns true iff
+ * the resume action (possibly after a stop) requested another report.
+ */
+static bool finish_resume_report(struct task_struct *task,
+                                struct utrace *utrace,
+                                struct utrace_report *report)
+{
+       enum utrace_resume_action action;
+
+       finish_report_reset(task, utrace, report);
+
+       action = report->action;
+       if (action == UTRACE_STOP) {
+               utrace_stop(task, utrace, report->resume_action, true);
+               action = start_report(utrace);
+               WARN_ON(action == UTRACE_STOP);
+       }
+
+       return apply_resume_action(task, utrace, action);
+}
+
+/*
  * Do a normal reporting pass for engines interested in @event.
  * @callback is the name of the member in the ops vector, and remaining
  * args are the extras it takes after the standard three args.
@@ -1568,17 +1644,21 @@ static inline u32 do_report_syscall_entr
                                          struct utrace_report *report,
                                          u32 resume_report)
 {
+       bool again;
+
        start_report(utrace);
        REPORT_CALLBACKS(_reverse, task, utrace, report,
                         UTRACE_EVENT(SYSCALL_ENTRY), report_syscall_entry,
                         resume_report | report->result | report->action,
                         engine, current, regs);
-       finish_report(task, utrace, report, false);
-
-       if (report->action != UTRACE_STOP)
-               return 0;
 
-       utrace_stop(task, utrace, report->resume_action);
+       /*
+        * Now we'll stop if asked.  If we're proceeding with the syscall,
+        * now we'll first enable single-step if asked.  That leaves the
+        * task in "stepping" state so tracehook_report_syscall_exit() and
+        * the arch code that calls it will know.
+        */
+       again = finish_resume_report(task, utrace, report);
 
        if (fatal_signal_pending(task)) {
                /*
@@ -1586,7 +1666,7 @@ static inline u32 do_report_syscall_entr
                 * SIGKILL.  Don't let the system call actually proceed.
                 */
                report->result = UTRACE_SYSCALL_ABORT;
-       } else if (utrace->resume <= UTRACE_REPORT) {
+       } else if (again) {
                /*
                 * If we've been asked for another report after our stop,
                 * go back to report (and maybe stop) again before we run
@@ -1688,10 +1768,8 @@ void utrace_finish_vfork(struct task_str
        struct utrace *utrace = task_utrace_struct(task);
 
        if (utrace->vfork_stop) {
-               spin_lock(&utrace->lock);
-               utrace->vfork_stop = 0;
-               spin_unlock(&utrace->lock);
-               utrace_stop(task, utrace, UTRACE_RESUME); /* XXX */
+               utrace_stop(task, utrace, UTRACE_RESUME, true);
+               apply_resume_action(task, utrace, start_report(utrace));
        }
 }
 
@@ -1728,7 +1806,7 @@ void utrace_report_exit(long *exit_code)
               report_exit, orig_code, exit_code);
 
        if (report.action == UTRACE_STOP)
-               utrace_stop(task, utrace, report.resume_action);
+               utrace_stop(task, utrace, report.resume_action, true);
 }
 
 /*
@@ -1786,58 +1864,6 @@ void utrace_report_death(struct task_str
 }
 
 /*
- * Finish the last reporting pass before returning to user mode.
- */
-static void finish_resume_report(struct task_struct *task,
-                                struct utrace *utrace,
-                                struct utrace_report *report)
-{
-       finish_report_reset(task, utrace, report);
-
-       switch (report->action) {
-       case UTRACE_STOP:
-               utrace_stop(task, utrace, report->resume_action);
-               break;
-
-       case UTRACE_INTERRUPT:
-               if (!signal_pending(task))
-                       set_tsk_thread_flag(task, TIF_SIGPENDING);
-               break;
-
-       case UTRACE_BLOCKSTEP:
-               if (likely(arch_has_block_step())) {
-                       user_enable_block_step(task);
-                       break;
-               }
-
-               /*
-                * This means some callback is to blame for failing
-                * to check arch_has_block_step() itself.  Warn and
-                * then fall through to treat it as SINGLESTEP.
-                */
-               WARN_ON(1);
-
-       case UTRACE_SINGLESTEP:
-               if (likely(arch_has_single_step()))
-                       user_enable_single_step(task);
-               else
-                       /*
-                        * This means some callback is to blame for failing
-                        * to check arch_has_single_step() itself.  Spew
-                        * about it so the loser will fix his module.
-                        */
-                       WARN_ON(1);
-               break;
-
-       case UTRACE_REPORT:
-       case UTRACE_RESUME:
-       default:
-               user_disable_single_step(task);
-               break;
-       }
-}
-
-/*
  * This is called when TIF_NOTIFY_RESUME had been set (and is now clear).
  * We are close to user mode, and this is the place to report or stop.
  * When we return, we're going to user mode or into the signals code.
@@ -1994,11 +2020,8 @@ int utrace_get_signal(struct task_struct
                if (resume > UTRACE_REPORT) {
                        /*
                         * We only got here to process utrace->resume.
-                        * Despite no callbacks, this report is not spurious.
                         */
-                       report.action = resume;
-                       report.spurious = false;
-                       finish_resume_report(task, utrace, &report);
+                       apply_resume_action(task, utrace, resume);
                        return -1;
                } else if (!(task->utrace_flags & UTRACE_EVENT(QUIESCE))) {
                        /*

Reply via email to