You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by je...@apache.org on 2020/12/10 07:34:09 UTC

[incubator-nuttx] 04/04: sim: Fix interrupt handling for SMP

This is an automated email from the ASF dual-hosted git repository.

jerpelea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit f3a81cb1b70a8ccf024c2deb593b1102aece62c3
Author: Masayuki Ishikawa <ma...@gmail.com>
AuthorDate: Thu Nov 26 20:43:51 2020 +0900

    sim: Fix interrupt handling for SMP
    
    Summary
    - This commit fixes interrupt handling for SMP
    - The following are the changes
    - Introduce up_copyfullstate.c
    - Add enter_critical_section() to up_exit()
    - Add a critical section to up_schedule_sigaction()
    - Introduce pseudo timer thread to send periodic events
    - UART and interval timer are now handled in the pause handler
    - Apply the same SMP related code as other CPU architectures
    - However, signal handling and context switching are not changed
    - Also enable debug features and some tools in smp/defconfig
    
    Imact
    - SMP only
    
    Testing
    - Tested with sim:smp on ubuntu18.04 x86_64
    - Tested with hello, taskset, smp, ostest
    
    Signed-off-by: Masayuki Ishikawa <Ma...@jp.sony.com>
---
 arch/sim/src/Makefile                    |   1 +
 arch/sim/src/sim/up_blocktask.c          |   9 +-
 arch/sim/src/sim/up_copyfullstate.c      |  64 +++++++++
 arch/sim/src/sim/up_exit.c               |   6 +
 arch/sim/src/sim/up_hostirq.c            |   5 +
 arch/sim/src/sim/up_idle.c               |  28 ++++
 arch/sim/src/sim/up_internal.h           |  35 +++--
 arch/sim/src/sim/up_interruptcontext.c   |  54 ++++---
 arch/sim/src/sim/up_releasepending.c     |   9 +-
 arch/sim/src/sim/up_reprioritizertr.c    |   9 +-
 arch/sim/src/sim/up_schedulesigaction.c  |  10 ++
 arch/sim/src/sim/up_simsmp.c             | 131 +++++------------
 arch/sim/src/sim/up_smpsignal.c          | 240 +++++++++++++++++++++++++------
 arch/sim/src/sim/up_unblocktask.c        |  27 +++-
 boards/sim/sim/sim/configs/smp/defconfig |   7 +-
 15 files changed, 459 insertions(+), 176 deletions(-)

diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile
index a6935ef..f21bcbc 100644
--- a/arch/sim/src/Makefile
+++ b/arch/sim/src/Makefile
@@ -74,6 +74,7 @@ CSRCS += up_createstack.c up_usestack.c up_releasestack.c up_stackframe.c
 CSRCS += up_unblocktask.c up_blocktask.c up_releasepending.c
 CSRCS += up_reprioritizertr.c up_exit.c up_schedulesigaction.c
 CSRCS += up_allocateheap.c up_uart.c
+CSRCS += up_copyfullstate.c
 
 VPATH = sim
 DEPPATH = $(patsubst %,--dep-path %,$(subst :, ,$(VPATH)))
diff --git a/arch/sim/src/sim/up_blocktask.c b/arch/sim/src/sim/up_blocktask.c
index e81df88..8edf230 100644
--- a/arch/sim/src/sim/up_blocktask.c
+++ b/arch/sim/src/sim/up_blocktask.c
@@ -113,12 +113,19 @@ void up_block_task(struct tcb_s *tcb, tstate_t task_state)
 
       nxsched_suspend_scheduler(rtcb);
 
+      /* TODO */
+
+      if (CURRENT_REGS)
+        {
+          ASSERT(false);
+        }
+
       /* Copy the exception context into the TCB at the (old) head of the
        * ready-to-run Task list. if up_setjmp returns a non-zero
        * value, then this is really the previously running task restarting!
        */
 
