From: Chris Metcalf Date: Tue, 22 Sep 2015 18:49:41 +0000 (-0400) Subject: arch/tile: adopt prepare_exit_to_usermode() model from x86 X-Git-Tag: v4.5-rc1~63^2~7 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=583b24a210ada7e88fc12aaf50024975ec882816;p=karo-tx-linux.git arch/tile: adopt prepare_exit_to_usermode() model from x86 This change is a prerequisite change for TASK_ISOLATION but also stands on its own for readability and maintainability. The existing tile do_work_pending() was called in a loop from assembly on the slow path; this change moves the loop into C code as well. For the x86 version see commit c5c46f59e4e7 ("x86/entry: Add new, comprehensible entry and exit handlers written in C"). This change exposes a pre-existing bug on the older tilepro platform; the singlestep processing is done last, but on tilepro (unlike tilegx) we enable interrupts while doing that processing, so we could in theory miss a signal or other asynchronous event. A future change could fix this by breaking the singlestep work into a "prepare" step done in the main loop, and a "trigger" step done after exiting the loop. Since this change is intended as purely a restructuring change, we call out the bug explicitly now, but don't yet fix it. Signed-off-by: Chris Metcalf --- diff --git a/arch/tile/include/asm/processor.h b/arch/tile/include/asm/processor.h index 139dfdee0134..0684e88aacd8 100644 --- a/arch/tile/include/asm/processor.h +++ b/arch/tile/include/asm/processor.h @@ -212,7 +212,7 @@ static inline void release_thread(struct task_struct *dead_task) /* Nothing for now */ } -extern int do_work_pending(struct pt_regs *regs, u32 flags); +extern void prepare_exit_to_usermode(struct pt_regs *regs, u32 flags); /* diff --git a/arch/tile/include/asm/thread_info.h b/arch/tile/include/asm/thread_info.h index dc1fb28d9636..4b7cef9e94e0 100644 --- a/arch/tile/include/asm/thread_info.h +++ b/arch/tile/include/asm/thread_info.h @@ -140,10 +140,14 @@ extern void _cpu_idle(void); #define _TIF_POLLING_NRFLAG (1<flags. It is - * entered with interrupts disabled so we don't miss an event - * that modified the thread_info flags. If any flag is set, we - * handle it and return, and the calling assembly code will - * re-disable interrupts, reload the thread flags, and call back - * if more flags need to be handled. - * - * We return whether we need to check the thread_info flags again - * or not. Note that we don't clear TIF_SINGLESTEP here, so it's - * important that it be tested last, and then claim that we don't - * need to recheck the flags. + * TIF_ALLWORK_MASK flags are set in thread_info->flags. It is + * entered with interrupts disabled so we don't miss an event that + * modified the thread_info flags. We loop until all the tested flags + * are clear. Note that the function is called on certain conditions + * that are not listed in the loop condition here (e.g. SINGLESTEP) + * which guarantees we will do those things once, and redo them if any + * of the other work items is re-done, but won't continue looping if + * all the other work is done. */ -int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) +void prepare_exit_to_usermode(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 (WARN_ON(!user_mode(regs))) + return; - user_exit(); + do { + local_irq_enable(); - /* Enable interrupts; they are disabled again on return to caller. */ - local_irq_enable(); + if (thread_info_flags & _TIF_NEED_RESCHED) + schedule(); - if (thread_info_flags & _TIF_NEED_RESCHED) { - schedule(); - return 1; - } #if CHIP_HAS_TILE_DMA() - if (thread_info_flags & _TIF_ASYNC_TLB) { - do_async_page_fault(regs); - return 1; - } + if (thread_info_flags & _TIF_ASYNC_TLB) + do_async_page_fault(regs); #endif - if (thread_info_flags & _TIF_SIGPENDING) { - do_signal(regs); - return 1; - } - if (thread_info_flags & _TIF_NOTIFY_RESUME) { - clear_thread_flag(TIF_NOTIFY_RESUME); - tracehook_notify_resume(regs); - return 1; - } - if (thread_info_flags & _TIF_SINGLESTEP) + + if (thread_info_flags & _TIF_SIGPENDING) + do_signal(regs); + + if (thread_info_flags & _TIF_NOTIFY_RESUME) { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(regs); + } + + local_irq_disable(); + thread_info_flags = READ_ONCE(current_thread_info()->flags); + + } while (thread_info_flags & _TIF_WORK_MASK); + + if (thread_info_flags & _TIF_SINGLESTEP) { single_step_once(regs); +#ifndef __tilegx__ + /* + * FIXME: on tilepro, since we enable interrupts in + * this routine, it's possible that we miss a signal + * or other asynchronous event. + */ + local_irq_disable(); +#endif + } user_enter(); - - return 0; } unsigned long get_wchan(struct task_struct *p)