arch/tile: fix up some issues in calling do_work_pending()

First, we were at risk of handling thread-info flags, in particular
do_signal(), when returning from kernel space.  This could happen
after a failed kernel_execve(), or when forking a kernel thread.
The fix is to test in do_work_pending() for user_mode() and return
immediately if so; we already had this test for one of the flags,
so I just hoisted it to the top of the function.

Second, if a ptraced process updated the callee-saved registers
in the ptregs struct and then processed another thread-info flag, we
would overwrite the modifications with the original callee-saved
registers.  To fix this, we add a register to note if we've already
saved the registers once, and skip doing it on additional passes
through the loop.  To avoid a performance hit from the couple of
extra instructions involved, I modified the GET_THREAD_INFO() macro
to be guaranteed to be one instruction, then bundled it with adjacent
instructions, yielding an overall net savings.

Reported-By: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
This commit is contained in:
Chris Metcalf
2012-04-28 18:51:43 -04:00
parent 36be50515f
commit fc327e268f
4 changed files with 68 additions and 27 deletions

View File

@@ -567,6 +567,10 @@ struct task_struct *__sched _switch_to(struct task_struct *prev,
*/
int do_work_pending(struct pt_regs *regs, u32 thread_info_flags)
{
/* If we enter in kernel mode, do nothing and exit the caller loop. */
if (!user_mode(regs))
return 0;
if (thread_info_flags & _TIF_NEED_RESCHED) {
schedule();
return 1;
@@ -589,8 +593,7 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags)
return 1;
}
if (thread_info_flags & _TIF_SINGLESTEP) {
if ((regs->ex1 & SPR_EX_CONTEXT_1_1__PL_MASK) == 0)
single_step_once(regs);
single_step_once(regs);
return 0;
}
panic("work_pending: bad flags %#x\n", thread_info_flags);