arch/xtensa: Replace the xcp context with stack context to improve context switching

Signed-off-by: Abdelatif Guettouche <abdelatif.guettouche@espressif.com>
This commit is contained in:
Abdelatif Guettouche 2022-04-20 23:50:12 +02:00 committed by Xiang Xiao
parent e674d5cb86
commit da273fce0b
20 changed files with 134 additions and 127 deletions

View File

@ -149,12 +149,11 @@ struct xcptcontext
* another signal handler is executing will be ignored!
*/
uint32_t saved_pc;
uint32_t saved_ps;
uint32_t *saved_regs;
/* Register save area */
uint32_t regs[XCPTCONTEXT_REGS];
uint32_t *regs;
#if XCHAL_CP_NUM > 0
/* Co-processor save area */

View File

@ -100,21 +100,21 @@
/* SYS call 0:
*
* int xtensa_saveusercontext(uint32_t *saveregs);
* int up_saveusercontext(void *saveregs);
*/
#define SYS_save_context (0)
/* SYS call 1:
*
* void xtensa_fullcontextrestore(uint32_t *restoreregs) noreturn_function;
* void xtensa_context_restore(uint32_t **restoreregs) noreturn_function;
*/
#define SYS_restore_context (1)
/* SYS call 2:
*
* void xtensa_switchcontext(uint32_t *saveregs, uint32_t *restoreregs);
* void xtensa_switchcontext(uint32_t **saveregs, uint32_t *restoreregs);
*/
#define SYS_switch_context (2)

View File

@ -103,15 +103,12 @@
#define IDLETHREAD_STACKSIZE ((CONFIG_IDLETHREAD_STACKSIZE + 15) & ~15)
#define IDLETHREAD_STACKWORDS (IDLETHREAD_STACKSIZE >> 2)
/* In the XTENSA model, the state is copied from the stack to the TCB, but
* only a referenced is passed to get the state from the TCB.
*
* REVISIT: It would not be too difficult to save only a pointer to the
* state save area in the TCB and thus avoid the copy.
/* In the Xtensa model, the state is saved in stack,
* only a reference stored in TCB.
*/
#define xtensa_savestate(regs) xtensa_copystate(regs, (uint32_t*)CURRENT_REGS)
#define xtensa_restorestate(regs) do { CURRENT_REGS = regs; } while (0)
#define xtensa_savestate(regs) ((regs) = (uint32_t *)CURRENT_REGS)
#define xtensa_restorestate(regs) (CURRENT_REGS = (regs))
/* Context switching via system calls ***************************************/

View File

@ -140,7 +140,7 @@ void up_block_task(struct tcb_s *tcb, tstate_t task_state)
/* Then switch contexts */
xtensa_switchcontext(rtcb->xcp.regs, nexttcb->xcp.regs);
xtensa_switchcontext(&rtcb->xcp.regs, nexttcb->xcp.regs);
/* xtensa_switchcontext forces a context switch to the task at the
* head of the ready-to-run list. It does not 'return' in the

View File

@ -1,58 +0,0 @@
/****************************************************************************
* arch/xtensa/src/common/xtensa_copystate.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <arch/irq.h>
#include "xtensa.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: xtensa_copystate
****************************************************************************/
/* A little faster than most memcpy's */
void xtensa_copystate(uint32_t *dest, uint32_t *src)
{
int i;
/* In the XTENSA model, the state is copied from the stack to the TCB,
* but only a reference is passed to get the state from the TCB. So the
* following check avoids copying the TCB save area onto itself:
*/
if (src != dest)
{
for (i = 0; i < XCPTCONTEXT_REGS; i++)
{
*dest++ = *src++;
}
}
}

View File

@ -295,12 +295,11 @@ void xtensa_dumpstate(void)
if (CURRENT_REGS)
{
memcpy(rtcb->xcp.regs,
(uintptr_t *)CURRENT_REGS, XCPTCONTEXT_SIZE);
rtcb->xcp.regs = (uint32_t *)CURRENT_REGS;
}
else
{
up_saveusercontext(rtcb->xcp.regs);
up_saveusercontext(&rtcb->xcp.regs);
}
/* Dump the registers (if available) */

