SMP: Initial implementation of up_cpu_pause and up_cpu_resume. Does not yet work.

This commit is contained in:
Gregory Nutt 2016-02-18 11:12:43 -06:00
parent e3ea40e4f9
commit 3deac3d43d
5 changed files with 264 additions and 11 deletions

View File

@ -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

View File

@ -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

View File

@ -51,6 +51,7 @@
# include <arch/irq.h>
# ifdef CONFIG_SMP
# include <nuttx/sched.h>
# include <arch/spinlock.h>
# 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

View File

@ -40,10 +40,27 @@
#include <stdint.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <errno.h>
/****************************************************************************
* 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;
}

134
arch/sim/src/up_smpsignal.c Normal file
View File

@ -0,0 +1,134 @@
/****************************************************************************
* arch/sim/src/up_simsmp.c
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <nuttx/sched.h>
#include <nuttx/spinlock.h>
#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 */