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:
hujun5 2024-09-20 20:50:51 +08:00 committed by Xiang Xiao
parent b5c5e4b850
commit c5ecc49c10
5 changed files with 50 additions and 309 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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());
}

View File

@ -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);