View File

@ -145,7 +145,7 @@ void up_exit(int status)
/* Then switch contexts */
xtensa_context_restore(tcb->xcp.regs);
xtensa_context_restore(&tcb->xcp.regs);
/* xtensa_context_restore() should not return but could if the
* software interrupts are disabled.

View File

@ -58,6 +58,10 @@ void up_initial_state(struct tcb_s *tcb)
{
struct xcptcontext *xcp = &tcb->xcp;
/* Initialize the initial exception register context structure */
memset(xcp, 0, sizeof(struct xcptcontext));
/* Initialize the idle thread stack */
if (tcb->pid == IDLE_PROCESS_ID)
@ -74,11 +78,18 @@ void up_initial_state(struct tcb_s *tcb)
xtensa_stack_color(tcb->stack_alloc_ptr, 0);
#endif /* CONFIG_STACK_COLORATION */
return;
}
/* Initialize the initial exception register context structure */
/* Initialize the context registers to stack top */
memset(xcp, 0, sizeof(struct xcptcontext));
xcp->regs = (void *)((uint32_t)tcb->stack_base_ptr +
tcb->adj_stack_size -
XCPTCONTEXT_SIZE);
/* Initialize the xcp registers */
memset(xcp->regs, 0, XCPTCONTEXT_SIZE);
/* Set initial values of registers */

View File

@ -137,6 +137,15 @@ g_intstacktop:
.macro dispatch_c_isr level mask tmp
/* If the interrupt stack is disabled, reserve xcpcontext to ensure
* that signal processing can have a separate xcpcontext to handle
* signal context (ref: xtensa_schedulesigaction.c):
*/
#if CONFIG_ARCH_INTERRUPTSTACK < 15
addi sp, sp, -XCPTCONTEXT_SIZE
#endif
/* Set up PS for C, enable interrupts above this level and clear EXCM. */
ps_setup \level \tmp
@ -217,6 +226,10 @@ g_intstacktop:
mov a12, a6 /* Switch to the save area of the new thread */
#endif
#if CONFIG_ARCH_INTERRUPTSTACK < 15
addi sp, sp, XCPTCONTEXT_SIZE
#endif
/* Done */
1:

View File