-      if (!up_setjmp(rtcb->xcp.regs))
+      else if (!up_setjmp(rtcb->xcp.regs))
         {
           /* Restore the exception context of the rtcb at the (new) head
            * of the ready-to-run task list.
diff --git a/arch/sim/src/sim/up_copyfullstate.c b/arch/sim/src/sim/up_copyfullstate.c
new file mode 100644
index 0000000..25b2bc4
--- /dev/null
+++ b/arch/sim/src/sim/up_copyfullstate.c
@@ -0,0 +1,64 @@
+/****************************************************************************
+ * arch/sim/src/sim/sim_copyfullstate.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 "up_internal.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_copyfullstate
+ *
+ * Description:
+ *    Copy the entire register save area
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_HOST_X86_64) && !defined(CONFIG_SIM_M32)
+void up_copyfullstate(unsigned long *dest, unsigned long *src)
+#else
+void up_copyfullstate(uint32_t *dest, uint32_t *src)
+#endif
+{
+  int i;
+
+  /* In the sim 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++;
+        }
+    }
+}
diff --git a/arch/sim/src/sim/up_exit.c b/arch/sim/src/sim/up_exit.c
index 8cac071..400a3ee 100644
--- a/arch/sim/src/sim/up_exit.c
+++ b/arch/sim/src/sim/up_exit.c
@@ -67,6 +67,12 @@ void up_exit(int status)
 {
   FAR struct tcb_s *tcb;
 
+  /* Make sure that we are in a critical section with local interrupts.
+   * The IRQ state will be restored when the next task is started.
+   */
+
+  enter_critical_section();
+
   sinfo("TCB=%p exiting\n", this_task());
 
   /* Destroy the task at the head of the ready to run list. */
diff --git a/arch/sim/src/sim/up_hostirq.c b/arch/sim/src/sim/up_hostirq.c
index b929620..132bc9e 100644
--- a/arch/sim/src/sim/up_hostirq.c
+++ b/arch/sim/src/sim/up_hostirq.c
@@ -111,6 +111,11 @@ void up_irq_restore(uint64_t flags)
 
 void up_irqinitialize(void)
 {
+#ifdef CONFIG_SMP
+  /* Register the pause handler */
+
+  up_cpu_set_pause_handler(SIGUSR1);
+#endif
 }
 
 /****************************************************************************
diff --git a/arch/sim/src/sim/up_idle.c b/arch/sim/src/sim/up_idle.c
index a17d8d3..b5c5891 100644
--- a/arch/sim/src/sim/up_idle.c
+++ b/arch/sim/src/sim/up_idle.c
@@ -70,6 +70,7 @@
  *
  ****************************************************************************/
 
+#ifndef CONFIG_SMP
 void up_idle(void)
 {
 #ifdef CONFIG_PM
@@ -124,3 +125,30 @@ void up_idle(void)
   up_timer_update();
 #endif
 }
+#endif /* !CONFIG_SMP */
+
+#ifdef CONFIG_SMP
+void up_idle(void)
+{
+  host_sleep(100 * 1000);
+}
+#endif
+
+/****************************************************************************
+ * Name: sim_timer_handler
+ ****************************************************************************/
+
+#ifdef CONFIG_SMP
+void sim_timer_handler(void)
+{
+  /* Handle UART data availability */
+
+  up_uartloop();
+
+#ifdef CONFIG_ONESHOT
+  /* Driver the simulated interval timer */
+
+  up_timer_update();
+#endif
+}
+#endif /* CONFIG_SMP */
diff --git a/arch/sim/src/sim/up_internal.h b/arch/sim/src/sim/up_internal.h
index 48ea33f..3367d78 100644
--- a/arch/sim/src/sim/up_internal.h
+++ b/arch/sim/src/sim/up_internal.h
@@ -128,6 +128,11 @@
 
 #define SIM_HEAP_SIZE (64*1024*1024)
 
+/* Macros to handle saving and restoring interrupt state ********************/
+
+#define up_savestate(regs) up_copyfullstate(regs, (xcpt_reg_t *)CURRENT_REGS)
+#define up_restorestate(regs) (CURRENT_REGS = regs)
+
 /* File System Definitions **************************************************/
 
 /* These definitions characterize the compressed filesystem image */
@@ -188,29 +193,18 @@ extern volatile void *g_current_regs[1];
 
 #endif
 
-#ifdef CONFIG_SMP
-/* These spinlocks are used in the SMP configuration in order to implement
- * up_cpu_pause().  The protocol for CPUn to pause CPUm is as follows
- *
- * 1. The up_cpu_pause() implementation on CPUn locks both g_cpu_wait[m]
- *    and g_cpu_paused[m].  CPUn then waits spinning on g_cpu_paused[m].
- * 2. CPUm receives the interrupt it (1) unlocks g_cpu_paused[m] and
- *    (2) locks g_cpu_wait[m].  The first unblocks CPUn and the second
- *    blocks CPUm in the interrupt handler.
- *
- * When CPUm resumes, CPUn unlocks g_cpu_wait[m] and the interrupt handler
- * on CPUm continues.  CPUm must, of course, also then unlock g_cpu_wait[m]
- * so that it will be ready for the next pause operation.
- */
-
-extern volatile uint8_t g_cpu_wait[CONFIG_SMP_NCPUS];
-extern volatile uint8_t g_cpu_paused[CONFIG_SMP_NCPUS];
-#endif
-
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/
 
+/* Context switching */
+
+#if defined(CONFIG_HOST_X86_64) && !defined(CONFIG_SIM_M32)
+void up_copyfullstate(unsigned long *dest, unsigned long *src);
+#else
+void up_copyfullstate(uint32_t *dest, uint32_t *src);
+#endif
+
 void *up_doirq(int irq, void *regs);
 
 /* up_setjmp32.S ************************************************************/
@@ -244,6 +238,9 @@ void up_cpu_started(void);
 int up_cpu_paused(int cpu);
 struct tcb_s *up_this_task(void);
 int up_cpu_set_pause_handler(int irq);
+void sim_send_ipi(int cpu);
+void sim_timer_handler(void);
+void sim_sigdeliver(void);
 #endif
 
 /* up_oneshot.c *************************************************************/
diff --git a/arch/sim/src/sim/up_interruptcontext.c b/arch/sim/src/sim/up_interruptcontext.c
index 7a3a9e4..853a779 100644
--- a/arch/sim/src/sim/up_interruptcontext.c
+++ b/arch/sim/src/sim/up_interruptcontext.c
@@ -66,31 +66,53 @@ bool up_interrupt_context(void)
  * Name: up_doirq
  ****************************************************************************/
 
-void *up_doirq(int irq, void *regs)
+void *up_doirq(int irq, void *context)
 {
-  /* Current regs non-zero indicates that we are processing an interrupt;
+  /* Allocate temporary context on the stack */
+
+  xcpt_reg_t tmp[XCPTCONTEXT_REGS];
+  void *regs = (void *)tmp;
+
+  /* CURRENT_REGS non-zero indicates that we are processing an interrupt.
    * CURRENT_REGS is also used to manage interrupt level context switches.
    */
 
-  CURRENT_REGS = regs;
+#ifdef CONFIG_SMP
+  if (up_setjmp(regs) == 0)
+    {
+#endif
 
-  /* Deliver the IRQ */
+      CURRENT_REGS = regs;
 
-  irq_dispatch(irq, regs);
+      /* Deliver the IRQ */
 
-  /* If a context switch occurred while processing the interrupt then
-   * CURRENT_REGS may have change value.  If we return any value different
-   * from the input regs, then the lower level will know that a context
-   * switch occurred during interrupt processing.
-   */
+      irq_dispatch(irq, regs);
 
-  regs = (void *)CURRENT_REGS;
+      /* If a context switch occurred while processing the interrupt then
+       * CURRENT_REGS may have change value.  If we return any value
+       * different from the input regs, then the lower level will know that
+       * context switch occurred during interrupt processing.
+       */
 
-  /* Restore the previous value of CURRENT_REGS.  NULL would indicate that
-   * we are no longer in an interrupt handler.  It will be non-NULL if we
-   * are returning from a nested interrupt.
-   */
+      regs = (void *)CURRENT_REGS;
+
+      /* Restore the previous value of CURRENT_REGS.  NULL would indicate
+       * that we are no longer in an interrupt handler.  It will be non-NULL
+       * if we are returning from a nested interrupt.
+       */
+
+      CURRENT_REGS = NULL;
+
+#ifdef CONFIG_SMP
+      /* Handle signal */
+
+      sim_sigdeliver();
+
+      /* Then switch contexts */
+
+      up_longjmp(regs, 1);
+    }
+#endif
 
-  CURRENT_REGS = NULL;
   return regs;
 }
diff --git a/arch/sim/src/sim/up_releasepending.c b/arch/sim/src/sim/up_releasepending.c
index b1c1fb9..77210d2 100644
--- a/arch/sim/src/sim/up_releasepending.c
+++ b/arch/sim/src/sim/up_releasepending.c
@@ -82,12 +82,19 @@ void up_release_pending(void)
 
       nxsched_suspend_scheduler(rtcb);
 
+      /* TODO */
+
+      if (CURRENT_REGS)
+        {
+          ASSERT(false);
+        }
+
       /* Copy the exception context into the TCB of the task that was
        * currently active. if up_setjmp returns a non-zero value, then
        * this is really the previously running task restarting!
        */
 
-      if (!up_setjmp(rtcb->xcp.regs))
+      else if (!up_setjmp(rtcb->xcp.regs))
         {
           /* Restore the exception context of the rtcb at the (new) head
            * of the ready-to-run task list.
diff --git a/arch/sim/src/sim/up_reprioritizertr.c b/arch/sim/src/sim/up_reprioritizertr.c
index c4ce317..7d529d2 100644
--- a/arch/sim/src/sim/up_reprioritizertr.c
+++ b/arch/sim/src/sim/up_reprioritizertr.c
@@ -136,13 +136,20 @@ void up_reprioritize_rtr(struct tcb_s *tcb, uint8_t priority)
 
           nxsched_suspend_scheduler(rtcb);
 
+          if (CURRENT_REGS)
+            {
+              /* TODO */
+
+              ASSERT(false);
+            }
+
           /* Copy the exception context into the TCB at the (old) head of the
            * ready-to-run Task list. if up_setjmp returns a non-zero
            * value, then this is really the previously running task
            * restarting!
            */
 
-          if (!up_setjmp(rtcb->xcp.regs))
+          else if (!up_setjmp(rtcb->xcp.regs))
             {
               /* Restore the exception context of the rtcb at the (new) head
                * of the ready-to-run task list.
diff --git a/arch/sim/src/sim/up_schedulesigaction.c b/arch/sim/src/sim/up_schedulesigaction.c
index f624e1b..b95d10e 100644
--- a/arch/sim/src/sim/up_schedulesigaction.c
+++ b/arch/sim/src/sim/up_schedulesigaction.c
@@ -86,8 +86,16 @@
 
 void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
 {
+  irqstate_t flags;
+
   /* We don't have to anything complex for the simulated target */
 
+  sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver);
+
+  /* Make sure that interrupts are disabled */
+
+  flags = enter_critical_section();
+
   if (tcb == this_task())
     {
       sigdeliver(tcb);
@@ -96,4 +104,6 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
     {
       tcb->xcp.sigdeliver = sigdeliver;
     }
+
+  leave_critical_section(flags);
 }
diff --git a/arch/sim/src/sim/up_simsmp.c b/arch/sim/src/sim/up_simsmp.c
index 478b4d7..bbe7f80 100644
--- a/arch/sim/src/sim/up_simsmp.c
+++ b/arch/sim/src/sim/up_simsmp.c
@@ -66,22 +66,7 @@ struct sim_cpuinfo_s
 static pthread_key_t g_cpu_key;
 static pthread_t     g_cpu_thread[CONFIG_SMP_NCPUS];
 
-/* These spinlocks are used in the SMP configuration in order to implement
- * up_cpu_pause().  The protocol for CPUn to pause CPUm is as follows
- *
- * 1. The up_cpu_pause() implementation on CPUn locks both g_cpu_wait[m]
- *    and g_cpu_paused[m].  CPUn then waits spinning on g_cpu_paused[m].
- * 2. CPUm receives the interrupt it (1) unlocks g_cpu_paused[m] and
- *    (2) locks g_cpu_wait[m].  The first unblocks CPUn and the second
- *    blocks CPUm in the interrupt handler.
- *
- * When CPUm resumes, CPUn unlocks g_cpu_wait[m] and the interrupt handler
- * on CPUm continues.  CPUm must, of course, also then unlock g_cpu_wait[m]
- * so that it will be ready for the next pause operation.
- */
-
-volatile uint8_t g_cpu_wait[CONFIG_SMP_NCPUS];
-volatile uint8_t g_cpu_paused[CONFIG_SMP_NCPUS];
+static pthread_t     g_timer_thread;
 
 /****************************************************************************
  * NuttX domain function prototypes
@@ -93,6 +78,10 @@ void sched_note_cpu_pause(struct tcb_s *tcb, int cpu);
 void sched_note_cpu_resume(struct tcb_s *tcb, int cpu);
 #endif
 
+void up_irqinitialize(void);
+
+extern uint8_t g_nx_initstate;
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -134,9 +123,9 @@ static void *sim_idle_trampoline(void *arg)
       return NULL;
     }
 
-  /* Make sure the SIGUSR1 is not masked */
+  /* Initialize IRQ */
 
-  up_cpu_set_pause_handler(SIGUSR1);
+  up_irqinitialize();
 
   /* Let up_cpu_start() continue */
 
@@ -173,6 +162,30 @@ static void *sim_idle_trampoline(void *arg)
 }
 
 /****************************************************************************
+ * Name: sim_host_timer_handler
+ ****************************************************************************/
+
+static void *sim_host_timer_handler(void *arg)
+{
+  /* Wait until OSINIT_OSREADY(5) */
+
+  while (g_nx_initstate < 5)
+    {
+      host_sleep(10 * 1000); /* 10ms */
+    }
+
+  /* Send a periodic timer event to CPU0 */
+
+  while (1)
+    {
+      pthread_kill(g_cpu_thread[0], SIGUSR1);
+      host_sleep(10 * 1000); /* 10ms */
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
  * Public Functions
  ****************************************************************************/
 
@@ -213,9 +226,12 @@ void sim_cpu0_start(void)
       return;
     }
 
-  /* Register the common signal handler for all threads */
+  /* NOTE: IRQ initialization will be done in up_irqinitialize */
 
-  up_cpu_set_pause_handler(SIGUSR1);
+  /* Create timer thread to send a periodic timer event */
+
+  ret = pthread_create(&g_timer_thread,
+                       NULL, sim_host_timer_handler, NULL);
 }
 
 /****************************************************************************
@@ -316,81 +332,10 @@ errout_with_cond:
 }
 
 /****************************************************************************
- * Name: up_cpu_pause
- *
- * Description:
- *   Save the state of the current task at the head of the
- *   g_assignedtasks[cpu] task list and then pause task execution on the
- *   CPU.
- *
- *   This function is called by the OS when the logic executing on one CPU
- *   needs to modify the state of the g_assignedtasks[cpu] list for another
- *   CPU.
- *
- * Input Parameters:
- *   cpu - The index of the CPU to be stopped/
- *
- * Returned Value:
- *   Zero on success; a negated errno value on failure.
- *
+ * Name: sim_send_ipi(int cpu)
  ****************************************************************************/
 
