[PATCH 3/3] score: Add thread pin/unpin support

Sebastian Huber sebastian.huber at embedded-brains.de
Fri Sep 7 13:48:21 UTC 2018


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.
---
 cpukit/include/rtems/score/scheduler.h             |  48 ++
 cpukit/include/rtems/score/scheduleredfsmp.h       |  33 +-
 cpukit/include/rtems/score/schedulerimpl.h         |  20 +-
 .../rtems/score/schedulerpriorityaffinitysmp.h     |   2 +
 cpukit/include/rtems/score/schedulerprioritysmp.h  |   4 +-
 cpukit/include/rtems/score/schedulersimplesmp.h    |   4 +-
 cpukit/include/rtems/score/schedulersmpimpl.h      |   1 +
 cpukit/include/rtems/score/schedulerstrongapa.h    |   4 +-
 cpukit/include/rtems/score/smpimpl.h               |   3 +-
 cpukit/include/rtems/score/thread.h                |  43 +-
 cpukit/include/rtems/score/threadimpl.h            |  52 +-
 cpukit/rtems/src/scheduleraddprocessor.c           |   2 +-
 cpukit/score/Makefile.am                           |   2 +
 cpukit/score/src/schedulerdefaultpinunpin.c        |  35 ++
 cpukit/score/src/scheduleredfsmp.c                 |  96 +++-
 cpukit/score/src/threaddispatch.c                  |  93 +++-
 cpukit/score/src/threadinitialize.c                |   2 +-
 cpukit/score/src/threadunpin.c                     |  72 +++
 testsuites/smptests/Makefile.am                    |  11 +
 testsuites/smptests/configure.ac                   |   1 +
 testsuites/smptests/smpthreadpin01/init.c          | 620 +++++++++++++++++++++
 .../smptests/smpthreadpin01/smpthreadpin01.doc     |  12 +
 .../smptests/smpthreadpin01/smpthreadpin01.scn     |   7 +
 23 files changed, 1114 insertions(+), 53 deletions(-)
 create mode 100644 cpukit/score/src/schedulerdefaultpinunpin.c
 create mode 100644 cpukit/score/src/threadunpin.c
 create mode 100644 testsuites/smptests/smpthreadpin01/init.c
 create mode 100644 testsuites/smptests/smpthreadpin01/smpthreadpin01.doc
 create mode 100644 testsuites/smptests/smpthreadpin01/smpthreadpin01.scn

diff --git a/cpukit/include/rtems/score/scheduler.h b/cpukit/include/rtems/score/scheduler.h
index a6066c8e4a..fe6315daef 100644
--- a/cpukit/include/rtems/score/scheduler.h
+++ b/cpukit/include/rtems/score/scheduler.h
@@ -137,6 +137,36 @@ typedef struct {
   );
 
   /**
+   * @brief Pin thread operation.
+   *
+   * @param[in] scheduler The scheduler instance of the specified processor.
+   * @param[in] the_thread The thread to pin.
+   * @param[in] node The scheduler node of the thread.
+   * @param[in] cpu The processor to pin the thread.
+   */
+  void ( *pin )(
+    const Scheduler_Control *scheduler,
+    Thread_Control          *the_thread,
+    Scheduler_Node          *node,
+    struct Per_CPU_Control  *cpu
+  );
+
+  /**
+   * @brief Unpin thread operation.
+   *
+   * @param[in] scheduler The scheduler instance of the specified processor.
+   * @param[in] the_thread The thread to unpin.
+   * @param[in] node The scheduler node of the thread.
+   * @param[in] cpu The processor to unpin the thread.
+   */
+  void ( *unpin )(
+    const Scheduler_Control *scheduler,
+    Thread_Control          *the_thread,
+    Scheduler_Node          *node,
+    struct Per_CPU_Control  *cpu
+  );
+
+  /**
    * @brief Add processor operation.
    *
    * @param[in] scheduler The scheduler instance to add the processor.
@@ -405,10 +435,28 @@ Priority_Control _Scheduler_default_Unmap_priority(
     Thread_Scheduler_state   next_state
   );
 
+  /**
+   * @brief Does nothing in a single processor system, otherwise a fatal error
+   * is issued.
+   *
+   * @param[in] scheduler Unused.
+   * @param[in] the_thread Unused.
+   * @param[in] node Unused.
+   * @param[in] cpu Unused.
+   */
+  void _Scheduler_default_Pin_or_unpin(
+    const Scheduler_Control *scheduler,
+    Thread_Control          *the_thread,
+    Scheduler_Node          *node,
+    struct Per_CPU_Control  *cpu
+  );
+
   #define SCHEDULER_OPERATION_DEFAULT_ASK_FOR_HELP \
     _Scheduler_default_Ask_for_help, \
     _Scheduler_default_Reconsider_help_request, \
     _Scheduler_default_Withdraw_node, \
+    _Scheduler_default_Pin_or_unpin, \
+    _Scheduler_default_Pin_or_unpin, \
     NULL, \
     NULL,
 #else
