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 <hujun5@xiaomi.com>
This commit is contained in:
hujun5 2024-09-27 11:42:44 +08:00 committed by Mateusz Szafoni
parent a9df7f0d1e
commit 542e46319c
6 changed files with 58 additions and 360 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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