@ -109,7 +109,7 @@ void up_release_pending(void)
* ready to run list.
*/
xtensa_switchcontext(rtcb->xcp.regs, nexttcb->xcp.regs);
xtensa_switchcontext(&rtcb->xcp.regs, nexttcb->xcp.regs);
/* xtensa_switchcontext forces a context switch to the task at the
* head of the ready-to-run list. It does not 'return' in the

View File

@ -163,7 +163,7 @@ void up_reprioritize_rtr(struct tcb_s *tcb, uint8_t priority)
* ready to run list.
*/
xtensa_switchcontext(rtcb->xcp.regs, nexttcb->xcp.regs);
xtensa_switchcontext(&rtcb->xcp.regs, nexttcb->xcp.regs);
/* xtensa_switchcontext forces a context switch to the task at
* the head of the ready-to-run list. It does not 'return' in

View File

@ -81,6 +81,7 @@
void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
{
sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver);
DEBUGASSERT(tcb != NULL && sigdeliver != NULL);
/* Refuse to handle nested signal actions */
@ -121,20 +122,30 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
else
{
/* Save the return pc and ps. These will be restored by the
/* 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.sigdeliver = sigdeliver;
tcb->xcp.saved_pc = CURRENT_REGS[REG_PC];
tcb->xcp.saved_ps = CURRENT_REGS[REG_PS];
xtensa_savestate(tcb->xcp.saved_regs);
/* Duplicate the register context. These will be
* restored by the signal trampoline after the signal has
* been delivered.
*/
CURRENT_REGS = (void *)((uint32_t)CURRENT_REGS -
XCPTCONTEXT_SIZE);
memcpy((uint32_t *)CURRENT_REGS, tcb->xcp.saved_regs,
XCPTCONTEXT_SIZE);
/* Then set up to vector to the trampoline with interrupts
* disabled
*/
tcb->xcp.sigdeliver = sigdeliver;
CURRENT_REGS[REG_PC] = (uint32_t)_xtensa_sig_trampoline;
#ifdef __XTENSA_CALL0_ABI__
CURRENT_REGS[REG_PS] = (uint32_t)
@ -144,11 +155,8 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
(PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE);
#endif
/* And make sure that the saved context in the TCB is the same
* as the interrupt return context.
*/
xtensa_savestate(tcb->xcp.regs);
CURRENT_REGS[REG_A1] = (uint32_t)CURRENT_REGS +
XCPTCONTEXT_SIZE;
}
}
@ -159,16 +167,26 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
else
{
/* Save the return pc and ps. These will be restored by the
/* 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.sigdeliver = sigdeliver;
tcb->xcp.saved_pc = tcb->xcp.regs[REG_PC];
tcb->xcp.saved_ps = tcb->xcp.regs[REG_PS];
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
*/
@ -250,7 +268,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
/* Now tcb on the other CPU can be accessed safely */
/* Copy tcb->xcp.regs to tcp.xcp.saved. These will be
/* Copy tcb->xcp.regs to tcp.xcp.saved_regs. These will be
* restored by the signal trampoline after the signal has
* been delivered.
*
@ -258,8 +276,21 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
*/
tcb->xcp.sigdeliver = sigdeliver;
tcb->xcp.saved_pc = tcb->xcp.regs[REG_PC];
tcb->xcp.saved_ps = tcb->xcp.regs[REG_PS];
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 -
(uint32_t)XCPTCONTEXT_SIZE);
memcpy(tcb->xcp.regs, tcb->xcp.saved_regs,
XCPTCONTEXT_SIZE);
tcb->xcp.regs[REG_A1] = (uint32_t)tcb->xcp.regs +
(uint32_t)XCPTCONTEXT_SIZE;
/* Then set up to vector to the trampoline with interrupts
* disabled
@ -278,7 +309,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
{
/* tcb is running on the same CPU */
/* Copy tcb->xcp.regs to tcp.xcp.saved. These will be
/* Copy tcb->xcp.regs to tcp.xcp.saved_regs. These will be
* restored by the signal trampoline after the signal has
* been delivered.
*
@ -286,8 +317,21 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
*/
tcb->xcp.sigdeliver = sigdeliver;
tcb->xcp.saved_pc = CURRENT_REGS[REG_PC];
tcb->xcp.saved_ps = CURRENT_REGS[REG_PS];
xtensa_savestate(tcb->xcp.saved_regs);
/* Duplicate the register context. These will be
* restored by the signal trampoline after the signal has
* been delivered.
*/
CURRENT_REGS = (void *)
((uint32_t)CURRENT_REGS -
(uint32_t)XCPTCONTEXT_SIZE);
memcpy((uint32_t *)CURRENT_REGS, tcb->xcp.saved_regs,
XCPTCONTEXT_SIZE);
CURRENT_REGS[REG_A1] = (uint32_t)CURRENT_REGS +
(uint32_t)XCPTCONTEXT_SIZE;
/* Then set up to vector to the trampoline with interrupts
* disabled
@ -301,11 +345,6 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
CURRENT_REGS[REG_PS] = (uint32_t)
(PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE);
#endif
/* And make sure that the saved context in the TCB is the
* same as the interrupt return context.
*/
xtensa_savestate(tcb->xcp.regs);
}
/* Increment the IRQ lock count so that when the task is
@ -338,15 +377,27 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
else
{
/* Save the return pc and ps. These will be restored by the
/* 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.sigdeliver = sigdeliver;
tcb->xcp.saved_pc = tcb->xcp.regs[REG_PC];
tcb->xcp.saved_ps = tcb->xcp.regs[REG_PS];
tcb->xcp.sigdeliver = sigdeliver;
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 -
(uint32_t)XCPTCONTEXT_SIZE);
memcpy(tcb->xcp.regs, tcb->xcp.saved_regs, XCPTCONTEXT_SIZE);
tcb->xcp.regs[REG_A1] = (uint32_t)tcb->xcp.regs +
(uint32_t)XCPTCONTEXT_SIZE;
/* Increment the IRQ lock count so that when the task is restarted,
* it will hold the IRQ spinlock.

View File

@ -54,7 +54,7 @@
void xtensa_sig_deliver(void)
{
struct tcb_s *rtcb = this_task();
uint32_t regs[XCPTCONTEXT_REGS];
uint32_t *regs = rtcb->xcp.saved_regs;
#ifdef CONFIG_SMP
/* In the SMP case, we must terminate the critical section while the signal
@ -71,10 +71,6 @@ void xtensa_sig_deliver(void)
rtcb, rtcb->xcp.sigdeliver, rtcb->sigpendactionq.head);
DEBUGASSERT(rtcb->xcp.sigdeliver != NULL);
/* Save the return state on the stack. */
xtensa_copystate(regs, rtcb->xcp.regs);
#ifdef CONFIG_SMP
/* In the SMP case, up_schedule_sigaction(0) will have incremented
* 'irqcount' in order to force us into a critical section. Save the
@ -141,8 +137,6 @@ void xtensa_sig_deliver(void)
* could be modified by a hostile program.
*/
regs[REG_PC] = rtcb->xcp.saved_pc;
regs[REG_PS] = rtcb->xcp.saved_ps;
rtcb->xcp.sigdeliver = NULL; /* Allows next handler to be scheduled */
/* Issue:
@ -184,5 +178,5 @@ void xtensa_sig_deliver(void)
*/
board_autoled_off(LED_SIGNAL);
xtensa_context_restore(regs);
xtensa_context_restore(&regs);
}

