From 65a673959306cc48267d43af17b2af7743890c3e Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 21 Feb 2016 11:33:13 -0600 Subject: [PATCH] Add test for verifying SMP operation --- ChangeLog.txt | 1 + examples/Kconfig | 1 + examples/README.txt | 6 + examples/smp/.gitignore | 11 ++ examples/smp/Kconfig | 40 +++++ examples/smp/Make.defs | 39 +++++ examples/smp/Makefile | 56 +++++++ examples/smp/smp_main.c | 341 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 495 insertions(+) create mode 100644 examples/smp/.gitignore create mode 100644 examples/smp/Kconfig create mode 100644 examples/smp/Make.defs create mode 100644 examples/smp/Makefile create mode 100644 examples/smp/smp_main.c diff --git a/ChangeLog.txt b/ChangeLog.txt index b7ab92c11..7cdd934be 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1554,4 +1554,5 @@ (2016-02-19). * apps/examples/leds: An example to demonstrate use of LED driver (2016-02-20). + * apps/examples/smp: An verifying SMP configurations (2016-02-21). diff --git a/examples/Kconfig b/examples/Kconfig index 553b6cfc5..396f51e18 100644 --- a/examples/Kconfig +++ b/examples/Kconfig @@ -74,6 +74,7 @@ source "$APPSDIR/examples/slcd/Kconfig" source "$APPSDIR/examples/flash_test/Kconfig" source "$APPSDIR/examples/smart_test/Kconfig" source "$APPSDIR/examples/smart/Kconfig" +source "$APPSDIR/examples/smp/Kconfig" source "$APPSDIR/examples/tcpecho/Kconfig" source "$APPSDIR/examples/telnetd/Kconfig" source "$APPSDIR/examples/thttpd/Kconfig" diff --git a/examples/README.txt b/examples/README.txt index cf26669f6..63a822ef3 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -1829,6 +1829,12 @@ examples/smart_test * CONFIG_NSH_BUILTIN_APPS=y: This test can be built only as an NSH command +examples/smp +^^^^^^^^^^^^ + + This is a simple test for SMP functionality. It is basically just the + pthread barrier test with some custom instrumentation. + examples/tcpecho ^^^^^^^^^^^^^^^^ diff --git a/examples/smp/.gitignore b/examples/smp/.gitignore new file mode 100644 index 000000000..fa1ec7579 --- /dev/null +++ b/examples/smp/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.obj +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src diff --git a/examples/smp/Kconfig b/examples/smp/Kconfig new file mode 100644 index 000000000..c38fb38c2 --- /dev/null +++ b/examples/smp/Kconfig @@ -0,0 +1,40 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_SMP + bool "SMP example" + default n + ---help--- + Enable the SMP example. This example basically extracts the pthread + barrier test from apps/examples/ostest and adds some instrumentation + useful for debugging SMP implementations. + +if EXAMPLES_SMP + +config EXAMPLES_SMP_NBARRIER_THREADS + int "Number of barrier threads" + default 8 + ---help--- + Specifies the number of threads to create in the SMP test. The default + is 8 but a smaller number may be needed on systems without sufficient memory + to start so many threads. + +config EXAMPLES_SMP_PROGNAME + string "Program name" + default "smp" + depends on BUILD_KERNEL + ---help--- + This is the name of the program that will be use when the NSH ELF + program is installed. + +config EXAMPLES_SMP_PRIORITY + int "SMP task priority" + default 100 + +config EXAMPLES_SMP_STACKSIZE + int "SMP stack size" + default 2048 + +endif diff --git a/examples/smp/Make.defs b/examples/smp/Make.defs new file mode 100644 index 000000000..38ef7e86b --- /dev/null +++ b/examples/smp/Make.defs @@ -0,0 +1,39 @@ +############################################################################ +# apps/examples/smp/Make.defs +# Adds selected applications to apps/ build +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_EXAMPLES_HELLO),y) +CONFIGURED_APPS += examples/smp +endif diff --git a/examples/smp/Makefile b/examples/smp/Makefile new file mode 100644 index 000000000..3086b2ab0 --- /dev/null +++ b/examples/smp/Makefile @@ -0,0 +1,56 @@ +############################################################################ +# apps/examples/smp/Makefile +# +# Copyright (C) 2008, 2010-2013 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. +# +############################################################################ + +-include $(TOPDIR)/Make.defs + +# SMP built-in application info + +CONFIG_EXAMPLES_SMP_PRIORITY ?= SCHED_PRIORITY_DEFAULT +CONFIG_EXAMPLES_SMP_STACKSIZE ?= 2048 + +APPNAME = smp +PRIORITY = $(CONFIG_EXAMPLES_SMP_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_SMP_STACKSIZE) + +# SMP Example + +ASRCS = +CSRCS = +MAINSRC = smp_main.c + +CONFIG_EXAMPLES_SMP_PROGNAME ?= smp$(EXEEXT) +PROGNAME = $(CONFIG_EXAMPLES_SMP_PROGNAME) + +include $(APPDIR)/Application.mk diff --git a/examples/smp/smp_main.c b/examples/smp/smp_main.c new file mode 100644 index 000000000..0996376c1 --- /dev/null +++ b/examples/smp/smp_main.c @@ -0,0 +1,341 @@ +/**************************************************************************** + * examples/smp/smp_main.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 +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HOG_MSEC 1000 +#define YIELD_MSEC 100 +#define IMPOSSIBLE_CPU -1 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static pthread_barrier_t g_smp_barrier; + +#if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT) +static volatile int g_thread_cpu[CONFIG_EXAMPLES_SMP_NBARRIER_THREADS+1]; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: show_cpu / show_cpu_conditional + * + * Description: + * This depends on internal OS interfaces that are not generally available + * but can be accessed (albeit inappropriatley) in a FLAT build + * + ****************************************************************************/ + +#if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT) +int up_cpu_index(void); + +static void show_cpu(FAR const char *caller, int threadno) +{ + g_thread_cpu[threadno] = up_cpu_index(); + printf("%s[%d]: Running on CPU%d\n", caller, threadno, g_thread_cpu[threadno]); +} + +static void show_cpu_conditional(FAR const char *caller, int threadno) +{ + int cpu = up_cpu_index(); + + if (cpu != g_thread_cpu[threadno]) + { + g_thread_cpu[threadno] = cpu; + printf("%s[%d]: Now running on CPU%d\n", caller, threadno, cpu); + } +} +#else +# define show_cpu(c) +# define show_cpu_conditional(c) +#endif + +/**************************************************************************** + * Name: hog_milliseconds + * + * Description: + * Delay inline for the specified number of milliseconds. + * + ****************************************************************************/ + +static void hog_milliseconds(unsigned int milliseconds) +{ + volatile unsigned int i; + volatile unsigned int j; + + for (i = 0; i < milliseconds; i++) + { + for (j = 0; j < CONFIG_BOARD_LOOPSPERMSEC; j++) + { + } + } +} + +/**************************************************************************** + * Name: hog_time + * + * Description: + * Delay for awhile, calling pthread_yield() now and then to allow other + * pthreads to get CPU time. + * + ****************************************************************************/ + +static void hog_time(FAR const char *caller, int threadno) +{ + unsigned int remaining = HOG_MSEC; + unsigned int hogmsec; + + while (remaining > 0) + { + /* Hog some CPU */ + + hogmsec = YIELD_MSEC; + if (hogmsec > remaining) + { + hogmsec = remaining; + } + + hog_milliseconds(hogmsec); + remaining -= hogmsec; + + /* Let other threads run */ + + (void)pthread_yield(); + show_cpu_conditional(caller, threadno); + } +} + +/**************************************************************************** + * Name: barrier_thread + ****************************************************************************/ + +static pthread_addr_t barrier_thread(pthread_addr_t parameter) +{ + int threadno = (int)((intptr_t)parameter); + int ret; + + printf("Thread[%d]: Started\n", threadno); + show_cpu("Thread", threadno); + + /* Hog some CPU time */ + + hog_time("Thread", threadno); + + /* Wait at the barrier until all threads are synchronized. */ + + printf("Thread[%d]: Calling pthread_barrier_wait()\n", + threadno); + fflush(stdout); + show_cpu_conditional("Thread", threadno); + + ret = pthread_barrier_wait(&g_smp_barrier); + if (ret == 0) + { + printf("Thread[%d]: Back with ret=0 (I am not special)\n", + threadno); + } + else if (ret == PTHREAD_BARRIER_SERIAL_THREAD) + { + printf("Thread[%d]: Back with " + "ret=PTHREAD_BARRIER_SERIAL_THREAD (I AM SPECIAL)\n", + threadno); + } + else + { + printf("Thread[%d]: ERROR could not get semaphore value\n", + threadno); + } + + fflush(stdout); + show_cpu_conditional("Thread", threadno); + + /* Hog some more CPU time */ + + hog_time("Thread", threadno); + + /* Then exit */ + + printf("Thread[%d]: Done\n", threadno); + fflush(stdout); + show_cpu_conditional("Thread", threadno); + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * smp_main + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int smp_main(int argc, char *argv[]) +#endif +{ + pthread_t threadid[CONFIG_EXAMPLES_SMP_NBARRIER_THREADS]; + pthread_addr_t result; + pthread_attr_t attr; + pthread_barrierattr_t barrierattr; + int errcode = EXIT_SUCCESS; + int ret; + int i; + + show_cpu(" Main", 0); + printf(" Main[0]: Initializing barrier\n"); + + ret = pthread_barrierattr_init(&barrierattr); + if (ret != OK) + { + printf(" Main[0]: pthread_barrierattr_init failed, ret=%d\n", ret); + + errcode = EXIT_FAILURE; + goto errout; + } + + ret = pthread_barrier_init(&g_smp_barrier, &barrierattr, + CONFIG_EXAMPLES_SMP_NBARRIER_THREADS); + if (ret != OK) + { + printf(" Main[0]: pthread_barrierattr_init failed, ret=%d\n", ret); + + errcode = EXIT_FAILURE; + goto errout_with_attr; + } + + /* Create the barrier */ + + (void)pthread_barrierattr_init(&barrierattr); + + /* Start CONFIG_EXAMPLES_SMP_NBARRIER_THREADS thread instances */ + + ret = pthread_attr_init(&attr); + if (ret != OK) + { + printf(" Main[0]: pthread_attr_init failed, ret=%d\n", ret); + + errcode = EXIT_FAILURE; + goto errout_with_barrier; + } + + memset(threadid, 0, sizeof(pthread_t) * CONFIG_EXAMPLES_SMP_NBARRIER_THREADS); + for (i = 0; i < CONFIG_EXAMPLES_SMP_NBARRIER_THREADS; i++) + { +#if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT) + g_thread_cpu[i] = IMPOSSIBLE_CPU; +#endif + + ret = pthread_create(&threadid[i], &attr, barrier_thread, + (pthread_addr_t)((uintptr_t)i+1)); + if (ret != 0) + { + printf(" Main[0]: Error in thread %d create, ret=%d\n", i+1, ret); + printf(" Main[0]: Test aborted with waiting threads\n"); + + errcode = EXIT_FAILURE; + break; + } + else + { + printf(" Main[0]: Thread %d created\n", i+1); + } + + show_cpu_conditional(" Main", 0); + } + + fflush(stdout); + show_cpu_conditional(" Main", 0); + + /* Wait for all thread instances to complete */ + + for (i = 0; i < CONFIG_EXAMPLES_SMP_NBARRIER_THREADS; i++) + { + if (threadid[i] != 0); + { + ret = pthread_join(threadid[i], &result); + show_cpu_conditional(" Main", 0); + + if (ret != 0) + { + printf(" Main[0]: Error in thread %d join, ret=%d\n", i+1, ret); + errcode = EXIT_FAILURE; + } + else + { + printf(" Main[0]: Thread %d completed with result=%p\n", i+1, result); + } + } + } + + /* Destroy the barrier */ + +errout_with_barrier: + ret = pthread_barrier_destroy(&g_smp_barrier); + if (ret != OK) + { + printf(" Main[0]: pthread_barrier_destroy failed, ret=%d\n", ret); + } + +errout_with_attr: + ret = pthread_barrierattr_destroy(&barrierattr); + if (ret != OK) + { + printf(" Main[0]: pthread_barrierattr_destroy failed, ret=%d\n", + ret); + } + +errout: + fflush(stdout); + show_cpu_conditional(" Main", 0); + return errcode; +}