-int up_cpu_pause(int cpu)
+void sim_send_ipi(int cpu)
 {
-#ifdef CONFIG_SCHED_INSTRUMENTATION
-  /* Notify of the pause event */
-
-  sched_note_cpu_pause(up_this_task(), cpu);
-#endif
-
-  /* Take the spinlock that will prevent the CPU thread from running */
-
-  g_cpu_wait[cpu]   = 1;
-  g_cpu_paused[cpu] = 1;
-
-  /* Signal the CPU thread */
-
   pthread_kill(g_cpu_thread[cpu], SIGUSR1);
-
-  /* Spin, waiting for the thread to pause */
-
-  while (g_cpu_paused[cpu] != 0)
-    {
-      sched_yield();
-    }
-
-  return 0;
-}
-
-/****************************************************************************
- * Name: up_cpu_resume
- *
- * Description:
- *   Restart the cpu after it was paused via up_cpu_pause(), restoring the
- *   state of the task at the head of the g_assignedtasks[cpu] list, and
- *   resume normal tasking.
- *
- *   This function is called after up_cpu_pause in order resume operation of
- *   the CPU after modifying its g_assignedtasks[cpu] list.
- *
- * Input Parameters:
- *   cpu - The index of the CPU being re-started.
- *
- * Returned Value:
- *   Zero on success; a negated errno value on failure.
- *
- ****************************************************************************/
-
-int up_cpu_resume(int cpu)
-{
-#ifdef CONFIG_SCHED_INSTRUMENTATION
-  /* Notify of the resume event */
-
-  sched_note_cpu_resume(up_this_task(), cpu);
-#endif
-
-  /* Release the spinlock that will alloc the CPU thread to continue */
-
-  g_cpu_wait[cpu] = 0;
-  return 0;
 }