diff --git a/cpukit/include/rtems/score/scheduleredfsmp.h b/cpukit/include/rtems/score/scheduleredfsmp.h
index 018568190e..72c906eefe 100644
--- a/cpukit/include/rtems/score/scheduleredfsmp.h
+++ b/cpukit/include/rtems/score/scheduleredfsmp.h
@@ -7,7 +7,7 @@
  */
 
 /*
- * Copyright (c) 2017 embedded brains GmbH.
+ * Copyright (c) 2017, 2018 embedded brains GmbH.
  *
  * The license and distribution terms for this file may be
  * found in the file LICENSE in this distribution or at
@@ -43,13 +43,24 @@ typedef struct {
   int64_t generation;
 
   /**
-   * @brief The ready queue index depending on the processor affinity of the thread.
+   * @brief The ready queue index depending on the processor affinity and
+   * pinning of the thread.
    *
    * The ready queue index zero is used for threads with a one-to-all thread
    * processor affinity.  Threads with a one-to-one processor affinity use the
    * processor index plus one as the ready queue index.
    */
-  uint32_t ready_queue_index;
+  uint8_t ready_queue_index;
+
+  /**
+   * @brief Ready queue index according to thread affinity.
+   */
+  uint8_t affinity_ready_queue_index;
+
+  /**
+   * @brief Ready queue index according to thread pinning.
+   */
+  uint8_t pinning_ready_queue_index;
 } Scheduler_EDF_SMP_Node;
 
 typedef struct {
@@ -105,6 +116,8 @@ typedef struct {
     _Scheduler_EDF_SMP_Ask_for_help, \
     _Scheduler_EDF_SMP_Reconsider_help_request, \
     _Scheduler_EDF_SMP_Withdraw_node, \
+    _Scheduler_EDF_SMP_Pin, \
+    _Scheduler_EDF_SMP_Unpin, \
     _Scheduler_EDF_SMP_Add_processor, \
     _Scheduler_EDF_SMP_Remove_processor, \
     _Scheduler_EDF_SMP_Node_initialize, \
@@ -162,6 +175,20 @@ void _Scheduler_EDF_SMP_Withdraw_node(
   Thread_Scheduler_state   next_state
 );
 
+void _Scheduler_EDF_SMP_Pin(
+  const Scheduler_Control *scheduler,
+  Thread_Control          *the_thread,
+  Scheduler_Node          *node,
+  struct Per_CPU_Control  *cpu
+);
+
+void _Scheduler_EDF_SMP_Unpin(
+  const Scheduler_Control *scheduler,
+  Thread_Control          *the_thread,
+  Scheduler_Node          *node,
+  struct Per_CPU_Control  *cpu
+);
+
 void _Scheduler_EDF_SMP_Add_processor(
   const Scheduler_Control *scheduler,
   Thread_Control          *idle
diff --git a/cpukit/include/rtems/score/schedulerimpl.h b/cpukit/include/rtems/score/schedulerimpl.h
index 2fe30b3d6b..fda043ea5a 100644
--- a/cpukit/include/rtems/score/schedulerimpl.h
+++ b/cpukit/include/rtems/score/schedulerimpl.h
@@ -1095,17 +1095,13 @@ RTEMS_INLINE_ROUTINE Status_Control _Scheduler_Set(
     &the_thread->Real_priority
   );
 
-  if ( !_Priority_Is_empty( &old_scheduler_node->Wait.Priority ) ) {
-    _Priority_Plain_insert(
-      &old_scheduler_node->Wait.Priority,
-      &the_thread->Real_priority,
-      the_thread->Real_priority.priority
-    );
-    return STATUS_RESOURCE_IN_USE;
-  }
-
+  if (
+    !_Priority_Is_empty( &old_scheduler_node->Wait.Priority )
 #if defined(RTEMS_SMP)
-  if ( !_Chain_Has_only_one_node( &the_thread->Scheduler.Wait_nodes ) ) {
+      || !_Chain_Has_only_one_node( &the_thread->Scheduler.Wait_nodes )
+      || the_thread->Scheduler.pin_level != 0
+#endif
+  ) {
     _Priority_Plain_insert(
       &old_scheduler_node->Wait.Priority,
       &the_thread->Real_priority,
@@ -1114,6 +1110,7 @@ RTEMS_INLINE_ROUTINE Status_Control _Scheduler_Set(
     return STATUS_RESOURCE_IN_USE;
   }
 
+#if defined(RTEMS_SMP)
   old_scheduler = _Thread_Scheduler_get_home( the_thread );
   new_scheduler_node = _Thread_Scheduler_get_node_by_index(
     the_thread,
@@ -1140,7 +1137,8 @@ RTEMS_INLINE_ROUTINE Status_Control _Scheduler_Set(
     return STATUS_UNSATISFIED;
   }
 
-  the_thread->Scheduler.home = new_scheduler;
+  _Assert( the_thread->Scheduler.pinned_scheduler == NULL );
+  the_thread->Scheduler.home_scheduler = new_scheduler;
 
   _Scheduler_Release_critical( new_scheduler, &lock_context );
 
diff --git a/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h b/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
index d988d5752a..e8ee528c90 100644
--- a/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
+++ b/cpukit/include/rtems/score/schedulerpriorityaffinitysmp.h
@@ -61,6 +61,8 @@ extern "C" {
     _Scheduler_priority_affinity_SMP_Ask_for_help, \
     _Scheduler_priority_affinity_SMP_Reconsider_help_request, \
     _Scheduler_priority_affinity_SMP_Withdraw_node, \
+    _Scheduler_default_Pin_or_unpin, \
+    _Scheduler_default_Pin_or_unpin, \
     _Scheduler_priority_affinity_SMP_Add_processor, \
     _Scheduler_priority_affinity_SMP_Remove_processor, \
     _Scheduler_priority_affinity_SMP_Node_initialize, \
diff --git a/cpukit/include/rtems/score/schedulerprioritysmp.h b/cpukit/include/rtems/score/schedulerprioritysmp.h
index 6671da5b7a..7d579f1846 100644
--- a/cpukit/include/rtems/score/schedulerprioritysmp.h
+++ b/cpukit/include/rtems/score/schedulerprioritysmp.h
@@ -7,7 +7,7 @@
  */
 
 /*
- * Copyright (c) 2013, 2016 embedded brains GmbH.  All rights reserved.
+ * Copyright (c) 2013, 2018 embedded brains GmbH.  All rights reserved.
  *
  *  embedded brains GmbH
  *  Dornierstr. 4
@@ -89,6 +89,8 @@ typedef struct {
     _Scheduler_priority_SMP_Ask_for_help, \
     _Scheduler_priority_SMP_Reconsider_help_request, \
     _Scheduler_priority_SMP_Withdraw_node, \
+    _Scheduler_default_Pin_or_unpin, \
+    _Scheduler_default_Pin_or_unpin, \
     _Scheduler_priority_SMP_Add_processor, \
     _Scheduler_priority_SMP_Remove_processor, \
     _Scheduler_priority_SMP_Node_initialize, \
diff --git a/cpukit/include/rtems/score/schedulersimplesmp.h b/cpukit/include/rtems/score/schedulersimplesmp.h
index bc75b205d5..d9b4c1d46c 100644
--- a/cpukit/include/rtems/score/schedulersimplesmp.h
+++ b/cpukit/include/rtems/score/schedulersimplesmp.h
@@ -9,7 +9,7 @@
 /*
  *  Copyright (C) 2011 On-Line Applications Research Corporation (OAR).
  *
- *  Copyright (c) 2013, 2016 embedded brains GmbH.
+ *  Copyright (c) 2013, 2018 embedded brains GmbH.
  *
  *  The license and distribution terms for this file may be
  *  found in the file LICENSE in this distribution or at
@@ -72,6 +72,8 @@ typedef struct {
     _Scheduler_simple_SMP_Ask_for_help, \
     _Scheduler_simple_SMP_Reconsider_help_request, \
     _Scheduler_simple_SMP_Withdraw_node, \
+    _Scheduler_default_Pin_or_unpin, \
+    _Scheduler_default_Pin_or_unpin, \
     _Scheduler_simple_SMP_Add_processor, \
     _Scheduler_simple_SMP_Remove_processor, \
     _Scheduler_simple_SMP_Node_initialize, \
diff --git a/cpukit/include/rtems/score/schedulersmpimpl.h b/cpukit/include/rtems/score/schedulersmpimpl.h
index 09c95662b6..d34d926fc6 100644
--- a/cpukit/include/rtems/score/schedulersmpimpl.h
+++ b/cpukit/include/rtems/score/schedulersmpimpl.h
@@ -1474,6 +1474,7 @@ static inline void _Scheduler_SMP_Set_affinity(
     ( *set_affinity )( context, node, arg );
     ( *enqueue )( context, node, insert_priority );
   } else {
+    _Assert( node_state == SCHEDULER_SMP_NODE_BLOCKED );
     ( *set_affinity )( context, node, arg );
   }
 }
diff --git a/cpukit/include/rtems/score/schedulerstrongapa.h b/cpukit/include/rtems/score/schedulerstrongapa.h
index d961f20c68..bc113197e7 100644
--- a/cpukit/include/rtems/score/schedulerstrongapa.h
+++ b/cpukit/include/rtems/score/schedulerstrongapa.h
@@ -7,7 +7,7 @@
  */
 
 /*
- * Copyright (c) 2013, 2016 embedded brains GmbH.  All rights reserved.
+ * Copyright (c) 2013, 2018 embedded brains GmbH.  All rights reserved.
  *
  *  embedded brains GmbH
  *  Dornierstr. 4
@@ -89,6 +89,8 @@ typedef struct {
     _Scheduler_strong_APA_Ask_for_help, \
     _Scheduler_strong_APA_Reconsider_help_request, \
     _Scheduler_strong_APA_Withdraw_node, \
+    _Scheduler_default_Pin_or_unpin, \
+    _Scheduler_default_Pin_or_unpin, \
     _Scheduler_strong_APA_Add_processor, \
     _Scheduler_strong_APA_Remove_processor, \
     _Scheduler_strong_APA_Node_initialize, \
diff --git a/cpukit/include/rtems/score/smpimpl.h b/cpukit/include/rtems/score/smpimpl.h
index 75d36ac865..532e5a6b4e 100644
--- a/cpukit/include/rtems/score/smpimpl.h
+++ b/cpukit/include/rtems/score/smpimpl.h
@@ -79,7 +79,8 @@ typedef enum {
   SMP_FATAL_MULTITASKING_START_ON_UNASSIGNED_PROCESSOR,
   SMP_FATAL_SHUTDOWN,
   SMP_FATAL_SHUTDOWN_RESPONSE,
-  SMP_FATAL_START_OF_MANDATORY_PROCESSOR_FAILED
+  SMP_FATAL_START_OF_MANDATORY_PROCESSOR_FAILED,
+  SMP_FATAL_SCHEDULER_PIN_OR_UNPIN_NOT_SUPPORTED
 } SMP_Fatal_code;
 
 static inline void _SMP_Fatal( SMP_Fatal_code code )
diff --git a/cpukit/include/rtems/score/thread.h b/cpukit/include/rtems/score/thread.h
index 7e0e2722dd..4dd1023045 100644
--- a/cpukit/include/rtems/score/thread.h
+++ b/cpukit/include/rtems/score/thread.h
@@ -259,9 +259,14 @@ typedef struct {
   Thread_Scheduler_state state;
 
   /**
-   * @brief The home scheduler control of this thread.
+   * @brief The home scheduler of this thread.
    */
-  const struct _Scheduler_Control *home;
+  const struct _Scheduler_Control *home_scheduler;
+
+  /**
+   * @brief The pinned scheduler of this thread.
+   */
+  const struct _Scheduler_Control *pinned_scheduler;
 
   /**
    * @brief The processor assigned by the current scheduler.
@@ -270,12 +275,12 @@ typedef struct {
 
   /**
    * @brief Scheduler nodes immediately available to the thread by its home
-   * scheduler instance and due to thread queue ownerships.
+   * scheduler and due to thread queue ownerships.
    *
    * This chain is protected by the thread wait lock.
    *
    * This chain is never empty.  The first scheduler node on the chain is the
-   * scheduler node of the home scheduler instance.
+   * scheduler node of the home scheduler.
    */
   Chain_Control Wait_nodes;
 
@@ -285,8 +290,12 @@ typedef struct {
    *
    * This chain is protected by the thread state lock.
    *
-   * This chain is never empty.  The first scheduler node on the chain is the
-   * scheduler node of the home scheduler instance.
+   * This chain is never empty for normal threads (the only exception are idle
+   * threads associated with an online processor which is not used by a
+   * scheduler).  In case a pinned scheduler is set for this thread, then the
+   * first scheduler node of this chain belongs to the pinned scheduler,
+   * otherwise the first scheduler node of this chain belongs to the home
+   * scheduler.
    */
   Chain_Control Scheduler_nodes;
 
@@ -313,6 +322,28 @@ typedef struct {
   Scheduler_Node *requests;
 
   /**
+   * @brief The thread pinning to current processor level.
+   *
+   * Must be touched only by the executing thread with thread dispatching
+   * disabled.  If non-zero, then the thread is pinned to its current
+   * processor.  The pin level is incremented and decremented by two.  The
+   * least-significant bit indicates that the thread was pre-empted and must
+   * undo the pinning with respect to the scheduler once the level changes from
+   * three to one.
+   *
+   * The thread pinning 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.
+   *
+   * Thread pinning should be used only for short critical sections and not all
+   * the time.  Thread pinning is a very low overhead operation in case the
+   * thread is not preempted during the pinning.
+   *
+   * @see _Thread_Pin() and _Thread_Unpin().
+   */
+  int pin_level;
+
+  /**
    * @brief The thread processor affinity set.
    */
   Processor_mask Affinity;
diff --git a/cpukit/include/rtems/score/threadimpl.h b/cpukit/include/rtems/score/threadimpl.h
index 4ab855d3ff..530035ff67 100644
--- a/cpukit/include/rtems/score/threadimpl.h
+++ b/cpukit/include/rtems/score/threadimpl.h
@@ -1025,7 +1025,7 @@ RTEMS_INLINE_ROUTINE const Scheduler_Control *_Thread_Scheduler_get_home(
 )
 {
 #if defined(RTEMS_SMP)
-  return the_thread->Scheduler.home;
+  return the_thread->Scheduler.home_scheduler;
 #else
   (void) the_thread;
   return &_Scheduler_Table[ 0 ];
@@ -1953,6 +1953,56 @@ size_t _Thread_Get_name(
   size_t                buffer_size
 );
 
+#if defined(RTEMS_SMP)
+#define THREAD_PIN_STEP 2
+
+#define THREAD_PIN_PREEMPTION 1
+
+void _Thread_Do_unpin(
+  Thread_Control  *executing,
+  Per_CPU_Control *cpu_self
+);
+#endif
+
+RTEMS_INLINE_ROUTINE void _Thread_Pin( Thread_Control *executing )
+{
+#if defined(RTEMS_SMP)
+  _Assert( executing == _Thread_Executing );
+
+  executing->Scheduler.pin_level += THREAD_PIN_STEP;
+#else
+  (void) executing;
+#endif
+}
+
+RTEMS_INLINE_ROUTINE void _Thread_Unpin(
+  Thread_Control  *executing,
+  Per_CPU_Control *cpu_self
+)
+{
+#if defined(RTEMS_SMP)
+  unsigned int pin_level;
+
+  _Assert( executing == _Thread_Executing );
+
+  pin_level = executing->Scheduler.pin_level;
+  _Assert( pin_level > 0 );
+
+  if (
+    RTEMS_PREDICT_TRUE(
+      pin_level != ( THREAD_PIN_STEP | THREAD_PIN_PREEMPTION )
+    )
+  ) {
+    executing->Scheduler.pin_level = pin_level - THREAD_PIN_STEP;
+  } else {
+    _Thread_Do_unpin( executing, cpu_self );
+  }
+#else
+  (void) executing;
+  (void) cpu_self;
+#endif
+}
+
 /** @}*/
 
 #ifdef __cplusplus
diff --git a/cpukit/rtems/src/scheduleraddprocessor.c b/cpukit/rtems/src/scheduleraddprocessor.c
index 56d6811afd..243fb6c246 100644
--- a/cpukit/rtems/src/scheduleraddprocessor.c
+++ b/cpukit/rtems/src/scheduleraddprocessor.c
@@ -73,7 +73,7 @@ rtems_status_code rtems_scheduler_add_processor(
     _Assert( idle != NULL );
     cpu->Scheduler.idle_if_online_and_unused = NULL;
 
-    idle->Scheduler.home = scheduler;
+    idle->Scheduler.home_scheduler = scheduler;
     idle->Start.initial_priority = idle_priority;
     scheduler_node =
       _Thread_Scheduler_get_node_by_index( idle, scheduler_index );
diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am
index e345f77daa..cdd9740d41 100644
--- a/cpukit/score/Makefile.am
+++ b/cpukit/score/Makefile.am
@@ -26,6 +26,7 @@ endif
 if HAS_SMP
 libscore_a_SOURCES += src/percpustatewait.c
 libscore_a_SOURCES += src/profilingsmplock.c
+libscore_a_SOURCES += src/schedulerdefaultpinunpin.c
 libscore_a_SOURCES += src/scheduleredfsmp.c
 libscore_a_SOURCES += src/schedulerpriorityaffinitysmp.c
 libscore_a_SOURCES += src/schedulerprioritysmp.c
@@ -38,6 +39,7 @@ libscore_a_SOURCES += src/schedulerdefaultaskforhelp.c
 libscore_a_SOURCES += src/schedulerdefaultsetaffinity.c
 libscore_a_SOURCES += src/schedulersmp.c
 libscore_a_SOURCES += src/schedulersmpstartidle.c
+libscore_a_SOURCES += src/threadunpin.c
 endif
 
 ## CORE_APIMUTEX_C_FILES
diff --git a/cpukit/score/src/schedulerdefaultpinunpin.c b/cpukit/score/src/schedulerdefaultpinunpin.c
new file mode 100644
index 0000000000..5fc9cca026
--- /dev/null
+++ b/cpukit/score/src/schedulerdefaultpinunpin.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 embedded brains GmbH
+ *
+ * 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.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/score/scheduler.h>
+#include <rtems/score/interr.h>
+#include <rtems/score/smpimpl.h>
+
+void _Scheduler_default_Pin_or_unpin(
+  const Scheduler_Control *scheduler,
+  Thread_Control          *the_thread,
+  Scheduler_Node          *node,
+  struct Per_CPU_Control  *cpu
+)
+{
+  (void) scheduler;
+  (void) the_thread;
+  (void) node;
+  (void) cpu;
+
+  if ( _SMP_Get_processor_count() > 1 ) {
+    _Terminate(
+      RTEMS_FATAL_SOURCE_SMP,
+      SMP_FATAL_SCHEDULER_PIN_OR_UNPIN_NOT_SUPPORTED
+    );
+  }
+}
diff --git a/cpukit/score/src/scheduleredfsmp.c b/cpukit/score/src/scheduleredfsmp.c
index 874ec3e58b..62ddbdafa0 100644
--- a/cpukit/score/src/scheduleredfsmp.c
+++ b/cpukit/score/src/scheduleredfsmp.c
@@ -143,7 +143,7 @@ static inline Scheduler_Node *_Scheduler_EDF_SMP_Get_highest_ready(
   Scheduler_EDF_SMP_Context *self;
   Scheduler_EDF_SMP_Node    *highest_ready;
   Scheduler_EDF_SMP_Node    *node;
-  uint32_t                   rqi;
+  uint8_t                    rqi;
   const Chain_Node          *tail;
   Chain_Node                *next;
 
@@ -199,7 +199,7 @@ static inline void _Scheduler_EDF_SMP_Set_scheduled(
 
 static inline Scheduler_EDF_SMP_Node *_Scheduler_EDF_SMP_Get_scheduled(
   const Scheduler_EDF_SMP_Context *self,
-  uint32_t                         rqi
+  uint8_t                          rqi
 )
 {
   return self->Ready[ rqi ].scheduled;
@@ -211,7 +211,7 @@ static inline Scheduler_Node *_Scheduler_EDF_SMP_Get_lowest_scheduled(
 )
 {
   Scheduler_EDF_SMP_Node *filter;
-  uint32_t                rqi;
+  uint8_t                 rqi;
 
   filter = _Scheduler_EDF_SMP_Node_downcast( filter_base );
   rqi = filter->ready_queue_index;
@@ -240,7 +240,7 @@ static inline void _Scheduler_EDF_SMP_Insert_ready(
 {
   Scheduler_EDF_SMP_Context     *self;
   Scheduler_EDF_SMP_Node        *node;
-  uint32_t                       rqi;
+  uint8_t                        rqi;
   Scheduler_EDF_SMP_Ready_queue *ready_queue;
   int                            generation_index;
   int                            increment;
@@ -306,7 +306,7 @@ static inline void _Scheduler_EDF_SMP_Extract_from_ready(
 {
   Scheduler_EDF_SMP_Context     *self;
   Scheduler_EDF_SMP_Node        *node;
-  uint32_t                       rqi;
+  uint8_t                        rqi;
   Scheduler_EDF_SMP_Ready_queue *ready_queue;
 
   self = _Scheduler_EDF_SMP_Get_self( context );
@@ -369,7 +369,7 @@ static inline void _Scheduler_EDF_SMP_Allocate_processor(
 {
   Scheduler_EDF_SMP_Context     *self;
   Scheduler_EDF_SMP_Node        *scheduled;
-  uint32_t                       rqi;
+  uint8_t                        rqi;
 
   (void) victim_base;
   self = _Scheduler_EDF_SMP_Get_self( context );
@@ -647,7 +647,7 @@ static inline void _Scheduler_EDF_SMP_Do_set_affinity(
 )
 {
   Scheduler_EDF_SMP_Node *node;
-  const uint32_t         *rqi;
+  const uint8_t          *rqi;
 
   node = _Scheduler_EDF_SMP_Node_downcast( node_base );
   rqi = arg;
@@ -672,16 +672,61 @@ void _Scheduler_EDF_SMP_Start_idle(
   );
 }
 
+void _Scheduler_EDF_SMP_Pin(
+  const Scheduler_Control *scheduler,
+  Thread_Control          *thread,
+  Scheduler_Node          *node_base,
+  struct Per_CPU_Control  *cpu
+)
+{
+  Scheduler_EDF_SMP_Node *node;
+  uint8_t                 rqi;
+
+  (void) scheduler;
+  node = _Scheduler_EDF_SMP_Node_downcast( node_base );
+  rqi = (uint8_t) _Per_CPU_Get_index( cpu ) + 1;
+
+  _Assert(
+    _Scheduler_SMP_Node_state( &node->Base.Base ) == SCHEDULER_SMP_NODE_BLOCKED
+  );
+
+  node = _Scheduler_EDF_SMP_Node_downcast( node_base );
+  node->ready_queue_index = rqi;
+  node->pinning_ready_queue_index = rqi;
+}
+
+void _Scheduler_EDF_SMP_Unpin(
+  const Scheduler_Control *scheduler,
+  Thread_Control          *thread,
+  Scheduler_Node          *node_base,
+  struct Per_CPU_Control  *cpu
+)
+{
+  Scheduler_EDF_SMP_Node *node;
+
+  (void) scheduler;
+  (void) cpu;
+  node = _Scheduler_EDF_SMP_Node_downcast( node_base );
+
+  _Assert(
+    _Scheduler_SMP_Node_state( &node->Base.Base ) == SCHEDULER_SMP_NODE_BLOCKED
+  );
+
+  node->ready_queue_index = node->affinity_ready_queue_index;
+  node->pinning_ready_queue_index = 0;
+}
+
 bool _Scheduler_EDF_SMP_Set_affinity(
   const Scheduler_Control *scheduler,
   Thread_Control          *thread,
-  Scheduler_Node          *node,
+  Scheduler_Node          *node_base,
   const Processor_mask    *affinity
 )
 {
-  Scheduler_Context *context;
-  Processor_mask     local_affinity;
-  uint32_t           rqi;
+  Scheduler_Context      *context;
+  Scheduler_EDF_SMP_Node *node;
+  Processor_mask          local_affinity;
+  uint8_t                 rqi;
 
   context = _Scheduler_Get_context( scheduler );
   _Processor_mask_And( &local_affinity, &context->Processors, affinity );
@@ -696,18 +741,23 @@ bool _Scheduler_EDF_SMP_Set_affinity(
     rqi = _Processor_mask_Find_last_set( &local_affinity );
   }
 
-  _Scheduler_SMP_Set_affinity(
-    context,
-    thread,
-    node,
-    &rqi,
-    _Scheduler_EDF_SMP_Do_set_affinity,
-    _Scheduler_EDF_SMP_Extract_from_ready,
-    _Scheduler_EDF_SMP_Get_highest_ready,
-    _Scheduler_EDF_SMP_Move_from_ready_to_scheduled,
-    _Scheduler_EDF_SMP_Enqueue,
-    _Scheduler_EDF_SMP_Allocate_processor
-  );
+  node = _Scheduler_EDF_SMP_Node_downcast( node_base );
+  node->affinity_ready_queue_index = rqi;
+
+  if ( node->pinning_ready_queue_index == 0 ) {
+    _Scheduler_SMP_Set_affinity(
+      context,
+      thread,
+      node_base,
+      &rqi,
+      _Scheduler_EDF_SMP_Do_set_affinity,
+      _Scheduler_EDF_SMP_Extract_from_ready,
+      _Scheduler_EDF_SMP_Get_highest_ready,
+      _Scheduler_EDF_SMP_Move_from_ready_to_scheduled,
+      _Scheduler_EDF_SMP_Enqueue,
+      _Scheduler_EDF_SMP_Allocate_processor
+    );
+  }
 
   return true;
 }
diff --git a/cpukit/score/src/threaddispatch.c b/cpukit/score/src/threaddispatch.c
index d6207bc898..a6542f51f1 100644
--- a/cpukit/score/src/threaddispatch.c
+++ b/cpukit/score/src/threaddispatch.c
@@ -9,7 +9,7 @@
  *  COPYRIGHT (c) 1989-2009.
  *  On-Line Applications Research Corporation (OAR).
  *
- *  Copyright (c) 2014, 2016 embedded brains GmbH.
+ *  Copyright (c) 2014, 2018 embedded brains GmbH.
  *
  *  The license and distribution terms for this file may be
  *  found in the file LICENSE in this distribution or at
@@ -37,11 +37,90 @@ Thread_Control *_Thread_Allocated_fp;
 CHAIN_DEFINE_EMPTY( _User_extensions_Switches_list );
 
 #if defined(RTEMS_SMP)
+static ISR_Level _Thread_Check_pinning(
+  Thread_Control  *executing,
+  Per_CPU_Control *cpu_self,
+  ISR_Level        level
+)
+{
+  unsigned int pin_level;
+
+  pin_level = executing->Scheduler.pin_level;
+
+  if (
+    RTEMS_PREDICT_FALSE( pin_level != 0 )
+      && ( pin_level & THREAD_PIN_PREEMPTION ) == 0
+  ) {
+    ISR_lock_Context         state_lock_context;
+    ISR_lock_Context         scheduler_lock_context;
+    const Scheduler_Control *pinned_scheduler;
+    Scheduler_Node          *pinned_node;
+    const Scheduler_Control *home_scheduler;
+
+    _ISR_Local_enable( level );
+
+    executing->Scheduler.pin_level = pin_level | THREAD_PIN_PREEMPTION;
+
+    _Thread_State_acquire( executing, &state_lock_context );
+
+    pinned_scheduler = _Scheduler_Get_by_CPU( cpu_self );
+    pinned_node = _Thread_Scheduler_get_node_by_index(
+      executing,
+      _Scheduler_Get_index( pinned_scheduler )
+    );
+
+    if ( _Thread_Is_ready( executing ) ) {
+      _Scheduler_Block( executing);
+    }
+
+    home_scheduler = _Thread_Scheduler_get_home( executing );
+    executing->Scheduler.pinned_scheduler = pinned_scheduler;
+
+    if ( home_scheduler != pinned_scheduler ) {
+      _Chain_Extract_unprotected( &pinned_node->Thread.Scheduler_node.Chain );
+      _Chain_Prepend_unprotected(
+        &executing->Scheduler.Scheduler_nodes,
+        &pinned_node->Thread.Scheduler_node.Chain
+      );
+    }
+
+    _Scheduler_Acquire_critical( pinned_scheduler, &scheduler_lock_context );
+
+    ( *pinned_scheduler->Operations.pin )(
+      pinned_scheduler,
+      executing,
+      pinned_node,
+      cpu_self
+    );
+
+    if ( _Thread_Is_ready( executing ) ) {
+      ( *pinned_scheduler->Operations.unblock )(
+        pinned_scheduler,
+        executing,
+        pinned_node
+      );
+    }
+
+    _Scheduler_Release_critical( pinned_scheduler, &scheduler_lock_context );
+
+    _Thread_State_release( executing, &state_lock_context );
+
+    _ISR_Local_disable( level );
+  }
+
+  return level;
+}
+
 static void _Thread_Ask_for_help( Thread_Control *the_thread )
 {
   Chain_Node       *node;
   const Chain_Node *tail;
 
+  if ( the_thread->Scheduler.pinned_scheduler != NULL ) {
+    /* Pinned threads are not allowed to ask for help */
+    return;
+  }
+
   node = _Chain_First( &the_thread->Scheduler.Scheduler_nodes );
   tail = _Chain_Immutable_tail( &the_thread->Scheduler.Scheduler_nodes );
 
@@ -77,9 +156,15 @@ static bool _Thread_Can_ask_for_help( const Thread_Control *executing )
 }
 #endif
 
-static void _Thread_Preemption_intervention( Per_CPU_Control *cpu_self )
+static ISR_Level _Thread_Preemption_intervention(
+  Thread_Control  *executing,
+  Per_CPU_Control *cpu_self,
+  ISR_Level        level
+)
 {
 #if defined(RTEMS_SMP)
+  level = _Thread_Check_pinning( executing, cpu_self, level );
+
   _Per_CPU_Acquire( cpu_self );
 
   while ( !_Chain_Is_empty( &cpu_self->Threads_in_need_for_help ) ) {
@@ -102,6 +187,8 @@ static void _Thread_Preemption_intervention( Per_CPU_Control *cpu_self )
 #else
   (void) cpu_self;
 #endif
+
+  return level;
 }
 
 static void _Thread_Post_switch_cleanup( Thread_Control *executing )
@@ -192,7 +279,7 @@ void _Thread_Do_dispatch( Per_CPU_Control *cpu_self, ISR_Level level )
   do {
     Thread_Control *heir;
 
-    _Thread_Preemption_intervention( cpu_self );
+    level = _Thread_Preemption_intervention( executing, cpu_self, level );
     heir = _Thread_Get_heir_and_make_it_executing( cpu_self );
 
     /*
diff --git a/cpukit/score/src/threadinitialize.c b/cpukit/score/src/threadinitialize.c
index c2296fbf55..401973ec9b 100644
--- a/cpukit/score/src/threadinitialize.c
+++ b/cpukit/score/src/threadinitialize.c
@@ -245,7 +245,7 @@ bool _Thread_Initialize(
 
 #if defined(RTEMS_SMP)
   RTEMS_STATIC_ASSERT( THREAD_SCHEDULER_BLOCKED == 0, Scheduler_state );
-  the_thread->Scheduler.home = scheduler;
+  the_thread->Scheduler.home_scheduler = scheduler;
   _ISR_lock_Initialize( &the_thread->Scheduler.Lock, "Thread Scheduler" );
   _Processor_mask_Assign(
     &the_thread->Scheduler.Affinity,
diff --git a/cpukit/score/src/threadunpin.c b/cpukit/score/src/threadunpin.c
new file mode 100644
index 0000000000..bf05790694
--- /dev/null
+++ b/cpukit/score/src/threadunpin.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018 embedded brains GmbH
+ *
+ * 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.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/score/schedulerimpl.h>
+
+void _Thread_Do_unpin( Thread_Control *executing, Per_CPU_Control *cpu_self )
+{
+  ISR_lock_Context         state_lock_context;
+  ISR_lock_Context         scheduler_lock_context;
+  Scheduler_Node          *pinned_node;
+  const Scheduler_Control *pinned_scheduler;
+  Scheduler_Node          *home_node;
+  const Scheduler_Control *home_scheduler;
+  const Scheduler_Control *scheduler;
+
+  _Thread_State_acquire( executing, &state_lock_context );
+
+  executing->Scheduler.pin_level = 0;
+
+  pinned_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE(
+    _Chain_First( &executing->Scheduler.Scheduler_nodes )
+  );
+  pinned_scheduler = _Scheduler_Node_get_scheduler( pinned_node );
+  home_node = _Thread_Scheduler_get_home_node( executing );
+  home_scheduler = _Thread_Scheduler_get_home( executing );
+  scheduler = pinned_scheduler;
+
+  executing->Scheduler.pinned_scheduler = NULL;
+
+  _Scheduler_Acquire_critical( scheduler, &scheduler_lock_context );
+
+  if ( _Thread_Is_ready( executing ) ) {
+    ( *scheduler->Operations.block )( scheduler, executing, pinned_node );
+  }
+
+  ( *scheduler->Operations.unpin )(
+    scheduler,
+    executing,
+    pinned_node,
+    cpu_self
+  );
+
+  if ( home_node != pinned_node ) {
+    _Scheduler_Release_critical( scheduler, &scheduler_lock_context );
+
+    _Chain_Extract_unprotected( &home_node->Thread.Scheduler_node.Chain );
+    _Chain_Prepend_unprotected(
+      &executing->Scheduler.Scheduler_nodes,
+      &home_node->Thread.Scheduler_node.Chain
+    );
+    scheduler = home_scheduler;
+
+    _Scheduler_Acquire_critical( scheduler, &scheduler_lock_context );
+  }
+
+  if ( _Thread_Is_ready( executing ) ) {
+    ( *scheduler->Operations.unblock )( scheduler, executing, home_node );
+  }
+
+  _Scheduler_Release_critical( scheduler, &scheduler_lock_context );
+
+  _Thread_State_release( executing, &state_lock_context );
+}
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 at 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..c76bfb1f1a
--- /dev/null
+++ b/testsuites/smptests/smpthreadpin01/smpthreadpin01.scn
@@ -0,0 +1,7 @@
+*** BEGIN OF TEST SMPTHREADPIN 1 ***
+*** TEST VERSION: 5.0.0.df0321fdc51b7488b03584abcb72d1883c8cbbcb
+*** TEST STATE: EXPECTED-PASS
+*** TEST BUILD: RTEMS_SMP
+*** TEST TOOLS: 7.3.0 20180125 (RTEMS 5, RSB 9670d7541e0621915e521fe76e7bb33de8cee661, Newlib d13c84eb07e35984bf7a974cd786a6cdac29e6b9)
+
+*** END OF TEST SMPTHREADPIN 1 ***
-- 
2.13.7




More information about the devel mailing list