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:
parent
36be50515f
commit
fc327e268f
@ -100,9 +100,14 @@ extern void cpu_idle_on_new_stack(struct thread_info *old_ti,
|
|||||||
|
|
||||||
#else /* __ASSEMBLY__ */
|
#else /* __ASSEMBLY__ */
|
||||||
|
|
||||||
/* how to get the thread information struct from ASM */
|
/*
|
||||||
|
* How to get the thread information struct from assembly.
|
||||||
|
* Note that we use different macros since different architectures
|
||||||
|
* have different semantics in their "mm" instruction and we would
|
||||||
|
* like to guarantee that the macro expands to exactly one instruction.
|
||||||
|
*/
|
||||||
#ifdef __tilegx__
|
#ifdef __tilegx__
|
||||||
#define GET_THREAD_INFO(reg) move reg, sp; mm reg, zero, LOG2_THREAD_SIZE, 63
|
#define EXTRACT_THREAD_INFO(reg) mm reg, zero, LOG2_THREAD_SIZE, 63
|
||||||
#else
|
#else
|
||||||
#define GET_THREAD_INFO(reg) mm reg, sp, zero, LOG2_THREAD_SIZE, 31
|
#define GET_THREAD_INFO(reg) mm reg, sp, zero, LOG2_THREAD_SIZE, 31
|
||||||
#endif
|
#endif
|
||||||
|
@ -838,6 +838,18 @@ STD_ENTRY(interrupt_return)
|
|||||||
.Lresume_userspace:
|
.Lresume_userspace:
|
||||||
FEEDBACK_REENTER(interrupt_return)
|
FEEDBACK_REENTER(interrupt_return)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use r33 to hold whether we have already loaded the callee-saves
|
||||||
|
* into ptregs. We don't want to do it twice in this loop, since
|
||||||
|
* then we'd clobber whatever changes are made by ptrace, etc.
|
||||||
|
* Get base of stack in r32.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
GET_THREAD_INFO(r32)
|
||||||
|
movei r33, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.Lretry_work_pending:
|
||||||
/*
|
/*
|
||||||
* Disable interrupts so as to make sure we don't
|
* Disable interrupts so as to make sure we don't
|
||||||
* miss an interrupt that sets any of the thread flags (like
|
* miss an interrupt that sets any of the thread flags (like
|
||||||
@ -848,9 +860,6 @@ STD_ENTRY(interrupt_return)
|
|||||||
IRQ_DISABLE(r20, r21)
|
IRQ_DISABLE(r20, r21)
|
||||||
TRACE_IRQS_OFF /* Note: clobbers registers r0-r29 */
|
TRACE_IRQS_OFF /* Note: clobbers registers r0-r29 */
|
||||||
|
|
||||||
/* Get base of stack in r32; note r30/31 are used as arguments here. */
|
|
||||||
GET_THREAD_INFO(r32)
|
|
||||||
|
|
||||||
|
|
||||||
/* Check to see if there is any work to do before returning to user. */
|
/* Check to see if there is any work to do before returning to user. */
|
||||||
{
|
{
|
||||||
@ -866,16 +875,18 @@ STD_ENTRY(interrupt_return)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure we have all the registers saved for signal
|
* Make sure we have all the registers saved for signal
|
||||||
* handling or single-step. Call out to C code to figure out
|
* handling, notify-resume, or single-step. Call out to C
|
||||||
* exactly what we need to do for each flag bit, then if
|
* code to figure out exactly what we need to do for each flag bit,
|
||||||
* necessary, reload the flags and recheck.
|
* then if necessary, reload the flags and recheck.
|
||||||
*/
|
*/
|
||||||
push_extra_callee_saves r0
|
|
||||||
{
|
{
|
||||||
PTREGS_PTR(r0, PTREGS_OFFSET_BASE)
|
PTREGS_PTR(r0, PTREGS_OFFSET_BASE)
|
||||||
jal do_work_pending
|
bnz r33, 1f
|
||||||
}
|
}
|
||||||
bnz r0, .Lresume_userspace
|
push_extra_callee_saves r0
|
||||||
|
movei r33, 1
|
||||||
|
1: jal do_work_pending
|
||||||
|
bnz r0, .Lretry_work_pending
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the NMI case we
|
* In the NMI case we
|
||||||
@ -1180,10 +1191,12 @@ handle_syscall:
|
|||||||
add r20, r20, tp
|
add r20, r20, tp
|
||||||
lw r21, r20
|
lw r21, r20
|
||||||
addi r21, r21, 1
|
addi r21, r21, 1
|
||||||
sw r20, r21
|
{
|
||||||
|
sw r20, r21
|
||||||
|
GET_THREAD_INFO(r31)
|
||||||
|
}
|
||||||
|
|
||||||
/* Trace syscalls, if requested. */
|
/* Trace syscalls, if requested. */
|
||||||
GET_THREAD_INFO(r31)
|
|
||||||
addi r31, r31, THREAD_INFO_FLAGS_OFFSET
|
addi r31, r31, THREAD_INFO_FLAGS_OFFSET
|
||||||
lw r30, r31
|
lw r30, r31
|
||||||
andi r30, r30, _TIF_SYSCALL_TRACE
|
andi r30, r30, _TIF_SYSCALL_TRACE
|
||||||
@ -1362,7 +1375,10 @@ handle_ill:
|
|||||||
3:
|
3:
|
||||||
/* set PC and continue */
|
/* set PC and continue */
|
||||||
lw r26, r24
|
lw r26, r24
|
||||||
sw r28, r26
|
{
|
||||||
|
sw r28, r26
|
||||||
|
GET_THREAD_INFO(r0)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear TIF_SINGLESTEP to prevent recursion if we execute an ill.
|
* Clear TIF_SINGLESTEP to prevent recursion if we execute an ill.
|
||||||
@ -1370,7 +1386,6 @@ handle_ill:
|
|||||||
* need to clear it here and can't really impose on all other arches.
|
* need to clear it here and can't really impose on all other arches.
|
||||||
* So what's another write between friends?
|
* So what's another write between friends?
|
||||||
*/
|
*/
|
||||||
GET_THREAD_INFO(r0)
|
|
||||||
|
|
||||||
addi r1, r0, THREAD_INFO_FLAGS_OFFSET
|
addi r1, r0, THREAD_INFO_FLAGS_OFFSET
|
||||||
{
|
{
|
||||||
|
@ -646,6 +646,20 @@ STD_ENTRY(interrupt_return)
|
|||||||
.Lresume_userspace:
|
.Lresume_userspace:
|
||||||
FEEDBACK_REENTER(interrupt_return)
|
FEEDBACK_REENTER(interrupt_return)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use r33 to hold whether we have already loaded the callee-saves
|
||||||
|
* into ptregs. We don't want to do it twice in this loop, since
|
||||||
|
* then we'd clobber whatever changes are made by ptrace, etc.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
movei r33, 0
|
||||||
|
move r32, sp
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get base of stack in r32. */
|
||||||
|
EXTRACT_THREAD_INFO(r32)
|
||||||
|
|
||||||
|
.Lretry_work_pending:
|
||||||
/*
|
/*
|
||||||
* Disable interrupts so as to make sure we don't
|
* Disable interrupts so as to make sure we don't
|
||||||
* miss an interrupt that sets any of the thread flags (like
|
* miss an interrupt that sets any of the thread flags (like
|
||||||
@ -656,9 +670,6 @@ STD_ENTRY(interrupt_return)
|
|||||||
IRQ_DISABLE(r20, r21)
|
IRQ_DISABLE(r20, r21)
|
||||||
TRACE_IRQS_OFF /* Note: clobbers registers r0-r29 */
|
TRACE_IRQS_OFF /* Note: clobbers registers r0-r29 */
|
||||||
|
|
||||||
/* Get base of stack in r32; note r30/31 are used as arguments here. */
|
|
||||||
GET_THREAD_INFO(r32)
|
|
||||||
|
|
||||||
|
|
||||||
/* Check to see if there is any work to do before returning to user. */
|
/* Check to see if there is any work to do before returning to user. */
|
||||||
{
|
{
|
||||||
@ -674,16 +685,18 @@ STD_ENTRY(interrupt_return)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure we have all the registers saved for signal
|
* Make sure we have all the registers saved for signal
|
||||||
* handling or single-step. Call out to C code to figure out
|
* handling or notify-resume. Call out to C code to figure out
|
||||||
* exactly what we need to do for each flag bit, then if
|
* exactly what we need to do for each flag bit, then if
|
||||||
* necessary, reload the flags and recheck.
|
* necessary, reload the flags and recheck.
|
||||||
*/
|
*/
|
||||||
push_extra_callee_saves r0
|
|
||||||
{
|
{
|
||||||
PTREGS_PTR(r0, PTREGS_OFFSET_BASE)
|
PTREGS_PTR(r0, PTREGS_OFFSET_BASE)
|
||||||
jal do_work_pending
|
bnez r33, 1f
|
||||||
}
|
}
|
||||||
bnez r0, .Lresume_userspace
|
push_extra_callee_saves r0
|
||||||
|
movei r33, 1
|
||||||
|
1: jal do_work_pending
|
||||||
|
bnez r0, .Lretry_work_pending
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the NMI case we
|
* In the NMI case we
|
||||||
@ -968,11 +981,16 @@ handle_syscall:
|
|||||||
shl16insli r20, r20, hw0(irq_stat + IRQ_CPUSTAT_SYSCALL_COUNT_OFFSET)
|
shl16insli r20, r20, hw0(irq_stat + IRQ_CPUSTAT_SYSCALL_COUNT_OFFSET)
|
||||||
add r20, r20, tp
|
add r20, r20, tp
|
||||||
ld4s r21, r20
|
ld4s r21, r20
|
||||||
addi r21, r21, 1
|
{
|
||||||
st4 r20, r21
|
addi r21, r21, 1
|
||||||
|
move r31, sp
|
||||||
|
}
|
||||||
|
{
|
||||||
|
st4 r20, r21
|
||||||
|
EXTRACT_THREAD_INFO(r31)
|
||||||
|
}
|
||||||
|
|
||||||
/* Trace syscalls, if requested. */
|
/* Trace syscalls, if requested. */
|
||||||
GET_THREAD_INFO(r31)
|
|
||||||
addi r31, r31, THREAD_INFO_FLAGS_OFFSET
|
addi r31, r31, THREAD_INFO_FLAGS_OFFSET
|
||||||
ld r30, r31
|
ld r30, r31
|
||||||
andi r30, r30, _TIF_SYSCALL_TRACE
|
andi r30, r30, _TIF_SYSCALL_TRACE
|
||||||
|
@ -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)
|
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) {
|
if (thread_info_flags & _TIF_NEED_RESCHED) {
|
||||||
schedule();
|
schedule();
|
||||||
return 1;
|
return 1;
|
||||||
@ -589,8 +593,7 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (thread_info_flags & _TIF_SINGLESTEP) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
panic("work_pending: bad flags %#x\n", thread_info_flags);
|
panic("work_pending: bad flags %#x\n", thread_info_flags);
|
||||||
|
Loading…
Reference in New Issue
Block a user