From 3deac3d43d16e27d9bc368c727620bc065e01fc9 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 18 Feb 2016 11:12:43 -0600 Subject: [PATCH] SMP: Initial implementation of up_cpu_pause and up_cpu_resume. Does not yet work. --- arch/sim/src/Makefile | 7 +- arch/sim/src/nuttx-names.dat | 11 +-- arch/sim/src/up_internal.h | 8 +++ arch/sim/src/up_simsmp.c | 115 ++++++++++++++++++++++++++++-- arch/sim/src/up_smpsignal.c | 134 +++++++++++++++++++++++++++++++++++ 5 files changed, 264 insertions(+), 11 deletions(-) create mode 100644 arch/sim/src/up_smpsignal.c diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index dc5aceaeff..5b65947901 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -70,6 +70,7 @@ ifeq ($(CONFIG_SPINLOCK),y) endif ifeq ($(CONFIG_SMP),y) + CSRCS += up_smpsignal.c HOSTSRCS += up_simsmp.c endif @@ -124,7 +125,7 @@ endif endif ifeq ($(CONFIG_SMP),y) - HOSTCFLAGS += -DCONFIG_SMP=1 + HOSTCFLAGS += -DCONFIG_SMP=1 -DCONFIG_SMP_NCPUS=$(CONFIG_SMP_NCPUS) endif ifeq ($(CONFIG_FS_HOSTFS),y) @@ -179,6 +180,10 @@ ifeq ($(CONFIG_SIM_TOUCHSCREEN),y) endif endif +ifeq ($(CONFIG_SMP),y) + REQUIREDOBJS += up_smpsignal$(OBJEXT) +endif + # Determine which NuttX libraries will need to be linked in # Most are provided by LINKLIBS on the MAKE command line diff --git a/arch/sim/src/nuttx-names.dat b/arch/sim/src/nuttx-names.dat index 250b692330..4123de09aa 100644 --- a/arch/sim/src/nuttx-names.dat +++ b/arch/sim/src/nuttx-names.dat @@ -29,14 +29,16 @@ nanosleep NXnanosleep poll NXpoll printf NXprintf pthread_create NXpthread_create -pthread_yield NXpthread_yield +pthread_getspecific NXpthread_getspecific +pthread_key_create NXpthread_key_create +pthread_kill NXpthread_kill +pthread_mutex_destroy NXpthread_mutex_destroy pthread_mutex_init NXpthread_mutex_init pthread_mutex_lock NXpthread_mutex_lock pthread_mutex_unlock NXpthread_mutex_unlock -pthread_mutex_destroy NXpthread_mutex_destroy -pthread_key_create NXpthread_key_create pthread_setspecific NXpthread_setspecific -pthread_getspecific NXpthread_getspecific +pthread_sigmask NXpthread_sigmask +pthread_yield NXpthread_yield puts NXputs read NXread readdir NXreaddir @@ -53,6 +55,7 @@ sem_wait NXsem_wait send NXsend sendto NXsendto setsockopt NXsetsockopt +sigaction NXsigaction sighold NXsighold sigprocmask NXsigprocmask sigrelse NXsigrelse diff --git a/arch/sim/src/up_internal.h b/arch/sim/src/up_internal.h index d8bbf2ac4d..28eaf217fc 100644 --- a/arch/sim/src/up_internal.h +++ b/arch/sim/src/up_internal.h @@ -51,6 +51,7 @@ # include # ifdef CONFIG_SMP # include +# include # endif #endif @@ -220,6 +221,13 @@ int sim_cpu0_initialize(void); int sim_cpustart(int cpu, main_t idletask); #endif +/* up_smpsignal.c *********************************************************/ + +#ifdef CONFIG_SMP +void sim_cpupause(int cpu, FAR volatile spinlock_t *wait, + FAR volatile unsigned char *paused); +#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 5d7281478a..9650022d9c 100644 --- a/arch/sim/src/up_simsmp.c +++ b/arch/sim/src/up_simsmp.c @@ -40,10 +40,27 @@ #include #include #include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Must match definitions in arch/sim/include/spinlock.h */ + +#define SP_UNLOCKED 0 /* The Un-locked state */ +#define SP_LOCKED 1 /* The Locked state */ /**************************************************************************** * Private Types ****************************************************************************/ +/* Must match definitions in arch/sim/include/spinlock.h. Assuming that + * bool and unsigned char are equivalent. + */ + +typedef unsigned char spinlock_t; + +/* Task entry point type */ typedef int (*main_t)(int argc, char **argv); @@ -58,7 +75,17 @@ struct sim_cpuinfo_s * Private Data ****************************************************************************/ -static pthread_key_t g_cpukey; +static pthread_key_t g_cpukey; +static pthread_t g_sim_cputhread[CONFIG_SMP_NCPUS]; +static volatile unsigned char g_sim_cpupaused[CONFIG_SMP_NCPUS]; +static volatile spinlock_t g_sim_cpuwait[CONFIG_SMP_NCPUS]; + +/**************************************************************************** + * NuttX domain function prototypes + ****************************************************************************/ + +void sim_cpupause(int cpu, volatile spinlock_t *wait, + volatile unsigned char *paused); /**************************************************************************** * Private Functions @@ -77,7 +104,7 @@ static pthread_key_t g_cpukey; * point. * * Input Parameters: - * arg - Stanard pthread argument + * arg - Standard pthread argument * * Returned Value: * This function does not return @@ -87,6 +114,7 @@ static pthread_key_t g_cpukey; static void *sim_idle_trampoline(void *arg) { struct sim_cpuinfo_s *cpuinfo = (struct sim_cpuinfo_s *)arg; + sigset_t set; int ret; /* Set the CPU number zero for the CPU thread */ @@ -97,6 +125,17 @@ static void *sim_idle_trampoline(void *arg) return NULL; } + /* Make sure the SIGUSR1 is not masked */ + + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + + ret = pthread_sigmask(SIG_UNBLOCK, &set, NULL); + if (ret < 0) + { + return NULL; + } + /* Let up_cpu_start() continue */ (void)pthread_mutex_unlock(&cpuinfo->mutex); @@ -110,6 +149,30 @@ static void *sim_idle_trampoline(void *arg) return NULL; } +/**************************************************************************** + * Name: sim_handle_signal + * + * Description: + * This is the SIGUSR signal handler. It implements the core logic of + * up_cpu_pause() on the thread of execution the simulated CPU. + * + * Input Parameters: + * arg - Standard sigaction arguments + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sim_handle_signal(int signo, siginfo_t *info, void *context) +{ + int cpu = (int)((uintptr_t)pthread_getspecific(g_cpukey)); + + /* We need to perform the actual tasking operations in the NuttX domain */ + + sim_cpupause(cpu, &g_sim_cpuwait[cpu], &g_sim_cpupaused[cpu]); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -132,6 +195,8 @@ static void *sim_idle_trampoline(void *arg) int sim_cpu0_initialize(void) { + struct sigaction act; + sigset_t set; int ret; /* Create the pthread key */ @@ -150,6 +215,29 @@ int sim_cpu0_initialize(void) return -ret; } + /* Register the common signal handler for all threads */ + + act.sa_sigaction = sim_handle_signal; + act.sa_flags = SA_SIGINFO; + sigemptyset(&act.sa_mask); + + ret = sigaction(SIGUSR1, &act, NULL); + if (ret < 0) + { + return -errno; + } + + /* Make sure the SIGUSR1 is not masked */ + + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + + ret = sigprocmask(SIG_UNBLOCK, &set, NULL); + if (ret < 0) + { + return -errno; + } + return 0; } @@ -206,7 +294,6 @@ int up_cpu_index(void) int up_cpu_start(int cpu, main_t idletask) { struct sim_cpuinfo_s cpuinfo; - pthread_t thread; int ret; /* Initialize the CPU info */ @@ -232,7 +319,7 @@ int up_cpu_start(int cpu, main_t idletask) * in a multi-CPU hardware model. */ - ret = pthread_create(&thread, NULL, sim_idle_trampoline, &cpuinfo); + ret = pthread_create(&g_sim_cputhread[cpu], NULL, sim_idle_trampoline, &cpuinfo); if (ret != 0) { ret = -ret; /* REVISIT: That is a host errno value. */ @@ -279,7 +366,21 @@ errout_with_mutex: int up_cpu_pause(int cpu) { -#warning Missing SMP logic + /* Take the spinlock that will prevent the CPU thread from running */ + + g_sim_cpuwait[cpu] = SP_LOCKED; + + /* Signal the CPU thread */ + + pthread_kill(g_sim_cputhread[cpu], SIGUSR1); + + /* Spin, waiting for the thread to pause */ + + while (!g_sim_cpupaused[cpu]) + { + pthread_yield(); + } + return 0; } @@ -304,6 +405,8 @@ int up_cpu_pause(int cpu) int up_cpu_resume(int cpu) { -#warning Missing SMP logic + /* Release the spinlock that will alloc the CPU thread to continue */ + + g_sim_cpuwait[cpu] = SP_UNLOCKED; return 0; } diff --git a/arch/sim/src/up_smpsignal.c b/arch/sim/src/up_smpsignal.c new file mode 100644 index 0000000000..f1e7aa8d1b --- /dev/null +++ b/arch/sim/src/up_smpsignal.c @@ -0,0 +1,134 @@ +/**************************************************************************** + * arch/sim/src/up_simsmp.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "sched/sched.h" +#include "up_internal.h" + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sim_cpupause + * + * Description: + * This is the SIGUSR1 signal handling logic. It implements the core + * logic of up_cpu_pause() on the thread of execution the simulated CPU. + * This is the part of the implementation that must be performed in the + * NuttX vs. the host domain. + * + * Input Parameters: + * cpu - The CPU being paused. + * wait - Spinlock to wait on to be un-paused + * paused - A boolean to set when we are in the paused state. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sim_cpupause(int cpu, volatile spinlock_t *wait, + volatile unsigned char *paused) +{ + struct tcb_s *rtcb = current_task(cpu); + + sdbg("CPU%d: Blocking TCB=%p\n", cpu, rtcb); + + /* Update scheduler parameters */ + + sched_suspend_scheduler(rtcb); + + /* 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! + */ + + if (up_setjmp(rtcb->xcp.regs) == 0) + { + /* Indicate that we are in the paused state */ + + *paused = 1; + + /* Spin until we are asked to resume. When we resume, we need to + * inicate that we are not longer paused. + */ + + spin_lock(wait); + *paused = 0; + + /* 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); + sdbg("CPU%d: New Active Task TCB=%p\n", cpu, rtcb); + + /* 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. + */ + + if (rtcb->xcp.sigdeliver) + { + sdbg("CPU%d: Delivering signals TCB=%p\n", cpu, rtcb); + ((sig_deliver_t)rtcb->xcp.sigdeliver)(rtcb); + rtcb->xcp.sigdeliver = NULL; + } + + /* Reset scheduler parameters */ + + sched_resume_scheduler(rtcb); + + /* Then switch contexts */ + + up_longjmp(rtcb->xcp.regs, 1); + } +} + +#endif /* CONFIG_SMP */ +