View File

@ -103,7 +103,7 @@ int xtensa_swint(int irq, void *context, void *arg)
case SYS_save_context:
{
DEBUGASSERT(regs[REG_A3] != 0);
memcpy((uint32_t *)regs[REG_A3], regs, (4 * XCPTCONTEXT_REGS));
memcpy(*(uint32_t **)regs[REG_A3], regs, XCPTCONTEXT_SIZE);
#if XCHAL_CP_NUM > 0
cpstate = (uintptr_t)regs[REG_A3] + cpstate_off;
xtensa_coproc_savestate((struct xtensa_cpstate_s *)cpstate);
@ -132,7 +132,7 @@ int xtensa_swint(int irq, void *context, void *arg)
case SYS_restore_context:
{
DEBUGASSERT(regs[REG_A3] != 0);
CURRENT_REGS = (uint32_t *)regs[REG_A3];
CURRENT_REGS = *(uint32_t **)regs[REG_A3];
#if XCHAL_CP_NUM > 0
cpstate = (uintptr_t)regs[REG_A3] + cpstate_off;
xtensa_coproc_restorestate((struct xtensa_cpstate_s *)cpstate);
@ -161,8 +161,7 @@ int xtensa_swint(int irq, void *context, void *arg)
case SYS_switch_context:
{
DEBUGASSERT(regs[REG_A3] != 0 && regs[REG_A4] != 0);
memcpy((uint32_t *)regs[REG_A3], regs, (4 * XCPTCONTEXT_REGS));
*(uint32_t **)regs[REG_A3] = regs;
CURRENT_REGS = (uint32_t *)regs[REG_A4];
}

View File

@ -125,7 +125,7 @@ void up_unblock_task(struct tcb_s *tcb)
* ready to run list.
*/
xtensa_switchcontext(rtcb->xcp.regs, nexttcb->xcp.regs);
xtensa_switchcontext(&rtcb->xcp.regs, nexttcb->xcp.regs);
/* xtensa_switchcontext forces a context switch to the task at the
* head of the ready-to-run list. It does not 'return' in the

View File

@ -31,7 +31,7 @@ HEAD_CSRC = esp32_start.c esp32_wdt.c
CMN_ASRCS = xtensa_context.S xtensa_coproc.S xtensa_cpuint.S xtensa_panic.S
CMN_ASRCS += xtensa_sigtramp.S
CMN_CSRCS = xtensa_assert.c xtensa_blocktask.c xtensa_copystate.c
CMN_CSRCS = xtensa_assert.c xtensa_blocktask.c
CMN_CSRCS += xtensa_cpenable.c xtensa_createstack.c xtensa_exit.c
CMN_CSRCS += xtensa_initialize.c xtensa_initialstate.c xtensa_interruptcontext.c
CMN_CSRCS += xtensa_irqdispatch.c xtensa_lowputs.c xtensa_mdelay.c

View File

@ -135,7 +135,8 @@ void IRAM_ATTR xtensa_appcpu_start(void)
* is to switch to a well-known IDLE thread stack.
*/
sp = (uint32_t)tcb->stack_base_ptr + tcb->adj_stack_size;
sp = (uint32_t)tcb->stack_base_ptr + tcb->adj_stack_size -
XCPTCONTEXT_SIZE;
__asm__ __volatile__("mov sp, %0\n" : : "r"(sp));
sinfo("CPU%d Started\n", up_cpu_index());
@ -208,7 +209,7 @@ void IRAM_ATTR xtensa_appcpu_start(void)
* be the CPUs NULL task.
*/
xtensa_context_restore(tcb->xcp.regs);
xtensa_context_restore(&tcb->xcp.regs);
}
/****************************************************************************

View File

@ -31,7 +31,7 @@ HEAD_CSRC = esp32s2_start.c esp32s2_wdt.c
CMN_ASRCS = xtensa_context.S xtensa_coproc.S xtensa_cpuint.S xtensa_panic.S
CMN_ASRCS += xtensa_sigtramp.S
CMN_CSRCS = xtensa_assert.c xtensa_blocktask.c xtensa_copystate.c
CMN_CSRCS = xtensa_assert.c xtensa_blocktask.c
CMN_CSRCS += xtensa_cpenable.c xtensa_createstack.c xtensa_exit.c
CMN_CSRCS += xtensa_initialize.c xtensa_initialstate.c xtensa_interruptcontext.c
CMN_CSRCS += xtensa_irqdispatch.c xtensa_lowputs.c xtensa_mdelay.c

View File

@ -31,7 +31,7 @@ HEAD_CSRC = esp32s3_start.c
CMN_ASRCS = xtensa_context.S xtensa_coproc.S xtensa_cpuint.S xtensa_panic.S
CMN_ASRCS += xtensa_sigtramp.S
CMN_CSRCS = xtensa_assert.c xtensa_blocktask.c xtensa_copystate.c
CMN_CSRCS = xtensa_assert.c xtensa_blocktask.c
CMN_CSRCS += xtensa_cpenable.c xtensa_createstack.c xtensa_exit.c
CMN_CSRCS += xtensa_initialize.c xtensa_initialstate.c xtensa_interruptcontext.c
CMN_CSRCS += xtensa_irqdispatch.c xtensa_lowputs.c xtensa_mdelay.c

View File

@ -115,7 +115,8 @@ void xtensa_appcpu_start(void)
* is to switch to a well-known IDLE thread stack.
*/
sp = (uint32_t)tcb->stack_base_ptr + tcb->adj_stack_size;
sp = (uint32_t)tcb->stack_base_ptr + tcb->adj_stack_size -
XCPTCONTEXT_SIZE;
__asm__ __volatile__("mov sp, %0\n" : : "r"(sp));
sinfo("CPU%d Started\n", up_cpu_index());
@ -168,7 +169,7 @@ void xtensa_appcpu_start(void)
* be the CPUs NULL task.
*/
xtensa_context_restore(tcb->xcp.regs);
xtensa_context_restore(&tcb->xcp.regs);
}
/****************************************************************************