summaryrefslogtreecommitdiffstats
path: root/testsuites
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-08-29 09:43:44 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-09-10 10:38:45 +0200
commit709796209c88e6749320b3096df57f369c2d62be (patch)
tree072e7cd5cef37aad7404a02344724a4348602f35 /testsuites
parentscore: Modify _Scheduler_Unblock() (diff)
downloadrtems-709796209c88e6749320b3096df57f369c2d62be.tar.bz2
score: Add thread pin/unpin support
Add support to temporarily pin a thread to its current processor. This may be used to access per-processor data structures in critical sections with enabled thread dispatching, e.g. a pinned thread is allowed to block. Update #3508.
Diffstat (limited to 'testsuites')
-rw-r--r--testsuites/smptests/Makefile.am11
-rw-r--r--testsuites/smptests/configure.ac1
-rw-r--r--testsuites/smptests/smpthreadpin01/init.c620
-rw-r--r--testsuites/smptests/smpthreadpin01/smpthreadpin01.doc12
-rw-r--r--testsuites/smptests/smpthreadpin01/smpthreadpin01.scn35
5 files changed, 679 insertions, 0 deletions
diff --git a/testsuites/smptests/Makefile.am b/testsuites/smptests/Makefile.am
index 3c746a45bf..4c1750df1e 100644
--- a/testsuites/smptests/Makefile.am
+++ b/testsuites/smptests/Makefile.am
@@ -622,6 +622,17 @@ endif
endif
if HAS_SMP
+if TEST_smpthreadpin01
+smp_tests += smpthreadpin01
+smp_screens += smpthreadpin01/smpthreadpin01.scn
+smp_docs += smpthreadpin01/smpthreadpin01.doc
+smpthreadpin01_SOURCES = smpthreadpin01/init.c
+smpthreadpin01_CPPFLAGS = $(AM_CPPFLAGS) \
+ $(TEST_FLAGS_smpthreadpin01) $(support_includes)
+endif
+endif
+
+if HAS_SMP
if TEST_smpunsupported01
smp_tests += smpunsupported01
smp_screens += smpunsupported01/smpunsupported01.scn
diff --git a/testsuites/smptests/configure.ac b/testsuites/smptests/configure.ac
index 0481029a43..9c5b5e0031 100644
--- a/testsuites/smptests/configure.ac
+++ b/testsuites/smptests/configure.ac
@@ -87,6 +87,7 @@ RTEMS_TEST_CHECK([smpsignal01])
RTEMS_TEST_CHECK([smpstrongapa01])
RTEMS_TEST_CHECK([smpswitchextension01])
RTEMS_TEST_CHECK([smpthreadlife01])
+RTEMS_TEST_CHECK([smpthreadpin01])
RTEMS_TEST_CHECK([smpunsupported01])
RTEMS_TEST_CHECK([smpwakeafter01])
diff --git a/testsuites/smptests/smpthreadpin01/init.c b/testsuites/smptests/smpthreadpin01/init.c
new file mode 100644
index 0000000000..c28b3b3d1e
--- /dev/null
+++ b/testsuites/smptests/smpthreadpin01/init.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2018 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/thread.h>
+#include <rtems/score/threadimpl.h>
+
+#include <tmacros.h>
+
+const char rtems_test_name[] = "SMPTHREADPIN 1";
+
+#define CPU_COUNT 2
+
+#define SCHED_A rtems_build_name(' ', ' ', ' ', 'A')
+
+#define SCHED_B rtems_build_name(' ', ' ', ' ', 'B')
+
+#define EVENT_WAKEUP_MASTER RTEMS_EVENT_0
+
+#define EVENT_MTX_LOCK RTEMS_EVENT_1
+
+#define EVENT_MTX_UNLOCK RTEMS_EVENT_2
+
+#define EVENT_MOVE_BUSY_TO_CPU_0 RTEMS_EVENT_3
+
+#define EVENT_MOVE_BUSY_TO_CPU_1 RTEMS_EVENT_4
+
+#define EVENT_MOVE_SELF_TO_CPU_0 RTEMS_EVENT_5
+
+#define EVENT_MOVE_SELF_TO_CPU_1 RTEMS_EVENT_6
+
+#define EVENT_SET_SELF_PRIO_TO_LOW RTEMS_EVENT_7
+
+#define EVENT_SET_BUSY_PRIO_TO_IDLE RTEMS_EVENT_8
+
+#define EVENT_SET_FLAG RTEMS_EVENT_9
+
+#define PRIO_IDLE 6
+
+#define PRIO_VERY_LOW 5
+
+#define PRIO_LOW 4
+
+#define PRIO_MIDDLE 3
+
+#define PRIO_HIGH 2
+
+#define PRIO_VERY_HIGH 1
+
+typedef struct {
+ rtems_id master;
+ rtems_id event;
+ rtems_id event_2;
+ rtems_id busy;
+ rtems_id sched_a;
+ rtems_id sched_b;
+ rtems_mutex mtx;
+ volatile bool flag;
+} test_context;
+
+static test_context test_instance;
+
+static rtems_task_priority set_prio(rtems_id id, rtems_task_priority prio)
+{
+ rtems_status_code sc;
+ rtems_task_priority old_prio;
+
+ old_prio = 0xffffffff;
+ sc = rtems_task_set_priority(id, prio, &old_prio);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ return old_prio;
+}
+
+static void set_affinity(rtems_id task, uint32_t cpu_index)
+{
+ rtems_status_code sc;
+ rtems_id sched_cpu;
+ rtems_id sched_task;
+ cpu_set_t set;
+
+ sc = rtems_scheduler_ident_by_processor(cpu_index, &sched_cpu);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_get_scheduler(task, &sched_task);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ if (sched_task != sched_cpu) {
+ rtems_task_priority prio;
+
+ CPU_FILL(&set);
+ sc = rtems_task_set_affinity(task, sizeof(set), &set);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ prio = set_prio(task, RTEMS_CURRENT_PRIORITY);
+ sc = rtems_task_set_scheduler(task, sched_cpu, prio);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+ }
+
+ CPU_ZERO(&set);
+ CPU_SET((int) cpu_index, &set);
+ sc = rtems_task_set_affinity(task, sizeof(set), &set);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void send_events(rtems_id task, rtems_event_set events)
+{
+ rtems_status_code sc;
+
+ sc = rtems_event_send(task, events);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static rtems_event_set wait_for_events(void)
+{
+ rtems_event_set events;
+ rtems_status_code sc;
+
+ sc = rtems_event_receive(
+ RTEMS_ALL_EVENTS,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ return events;
+}
+
+static void pin(bool blocked)
+{
+ Per_CPU_Control *cpu_self;
+ Thread_Control *executing;
+
+ cpu_self = _Thread_Dispatch_disable();
+ executing = _Per_CPU_Get_executing(cpu_self);
+
+ if (blocked) {
+ _Thread_Set_state(executing, STATES_SUSPENDED);
+ }
+
+ _Thread_Pin(executing);
+
+ if (blocked) {
+ _Thread_Clear_state(executing, STATES_SUSPENDED);
+ }
+
+ _Thread_Dispatch_enable(cpu_self);
+}
+
+static void unpin(bool blocked)
+{
+ Per_CPU_Control *cpu_self;
+ Thread_Control *executing;
+
+ cpu_self = _Thread_Dispatch_disable();
+ executing = _Per_CPU_Get_executing(cpu_self);
+
+ if (blocked) {
+ _Thread_Set_state(executing, STATES_SUSPENDED);
+ }
+
+ _Thread_Unpin(executing, cpu_self);
+
+ if (blocked) {
+ _Thread_Clear_state(executing, STATES_SUSPENDED);
+ }
+
+ _Thread_Dispatch_enable(cpu_self);
+}
+
+static void event_task(rtems_task_argument arg)
+{
+ test_context *ctx;
+
+ ctx = (test_context *) arg;
+
+ while (true) {
+ rtems_event_set events;
+
+ events = wait_for_events();
+
+ /*
+ * The order of event processing is important!
+ */
+
+ if ((events & EVENT_MTX_LOCK) != 0) {
+ rtems_mutex_lock(&ctx->mtx);
+ }
+
+ if ((events & EVENT_MTX_UNLOCK) != 0) {
+ rtems_mutex_unlock(&ctx->mtx);
+ }
+
+ if ((events & EVENT_MOVE_BUSY_TO_CPU_0) != 0) {
+ set_affinity(ctx->busy, 0);
+ }
+
+ if ((events & EVENT_MOVE_BUSY_TO_CPU_1) != 0) {
+ set_affinity(ctx->busy, 1);
+ }
+
+ if ((events & EVENT_MOVE_SELF_TO_CPU_0) != 0) {
+ set_affinity(RTEMS_SELF, 0);
+ }
+
+ if ((events & EVENT_MOVE_SELF_TO_CPU_1) != 0) {
+ set_affinity(RTEMS_SELF, 1);
+ }
+
+ if ((events & EVENT_SET_SELF_PRIO_TO_LOW) != 0) {
+ set_prio(RTEMS_SELF, PRIO_LOW);
+ }
+
+ if ((events & EVENT_SET_BUSY_PRIO_TO_IDLE) != 0) {
+ set_prio(ctx->busy, PRIO_IDLE);
+ }
+
+ if ((events & EVENT_SET_FLAG) != 0) {
+ ctx->flag = true;
+ }
+
+ if ((events & EVENT_WAKEUP_MASTER) != 0) {
+ send_events(ctx->master, EVENT_WAKEUP_MASTER);
+ }
+ }
+}
+
+static void busy_task(rtems_task_argument arg)
+{
+ (void) arg;
+
+#if CPU_PROVIDES_IDLE_THREAD_BODY == TRUE
+ _CPU_Thread_Idle_body(0);
+#else
+ while (true) {
+ /* Do nothing */
+ }
+#endif
+}
+
+static const char *blocked_or_ready(bool blocked)
+{
+ return blocked ? "blocked" : "ready";
+}
+
+static void reconfigure_scheduler(test_context *ctx)
+{
+ rtems_status_code sc;
+
+ puts("reconfigure scheduler");
+
+ set_prio(ctx->master, PRIO_MIDDLE);
+ set_prio(ctx->event, PRIO_LOW);
+ set_prio(ctx->event_2, PRIO_VERY_LOW);
+ set_prio(ctx->busy, PRIO_IDLE);
+
+ set_affinity(ctx->master, 0);
+ set_affinity(ctx->event, 0);
+ set_affinity(ctx->event_2, 0);
+ set_affinity(ctx->busy, 0);
+
+ sc = rtems_scheduler_remove_processor(ctx->sched_a, 1);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_scheduler_add_processor(ctx->sched_b, 1);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void test_simple_pin_unpin(test_context *ctx, int run)
+{
+ Per_CPU_Control *cpu_self;
+ Thread_Control *executing;
+
+ printf("test simple wait unpin (run %i)\n", run);
+
+ set_affinity(ctx->busy, 0);
+ set_prio(ctx->busy, PRIO_IDLE);
+ set_prio(RTEMS_SELF, PRIO_MIDDLE);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ cpu_self = _Thread_Dispatch_disable();
+ executing = _Per_CPU_Get_executing(cpu_self);
+ _Thread_Pin(executing);
+
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ _Thread_Unpin(executing, cpu_self);
+ _Thread_Dispatch_enable(cpu_self);
+
+ rtems_test_assert(rtems_get_current_processor() == 1);
+}
+
+static void test_pin_wait_unpin(test_context *ctx, bool blocked, int run)
+{
+ printf("test pin wait unpin (%s, run %i)\n", blocked_or_ready(blocked), run);
+
+ set_affinity(ctx->busy, 0);
+ set_prio(ctx->busy, PRIO_IDLE);
+ set_prio(RTEMS_SELF, PRIO_MIDDLE);
+ set_prio(ctx->event, PRIO_LOW);
+ set_affinity(ctx->event, 1);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ pin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ send_events(ctx->event, EVENT_WAKEUP_MASTER);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+ wait_for_events();
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ set_prio(ctx->busy, PRIO_HIGH);
+ set_affinity(ctx->busy, 0);
+ unpin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+}
+
+static void test_pin_preempt_unpin(test_context *ctx, bool blocked, int run)
+{
+ printf(
+ "test pin preempt unpin (%s, run %i)\n",
+ blocked_or_ready(blocked),
+ run
+ );
+
+ set_prio(RTEMS_SELF, PRIO_MIDDLE);
+ set_prio(ctx->event, PRIO_VERY_HIGH);
+ set_prio(ctx->busy, PRIO_HIGH);
+ set_affinity(ctx->event, 0);
+ set_affinity(ctx->busy, 0);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ pin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ ctx->flag = false;
+ send_events(
+ ctx->event,
+ EVENT_MOVE_BUSY_TO_CPU_1 | EVENT_SET_SELF_PRIO_TO_LOW
+ | EVENT_SET_BUSY_PRIO_TO_IDLE | EVENT_SET_FLAG
+ );
+
+ while (!ctx->flag) {
+ rtems_test_assert(rtems_get_current_processor() == 1);
+ }
+
+ set_affinity(ctx->busy, 0);
+ unpin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+}
+
+static void test_pin_home_no_help_unpin(
+ test_context *ctx,
+ bool blocked,
+ int run
+)
+{
+ rtems_status_code sc;
+
+ printf(
+ "test pin home no help unpin (%s, run %i)\n",
+ blocked_or_ready(blocked),
+ run
+ );
+
+ set_affinity(ctx->busy, 1);
+ set_prio(ctx->busy, PRIO_IDLE);
+ set_prio(RTEMS_SELF, PRIO_MIDDLE);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ pin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ sc = rtems_task_set_scheduler(RTEMS_SELF, ctx->sched_b, 1);
+ rtems_test_assert(sc == RTEMS_RESOURCE_IN_USE);
+
+ rtems_mutex_lock(&ctx->mtx);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ set_affinity(ctx->event, 1);
+ set_prio(ctx->event, PRIO_MIDDLE);
+
+ send_events(ctx->event, EVENT_MTX_LOCK);
+ set_prio(ctx->event_2, PRIO_LOW);
+ set_affinity(ctx->event_2, 1);
+ send_events(ctx->event_2, EVENT_WAKEUP_MASTER);
+ wait_for_events();
+
+ /* Now the event task can help us */
+ rtems_test_assert(ctx->mtx._Queue._heads != NULL);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ set_affinity(ctx->event_2, 0);
+ set_affinity(ctx->busy, 1);
+ set_prio(ctx->busy, PRIO_HIGH);
+ send_events(
+ ctx->event_2,
+ EVENT_MOVE_BUSY_TO_CPU_0 | EVENT_MOVE_SELF_TO_CPU_1
+ | EVENT_SET_SELF_PRIO_TO_LOW | EVENT_SET_BUSY_PRIO_TO_IDLE
+ );
+ set_prio(ctx->event_2, PRIO_VERY_HIGH);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ rtems_mutex_unlock(&ctx->mtx);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ send_events(ctx->event, EVENT_WAKEUP_MASTER | EVENT_MTX_UNLOCK);
+ wait_for_events();
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ unpin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+}
+
+static void test_pin_foreign_no_help_unpin(
+ test_context *ctx,
+ bool blocked,
+ int run
+)
+{
+ printf(
+ "test pin foreign no help unpin (%s, run %i)\n",
+ blocked_or_ready(blocked),
+ run
+ );
+
+ set_affinity(ctx->busy, 1);
+ set_prio(ctx->busy, PRIO_IDLE);
+ set_prio(RTEMS_SELF, PRIO_MIDDLE);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ rtems_mutex_lock(&ctx->mtx);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ set_affinity(ctx->event, 1);
+ set_prio(ctx->event, PRIO_MIDDLE);
+ send_events(ctx->event, EVENT_MTX_LOCK);
+ set_prio(ctx->event_2, PRIO_LOW);
+ set_affinity(ctx->event_2, 1);
+ send_events(ctx->event_2, EVENT_WAKEUP_MASTER);
+ wait_for_events();
+
+ /* Now the event task can help us */
+ rtems_test_assert(ctx->mtx._Queue._heads != NULL);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ /* Request help */
+ set_affinity(ctx->busy, 0);
+ set_prio(ctx->busy, PRIO_HIGH);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ /* Pin while using foreign scheduler */
+ pin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ set_affinity(ctx->event_2, 1);
+ send_events(
+ ctx->event_2,
+ EVENT_MOVE_BUSY_TO_CPU_1 | EVENT_MOVE_SELF_TO_CPU_0
+ | EVENT_SET_SELF_PRIO_TO_LOW | EVENT_SET_BUSY_PRIO_TO_IDLE
+ );
+ set_prio(ctx->event_2, PRIO_VERY_HIGH);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ unpin(blocked);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ set_prio(ctx->busy, PRIO_IDLE);
+ rtems_mutex_unlock(&ctx->mtx);
+ rtems_test_assert(rtems_get_current_processor() == 0);
+
+ send_events(ctx->event, EVENT_WAKEUP_MASTER | EVENT_MTX_UNLOCK);
+ wait_for_events();
+ rtems_test_assert(rtems_get_current_processor() == 0);
+}
+
+static void test(test_context *ctx)
+{
+ rtems_status_code sc;
+ int run;
+
+ ctx->master = rtems_task_self();
+
+ rtems_mutex_init(&ctx->mtx, "test");
+
+ sc = rtems_scheduler_ident(SCHED_A, &ctx->sched_a);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_scheduler_ident(SCHED_B, &ctx->sched_b);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_create(
+ rtems_build_name('B', 'U', 'S', 'Y'),
+ PRIO_HIGH,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->busy
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(ctx->busy, busy_task, (rtems_task_argument) ctx);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ set_affinity(ctx->busy, 0);
+ set_prio(ctx->busy, PRIO_IDLE);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ sc = rtems_task_create(
+ rtems_build_name('E', 'V', 'T', '1'),
+ PRIO_LOW,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->event
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(ctx->event, event_task, (rtems_task_argument) ctx);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ send_events(ctx->event, EVENT_WAKEUP_MASTER);
+ wait_for_events();
+
+ sc = rtems_task_create(
+ rtems_build_name('E', 'V', 'T', '2'),
+ PRIO_LOW,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->event_2
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(ctx->event_2, event_task, (rtems_task_argument) ctx);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ send_events(ctx->event_2, EVENT_WAKEUP_MASTER);
+ wait_for_events();
+
+ for (run = 1; run <= 3; ++run) {
+ test_simple_pin_unpin(ctx, run);
+ test_pin_wait_unpin(ctx, true, run);
+ test_pin_wait_unpin(ctx, false, run);
+ test_pin_preempt_unpin(ctx, true, run);
+ test_pin_preempt_unpin(ctx, false, run);
+ }
+
+ reconfigure_scheduler(ctx);
+
+ for (run = 1; run <= 3; ++run) {
+ test_pin_home_no_help_unpin(ctx, true, run);
+ test_pin_home_no_help_unpin(ctx, false, run);
+ test_pin_foreign_no_help_unpin(ctx, true, run);
+ test_pin_foreign_no_help_unpin(ctx, false, run);
+ }
+}
+
+static void Init(rtems_task_argument arg)
+{
+ TEST_BEGIN();
+
+ if (rtems_get_processor_count() == CPU_COUNT) {
+ test(&test_instance);
+ } else {
+ puts("warning: wrong processor count to run the test");
+ }
+
+ TEST_END();
+ rtems_test_exit(0);
+}
+
+#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
+
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+
+#define CONFIGURE_MAXIMUM_PROCESSORS CPU_COUNT
+
+#define CONFIGURE_MAXIMUM_TASKS 4
+
+#define CONFIGURE_INIT_TASK_PRIORITY PRIO_MIDDLE
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_SCHEDULER_EDF_SMP
+
+#include <rtems/scheduler.h>
+
+RTEMS_SCHEDULER_EDF_SMP(a, CONFIGURE_MAXIMUM_PROCESSORS);
+
+RTEMS_SCHEDULER_EDF_SMP(b, CONFIGURE_MAXIMUM_PROCESSORS);
+
+#define CONFIGURE_SCHEDULER_TABLE_ENTRIES \
+ RTEMS_SCHEDULER_TABLE_EDF_SMP(a, SCHED_A), \
+ RTEMS_SCHEDULER_TABLE_EDF_SMP(b, SCHED_B) \
+
+#define CONFIGURE_SCHEDULER_ASSIGNMENTS \
+ RTEMS_SCHEDULER_ASSIGN(0, RTEMS_SCHEDULER_ASSIGN_PROCESSOR_MANDATORY), \
+ RTEMS_SCHEDULER_ASSIGN(0, RTEMS_SCHEDULER_ASSIGN_PROCESSOR_OPTIONAL)
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/smptests/smpthreadpin01/smpthreadpin01.doc b/testsuites/smptests/smpthreadpin01/smpthreadpin01.doc
new file mode 100644
index 0000000000..23d2f165ea
--- /dev/null
+++ b/testsuites/smptests/smpthreadpin01/smpthreadpin01.doc
@@ -0,0 +1,12 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: smpthreadpin01
+
+directives:
+
+ - _Thread_Pin()
+ - _Thread_Unpin()
+
+concepts:
+
+ - Ensure that the thread to processor pinning works.
diff --git a/testsuites/smptests/smpthreadpin01/smpthreadpin01.scn b/testsuites/smptests/smpthreadpin01/smpthreadpin01.scn
new file mode 100644
index 0000000000..0030183059
--- /dev/null
+++ b/testsuites/smptests/smpthreadpin01/smpthreadpin01.scn
@@ -0,0 +1,35 @@
+*** BEGIN OF TEST SMPTHREADPIN 1 ***
+*** TEST VERSION: 5.0.0.2d1c3dff12bbbfec63997985f11bc80edfc1e994
+*** TEST STATE: EXPECTED-PASS
+*** TEST BUILD: RTEMS_SMP
+*** TEST TOOLS: 7.3.0 20180125 (RTEMS 5, RSB 9670d7541e0621915e521fe76e7bb33de8cee661, Newlib d13c84eb07e35984bf7a974cd786a6cdac29e6b9)
+test simple wait unpin (run 1)
+test pin wait unpin (blocked, run 1)
+test pin wait unpin (ready, run 1)
+test pin preempt unpin (blocked, run 1)
+test pin preempt unpin (ready, run 1)
+test simple wait unpin (run 2)
+test pin wait unpin (blocked, run 2)
+test pin wait unpin (ready, run 2)
+test pin preempt unpin (blocked, run 2)
+test pin preempt unpin (ready, run 2)
+test simple wait unpin (run 3)
+test pin wait unpin (blocked, run 3)
+test pin wait unpin (ready, run 3)
+test pin preempt unpin (blocked, run 3)
+test pin preempt unpin (ready, run 3)
+reconfigure scheduler
+test pin home no help unpin (blocked, run 1)
+test pin home no help unpin (ready, run 1)
+test pin foreign no help unpin (blocked, run 1)
+test pin foreign no help unpin (ready, run 1)
+test pin home no help unpin (blocked, run 2)
+test pin home no help unpin (ready, run 2)
+test pin foreign no help unpin (blocked, run 2)
+test pin foreign no help unpin (ready, run 2)
+test pin home no help unpin (blocked, run 3)
+test pin home no help unpin (ready, run 3)
+test pin foreign no help unpin (blocked, run 3)
+test pin foreign no help unpin (ready, run 3)
+
+*** END OF TEST SMPTHREADPIN 1 ***