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:
parent
a9df7f0d1e
commit
542e46319c
@ -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)\
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user