Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fc327e26 authored by Chris Metcalf's avatar Chris Metcalf
Browse files

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: default avatarAl Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: default avatarChris Metcalf <cmetcalf@tilera.com>
parent 36be5051
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -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
+28 −13
Original line number Original line Diff line number Diff line
@@ -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
	{
	{
+28 −10
Original line number Original line Diff line number Diff line
@@ -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
	 addi   r21, r21, 1
	 move   r31, sp
	}
	{
	 st4    r20, r21
	 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
+5 −2
Original line number Original line Diff line number Diff line
@@ -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,7 +593,6 @@ 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;
	}
	}