[PATCH] score: Add thread pin/unpin support
Sebastian Huber
sebastian.huber at embedded-brains.de
Wed Aug 29 12:25:10 UTC 2018
This is a proof of concept patch. Support for clustered scheduling is
not implemented.
Update #3508.
---
cpukit/include/rtems/score/scheduler.h | 48 +++
cpukit/include/rtems/score/scheduleredfsmp.h | 33 ++-
.../rtems/score/schedulerpriorityaffinitysmp.h | 2 +
cpukit/include/rtems/score/schedulerprioritysmp.h | 4 +-
cpukit/include/rtems/score/schedulersimplesmp.h | 4 +-
cpukit/include/rtems/score/schedulerstrongapa.h | 4 +-
cpukit/include/rtems/score/smpimpl.h | 3 +-
cpukit/include/rtems/score/thread.h | 12 +
cpukit/include/rtems/score/threadimpl.h | 49 ++++
cpukit/score/Makefile.am | 2 +
cpukit/score/src/schedulerdefaultpinunpin.c | 37 +++
cpukit/score/src/scheduleredfsmp.c | 113 +++++--
cpukit/score/src/threaddispatch.c | 57 +++-
cpukit/score/src/threadunpin.c | 40 +++
testsuites/smptests/Makefile.am | 11 +
testsuites/smptests/configure.ac | 1 +
testsuites/smptests/smpthreadpin01/init.c | 323 +++++++++++++++++++++
.../smptests/smpthreadpin01/smpthreadpin01.doc | 12 +
.../smptests/smpthreadpin01/smpthreadpin01.scn | 7 +
19 files changed, 729 insertions(+), 33 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/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/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..4c1b1b542d 100644
--- a/cpukit/include/rtems/score/thread.h
+++ b/cpukit/include/rtems/score/thread.h
@@ -313,6 +313,18 @@ typedef struct {
Scheduler_Node *requests;
/**
+ * @brief The thread pinning to a 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.
+ */
+ unsigned 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..9f9475d9fd 100644
--- a/cpukit/include/rtems/score/threadimpl.h
+++ b/cpukit/include/rtems/score/threadimpl.h
@@ -1953,6 +1953,55 @@ 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;
+
+ 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/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..19bb4e1827
--- /dev/null
+++ b/cpukit/score/src/schedulerdefaultpinunpin.c
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+#ifdef RTEMS_SMP
+ if ( _SMP_Get_processor_count() > 1 ) {
+ _Terminate(
+ RTEMS_FATAL_SOURCE_SMP,
+ SMP_FATAL_SCHEDULER_PIN_OR_UNPIN_NOT_SUPPORTED
+ );
+ }
+#endif
+}
diff --git a/cpukit/score/src/scheduleredfsmp.c b/cpukit/score/src/scheduleredfsmp.c
index 0980a336cc..315ba35fbb 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;
@@ -283,7 +283,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 );
@@ -346,7 +346,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 );
@@ -623,7 +623,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;
@@ -648,16 +648,78 @@ 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_Context *context;
+ Scheduler_EDF_SMP_Node *node;
+ uint8_t rqi;
+
+ context = _Scheduler_Get_context( scheduler );
+ rqi = (uint8_t) _Per_CPU_Get_index( cpu ) + 1;
+
+ node = _Scheduler_EDF_SMP_Node_downcast( node_base );
+ node->pinning_ready_queue_index = rqi;
+
+ _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
+ );
+}
+
+void _Scheduler_EDF_SMP_Unpin(
+ const Scheduler_Control *scheduler,
+ Thread_Control *thread,
+ Scheduler_Node *node_base,
+ struct Per_CPU_Control *cpu
+)
+{
+ Scheduler_Context *context;
+ Scheduler_EDF_SMP_Node *node;
+
+ (void) cpu;
+ context = _Scheduler_Get_context( scheduler );
+
+ node = _Scheduler_EDF_SMP_Node_downcast( node_base );
+ node->pinning_ready_queue_index = 0;
+
+ _Scheduler_SMP_Set_affinity(
+ context,
+ thread,
+ node_base,
+ &node->affinity_ready_queue_index,
+ _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
+ );
+}
+
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 );
@@ -672,18 +734,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..4693fafa7a 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,6 +37,49 @@ 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 lock_context;
+ ISR_lock_Context lock_context_2;
+ const Scheduler_Control *scheduler;
+ Scheduler_Node *node;
+
+ _ISR_Local_enable( level );
+
+ executing->Scheduler.pin_level = pin_level | THREAD_PIN_PREEMPTION;
+
+ scheduler = _Scheduler_Get_by_CPU( cpu_self );
+ node = _Thread_Scheduler_get_node_by_index(
+ executing,
+ _Scheduler_Get_index( scheduler )
+ );
+
+ _Thread_State_acquire( executing, &lock_context );
+
+ _Scheduler_Acquire_critical( scheduler, &lock_context_2 );
+ ( *scheduler->Operations.pin )( scheduler, executing, node, cpu_self );
+ _Scheduler_Release_critical( scheduler, &lock_context_2 );
+
+ _Thread_State_release( executing, &lock_context );
+
+ _ISR_Local_disable( level );
+ }
+
+ return level;
+}
+
static void _Thread_Ask_for_help( Thread_Control *the_thread )
{
Chain_Node *node;
@@ -77,9 +120,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 +151,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 +243,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/threadunpin.c b/cpukit/score/src/threadunpin.c
new file mode 100644
index 0000000000..21b52824bd
--- /dev/null
+++ b/cpukit/score/src/threadunpin.c
@@ -0,0 +1,40 @@
+/*
+ * 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 lock_context;
+ ISR_lock_Context lock_context_2;
+ const Scheduler_Control *scheduler;
+ Scheduler_Node *node;
+
+ executing->Scheduler.pin_level = 0;
+
+ scheduler = _Scheduler_Get_by_CPU( cpu_self );
+ node = _Thread_Scheduler_get_node_by_index(
+ executing,
+ _Scheduler_Get_index( scheduler )
+ );
+
+ _Thread_State_acquire_critical( executing, &lock_context );
+
+ _Scheduler_Acquire_critical( scheduler, &lock_context_2 );
+ ( *scheduler->Operations.unpin )( scheduler, executing, node, cpu_self );
+ _Scheduler_Release_critical( scheduler, &lock_context_2 );
+
+ _Thread_State_release_critical( executing, &lock_context );
+}
diff --git a/testsuites/smptests/Makefile.am b/testsuites/smptests/Makefile.am
index f0096b8eba..246e399c20 100644
--- a/testsuites/smptests/Makefile.am
+++ b/testsuites/smptests/Makefile.am
@@ -633,6 +633,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 e14149be5e..873e4b6c13 100644
--- a/testsuites/smptests/configure.ac
+++ b/testsuites/smptests/configure.ac
@@ -88,6 +88,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..7ec996b4e2
--- /dev/null
+++ b/testsuites/smptests/smpthreadpin01/init.c
@@ -0,0 +1,323 @@
+/*
+ * 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/score/threadimpl.h>
+
+#include <tmacros.h>
+
+const char rtems_test_name[] = "SMPTHREADPIN 1";
+
+typedef struct {
+ rtems_id master;
+ rtems_id event;
+ rtems_id help;
+ rtems_id busy;
+ int counter;
+} test_context;
+
+static test_context test_instance;
+
+static void set_affinity(rtems_id task, uint32_t cpu_index)
+{
+ rtems_status_code sc;
+ cpu_set_t set;
+
+ 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 suspend(rtems_id task)
+{
+ rtems_status_code sc;
+
+ sc = rtems_task_suspend(task);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+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 send_event(rtems_id task, rtems_event_set event)
+{
+ rtems_status_code sc;
+
+ sc = rtems_event_send(task, event);
+ 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 checkpoint(test_context *ctx, int expected_counter)
+{
+ rtems_test_assert(ctx->counter == expected_counter);
+ ctx->counter = expected_counter + 1;
+}
+
+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();
+
+ if ((events & RTEMS_EVENT_0) != 0) {
+ send_event(ctx->master, RTEMS_EVENT_0);
+ }
+
+ if ((events & RTEMS_EVENT_1) != 0) {
+ send_event(ctx->help, RTEMS_EVENT_1);
+ }
+ }
+}
+
+static void help_task(rtems_task_argument arg)
+{
+ test_context *ctx;
+ rtems_task_priority prio;
+
+ ctx = (test_context *) arg;
+
+ checkpoint(ctx, 0);
+
+ prio = set_prio(ctx->master, 3);
+ rtems_test_assert(prio == 5);
+
+ wait_for_events();
+ checkpoint(ctx, 2);
+ send_event(ctx->master, RTEMS_EVENT_0);
+
+ prio = set_prio(ctx->busy, 6);
+ rtems_test_assert(prio == 2);
+ set_affinity(ctx->busy, 1);
+ set_affinity(ctx->event, 0);
+ send_event(ctx->event, RTEMS_EVENT_1);
+ wait_for_events();
+ prio = set_prio(ctx->busy, 8);
+ rtems_test_assert(prio == 6);
+ set_affinity(ctx->busy, 0);
+ set_affinity(ctx->master, 0);
+ checkpoint(ctx, 4);
+
+ suspend(RTEMS_SELF);
+}
+
+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 void simple_pin_unpin(void)
+{
+ Per_CPU_Control *cpu_self;
+ Thread_Control *executing;
+
+ 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 pin_wait_unpin(test_context *ctx)
+{
+ Per_CPU_Control *cpu_self;
+ Thread_Control *executing;
+
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ cpu_self = _Thread_Dispatch_disable();
+ executing = _Per_CPU_Get_executing(cpu_self);
+ _Thread_Pin(executing);
+ _Thread_Dispatch_enable(cpu_self);
+
+ send_event(ctx->help, RTEMS_EVENT_0);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+ wait_for_events();
+ rtems_test_assert(rtems_get_current_processor() == 1);
+ checkpoint(ctx, 3);
+
+ cpu_self = _Thread_Dispatch_disable();
+ _Thread_Unpin(executing, cpu_self);
+ _Thread_Dispatch_enable(cpu_self);
+
+ rtems_test_assert(rtems_get_current_processor() == 1);
+}
+
+static void pin_preempt_unpin(test_context *ctx)
+{
+ Per_CPU_Control *cpu_self;
+ Thread_Control *executing;
+ rtems_task_priority prio;
+
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ cpu_self = _Thread_Dispatch_disable();
+ executing = _Per_CPU_Get_executing(cpu_self);
+ _Thread_Pin(executing);
+ _Thread_Dispatch_enable(cpu_self);
+
+ rtems_test_assert(rtems_get_current_processor() == 1);
+ prio = set_prio(RTEMS_SELF, 7);
+ rtems_test_assert(prio == 3);
+
+ rtems_test_assert(rtems_get_current_processor() == 1);
+ checkpoint(ctx, 5);
+
+ cpu_self = _Thread_Dispatch_disable();
+ _Thread_Unpin(executing, cpu_self);
+ _Thread_Dispatch_enable(cpu_self);
+
+ rtems_test_assert(rtems_get_current_processor() == 0);
+}
+
+static void Init(rtems_task_argument arg)
+{
+ test_context *ctx;
+ rtems_status_code sc;
+ rtems_task_priority prio;
+
+ TEST_BEGIN();
+
+ ctx = &test_instance;
+ ctx->master = rtems_task_self();
+
+ sc = rtems_task_create(
+ rtems_build_name('B', 'U', 'S', 'Y'),
+ 2,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->busy
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ set_affinity(ctx->busy, 1);
+
+ sc = rtems_task_start(ctx->busy, busy_task, (rtems_task_argument) ctx);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ rtems_test_assert(rtems_get_current_processor() == 0);
+ set_affinity(ctx->busy, 0);
+ rtems_test_assert(rtems_get_current_processor() == 1);
+
+ sc = rtems_task_create(
+ rtems_build_name('E', 'V', 'N', 'T'),
+ 6,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->event
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ set_affinity(ctx->event, 1);
+
+ sc = rtems_task_start(ctx->event, event_task, (rtems_task_argument) ctx);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ send_event(ctx->event, RTEMS_EVENT_0);
+ wait_for_events();
+
+ sc = rtems_task_create(
+ rtems_build_name('H', 'E', 'L', 'P'),
+ 4,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->help
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ set_affinity(ctx->help, 1);
+
+ sc = rtems_task_start(ctx->help, help_task, (rtems_task_argument) ctx);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ prio = set_prio(RTEMS_SELF, 5);
+ rtems_test_assert(prio == 3);
+ checkpoint(ctx, 1);
+
+ simple_pin_unpin();
+ pin_wait_unpin(ctx);
+ pin_preempt_unpin(ctx);
+
+ 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 2
+
+#define CONFIGURE_MAXIMUM_TASKS 4
+
+#define CONFIGURE_INIT_TASK_PRIORITY 3
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#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