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))) { /*