diff --git a/arch/sim/src/sim/up_smpsignal.c b/arch/sim/src/sim/up_smpsignal.c
index 7d9baca..86cf1e7 100644
--- a/arch/sim/src/sim/up_smpsignal.c
+++ b/arch/sim/src/sim/up_smpsignal.c
@@ -46,6 +46,23 @@
 #include "sched/sched.h"
 #include "up_internal.h"
 
+/* These spinlocks are used in the SMP configuration in order to implement
+ * up_cpu_pause().  The protocol for CPUn to pause CPUm is as follows
+ *
+ * 1. The up_cpu_pause() implementation on CPUn locks both g_cpu_wait[m]
+ *    and g_cpu_paused[m].  CPUn then waits spinning on g_cpu_paused[m].
+ * 2. CPUm receives the interrupt it (1) unlocks g_cpu_paused[m] and
+ *    (2) locks g_cpu_wait[m].  The first unblocks CPUn and the second
+ *    blocks CPUm in the interrupt handler.
+ *
+ * When CPUm resumes, CPUn unlocks g_cpu_wait[m] and the interrupt handler
+ * on CPUm continues.  CPUm must, of course, also then unlock g_cpu_wait[m]
+ * so that it will be ready for the next pause operation.
+ */
+
+static volatile spinlock_t g_cpu_wait[CONFIG_SMP_NCPUS];
+static volatile spinlock_t g_cpu_paused[CONFIG_SMP_NCPUS];
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -69,7 +86,41 @@
 
 static int sim_cpupause_handler(int irq, FAR void *context, FAR void *arg)
 {
-  return up_cpu_paused(this_cpu());
+  int cpu = this_cpu();
+
+  /* Check for false alarms.  Such false could occur as a consequence of
+   * some deadlock breaking logic that might have already serviced the SG2
+   * interrupt by calling up_cpu_paused().  If the pause event has already
+   * been processed then g_cpu_paused[cpu] will not be locked.
+   */
+
+  if (up_cpu_pausereq(cpu))
+    {
+      /* NOTE: The following enter_critical_section() will call
+       * up_cpu_paused() to process a pause request to break a deadlock
+       * because the caller held a critical section. Once up_cpu_paused()
+       * finished, the caller will proceed and release the g_cpu_irqlock.
+       * Then this CPU will acquire g_cpu_irqlock in the function.
+       */
+
+      irqstate_t flags = enter_critical_section();
+
+      /* NOTE: the pause request should not exist here */
+
+      DEBUGVERIFY(!up_cpu_pausereq(cpu));
+
+      leave_critical_section(flags);
+    }
+  else
+    {
+      /* NOTE: sim specific logic
+       * In the case of no pause request, call sim_timer_handler()
+       */
+
+      sim_timer_handler();
+    }
+
+  return OK;
 }
 
 /****************************************************************************
@@ -125,72 +176,56 @@ bool up_cpu_pausereq(int cpu)
 
 int up_cpu_paused(int cpu)
 {
-  struct tcb_s *rtcb = current_task(cpu);
+  struct tcb_s *tcb = current_task(cpu);
 
   /* Update scheduler parameters */
 
