From b794daaf0f332001c4d44c90b59617192e9825af Mon Sep 17 00:00:00 2001 From: hujun5 Date: Fri, 20 Sep 2024 20:50:51 +0800 Subject: [PATCH] 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 --- arch/risc-v/src/common/riscv_doirq.c | 13 +- arch/risc-v/src/common/riscv_internal.h | 11 - .../src/common/riscv_schedulesigaction.c | 293 ++---------------- arch/risc-v/src/common/riscv_swint.c | 22 +- .../common/supervisor/riscv_perform_syscall.c | 10 +- 5 files changed, 45 insertions(+), 304 deletions(-) diff --git a/arch/risc-v/src/common/riscv_doirq.c b/arch/risc-v/src/common/riscv_doirq.c index 44da81d41852b..895bd910ded1f 100644 --- a/arch/risc-v/src/common/riscv_doirq.c +++ b/arch/risc-v/src/common/riscv_doirq.c @@ -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 diff --git a/arch/risc-v/src/common/riscv_internal.h b/arch/risc-v/src/common/riscv_internal.h index 13a887615a065..d9165356a97c1 100644 --- a/arch/risc-v/src/common/riscv_internal.h +++ b/arch/risc-v/src/common/riscv_internal.h @@ -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 */ diff --git a/arch/risc-v/src/common/riscv_schedulesigaction.c b/arch/risc-v/src/common/riscv_schedulesigaction.c index ecc5260cded5a..d886443d9ee0e 100644 --- a/arch/risc-v/src/common/riscv_schedulesigaction.c +++ b/arch/risc-v/src/common/riscv_schedulesigaction.c @@ -76,7 +76,6 @@ * ****************************************************************************/ -#ifndef CONFIG_SMP void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) { uintptr_t int_ctx; @@ -89,289 +88,33 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) { tcb->xcp.sigdeliver = sigdeliver; - /* First, handle some special cases when the signal is - * being delivered to the currently executing task. + /* First, handle some special cases when the signal is being delivered + * to task that is currently executing on any CPU. */ - sinfo("rtcb=%p current_regs=%p\n", this_task(), up_current_regs()); - - if (tcb == this_task()) + if (tcb == this_task() && !up_interrupt_context()) { - /* 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()! + /* In this case just deliver the signal now. + * REVISIT: Signal handler will run in a critical section! */ - 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]); - } + 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 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); + int cpu = tcb->cpu; + int me = this_cpu(); - /* 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 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) - { - me = this_cpu(); - cpu = tcb->cpu; - - /* CASE 1: We are not in an interrupt handler and a task is - * signaling itself for some reason. - */ - - if (cpu == me && !up_current_regs()) + if (cpu != me) { - /* In this case just deliver the signal now. - * REVISIT: Signal handler will run in a critical section! - */ + /* Pause the CPU */ - sigdeliver(tcb); - tcb->xcp.sigdeliver = NULL; + up_cpu_pause(cpu); } - - /* 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); - } - } - } - - /* 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 - { /* 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 */ diff --git a/arch/risc-v/src/common/riscv_swint.c b/arch/risc-v/src/common/riscv_swint.c index f0ed4825d8460..510161db6fefc 100644 --- a/arch/risc-v/src/common/riscv_swint.c +++ b/arch/risc-v/src/common/riscv_swint.c @@ -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()); } diff --git a/arch/risc-v/src/common/supervisor/riscv_perform_syscall.c b/arch/risc-v/src/common/supervisor/riscv_perform_syscall.c index 8b9d287c8d988..8e26221e40c5b 100644 --- a/arch/risc-v/src/common/supervisor/riscv_perform_syscall.c +++ b/arch/risc-v/src/common/supervisor/riscv_perform_syscall.c @@ -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);