riscv: g_current_regs is only used to determine if we are in irq,
with other functionalities removed. reason: by doing this we can reduce context switch time, When we exit from an interrupt handler, we directly use tcb->xcp.regs before text data bss dec hex filename 138805 337 24256 163398 27e46 nuttx after text data bss dec hex filename 138499 337 24240 163076 27d04 nuttx szie change -322 Signed-off-by: hujun5 <hujun5@xiaomi.com>
This commit is contained in:
parent
b5c5e4b850
commit
c5ecc49c10
@ -58,6 +58,8 @@
|
||||
|
||||
uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
|
||||
{
|
||||
struct tcb_s *tcb = this_task();
|
||||
|
||||
board_autoled_on(LED_INIRQ);
|
||||
#ifdef CONFIG_SUPPRESS_INTERRUPTS
|
||||
PANIC();
|
||||
@ -69,6 +71,10 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
|
||||
{
|
||||
regs[REG_EPC] += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
tcb->xcp.regs = regs;
|
||||
}
|
||||
|
||||
/* Current regs non-zero indicates that we are processing an interrupt;
|
||||
* current_regs is also used to manage interrupt level context switches.
|
||||
@ -82,6 +88,7 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
|
||||
/* Deliver the IRQ */
|
||||
|
||||
irq_dispatch(irq, regs);
|
||||
tcb = this_task();
|
||||
|
||||
/* Check for a context switch. If a context switch occurred, then
|
||||
* current_regs will have a different value than it did on entry. If an
|
||||
@ -90,7 +97,7 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
|
||||
* returning from the interrupt.
|
||||
*/
|
||||
|
||||
if (regs != up_current_regs())
|
||||
if (regs != tcb->xcp.regs)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
/* Make sure that the address environment for the previously
|
||||
@ -107,7 +114,7 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
|
||||
* crashes.
|
||||
*/
|
||||
|
||||
g_running_tasks[this_cpu()] = this_task();
|
||||
g_running_tasks[this_cpu()] = tcb;
|
||||
|
||||
/* If a context switch occurred while processing the interrupt then
|
||||
* current_regs may have change value. If we return any value
|
||||
@ -115,7 +122,7 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
|
||||
* that a context switch occurred during interrupt processing.
|
||||
*/
|
||||
|
||||
regs = up_current_regs();
|
||||
regs = tcb->xcp.regs;
|
||||
}
|
||||
|
||||
/* Set current_regs to NULL to indicate that we are no longer in an
|
||||
|
@ -85,13 +85,6 @@
|
||||
/* Interrupt Stack macros */
|
||||
#define INT_STACK_SIZE (STACK_ALIGN_DOWN(CONFIG_ARCH_INTERRUPTSTACK))
|
||||
|
||||
/* In the RISC-V model, the state is saved in stack,
|
||||
* only a reference stored in TCB.
|
||||
*/
|
||||
|
||||
#define riscv_savestate(regs) (regs = up_current_regs())
|
||||
#define riscv_restorestate(regs) up_set_current_regs(regs)
|
||||
|
||||
/* Determine which (if any) console driver to use. If a console is enabled
|
||||
* and no other console device is specified, then a serial console is
|
||||
* assumed.
|
||||
@ -322,8 +315,6 @@ static inline uintptr_t *riscv_vpuregs(struct tcb_s *tcb)
|
||||
|
||||
static inline void riscv_savecontext(struct tcb_s *tcb)
|
||||
{
|
||||
tcb->xcp.regs = (uintreg_t *)up_current_regs();
|
||||
|
||||
#ifdef CONFIG_ARCH_FPU
|
||||
/* Save current process FPU state to TCB */
|
||||
|
||||
@ -339,8 +330,6 @@ static inline void riscv_savecontext(struct tcb_s *tcb)
|
||||
|
||||
static inline void riscv_restorecontext(struct tcb_s *tcb)
|
||||
{
|
||||
up_set_current_regs(tcb->xcp.regs);
|
||||
|
||||
#ifdef CONFIG_ARCH_FPU
|
||||
/* Restore FPU state for next process */
|
||||
|
||||
|
@ -76,7 +76,6 @@
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
{
|
||||
uintptr_t int_ctx;
|
||||
@ -85,144 +84,6 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
|
||||
/* Refuse to handle nested signal actions */
|
||||
|
||||
if (!tcb->xcp.sigdeliver)
|
||||
{
|
||||
tcb->xcp.sigdeliver = sigdeliver;
|
||||
|
||||
/* First, handle some special cases when the signal is
|
||||
* being delivered to the currently executing task.
|
||||
*/
|
||||
|
||||
sinfo("rtcb=%p current_regs=%p\n", this_task(), up_current_regs());
|
||||
|
||||
if (tcb == this_task())
|
||||
{
|
||||
/* CASE 1: We are not in an interrupt handler and
|
||||
* a task is signalling itself for some reason.
|
||||
*/
|
||||
|
||||
if (!up_current_regs())
|
||||
{
|
||||
/* In this case just deliver the signal now. */
|
||||
|
||||
sigdeliver(tcb);
|
||||
tcb->xcp.sigdeliver = NULL;
|
||||
}
|
||||
|
||||
/* CASE 2: We are in an interrupt handler AND the
|
||||
* interrupted task is the same as the one that
|
||||
* must receive the signal, then we will have to modify
|
||||
* the return state as well as the state in the TCB.
|
||||
*
|
||||
* Hmmm... there looks like a latent bug here: The following
|
||||
* logic would fail in the strange case where we are in an
|
||||
* interrupt handler, the thread is signalling itself, but
|
||||
* a context switch to another task has occurred so that
|
||||
* current_regs does not refer to the thread of this_task()!
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
/* Save the context registers. These will be
|
||||
* restored by the signal trampoline after the signals have
|
||||
* been delivered.
|
||||
*/
|
||||
|
||||
riscv_savestate(tcb->xcp.saved_regs);
|
||||
|
||||
/* Duplicate the register context. These will be
|
||||
* restored by the signal trampoline after the signal has
|
||||
* been delivered.
|
||||
*/
|
||||
|
||||
up_set_current_regs(up_current_regs() - XCPTCONTEXT_REGS);
|
||||
|
||||
memcpy(up_current_regs(), tcb->xcp.saved_regs,
|
||||
XCPTCONTEXT_SIZE);
|
||||
|
||||
/* Then set up to vector to the trampoline with interrupts
|
||||
* disabled. The kernel-space trampoline must run in
|
||||
* privileged thread mode.
|
||||
*/
|
||||
|
||||
up_current_regs()[REG_EPC] = (uintptr_t)riscv_sigdeliver;
|
||||
|
||||
int_ctx = up_current_regs()[REG_INT_CTX];
|
||||
int_ctx &= ~STATUS_PIE;
|
||||
#ifndef CONFIG_BUILD_FLAT
|
||||
int_ctx |= STATUS_PPP;
|
||||
#endif
|
||||
up_current_regs()[REG_INT_CTX] = int_ctx;
|
||||
|
||||
up_current_regs()[REG_SP] = (uintptr_t)(up_current_regs() +
|
||||
XCPTCONTEXT_REGS);
|
||||
|
||||
sinfo("PC/STATUS Saved: %" PRIxREG "/%" PRIxREG
|
||||
" New: %" PRIxREG "/%" PRIxREG "\n",
|
||||
tcb->xcp.saved_regs[REG_EPC],
|
||||
tcb->xcp.saved_regs[REG_INT_CTX],
|
||||
up_current_regs()[REG_EPC],
|
||||
up_current_regs()[REG_INT_CTX]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, we are (1) signaling a task is not running
|
||||
* from an interrupt handler or (2) we are not in an
|
||||
* interrupt handler and the running task is signalling
|
||||
* some non-running task.
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
/* Save the return EPC and STATUS registers. These will be
|
||||
* restored by the signal trampoline after the signals have
|
||||
* been delivered.
|
||||
*/
|
||||
|
||||
/* Save the current register context location */
|
||||
|
||||
tcb->xcp.saved_regs = tcb->xcp.regs;
|
||||
|
||||
/* Duplicate the register context. These will be
|
||||
* restored by the signal trampoline after the signal has been
|
||||
* delivered.
|
||||
*/
|
||||
|
||||
tcb->xcp.regs = (uintreg_t *)((uintptr_t)tcb->xcp.regs -
|
||||
XCPTCONTEXT_SIZE);
|
||||
|
||||
memcpy(tcb->xcp.regs, tcb->xcp.saved_regs, XCPTCONTEXT_SIZE);
|
||||
|
||||
tcb->xcp.regs[REG_SP] = (uintptr_t)tcb->xcp.regs +
|
||||
XCPTCONTEXT_SIZE;
|
||||
|
||||
tcb->xcp.regs[REG_EPC] = (uintptr_t)riscv_sigdeliver;
|
||||
int_ctx = tcb->xcp.regs[REG_INT_CTX];
|
||||
int_ctx &= ~STATUS_PIE;
|
||||
|
||||
tcb->xcp.regs[REG_INT_CTX] = int_ctx;
|
||||
|
||||
sinfo("PC/STATUS Saved: %" PRIxREG "/%" PRIxREG
|
||||
" New: %" PRIxREG "/%" PRIxREG "\n",
|
||||
tcb->xcp.saved_regs[REG_EPC],
|
||||
tcb->xcp.saved_regs[REG_INT_CTX],
|
||||
tcb->xcp.regs[REG_EPC], tcb->xcp.regs[REG_INT_CTX]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_SMP */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
{
|
||||
uintptr_t int_ctx;
|
||||
int cpu;
|
||||
int me;
|
||||
|
||||
sinfo("tcb=%p sigdeliver=%p\n", tcb, sigdeliver);
|
||||
|
||||
/* Refuse to handle nested signal actions */
|
||||
|
||||
if (!tcb->xcp.sigdeliver)
|
||||
{
|
||||
tcb->xcp.sigdeliver = sigdeliver;
|
||||
@ -231,147 +92,29 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
* to task that is currently executing on any CPU.
|
||||
*/
|
||||
|
||||
sinfo("rtcb=%p current_regs=%p\n", this_task(), up_current_regs());
|
||||
|
||||
if (tcb->task_state == TSTATE_TASK_RUNNING)
|
||||
if (tcb == this_task() && !up_interrupt_context())
|
||||
{
|
||||
me = this_cpu();
|
||||
cpu = tcb->cpu;
|
||||
|
||||
/* CASE 1: We are not in an interrupt handler and a task is
|
||||
* signaling itself for some reason.
|
||||
/* In this case just deliver the signal now.
|
||||
* REVISIT: Signal handler will run in a critical section!
|
||||
*/
|
||||
|
||||
if (cpu == me && !up_current_regs())
|
||||
{
|
||||
/* In this case just deliver the signal now.
|
||||
* REVISIT: Signal handler will run in a critical section!
|
||||
*/
|
||||
|
||||
sigdeliver(tcb);
|
||||
tcb->xcp.sigdeliver = NULL;
|
||||
}
|
||||
|
||||
/* CASE 2: The task that needs to receive the signal is running.
|
||||
* This could happen if the task is running on another CPU OR if
|
||||
* we are in an interrupt handler and the task is running on this
|
||||
* CPU. In the former case, we will have to PAUSE the other CPU
|
||||
* first. But in either case, we will have to modify the return
|
||||
* state as well as the state in the TCB.
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
/* If we signaling a task running on the other CPU, we have
|
||||
* to PAUSE the other CPU.
|
||||
*/
|
||||
|
||||
if (cpu != me)
|
||||
{
|
||||
/* Pause the CPU */
|
||||
|
||||
up_cpu_pause(cpu);
|
||||
|
||||
/* Now tcb on the other CPU can be accessed safely */
|
||||
|
||||
/* Copy tcb->xcp.regs to tcp.xcp.saved. These will be
|
||||
* restored by the signal trampoline after the signal has
|
||||
* been delivered.
|
||||
*/
|
||||
|
||||
/* Then set up vector to the trampoline with interrupts
|
||||
* disabled. We must already be in privileged thread mode
|
||||
* to be here.
|
||||
*/
|
||||
|
||||
/* Save the current register context location */
|
||||
|
||||
tcb->xcp.saved_regs = tcb->xcp.regs;
|
||||
|
||||
/* Duplicate the register context. These will be
|
||||
* restored by the signal trampoline after the signal has
|
||||
* been delivered.
|
||||
*/
|
||||
|
||||
tcb->xcp.regs = (uintreg_t *)((uintptr_t)tcb->xcp.regs -
|
||||
XCPTCONTEXT_SIZE);
|
||||
|
||||
memcpy(tcb->xcp.regs, tcb->xcp.saved_regs,
|
||||
XCPTCONTEXT_SIZE);
|
||||
|
||||
tcb->xcp.regs[REG_SP] = (uintptr_t)tcb->xcp.regs +
|
||||
XCPTCONTEXT_SIZE;
|
||||
|
||||
tcb->xcp.regs[REG_EPC] = (uintptr_t)riscv_sigdeliver;
|
||||
int_ctx = tcb->xcp.regs[REG_INT_CTX];
|
||||
int_ctx &= ~STATUS_PIE;
|
||||
#ifndef CONFIG_BUILD_FLAT
|
||||
int_ctx |= STATUS_PPP;
|
||||
#endif
|
||||
tcb->xcp.regs[REG_INT_CTX] = int_ctx;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* tcb is running on the same CPU */
|
||||
|
||||
/* Save the context registers. These will be
|
||||
* restored by the signal trampoline after the signal has
|
||||
* been delivered.
|
||||
*/
|
||||
|
||||
tcb->xcp.saved_regs = up_current_regs();
|
||||
|
||||
/* Duplicate the register context. These will be
|
||||
* restored by the signal trampoline after the signal has
|
||||
* been delivered.
|
||||
*/
|
||||
|
||||
up_set_current_regs(up_current_regs() - XCPTCONTEXT_REGS);
|
||||
|
||||
memcpy(up_current_regs(), tcb->xcp.saved_regs,
|
||||
XCPTCONTEXT_SIZE);
|
||||
|
||||
up_current_regs()[REG_SP] = (uintptr_t)(up_current_regs()
|
||||
+ XCPTCONTEXT_REGS);
|
||||
|
||||
/* Then set up vector to the trampoline with interrupts
|
||||
* disabled. The kernel-space trampoline must run in
|
||||
* privileged thread mode.
|
||||
*/
|
||||
|
||||
up_current_regs()[REG_EPC] = (uintptr_t)riscv_sigdeliver;
|
||||
|
||||
int_ctx = up_current_regs()[REG_INT_CTX];
|
||||
int_ctx &= ~STATUS_PIE;
|
||||
#ifndef CONFIG_BUILD_FLAT
|
||||
int_ctx |= STATUS_PPP;
|
||||
#endif
|
||||
up_current_regs()[REG_INT_CTX] = int_ctx;
|
||||
}
|
||||
|
||||
/* NOTE: If the task runs on another CPU(cpu), adjusting
|
||||
* global IRQ controls will be done in the pause handler
|
||||
* on the CPU(cpu) by taking a critical section.
|
||||
* If the task is scheduled on this CPU(me), do nothing
|
||||
* because this CPU already took a critical section
|
||||
*/
|
||||
|
||||
/* RESUME the other CPU if it was PAUSED */
|
||||
|
||||
if (cpu != me)
|
||||
{
|
||||
up_cpu_resume(cpu);
|
||||
}
|
||||
}
|
||||
sigdeliver(tcb);
|
||||
tcb->xcp.sigdeliver = NULL;
|
||||
}
|
||||
|
||||
/* Otherwise, we are (1) signaling a task is not running from an
|
||||
* interrupt handler or (2) we are not in an interrupt handler and the
|
||||
* running task is signaling some other non-running task.
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
int cpu = tcb->cpu;
|
||||
int me = this_cpu();
|
||||
|
||||
if (cpu != me)
|
||||
{
|
||||
/* Pause the CPU */
|
||||
|
||||
up_cpu_pause(cpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Save the return EPC and STATUS registers. These will be
|
||||
* by the signal trampoline after the signal has been delivered.
|
||||
*/
|
||||
@ -403,9 +146,19 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
|
||||
|
||||
int_ctx = tcb->xcp.regs[REG_INT_CTX];
|
||||
int_ctx &= ~STATUS_PIE;
|
||||
#ifndef CONFIG_BUILD_FLAT
|
||||
int_ctx |= STATUS_PPP;
|
||||
#endif
|
||||
|
||||
tcb->xcp.regs[REG_INT_CTX] = int_ctx;
|
||||
#ifdef CONFIG_SMP
|
||||
/* RESUME the other CPU if it was PAUSED */
|
||||
|
||||
if (cpu != me)
|
||||
{
|
||||
up_cpu_resume(cpu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
@ -199,8 +199,7 @@ uintptr_t dispatch_syscall(unsigned int nbr, uintptr_t parm1,
|
||||
int riscv_swint(int irq, void *context, void *arg)
|
||||
{
|
||||
uintreg_t *regs = (uintreg_t *)context;
|
||||
|
||||
DEBUGASSERT(regs && regs == up_current_regs());
|
||||
uintreg_t *new_regs = regs;
|
||||
|
||||
/* Software interrupt 0 is invoked with REG_A0 (REG_X10) = system call
|
||||
* command and REG_A1-6 = variable number of
|
||||
@ -225,11 +224,6 @@ int riscv_swint(int irq, void *context, void *arg)
|
||||
*
|
||||
* A0 = SYS_restore_context
|
||||
* A1 = next
|
||||
*
|
||||
* In this case, we simply need to set current_regs to restore register
|
||||
* area referenced in the saved A1. context == current_regs is the
|
||||
* normal exception return. By setting current_regs = context[A1], we
|
||||
* force the return to the saved context referenced in $a1.
|
||||
*/
|
||||
|
||||
case SYS_restore_context:
|
||||
@ -237,6 +231,7 @@ int riscv_swint(int irq, void *context, void *arg)
|
||||
struct tcb_s *next = (struct tcb_s *)(uintptr_t)regs[REG_A1];
|
||||
|
||||
DEBUGASSERT(regs[REG_A1] != 0);
|
||||
new_regs = next->xcp.regs;
|
||||
riscv_restorecontext(next);
|
||||
}
|
||||
break;
|
||||
@ -253,9 +248,7 @@ int riscv_swint(int irq, void *context, void *arg)
|
||||
* A2 = next
|
||||
*
|
||||
* In this case, we save the context registers to the save register
|
||||
* area referenced by the saved contents of R5 and then set
|
||||
* current_regs to the save register area referenced by the saved
|
||||
* contents of R6.
|
||||
* area referenced by the saved contents of R5.
|
||||
*/
|
||||
|
||||
case SYS_switch_context:
|
||||
@ -264,7 +257,9 @@ int riscv_swint(int irq, void *context, void *arg)
|
||||
struct tcb_s *next = (struct tcb_s *)(uintptr_t)regs[REG_A2];
|
||||
|
||||
DEBUGASSERT(regs[REG_A1] != 0 && regs[REG_A2] != 0);
|
||||
prev->xcp.regs = regs;
|
||||
riscv_savecontext(prev);
|
||||
new_regs = next->xcp.regs;
|
||||
riscv_restorecontext(next);
|
||||
}
|
||||
break;
|
||||
@ -478,7 +473,6 @@ int riscv_swint(int irq, void *context, void *arg)
|
||||
#endif
|
||||
|
||||
default:
|
||||
|
||||
DEBUGPANIC();
|
||||
break;
|
||||
}
|
||||
@ -488,10 +482,10 @@ int riscv_swint(int irq, void *context, void *arg)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_SYSCALL_INFO
|
||||
if (regs != up_current_regs())
|
||||
if (regs != new_regs)
|
||||
{
|
||||
svcinfo("SWInt Return: Context switch!\n");
|
||||
up_dump_register(up_current_regs());
|
||||
up_dump_register(new_regs);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -499,7 +493,7 @@ int riscv_swint(int irq, void *context, void *arg)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (regs != up_current_regs())
|
||||
if (regs != new_regs)
|
||||
{
|
||||
restore_critical_section(this_task(), this_cpu());
|
||||
}
|
||||
|
@ -47,10 +47,11 @@ void *riscv_perform_syscall(uintreg_t *regs)
|
||||
/* Run the system call handler (swint) */
|
||||
|
||||
riscv_swint(0, regs, NULL);
|
||||
tcb = this_task();
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
if (regs != up_current_regs())
|
||||
if (regs != tcb->xcp.regs)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
/* Make sure that the address environment for the previously
|
||||
* running task is closed down gracefully (data caches dump,
|
||||
* MMU flushed) and set up the address environment for the new
|
||||
@ -58,11 +59,8 @@ void *riscv_perform_syscall(uintreg_t *regs)
|
||||
*/
|
||||
|
||||
addrenv_switch(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (regs != up_current_regs())
|
||||
{
|
||||
/* Record the new "running" task. g_running_tasks[] is only used by
|
||||
* assertion logic for reporting crashes.
|
||||
*/
|
||||
@ -81,7 +79,7 @@ void *riscv_perform_syscall(uintreg_t *regs)
|
||||
* that a context switch occurred during interrupt processing.
|
||||
*/
|
||||
|
||||
regs = up_current_regs();
|
||||
regs = tcb->xcp.regs;
|
||||
}
|
||||
|
||||
up_set_current_regs(NULL);
|
||||
|
Loading…
Reference in New Issue
Block a user