-  nxsched_suspend_scheduler(rtcb);
+  nxsched_suspend_scheduler(tcb);
 
 #ifdef CONFIG_SCHED_INSTRUMENTATION
   /* Notify that we are paused */
 
-  sched_note_cpu_paused(rtcb);
+  sched_note_cpu_paused(tcb);
 #endif
 
-  /* Copy the exception context into the TCB at the (old) head of the
-   * CPUs assigned task list. if up_setjmp returns a non-zero value, then
-   * this is really the previously running task restarting!
+  /* Save the current context at CURRENT_REGS into the TCB at the head
+   * of the assigned task list for this CPU.
    */
 
-  if (up_setjmp(rtcb->xcp.regs) == 0)
-    {
-      /* Unlock the g_cpu_paused spinlock to indicate that we are in the
-       * paused state
-       */
-
-      spin_unlock(&g_cpu_paused[cpu]);
+  up_savestate(tcb->xcp.regs);
 
-      /* Spin until we are asked to resume.  When we resume, we need to
-       * inicate that we are not longer paused.
-       */
+  /* Wait for the spinlock to be released */
 
-      spin_lock(&g_cpu_wait[cpu]);
-      spin_unlock(&g_cpu_wait[cpu]);
+  spin_unlock(&g_cpu_paused[cpu]);
+  spin_lock(&g_cpu_wait[cpu]);
 
-      /* While we were paused, logic on a different CPU probably changed
-       * the task as that head of the assigned task list.  So now we need
-       * restore the exception context of the rtcb at the (new) head
-       * of the assigned list in order to instantiate the new task.
-       */
-
-      rtcb = current_task(cpu);
+  /* Restore the exception context of the tcb at the (new) head of the
+   * assigned task list.
+   */
 
-      /* The way that we handle signals in the simulation is kind of a
-       * kludge.  This would be unsafe in a truly multi-threaded,
-       * interrupt driven environment.
-       */
+  tcb = current_task(cpu);
 
-      if (rtcb->xcp.sigdeliver)
-        {
-          sinfo("CPU%d: Delivering signals TCB=%p\n", cpu, rtcb);
-          ((sig_deliver_t)rtcb->xcp.sigdeliver)(rtcb);
-          rtcb->xcp.sigdeliver = NULL;
-        }
+  /* The way that we handle signals in the simulation is kind of a
+   * kludge.  This would be unsafe in a truly multi-threaded,
+   * interrupt driven environment.
+   */
 
 #ifdef CONFIG_SCHED_INSTRUMENTATION
-      /* Notify that we have resumed */
+  /* Notify that we have resumed */
 
-      sched_note_cpu_resumed(rtcb);
+  sched_note_cpu_resumed(tcb);
 #endif
 
-      /* Reset scheduler parameters */
+  /* Reset scheduler parameters */
 
-      nxsched_resume_scheduler(rtcb);
+  nxsched_resume_scheduler(tcb);
 
-      /* Then switch contexts */
+  /* Then switch contexts.  Any necessary address environment changes
+   * will be made when the interrupt returns.
+   */
 
-      up_longjmp(rtcb->xcp.regs, 1);
-    }
+  up_restorestate(tcb->xcp.regs);
+  spin_unlock(&g_cpu_wait[cpu]);
 
   return OK;
 }
