From 542e46319caa3f989d1e9e3c7d969ee4ad86480d Mon Sep 17 00:00:00 2001 From: hujun5 Date: Fri, 27 Sep 2024 11:42:44 +0800 Subject: [PATCH] xtensa: 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 178368 876 130604 309848 4ba58 nuttx after text data bss dec hex filename 178120 876 130212 309208 4b7d8 nuttx szie change -248 Signed-off-by: hujun5 --- arch/xtensa/src/common/xtensa.h | 7 - arch/xtensa/src/common/xtensa_cpupause.c | 12 +- arch/xtensa/src/common/xtensa_irqdispatch.c | 22 +- .../xtensa/src/common/xtensa_schedsigaction.c | 339 ++---------------- arch/xtensa/src/common/xtensa_swint.c | 19 +- arch/xtensa/src/common/xtensa_switchcontext.c | 19 +- 6 files changed, 58 insertions(+), 360 deletions(-) diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index 0d49184bdb..bae9bd1f81 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -103,13 +103,6 @@ #define IDLETHREAD_STACKSIZE ((CONFIG_IDLETHREAD_STACKSIZE + 15) & ~15) #define IDLETHREAD_STACKWORDS (IDLETHREAD_STACKSIZE >> 2) -/* In the Xtensa model, the state is saved in stack, - * only a reference stored in TCB. - */ - -#define xtensa_savestate(regs) ((regs) = up_current_regs()) -#define xtensa_restorestate(regs) up_set_current_regs(regs) - /* Context switching via system calls ***************************************/ #define xtensa_context_restore(regs)\ diff --git a/arch/xtensa/src/common/xtensa_cpupause.c b/arch/xtensa/src/common/xtensa_cpupause.c index 3709e45da8..1eeb13c584 100644 --- a/arch/xtensa/src/common/xtensa_cpupause.c +++ b/arch/xtensa/src/common/xtensa_cpupause.c @@ -102,11 +102,7 @@ int up_cpu_paused_save(void) sched_note_cpu_paused(tcb); #endif - /* Save the current context at current_regs into the TCB at the head - * of the assigned task list for this CPU. - */ - - xtensa_savestate(tcb->xcp.regs); + UNUSED(tcb); return OK; } @@ -186,11 +182,7 @@ int up_cpu_paused_restore(void) nxsched_resume_scheduler(tcb); - /* Then switch contexts. Any necessary address environment changes - * will be made when the interrupt returns. - */ - - xtensa_restorestate(tcb->xcp.regs); + UNUSED(tcb); return OK; } diff --git a/arch/xtensa/src/common/xtensa_irqdispatch.c b/arch/xtensa/src/common/xtensa_irqdispatch.c index daca91ba3e..c3023994c8 100644 --- a/arch/xtensa/src/common/xtensa_irqdispatch.c +++ b/arch/xtensa/src/common/xtensa_irqdispatch.c @@ -44,6 +44,8 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) { + struct tcb_s *tcb = this_task(); + #ifdef CONFIG_SUPPRESS_INTERRUPTS board_autoled_on(LED_INIRQ); PANIC(); @@ -62,15 +64,23 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) up_set_current_regs(regs); + if (irq != XTENSA_IRQ_SWINT) + { + /* we are not trigger by syscall */ + + tcb->xcp.regs = 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 (regs != up_current_regs()) + if (regs != tcb->xcp.regs) { #ifdef CONFIG_ARCH_ADDRENV /* Make sure that the address environment for the previously @@ -92,14 +102,8 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) * crashes. */ - g_running_tasks[this_cpu()] = this_task(); - } - - /* Restore the cpu lock */ - - if (regs != up_current_regs()) - { - regs = up_current_regs(); + g_running_tasks[this_cpu()] = tcb; + regs = tcb->xcp.regs; } /* Set current_regs to NULL to indicate that we are no longer in an diff --git a/arch/xtensa/src/common/xtensa_schedsigaction.c b/arch/xtensa/src/common/xtensa_schedsigaction.c index c69fe20d8f..2d3a381583 100644 --- a/arch/xtensa/src/common/xtensa_schedsigaction.c +++ b/arch/xtensa/src/common/xtensa_schedsigaction.c @@ -78,147 +78,8 @@ * ****************************************************************************/ -#ifndef CONFIG_SMP void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) { - sinfo("tcb=%p sigdeliver=%p\n", tcb, sigdeliver); - DEBUGASSERT(tcb != NULL && sigdeliver != NULL); - - /* Refuse to handle nested signal actions */ - - if (!tcb->xcp.sigdeliver) - { - tcb->xcp.sigdeliver = sigdeliver; - - sinfo("rtcb=%p current_regs=%p\n", this_task(), up_current_regs()); - - /* First, handle some special cases when the signal is being delivered - * to the currently executing task. - */ - - if (tcb == this_task()) - { - /* CASE 1: We are not in an interrupt handler and a task is - * signaling itself for some reason. - */ - - if (!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: 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 signaling 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. - * - * NOTE: that hi-priority interrupts are not disabled. - */ - - xtensa_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 - */ - - up_current_regs()[REG_PC] = (uint32_t)xtensa_sig_deliver; -#ifdef __XTENSA_CALL0_ABI__ - up_current_regs()[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM); -#else - up_current_regs()[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | - PS_WOE | PS_CALLINC(1)); -#endif -#ifndef CONFIG_BUILD_FLAT - xtensa_raiseprivilege(up_current_regs()); -#endif - - up_current_regs()[REG_A1] = (uint32_t)(up_current_regs() + - XCPTCONTEXT_REGS); - } - } - - /* 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 non-running task. - */ - - else - { - /* Save the context registers. These will be restored by the - * signal trampoline after the signals have been delivered. - * - * NOTE: that hi-priority interrupts are not disabled. - */ - - 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 = (void *)((uint32_t)tcb->xcp.regs - - XCPTCONTEXT_SIZE); - memcpy(tcb->xcp.regs, tcb->xcp.saved_regs, XCPTCONTEXT_SIZE); - - tcb->xcp.regs[REG_A1] = (uint32_t)tcb->xcp.regs + - XCPTCONTEXT_SIZE; - /* Then set up to vector to the trampoline with interrupts - * disabled - */ - - tcb->xcp.regs[REG_PC] = (uint32_t)xtensa_sig_deliver; -#ifdef __XTENSA_CALL0_ABI__ - tcb->xcp.regs[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM); -#else - tcb->xcp.regs[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | - PS_WOE | PS_CALLINC(1)); -#endif -#ifndef CONFIG_BUILD_FLAT - xtensa_raiseprivilege(tcb->xcp.regs); -#endif - } - } -} -#endif /* !CONFIG_SMP */ - -#ifdef CONFIG_SMP -void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) -{ - int cpu; - int me; - sinfo("tcb=%p sigdeliver=%p\n", tcb, sigdeliver); /* Refuse to handle nested signal actions */ @@ -231,180 +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_regs. These will be - * restored by the signal trampoline after the signal has - * been delivered. - * - * NOTE: that hi-priority interrupts are not disabled. - */ - - tcb->xcp.saved_regs = tcb->xcp.regs; - - /* The Inter-Processor Interrupt that pauses the other CPU - * generates a level-1 interrupt which sets the PS.EXCM. - * This level-1 interrupt is treated as an Exception and - * the bit PS.EXCM bit is automatically reset on return - * from Exception. However, this is not the case here - * because we are changing the execution to the signal - * trampoline. Restoring the PS register with PS.EXCM bit - * set would cause any other exception to deviate execution - * to the DEC (double exception vector), avoiding it to be - * treated correctly. According to xtensa ISA: "The process - * of taking an interrupt does not clear the interrupt - * request. The process does set PS.EXCM to 1, which - * disables level-1 interrupts in the interrupt handler. - * Typically, PS.EXCM is reset to 0 by the handler, after - * it has set up the stack frame and masked the interrupt." - * Clean the saved PS.EXCM to 1) avoid an exception from - * being properly treated and 2) avoid interrupts to be - * masked while delivering the signal. - */ - - if ((tcb->xcp.saved_regs[REG_PS] & PS_EXCM_MASK) != 0) - { - tcb->xcp.saved_regs[REG_PS] &= ~PS_EXCM_MASK; - } - - /* Duplicate the register context. These will be - * restored by the signal trampoline after the signal has - * been delivered. - */ - - tcb->xcp.regs = (void *) - ((uint32_t)tcb->xcp.regs - - XCPTCONTEXT_SIZE); - memcpy(tcb->xcp.regs, tcb->xcp.saved_regs, - XCPTCONTEXT_SIZE); - - tcb->xcp.regs[REG_A1] = (uint32_t)tcb->xcp.regs + - XCPTCONTEXT_SIZE; - - /* Then set up to vector to the trampoline with interrupts - * disabled - */ - - tcb->xcp.regs[REG_PC] = (uint32_t)xtensa_sig_deliver; -#ifdef __XTENSA_CALL0_ABI__ - tcb->xcp.regs[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM); -#else - tcb->xcp.regs[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | - PS_WOE | PS_CALLINC(1)); -#endif -#ifndef CONFIG_BUILD_FLAT - xtensa_raiseprivilege(tcb->xcp.regs); -#endif - } - else - { - /* tcb is running on the same CPU */ - - /* Copy tcb->xcp.regs to tcp.xcp.saved_regs. These will be - * restored by the signal trampoline after the signal has - * been delivered. - * - * NOTE: that hi-priority interrupts are not disabled. - */ - - xtensa_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); - - up_current_regs()[REG_A1] = (uint32_t)(up_current_regs() - + XCPTCONTEXT_REGS); - - /* Then set up to vector to the trampoline with interrupts - * disabled - */ - - up_current_regs()[REG_PC] = (uint32_t)xtensa_sig_deliver; -#ifdef __XTENSA_CALL0_ABI__ - up_current_regs()[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM); -#else - up_current_regs()[REG_PS] = (uint32_t) - (PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | - PS_WOE | PS_CALLINC(1)); -#endif -#ifndef CONFIG_BUILD_FLAT - xtensa_raiseprivilege(up_current_regs()); -#endif - } - - /* 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 context registers. These will be restored by the * signal trampoline after the signals have been delivered. * @@ -413,6 +123,11 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) tcb->xcp.saved_regs = tcb->xcp.regs; + if ((tcb->xcp.saved_regs[REG_PS] & PS_EXCM_MASK) != 0) + { + tcb->xcp.saved_regs[REG_PS] &= ~PS_EXCM_MASK; + } + /* Duplicate the register context. These will be * restored by the signal trampoline after the signal has been * delivered. @@ -442,7 +157,15 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) #ifndef CONFIG_BUILD_FLAT xtensa_raiseprivilege(tcb->xcp.regs); #endif + +#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/xtensa/src/common/xtensa_swint.c b/arch/xtensa/src/common/xtensa_swint.c index d62a93ded1..943a772430 100644 --- a/arch/xtensa/src/common/xtensa_swint.c +++ b/arch/xtensa/src/common/xtensa_swint.c @@ -35,6 +35,7 @@ #include "sched/sched.h" #include "chip.h" #include "signal/signal.h" +#include "sched/sched.h" #include "xtensa.h" /**************************************************************************** @@ -57,11 +58,13 @@ int xtensa_swint(int irq, void *context, void *arg) { uint32_t *regs = (uint32_t *)context; + struct tcb_s *tcb = this_task(); uint32_t cmd; - DEBUGASSERT(regs != NULL && regs == up_current_regs()); + DEBUGASSERT(regs != NULL); cmd = regs[REG_A2]; + tcb->xcp.regs = regs; /* The syscall software interrupt is called with A2 = system call command * and A3..A9 = variable number of arguments depending on the system call. @@ -116,7 +119,7 @@ int xtensa_swint(int irq, void *context, void *arg) case SYS_restore_context: { DEBUGASSERT(regs[REG_A3] != 0); - up_set_current_regs((uint32_t *)regs[REG_A3]); + tcb->xcp.regs = (uint32_t *)regs[REG_A3]; } break; @@ -141,7 +144,7 @@ int xtensa_swint(int irq, void *context, void *arg) { DEBUGASSERT(regs[REG_A3] != 0 && regs[REG_A4] != 0); *(uint32_t **)regs[REG_A3] = regs; - up_set_current_regs((uint32_t *)regs[REG_A4]); + tcb->xcp.regs = (uint32_t *)regs[REG_A4]; } break; @@ -419,9 +422,9 @@ int xtensa_swint(int irq, void *context, void *arg) break; } - if ((up_current_regs()[REG_PS] & PS_EXCM_MASK) != 0) + if ((tcb->xcp.regs[REG_PS] & PS_EXCM_MASK) != 0) { - up_current_regs()[REG_PS] &= ~PS_EXCM_MASK; + tcb->xcp.regs[REG_PS] &= ~PS_EXCM_MASK; } /* Report what happened. That might difficult in the case of a context @@ -429,10 +432,10 @@ int xtensa_swint(int irq, void *context, void *arg) */ #ifdef CONFIG_DEBUG_SYSCALL_INFO - if (regs != up_current_regs()) + if (regs != tcb->xcp.regs) { svcinfo("SYSCALL Return: Context switch!\n"); - up_dump_register(up_current_regs()); + up_dump_register(tcb->xcp.regs); } else { @@ -440,7 +443,7 @@ int xtensa_swint(int irq, void *context, void *arg) } #endif - if (regs != up_current_regs()) + if (regs != tcb->xcp.regs) { restore_critical_section(this_task(), this_cpu()); } diff --git a/arch/xtensa/src/common/xtensa_switchcontext.c b/arch/xtensa/src/common/xtensa_switchcontext.c index ad84e86ef0..a98e3e1069 100644 --- a/arch/xtensa/src/common/xtensa_switchcontext.c +++ b/arch/xtensa/src/common/xtensa_switchcontext.c @@ -59,24 +59,7 @@ void up_switch_context(struct tcb_s *tcb, struct tcb_s *rtcb) { /* Are we in an interrupt handler? */ - if (up_current_regs()) - { - /* Yes, then we have to do things differently. - * Just copy the current_regs into the OLD rtcb. - */ - - xtensa_savestate(rtcb->xcp.regs); - - /* Then switch contexts. Any necessary address environment - * changes will be made when the interrupt returns. - */ - - xtensa_restorestate(tcb->xcp.regs); - } - - /* No, then we will need to perform the user context switch */ - - else + if (!up_current_regs()) { /* Switch context to the context of the task at the head of the * ready to run list.