diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2012-04-28 18:51:43 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2012-05-16 16:01:16 -0400 |
commit | fc327e268fbef08e129ad51aa3a7113ee9bc6ba5 (patch) | |
tree | ba75f2ac9509090c6896a4fbc6be7c3aaba1aaf6 /arch/tile/kernel/process.c | |
parent | 36be50515fe2aef61533b516fa2576a2c7fe7664 (diff) |
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>
Diffstat (limited to 'arch/tile/kernel/process.c')
-rw-r--r-- | arch/tile/kernel/process.c | 7 |
1 files changed, 5 insertions, 2 deletions
diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c index 2d5ef617bb39..54e6c64b85cc 100644 --- a/arch/tile/kernel/process.c +++ b/arch/tile/kernel/process.c | |||
@@ -567,6 +567,10 @@ struct task_struct *__sched _switch_to(struct task_struct *prev, | |||
567 | */ | 567 | */ |
568 | int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) | 568 | int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) |
569 | { | 569 | { |
570 | /* If we enter in kernel mode, do nothing and exit the caller loop. */ | ||
571 | if (!user_mode(regs)) | ||
572 | return 0; | ||
573 | |||
570 | if (thread_info_flags & _TIF_NEED_RESCHED) { | 574 | if (thread_info_flags & _TIF_NEED_RESCHED) { |
571 | schedule(); | 575 | schedule(); |
572 | return 1; | 576 | return 1; |
@@ -589,8 +593,7 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) | |||
589 | return 1; | 593 | return 1; |
590 | } | 594 | } |
591 | if (thread_info_flags & _TIF_SINGLESTEP) { | 595 | if (thread_info_flags & _TIF_SINGLESTEP) { |
592 | if ((regs->ex1 & SPR_EX_CONTEXT_1_1__PL_MASK) == 0) | 596 | single_step_once(regs); |
593 | single_step_once(regs); | ||
594 | return 0; | 597 | return 0; |
595 | } | 598 | } |
596 | panic("work_pending: bad flags %#x\n", thread_info_flags); | 599 | panic("work_pending: bad flags %#x\n", thread_info_flags); |