@@ -249,3 +284,122 @@ int up_cpu_set_pause_handler(int irq)
   up_enable_irq(irq);
   return irq_attach(irq, sim_cpupause_handler, NULL);
 }
+
+/****************************************************************************
+ * Name: up_cpu_pause
+ *
+ * Description:
+ *   Save the state of the current task at the head of the
+ *   g_assignedtasks[cpu] task list and then pause task execution on the
+ *   CPU.
+ *
+ *   This function is called by the OS when the logic executing on one CPU
+ *   needs to modify the state of the g_assignedtasks[cpu] list for another
+ *   CPU.
+ *
+ * Input Parameters:
+ *   cpu - The index of the CPU to be stopped
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int up_cpu_pause(int cpu)
+{
+  DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu());
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+  /* Notify of the pause event */
+
+  sched_note_cpu_pause(this_task(), cpu);
+#endif
+
+  /* Take the both spinlocks.  The g_cpu_wait spinlock will prevent the
+   * handler from returning until up_cpu_resume() is called; g_cpu_paused
+   * is a handshake that will prefent this function from returning until
+   * the CPU is actually paused.
+   */
+
+  DEBUGASSERT(!spin_islocked(&g_cpu_wait[cpu]) &&
+              !spin_islocked(&g_cpu_paused[cpu]));
+
+  spin_lock(&g_cpu_wait[cpu]);
+  spin_lock(&g_cpu_paused[cpu]);
+
+  /* Generate IRQ for CPU(cpu) */
+
+  sim_send_ipi(cpu);
+
+  /* Wait for the other CPU to unlock g_cpu_paused meaning that
+   * it is fully paused and ready for up_cpu_resume();
+   */
+
+  spin_lock(&g_cpu_paused[cpu]);
+  spin_unlock(&g_cpu_paused[cpu]);
+
+  /* On successful return g_cpu_wait will be locked, the other CPU will be
+   * spinning on g_cpu_wait and will not continue until g_cpu_resume() is
+   * called.  g_cpu_paused will be unlocked in any case.
+   */
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_cpu_resume
+ *
+ * Description:
+ *   Restart the cpu after it was paused via up_cpu_pause(), restoring the
+ *   state of the task at the head of the g_assignedtasks[cpu] list, and
+ *   resume normal tasking.
+ *
+ *   This function is called after up_cpu_pause in order resume operation of
+ *   the CPU after modifying its g_assignedtasks[cpu] list.
+ *
+ * Input Parameters:
+ *   cpu - The index of the CPU being re-started.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int up_cpu_resume(int cpu)
+{
+  DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu());
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+  /* Notify of the resume event */
+
+  sched_note_cpu_resume(this_task(), cpu);
+#endif
+
+  /* Release the spinlock.  Releasing the spinlock will cause the SGI2
+   * handler on 'cpu' to continue and return from interrupt to the newly
+   * established thread.
+   */
+
+  DEBUGASSERT(spin_islocked(&g_cpu_wait[cpu]) &&
+              !spin_islocked(&g_cpu_paused[cpu]));
+
+  spin_unlock(&g_cpu_wait[cpu]);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sim_sigdeliver
+ ****************************************************************************/
+
+void sim_sigdeliver(void)
+{
+  int cpu = this_cpu();
+  struct tcb_s *tcb = current_task(cpu);
+
+  if (tcb->xcp.sigdeliver)
+    {
+      sinfo("Delivering signals TCB=%p\n", tcb);
+      ((sig_deliver_t)tcb->xcp.sigdeliver)(tcb);
+      tcb->xcp.sigdeliver = NULL;
+    }
+}
diff --git a/arch/sim/src/sim/up_unblocktask.c b/arch/sim/src/sim/up_unblocktask.c
index 5f739bc..341608c 100644
--- a/arch/sim/src/sim/up_unblocktask.c
+++ b/arch/sim/src/sim/up_unblocktask.c
@@ -96,12 +96,37 @@ void up_unblock_task(FAR struct tcb_s *tcb)
 
       nxsched_suspend_scheduler(rtcb);
 
