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 c5c46f59e4
("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 <cmetcalf@ezchip.com>
This commit is contained in:
@@ -462,54 +462,57 @@ struct task_struct *__sched _switch_to(struct task_struct *prev,
|
||||
|
||||
/*
|
||||
* This routine is called on return from interrupt if any of the
|
||||
* TIF_WORK_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. 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)
|
||||
|
Reference in New Issue
Block a user