diff --git a/arch/arm/src/armv7-a/arm_cpupause.c b/arch/arm/src/armv7-a/arm_cpupause.c index 6b9bc96df37..585d574b06c 100644 --- a/arch/arm/src/armv7-a/arm_cpupause.c +++ b/arch/arm/src/armv7-a/arm_cpupause.c @@ -55,7 +55,8 @@ * Private Data ****************************************************************************/ -static spinlock_t g_pause_spinlock[CONFIG_SMP_NCPUS]; +static spinlock_t g_cpu_wait[CONFIG_SMP_NCPUS]; +static spinlock_t g_cpu_paused[CONFIG_SMP_NCPUS]; /**************************************************************************** * Public Functions @@ -98,7 +99,8 @@ int arm_pause_handler(int irq, FAR void *context) /* Wait for the spinlock to be released */ - spin_lock(&g_pause_spinlock[cpu]); + spin_unlock(&g_cpu_paused[cpu]); + spin_lock(&g_cpu_wait[cpu]); /* Restore the exception context of the tcb at the (new) head of the * assigned task list. @@ -115,7 +117,7 @@ int arm_pause_handler(int irq, FAR void *context) */ up_restorestate(tcb->xcp.regs); - spin_unlock(&g_pause_spinlock[cpu]); + spin_unlock(&g_cpu_wait[cpu]); return OK; } @@ -141,18 +143,48 @@ int arm_pause_handler(int irq, FAR void *context) int up_cpu_pause(int cpu) { + int ret; + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu()); - /* Take the spinlock. The spinlock will cause the SGI2 handler to block - * on 'cpu'. + /* Take the both spinlocks. The g_cpu_wait spinlock will prevent the SGI2 + * 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_pause_spinlock[cpu])); - spin_lock(&g_pause_spinlock[cpu]); + 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]); /* Execute SGI2 */ - return arm_cpu_sgi(GIC_IRQ_SGI2, (1 << cpu)); + ret = arm_cpu_sgi(GIC_IRQ_SGI2, (1 << cpu)); + if (ret < 0) + { + /* What happened? Unlock the g_cpu_wait spinlock */ + + spin_unlock(&g_cpu_wait[cpu]); + } + else + { + /* 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 + * spinninf on g_cpu_wait and will not continue until g_cpu_resume() is + * called. g_cpu_paused will be unlocked in any case. + */ + + return ret; } /**************************************************************************** @@ -183,8 +215,10 @@ int up_cpu_resume(int cpu) * established thread. */ - DEBUGASSERT(spin_islocked(&g_pause_spinlock[cpu])); - spin_unlock(&g_pause_spinlock[cpu]); + DEBUGASSERT(spin_islocked(&g_cpu_wait[cpu]) && + !spin_islocked(&g_cpu_paused[cpu])); + + spin_unlock(&g_cpu_wait[cpu]); return OK; } diff --git a/arch/arm/src/armv7-a/arm_cpustart.c b/arch/arm/src/armv7-a/arm_cpustart.c index 302409982ec..d5d0fe99bc6 100644 --- a/arch/arm/src/armv7-a/arm_cpustart.c +++ b/arch/arm/src/armv7-a/arm_cpustart.c @@ -108,14 +108,13 @@ int arm_start_handler(int irq, FAR void *context) * cpu - The index of the CPU being started. This will be a numeric * value in the range of from one to (CONFIG_SMP_NCPUS-1). (CPU * 0 is already active) - * idletask - The entry point to the IDLE task. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ -int up_cpu_start(int cpu, main_t idletask) +int up_cpu_start(int cpu) { DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu()); diff --git a/arch/arm/src/armv7-a/arm_gic.c b/arch/arm/src/armv7-a/arm_gic.c index 641c7f6d11a..0c40ca8898c 100644 --- a/arch/arm/src/armv7-a/arm_gic.c +++ b/arch/arm/src/armv7-a/arm_gic.c @@ -426,7 +426,7 @@ int arm_cpu_sgi(int sgi, unsigned int cpuset) DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS); -#if CONFIG_SMP +#ifdef CONFIG_SMP regval = GIC_ICDSGIR_INTID(sgi) | GIC_ICDSGIR_CPUTARGET(cpuset) | GIC_ICDSGIR_TGTFILTER_LIST; #else diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 5b659479012..5ae386c00b7 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -70,7 +70,7 @@ ifeq ($(CONFIG_SPINLOCK),y) endif ifeq ($(CONFIG_SMP),y) - CSRCS += up_smpsignal.c + CSRCS += up_smpsignal.c up_smphook.c HOSTSRCS += up_simsmp.c endif @@ -181,7 +181,7 @@ endif endif ifeq ($(CONFIG_SMP),y) - REQUIREDOBJS += up_smpsignal$(OBJEXT) + REQUIREDOBJS += up_smpsignal$(OBJEXT) up_smphook$(OBJEXT) endif # Determine which NuttX libraries will need to be linked in diff --git a/arch/sim/src/up_internal.h b/arch/sim/src/up_internal.h index 4768407c796..e1e69dc25bd 100644 --- a/arch/sim/src/up_internal.h +++ b/arch/sim/src/up_internal.h @@ -228,6 +228,12 @@ void sim_cpu_pause(int cpu, FAR volatile spinlock_t *wait, FAR volatile unsigned char *paused); #endif +/* up_smphook.c ***********************************************************/ + +#ifdef CONFIG_SMP +void sim_smp_hook(void); +#endif + /* up_tickless.c **********************************************************/ #ifdef CONFIG_SCHED_TICKLESS diff --git a/arch/sim/src/up_simsmp.c b/arch/sim/src/up_simsmp.c index 6ad52069e6b..7ff5cdb6d0d 100644 --- a/arch/sim/src/up_simsmp.c +++ b/arch/sim/src/up_simsmp.c @@ -70,7 +70,6 @@ struct sim_cpuinfo_s { int cpu; /* CPU number */ pthread_mutex_t mutex; /* For synchronization */ - main_t idletask; /* IDLE task entry point */ }; /**************************************************************************** @@ -89,6 +88,7 @@ static volatile spinlock_t g_sim_cpuwait[CONFIG_SMP_NCPUS]; void os_start(void) __attribute__ ((noreturn)); void sim_cpu_pause(int cpu, volatile spinlock_t *wait, volatile unsigned char *paused); +void sim_smp_hook(void); /**************************************************************************** * Private Functions @@ -191,9 +191,12 @@ static void *sim_idle_trampoline(void *arg) (void)pthread_mutex_unlock(&cpuinfo->mutex); - /* Give control to the IDLE task */ + /* Give control to the IDLE task via the nasty little sim_smp_hook(). + * sim_smp_hook() is logically a part of this function but needs to be + * inserted in the path because in needs to access NuttX domain definitions. + */ - (void)cpuinfo->idletask(0, (char **)0); + sim_smp_hook(); /* The IDLE task will not return. This is just to keep the compiler happy */ @@ -361,14 +364,13 @@ int up_cpu_index(void) * cpu - The index of the CPU being started. This will be a numeric * value in the range of from one to (CONFIG_SMP_NCPUS-1). (CPU * 0 is already active) - * idletask - The entry point to the IDLE task. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ -int up_cpu_start(int cpu, main_t idletask) +int up_cpu_start(int cpu) { struct sim_cpuinfo_s cpuinfo; int ret; @@ -376,7 +378,6 @@ int up_cpu_start(int cpu, main_t idletask) /* Initialize the CPU info */ cpuinfo.cpu = cpu; - cpuinfo.idletask = idletask; ret = pthread_mutex_init(&cpuinfo.mutex, NULL); if (ret != 0) {