+      /* Are we in an interrupt handler? */
+
+      if (CURRENT_REGS)
+        {
+          /* Yes, then we have to do things differently.
+           * Just copy the CURRENT_REGS into the OLD rtcb.
+           */
+
+          up_savestate(rtcb->xcp.regs);
+
+          /* Restore the exception context of the rtcb at the (new) head
+           * of the ready-to-run task list.
+           */
+
+          rtcb = this_task();
+
+          /* Update scheduler parameters */
+
+          nxsched_resume_scheduler(rtcb);
+
+          /* Then switch contexts */
+
+          up_restorestate(rtcb->xcp.regs);
+        }
+
       /* Copy the exception context into the TCB of the task that was
        * previously active.  if up_setjmp returns a non-zero value, then
        * this is really the previously running task restarting!
        */
 
-      if (!up_setjmp(rtcb->xcp.regs))
+      else if (!up_setjmp(rtcb->xcp.regs))
         {
           /* Restore the exception context of the new task that is ready to
            * run (probably tcb).  This is the new rtcb at the head of the
diff --git a/boards/sim/sim/sim/configs/smp/defconfig b/boards/sim/sim/sim/configs/smp/defconfig
index 8c916e7..cbc3554 100644
--- a/boards/sim/sim/sim/configs/smp/defconfig
+++ b/boards/sim/sim/sim/configs/smp/defconfig
@@ -13,6 +13,9 @@ CONFIG_ARCH_CHIP="sim"
 CONFIG_ARCH_SIM=y
 CONFIG_BOARDCTL_POWEROFF=y
 CONFIG_BUILTIN=y
+CONFIG_DEBUG_ASSERTIONS=y
+CONFIG_DEBUG_ERROR=y
+CONFIG_DEBUG_FEATURES=y
 CONFIG_DEBUG_SYMBOLS=y
 CONFIG_DRIVER_NOTE=y
 CONFIG_EXAMPLES_HELLO=y
@@ -23,9 +26,11 @@ CONFIG_NSH_READLINE=y
 CONFIG_READLINE_CMD_HISTORY=y
 CONFIG_SCHED_HAVE_PARENT=y
 CONFIG_SCHED_INSTRUMENTATION=y
-CONFIG_SCHED_WAITPID=y
 CONFIG_SMP=y
 CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_SYSTEM=y
+CONFIG_SYSTEM_TASKSET=y
+CONFIG_TESTING_GETPRIME=y
 CONFIG_TESTING_OSTEST=y
 CONFIG_TESTING_SMP=y
 CONFIG_USER_ENTRYPOINT="nsh_main"