summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cpukit/sapi/include/confdefs.h7
-rw-r--r--cpukit/score/include/rtems/score/threadq.h44
-rw-r--r--cpukit/score/include/rtems/score/threadqimpl.h16
-rw-r--r--cpukit/score/src/thread.c3
-rw-r--r--cpukit/score/src/threadinitialize.c4
-rw-r--r--cpukit/score/src/threadqops.c70
-rw-r--r--doc/user/smp.t54
-rw-r--r--testsuites/smptests/Makefile.am1
-rw-r--r--testsuites/smptests/configure.ac1
-rw-r--r--testsuites/smptests/smpmutex01/Makefile.am19
-rw-r--r--testsuites/smptests/smpmutex01/init.c326
-rw-r--r--testsuites/smptests/smpmutex01/smpmutex01.doc14
-rw-r--r--testsuites/smptests/smpmutex01/smpmutex01.scn2
13 files changed, 545 insertions, 16 deletions
diff --git a/cpukit/sapi/include/confdefs.h b/cpukit/sapi/include/confdefs.h
index 4b438ff540..66c8c7e750 100644
--- a/cpukit/sapi/include/confdefs.h
+++ b/cpukit/sapi/include/confdefs.h
@@ -1008,9 +1008,10 @@ const rtems_libio_helper rtems_fs_init_helper =
CONFIGURE_SCHEDULER_CONTROLS
};
+ #define CONFIGURE_SCHEDULER_COUNT RTEMS_ARRAY_SIZE( _Scheduler_Table )
+
#if defined(RTEMS_SMP)
- const size_t _Scheduler_Count =
- RTEMS_ARRAY_SIZE( _Scheduler_Table );
+ const size_t _Scheduler_Count = CONFIGURE_SCHEDULER_COUNT;
const Scheduler_Assignment _Scheduler_Assignments[] = {
#if defined(CONFIGURE_SMP_SCHEDULER_ASSIGNMENTS)
@@ -2970,7 +2971,7 @@ const rtems_libio_helper rtems_fs_init_helper =
( \
_Configure_Object_RAM(_tasks, sizeof(Configuration_Thread_control)) \
+ _Configure_From_workspace(_Configure_Max_Objects(_tasks) \
- * sizeof(Thread_queue_Heads)) \
+ * THREAD_QUEUE_HEADS_SIZE(CONFIGURE_SCHEDULER_COUNT)) \
+ _Configure_Max_Objects(_number_FP_tasks) \
* _Configure_From_workspace(CONTEXT_FP_SIZE) \
)
diff --git a/cpukit/score/include/rtems/score/threadq.h b/cpukit/score/include/rtems/score/threadq.h
index 06ba9f3f9d..cc07daed57 100644
--- a/cpukit/score/include/rtems/score/threadq.h
+++ b/cpukit/score/include/rtems/score/threadq.h
@@ -42,6 +42,26 @@ extern "C" {
typedef struct _Thread_Control Thread_Control;
/**
+ * @brief Thread priority queue.
+ */
+typedef struct {
+#if defined(RTEMS_SMP)
+ /**
+ * @brief Node to enqueue this queue in the FIFO chain of the corresponding
+ * heads structure.
+ *
+ * @see Thread_queue_Heads::Heads::Fifo.
+ */
+ Chain_Node Node;
+#endif
+
+ /**
+ * @brief The actual thread priority queue.
+ */
+ RBTree_Control Queue;
+} Thread_queue_Priority_queue;
+
+/**
* @brief Thread queue heads.
*
* Each thread is equipped with spare thread queue heads in case it is not
@@ -61,13 +81,19 @@ typedef struct _Thread_queue_Heads {
union {
/**
* @brief This is the FIFO discipline list.
+ *
+ * On SMP configurations this FIFO is used to enqueue the per scheduler
+ * instance priority queues of this structure. This ensures FIFO fairness
+ * among the highest priority thread of each scheduler instance.
*/
Chain_Control Fifo;
+#if !defined(RTEMS_SMP)
/**
* @brief This is the set of threads for priority discipline waiting.
*/
- RBTree_Control Priority;
+ Thread_queue_Priority_queue Priority;
+#endif
} Heads;
/**
@@ -81,8 +107,24 @@ typedef struct _Thread_queue_Heads {
* the thread queue heads dedicated to the thread queue of an object.
*/
Chain_Node Free_node;
+
+#if defined(RTEMS_SMP)
+ /**
+ * @brief One priority queue per scheduler instance.
+ */
+ Thread_queue_Priority_queue Priority[ RTEMS_ZERO_LENGTH_ARRAY ];
+#endif
} Thread_queue_Heads;
+#if defined(RTEMS_SMP)
+ #define THREAD_QUEUE_HEADS_SIZE( scheduler_count ) \
+ ( sizeof( Thread_queue_Heads ) \
+ + ( scheduler_count ) * sizeof( Thread_queue_Priority_queue ) )
+#else
+ #define THREAD_QUEUE_HEADS_SIZE( scheduler_count ) \
+ sizeof( Thread_queue_Heads )
+#endif
+
typedef struct {
/**
* @brief The thread queue heads.
diff --git a/cpukit/score/include/rtems/score/threadqimpl.h b/cpukit/score/include/rtems/score/threadqimpl.h
index 3828f415a6..bf01eb7404 100644
--- a/cpukit/score/include/rtems/score/threadqimpl.h
+++ b/cpukit/score/include/rtems/score/threadqimpl.h
@@ -22,6 +22,7 @@
#include <rtems/score/threadq.h>
#include <rtems/score/chainimpl.h>
#include <rtems/score/rbtreeimpl.h>
+#include <rtems/score/scheduler.h>
#include <rtems/score/thread.h>
#ifdef __cplusplus
@@ -51,6 +52,21 @@ typedef struct {
#endif
} Thread_queue_Syslock_queue;
+RTEMS_INLINE_ROUTINE void _Thread_queue_Heads_initialize(
+ Thread_queue_Heads *heads
+)
+{
+#if defined(RTEMS_SMP)
+ size_t i;
+
+ for ( i = 0; i < _Scheduler_Count; ++i ) {
+ _RBTree_Initialize_empty( &heads->Priority[ i ].Queue );
+ }
+#endif
+
+ _Chain_Initialize_empty( &heads->Free_chain );
+}
+
RTEMS_INLINE_ROUTINE void _Thread_queue_Queue_initialize(
Thread_queue_Queue *queue
)
diff --git a/cpukit/score/src/thread.c b/cpukit/score/src/thread.c
index e1d6d5c231..1ad7a59f32 100644
--- a/cpukit/score/src/thread.c
+++ b/cpukit/score/src/thread.c
@@ -20,6 +20,7 @@
#include <rtems/score/threadimpl.h>
#include <rtems/score/interr.h>
+#include <rtems/score/scheduler.h>
#include <rtems/score/wkspace.h>
#define THREAD_OFFSET_ASSERT( field ) \
@@ -73,7 +74,7 @@ void _Thread_Initialize_information(
&information->Free_thread_queue_heads,
_Workspace_Allocate_or_fatal_error,
_Objects_Maximum_per_allocation( maximum ),
- sizeof( Thread_queue_Heads )
+ THREAD_QUEUE_HEADS_SIZE( _Scheduler_Count )
);
}
diff --git a/cpukit/score/src/threadinitialize.c b/cpukit/score/src/threadinitialize.c
index 9a796e9bcc..bdb4370f47 100644
--- a/cpukit/score/src/threadinitialize.c
+++ b/cpukit/score/src/threadinitialize.c
@@ -142,12 +142,12 @@ bool _Thread_Initialize(
&information->Free_thread_queue_heads,
_Workspace_Allocate,
_Objects_Extend_size( &information->Objects ),
- sizeof( *the_thread->Wait.spare_heads )
+ THREAD_QUEUE_HEADS_SIZE( _Scheduler_Count )
);
if ( the_thread->Wait.spare_heads == NULL ) {
goto failed;
}
- _Chain_Initialize_empty( &the_thread->Wait.spare_heads->Free_chain );
+ _Thread_queue_Heads_initialize( the_thread->Wait.spare_heads );
/*
* Initialize the thread timer
diff --git a/cpukit/score/src/threadqops.c b/cpukit/score/src/threadqops.c
index d9dc94440e..07473f50bd 100644
--- a/cpukit/score/src/threadqops.c
+++ b/cpukit/score/src/threadqops.c
@@ -20,6 +20,7 @@
#include <rtems/score/assert.h>
#include <rtems/score/chainimpl.h>
#include <rtems/score/rbtreeimpl.h>
+#include <rtems/score/schedulerimpl.h>
static void _Thread_queue_Do_nothing_priority_change(
Thread_Control *the_thread,
@@ -150,22 +151,41 @@ static Thread_Control *_Thread_queue_FIFO_first(
return THREAD_CHAIN_NODE_TO_THREAD( first );
}
+static Thread_queue_Priority_queue *_Thread_queue_Priority_queue(
+ Thread_queue_Heads *heads,
+ const Thread_Control *the_thread
+)
+{
+#if defined(RTEMS_SMP)
+ return &heads->Priority[
+ _Scheduler_Get_index( _Scheduler_Get_own( the_thread ) )
+ ];
+#else
+ (void) the_thread;
+
+ return &heads->Heads.Priority;
+#endif
+}
+
static void _Thread_queue_Priority_priority_change(
Thread_Control *the_thread,
Priority_Control new_priority,
Thread_queue_Queue *queue
)
{
- Thread_queue_Heads *heads = queue->heads;
+ Thread_queue_Heads *heads = queue->heads;
+ Thread_queue_Priority_queue *priority_queue;
_Assert( heads != NULL );
+ priority_queue = _Thread_queue_Priority_queue( heads, the_thread );
+
_RBTree_Extract(
- &heads->Heads.Priority,
+ &priority_queue->Queue,
&the_thread->Wait.Node.RBTree
);
_RBTree_Insert(
- &heads->Heads.Priority,
+ &priority_queue->Queue,
&the_thread->Wait.Node.RBTree,
_Thread_queue_Compare_priority,
false
@@ -176,7 +196,11 @@ static void _Thread_queue_Priority_do_initialize(
Thread_queue_Heads *heads
)
{
+#if defined(RTEMS_SMP)
+ _Chain_Initialize_empty( &heads->Heads.Fifo );
+#else
_RBTree_Initialize_empty( &heads->Heads.Priority );
+#endif
}
static void _Thread_queue_Priority_do_enqueue(
@@ -184,8 +208,17 @@ static void _Thread_queue_Priority_do_enqueue(
Thread_Control *the_thread
)
{
+ Thread_queue_Priority_queue *priority_queue =
+ _Thread_queue_Priority_queue( heads, the_thread );
+
+#if defined(RTEMS_SMP)
+ if ( _RBTree_Is_empty( &priority_queue->Queue ) ) {
+ _Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
+ }
+#endif
+
_RBTree_Insert(
- &heads->Heads.Priority,
+ &priority_queue->Queue,
&the_thread->Wait.Node.RBTree,
_Thread_queue_Compare_priority,
false
@@ -197,10 +230,21 @@ static void _Thread_queue_Priority_do_extract(
Thread_Control *the_thread
)
{
+ Thread_queue_Priority_queue *priority_queue =
+ _Thread_queue_Priority_queue( heads, the_thread );
+
_RBTree_Extract(
- &heads->Heads.Priority,
+ &priority_queue->Queue,
&the_thread->Wait.Node.RBTree
);
+
+#if defined(RTEMS_SMP)
+ _Chain_Extract_unprotected( &priority_queue->Node );
+
+ if ( !_RBTree_Is_empty( &priority_queue->Queue ) ) {
+ _Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
+ }
+#endif
}
static void _Thread_queue_Priority_enqueue(
@@ -232,11 +276,19 @@ static Thread_Control *_Thread_queue_Priority_first(
Thread_queue_Heads *heads
)
{
- RBTree_Control *priority_queue = &heads->Heads.Priority;
- RBTree_Node *first;
+ Thread_queue_Priority_queue *priority_queue;
+ RBTree_Node *first;
+
+#if defined(RTEMS_SMP)
+ _Assert( !_Chain_Is_empty( &heads->Heads.Fifo ) );
+ priority_queue = (Thread_queue_Priority_queue *)
+ _Chain_First( &heads->Heads.Fifo );
+#else
+ priority_queue = &heads->Heads.Priority;
+#endif
- _Assert( !_RBTree_Is_empty( priority_queue ) );
- first = _RBTree_Minimum( priority_queue );
+ _Assert( !_RBTree_Is_empty( &priority_queue->Queue ) );
+ first = _RBTree_Minimum( &priority_queue->Queue );
return THREAD_RBTREE_NODE_TO_THREAD( first );
}
diff --git a/doc/user/smp.t b/doc/user/smp.t
index 12711e5997..db4114c564 100644
--- a/doc/user/smp.t
+++ b/doc/user/smp.t
@@ -186,6 +186,60 @@ To set the scheduler of a task see @ref{Symmetric Multiprocessing Services
SCHEDULER_IDENT - Get ID of a scheduler} and @ref{Symmetric Multiprocessing
Services TASK_SET_SCHEDULER - Set scheduler of a task}.
+@subsection Task Priority Queues
+
+Due to the support for clustered scheduling the task priority queues need
+special attention. It makes no sense to compare the priority values of two
+different scheduler instances. Thus, it is not possible to simply use one
+plain priority queue for tasks of different scheduler instances.
+
+One solution to this problem is to use two levels of queues. The top level
+queue provides FIFO ordering and contains priority queues. Each priority queue
+is associated with a scheduler instance and contains only tasks of this
+scheduler instance. Tasks are enqueued in the priority queue corresponding to
+their scheduler instance. In case this priority queue was empty, then it is
+appended to the FIFO. To dequeue a task the highest priority task of the first
+priority queue in the FIFO is selected. Then the first priority queue is
+removed from the FIFO. In case the previously first priority queue is not
+empty, then it is appended to the FIFO. So there is FIFO fairness with respect
+to the highest priority task of each scheduler instances. See also @cite{
+Brandenburg, Björn B.: A fully preemptive multiprocessor semaphore protocol for
+latency-sensitive real-time applications. In Proceedings of the 25th Euromicro
+Conference on Real-Time Systems (ECRTS 2013), pages 292–302, 2013.
+@uref{http://www.mpi-sws.org/~bbb/papers/pdf/ecrts13b.pdf}}.
+
+Such a two level queue may need a considerable amount of memory if fast enqueue
+and dequeue operations are desired (depends on the scheduler instance count).
+To mitigate this problem an approch of the FreeBSD kernel was implemented in
+RTEMS. We have the invariant that a task can be enqueued on at most one task
+queue. Thus, we need only as many queues as we have tasks. Each task is
+equipped with spare task queue which it can give to an object on demand. The
+task queue uses a dedicated memory space independent of the other memory used
+for the task itself. In case a task needs to block, then there are two options
+
+@itemize @bullet
+@item the object already has task queue, then the task enqueues itself to this
+already present queue and the spare task queue of the task is added to a list
+of free queues for this object, or
+@item otherwise, then the queue of the task is given to the object and the task
+enqueues itself to this queue.
+@end itemize
+
+In case the task is dequeued, then there are two options
+
+@itemize @bullet
+@item the task is the last task on the queue, then it removes this queue from
+the object and reclaims it for its own purpose, or
+@item otherwise, then the task removes one queue from the free list of the
+object and reclaims it for its own purpose.
+@end itemize
+
+Since there are usually more objects than tasks, this actually reduces the
+memory demands. In addition the objects contain only a pointer to the task
+queue structure. This helps to hide implementation details and makes it
+possible to use self-contained synchronization objects in Newlib and GCC (C++
+and OpenMP run-time support).
+
@subsection Scheduler Helping Protocol
The scheduler provides a helping protocol to support locking protocols like
diff --git a/testsuites/smptests/Makefile.am b/testsuites/smptests/Makefile.am
index 4b81a20b85..92f45286e5 100644
--- a/testsuites/smptests/Makefile.am
+++ b/testsuites/smptests/Makefile.am
@@ -26,6 +26,7 @@ SUBDIRS += smplock01
SUBDIRS += smpmigration01
SUBDIRS += smpmigration02
SUBDIRS += smpmrsp01
+SUBDIRS += smpmutex01
SUBDIRS += smpschedaffinity01
SUBDIRS += smpschedaffinity02
SUBDIRS += smpschedaffinity03
diff --git a/testsuites/smptests/configure.ac b/testsuites/smptests/configure.ac
index 5aee6ec34b..27e8f9c95a 100644
--- a/testsuites/smptests/configure.ac
+++ b/testsuites/smptests/configure.ac
@@ -81,6 +81,7 @@ smplock01/Makefile
smpmigration01/Makefile
smpmigration02/Makefile
smpmrsp01/Makefile
+smpmutex01/Makefile
smppsxaffinity01/Makefile
smppsxaffinity02/Makefile
smppsxsignal01/Makefile
diff --git a/testsuites/smptests/smpmutex01/Makefile.am b/testsuites/smptests/smpmutex01/Makefile.am
new file mode 100644
index 0000000000..1b9e01c447
--- /dev/null
+++ b/testsuites/smptests/smpmutex01/Makefile.am
@@ -0,0 +1,19 @@
+rtems_tests_PROGRAMS = smpmutex01
+smpmutex01_SOURCES = init.c
+
+dist_rtems_tests_DATA = smpmutex01.scn smpmutex01.doc
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
+include $(top_srcdir)/../automake/compile.am
+include $(top_srcdir)/../automake/leaf.am
+
+AM_CPPFLAGS += -I$(top_srcdir)/../support/include
+
+LINK_OBJS = $(smpmutex01_OBJECTS)
+LINK_LIBS = $(smpmutex01_LDLIBS)
+
+smpmutex01$(EXEEXT): $(smpmutex01_OBJECTS) $(smpmutex01_DEPENDENCIES)
+ @rm -f smpmutex01$(EXEEXT)
+ $(make-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/smptests/smpmutex01/init.c b/testsuites/smptests/smpmutex01/init.c
new file mode 100644
index 0000000000..1b2a1895ef
--- /dev/null
+++ b/testsuites/smptests/smpmutex01/init.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2015 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 "tmacros.h"
+
+const char rtems_test_name[] = "SMPMUTEX 1";
+
+#define SCHED_A rtems_build_name(' ', ' ', ' ', 'A')
+
+#define SCHED_B rtems_build_name(' ', ' ', ' ', 'B')
+
+#define PART_COUNT 2
+
+#define TASK_COUNT 8
+
+typedef enum {
+ REQ_WAKE_UP_MASTER = RTEMS_EVENT_0,
+ REQ_WAKE_UP_HELPER = RTEMS_EVENT_1,
+ REQ_MTX_OBTAIN = RTEMS_EVENT_2,
+ REQ_MTX_RELEASE = RTEMS_EVENT_3
+} request_id;
+
+typedef enum {
+ A_1,
+ A_2_0,
+ A_2_1,
+ M,
+ B_4,
+ B_5_0,
+ B_5_1,
+ H,
+ NONE
+} task_id;
+
+typedef struct {
+ rtems_id mtx;
+ rtems_id tasks[TASK_COUNT];
+ int generation[TASK_COUNT];
+ int expected_generation[TASK_COUNT];
+} test_context;
+
+static test_context test_instance;
+
+static void start_task(
+ test_context *ctx,
+ task_id id,
+ rtems_task_entry entry,
+ rtems_task_priority prio,
+ rtems_name scheduler
+)
+{
+ rtems_status_code sc;
+ rtems_id scheduler_id;
+
+ sc = rtems_task_create(
+ rtems_build_name('T', 'A', 'S', 'K'),
+ prio,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->tasks[id]
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_scheduler_ident(scheduler, &scheduler_id);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_set_scheduler(ctx->tasks[id], scheduler_id);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start(ctx->tasks[id], entry, id);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void send_event(test_context *ctx, task_id id, rtems_event_set events)
+{
+ rtems_status_code sc;
+
+ sc = rtems_event_send(ctx->tasks[id], 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 sync_with_helper(test_context *ctx)
+{
+ rtems_event_set events;
+
+ send_event(ctx, H, REQ_WAKE_UP_HELPER);
+ events = wait_for_events();
+ rtems_test_assert(events == REQ_WAKE_UP_MASTER);
+}
+
+static void request(test_context *ctx, task_id id, request_id req)
+{
+ send_event(ctx, id, req);
+ sync_with_helper(ctx);
+}
+
+static void obtain(test_context *ctx)
+{
+ rtems_status_code sc;
+
+ sc = rtems_semaphore_obtain(ctx->mtx, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void release(test_context *ctx)
+{
+ rtems_status_code sc;
+
+ sc = rtems_semaphore_release(ctx->mtx);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void check_generations(test_context *ctx, task_id a, task_id b)
+{
+ size_t i;
+
+ if (a != NONE) {
+ ++ctx->expected_generation[a];
+ }
+
+ if (b != NONE) {
+ ++ctx->expected_generation[b];
+ }
+
+ for (i = 0; i < TASK_COUNT; ++i) {
+ rtems_test_assert(ctx->generation[i] == ctx->expected_generation[i]);
+ }
+}
+
+static void helper(rtems_task_argument arg)
+{
+ test_context *ctx = &test_instance;
+
+ while (true) {
+ rtems_event_set events = wait_for_events();
+ rtems_test_assert(events == REQ_WAKE_UP_HELPER);
+ send_event(ctx, M, REQ_WAKE_UP_MASTER);
+ }
+}
+
+static void worker(rtems_task_argument arg)
+{
+ test_context *ctx = &test_instance;
+ task_id id = arg;
+
+ while (true) {
+ rtems_event_set events = wait_for_events();
+
+ if ((events & REQ_MTX_OBTAIN) != 0) {
+ obtain(ctx);
+ ++ctx->generation[id];
+ }
+
+ if ((events & REQ_MTX_RELEASE) != 0) {
+ release(ctx);
+ ++ctx->generation[id];
+ }
+ }
+}
+
+static void test(void)
+{
+ test_context *ctx = &test_instance;
+ rtems_status_code sc;
+
+ ctx->tasks[M] = rtems_task_self();
+ start_task(ctx, A_1, worker, 1, SCHED_A);
+ start_task(ctx, A_2_0, worker, 2, SCHED_A);
+ start_task(ctx, A_2_1, worker, 2, SCHED_A);
+ start_task(ctx, B_4, worker, 4, SCHED_B);
+ start_task(ctx, B_5_0, worker, 5, SCHED_B);
+ start_task(ctx, B_5_1, worker, 5, SCHED_B);
+ start_task(ctx, H, helper, 6, SCHED_B);
+
+ sc = rtems_semaphore_create(
+ rtems_build_name(' ', 'M', 'T', 'X'),
+ 1,
+ RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY,
+ 0,
+ &ctx->mtx
+ );
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ obtain(ctx);
+ request(ctx, A_1, REQ_MTX_OBTAIN);
+ check_generations(ctx, NONE, NONE);
+ release(ctx);
+ check_generations(ctx, A_1, NONE);
+ request(ctx, A_1, REQ_MTX_RELEASE);
+ check_generations(ctx, A_1, NONE);
+
+ obtain(ctx);
+ request(ctx, A_2_0, REQ_MTX_OBTAIN);
+ request(ctx, A_1, REQ_MTX_OBTAIN);
+ request(ctx, A_2_1, REQ_MTX_OBTAIN);
+ check_generations(ctx, NONE, NONE);
+ release(ctx);
+ check_generations(ctx, A_1, NONE);
+ request(ctx, A_1, REQ_MTX_RELEASE);
+ check_generations(ctx, A_1, A_2_0);
+ request(ctx, A_2_0, REQ_MTX_RELEASE);
+ check_generations(ctx, A_2_0, A_2_1);
+ request(ctx, A_2_1, REQ_MTX_RELEASE);
+ check_generations(ctx, A_2_1, NONE);
+
+ obtain(ctx);
+ request(ctx, B_5_0, REQ_MTX_OBTAIN);
+ request(ctx, B_4, REQ_MTX_OBTAIN);
+ request(ctx, B_5_1, REQ_MTX_OBTAIN);
+ check_generations(ctx, NONE, NONE);
+ release(ctx);
+ sync_with_helper(ctx);
+ check_generations(ctx, B_4, NONE);
+ request(ctx, B_4, REQ_MTX_RELEASE);
+ check_generations(ctx, B_4, B_5_0);
+ request(ctx, B_5_0, REQ_MTX_RELEASE);
+ check_generations(ctx, B_5_0, B_5_1);
+ request(ctx, B_5_1, REQ_MTX_RELEASE);
+ check_generations(ctx, B_5_1, NONE);
+
+ obtain(ctx);
+ request(ctx, A_2_0, REQ_MTX_OBTAIN);
+ request(ctx, B_5_0, REQ_MTX_OBTAIN);
+ request(ctx, B_5_1, REQ_MTX_OBTAIN);
+ request(ctx, B_4, REQ_MTX_OBTAIN);
+ request(ctx, A_2_1, REQ_MTX_OBTAIN);
+ request(ctx, A_1, REQ_MTX_OBTAIN);
+ check_generations(ctx, NONE, NONE);
+ release(ctx);
+ check_generations(ctx, A_1, NONE);
+ request(ctx, A_1, REQ_MTX_RELEASE);
+ check_generations(ctx, A_1, B_4);
+ request(ctx, B_4, REQ_MTX_RELEASE);
+ check_generations(ctx, B_4, A_2_0);
+ request(ctx, A_2_0, REQ_MTX_RELEASE);
+ check_generations(ctx, A_2_0, B_5_0);
+ request(ctx, B_5_0, REQ_MTX_RELEASE);
+ check_generations(ctx, B_5_0, A_2_1);
+ request(ctx, A_2_1, REQ_MTX_RELEASE);
+ check_generations(ctx, A_2_1, B_5_1);
+ request(ctx, B_5_1, REQ_MTX_RELEASE);
+ check_generations(ctx, B_5_1, NONE);
+}
+
+static void Init(rtems_task_argument arg)
+{
+ TEST_BEGIN();
+
+ if (rtems_get_processor_count() >= PART_COUNT) {
+ test();
+ }
+
+ TEST_END();
+ rtems_test_exit(0);
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+
+#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
+
+#define CONFIGURE_SMP_APPLICATION
+
+#define CONFIGURE_SMP_MAXIMUM_PROCESSORS PART_COUNT
+
+#define CONFIGURE_SCHEDULER_SIMPLE_SMP
+
+#include <rtems/scheduler.h>
+
+RTEMS_SCHEDULER_CONTEXT_SIMPLE_SMP(a);
+
+RTEMS_SCHEDULER_CONTEXT_SIMPLE_SMP(b);
+
+#define CONFIGURE_SCHEDULER_CONTROLS \
+ RTEMS_SCHEDULER_CONTROL_SIMPLE_SMP(a, SCHED_A), \
+ RTEMS_SCHEDULER_CONTROL_SIMPLE_SMP(b, SCHED_B)
+
+#define CONFIGURE_SMP_SCHEDULER_ASSIGNMENTS \
+ RTEMS_SCHEDULER_ASSIGN(0, RTEMS_SCHEDULER_ASSIGN_PROCESSOR_MANDATORY), \
+ RTEMS_SCHEDULER_ASSIGN(1, RTEMS_SCHEDULER_ASSIGN_PROCESSOR_OPTIONAL)
+
+#define CONFIGURE_MAXIMUM_TASKS TASK_COUNT
+
+#define CONFIGURE_MAXIMUM_SEMAPHORES 1
+
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+
+#define CONFIGURE_INIT_TASK_PRIORITY 3
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/smptests/smpmutex01/smpmutex01.doc b/testsuites/smptests/smpmutex01/smpmutex01.doc
new file mode 100644
index 0000000000..117d952e57
--- /dev/null
+++ b/testsuites/smptests/smpmutex01/smpmutex01.doc
@@ -0,0 +1,14 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: smpmutex01
+
+directives:
+
+ - _Thread_queue_Priority_do_enqueue()
+ - _Thread_queue_Priority_do_extract()
+ - _Thread_queue_Priority_first()
+
+concepts:
+
+ - Ensure that the thread queue priority discipline enforces FIFO fairness
+ among the highest priority thread of each scheduler instance.
diff --git a/testsuites/smptests/smpmutex01/smpmutex01.scn b/testsuites/smptests/smpmutex01/smpmutex01.scn
new file mode 100644
index 0000000000..aad67a75c3
--- /dev/null
+++ b/testsuites/smptests/smpmutex01/smpmutex01.scn
@@ -0,0 +1,2 @@
+*** BEGIN OF TEST SMPMUTEX 1 ***
+*** END OF TEST SMPMUTEX 1 ***