[PATCH 4/4] score: Implement priority boosting
Gedare Bloom
gedare at rtems.org
Thu Sep 3 15:22:15 UTC 2015
3 and 4 look good.
On Thu, Sep 3, 2015 at 8:01 AM, Sebastian Huber
<sebastian.huber at embedded-brains.de> wrote:
> ---
> cpukit/score/include/rtems/score/threadimpl.h | 31 +++++++++++++
> cpukit/score/include/rtems/score/threadqimpl.h | 25 +++++++++++
> cpukit/score/src/coremutexseize.c | 2 +-
> cpukit/score/src/coremutexsurrender.c | 1 +
> cpukit/score/src/mutex.c | 15 ++++---
> cpukit/score/src/threadchangepriority.c | 34 ++++++++++++++
> cpukit/score/src/threadqops.c | 22 ++++++++++
> doc/user/sem.t | 5 +++
> doc/user/smp.t | 6 ++-
> testsuites/smptests/smpmutex01/init.c | 61 +++++++++++++++++++++++---
> 10 files changed, 189 insertions(+), 13 deletions(-)
>
> diff --git a/cpukit/score/include/rtems/score/threadimpl.h b/cpukit/score/include/rtems/score/threadimpl.h
> index 4656881..1092b65 100644
> --- a/cpukit/score/include/rtems/score/threadimpl.h
> +++ b/cpukit/score/include/rtems/score/threadimpl.h
> @@ -437,6 +437,37 @@ void _Thread_Raise_priority(
> );
>
> /**
> + * @brief Inherit the priority of a thread.
> + *
> + * It changes the current priority of the inheritor thread to the current priority
> + * of the ancestor thread if it is higher than the current priority of the inheritor
> + * thread. In this case the inheritor thread is appended to its new priority group
> + * in its scheduler instance.
> + *
> + * On SMP configurations, the priority is changed to PRIORITY_PSEUDO_ISR in
> + * case the own schedulers of the inheritor and ancestor thread differ (priority
> + * boosting).
> + *
> + * @param[in] inheritor The thread to inherit the priority.
> + * @param[in] ancestor The thread to bequeath its priority to the inheritor
> + * thread.
> + */
> +#if defined(RTEMS_SMP)
> +void _Thread_Inherit_priority(
> + Thread_Control *inheritor,
> + Thread_Control *ancestor
> +);
> +#else
> +RTEMS_INLINE_ROUTINE void _Thread_Inherit_priority(
> + Thread_Control *inheritor,
> + Thread_Control *ancestor
> +)
> +{
> + _Thread_Raise_priority( inheritor, ancestor->current_priority );
> +}
> +#endif
> +
> +/**
> * @brief Sets the current to the real priority of a thread.
> *
> * Sets the priority restore hint to false.
> diff --git a/cpukit/score/include/rtems/score/threadqimpl.h b/cpukit/score/include/rtems/score/threadqimpl.h
> index bf01eb7..510f886 100644
> --- a/cpukit/score/include/rtems/score/threadqimpl.h
> +++ b/cpukit/score/include/rtems/score/threadqimpl.h
> @@ -522,6 +522,31 @@ RTEMS_INLINE_ROUTINE void _Thread_queue_Destroy(
> }
>
> /**
> + * @brief Boosts the priority of the thread if threads of another scheduler
> + * instance are enqueued on the thread queue.
> + *
> + * The thread queue must use the priority waiting discipline.
> + *
> + * @param[in] queue The actual thread queue.
> + * @param[in] the_thread The thread to boost the priority if necessary.
> + */
> +#if defined(RTEMS_SMP)
> +void _Thread_queue_Boost_priority(
> + Thread_queue_Queue *queue,
> + Thread_Control *the_thread
> +);
> +#else
> +RTEMS_INLINE_ROUTINE void _Thread_queue_Boost_priority(
> + Thread_queue_Queue *queue,
> + Thread_Control *the_thread
> +)
> +{
> + (void) queue;
> + (void) the_thread;
> +}
> +#endif
> +
> +/**
> * @brief Compare two thread's priority for RBTree Insertion.
> *
> * @param[in] left points to the left thread's RBnode
> diff --git a/cpukit/score/src/coremutexseize.c b/cpukit/score/src/coremutexseize.c
> index ddc5d6b..8059659 100644
> --- a/cpukit/score/src/coremutexseize.c
> +++ b/cpukit/score/src/coremutexseize.c
> @@ -75,7 +75,7 @@ void _CORE_mutex_Seize_interrupt_blocking(
> _Thread_queue_Release( &the_mutex->Wait_queue, lock_context );
> #endif
>
> - _Thread_Raise_priority( holder, executing->current_priority );
> + _Thread_Inherit_priority( holder, executing );
>
> #if !defined(RTEMS_SMP)
> _Thread_queue_Acquire( &the_mutex->Wait_queue, lock_context );
> diff --git a/cpukit/score/src/coremutexsurrender.c b/cpukit/score/src/coremutexsurrender.c
> index d3f965d..7d9c57f 100644
> --- a/cpukit/score/src/coremutexsurrender.c
> +++ b/cpukit/score/src/coremutexsurrender.c
> @@ -209,6 +209,7 @@ CORE_mutex_Status _CORE_mutex_Surrender(
> case CORE_MUTEX_DISCIPLINES_PRIORITY_INHERIT:
> _CORE_mutex_Push_priority( the_mutex, the_thread );
> the_thread->resource_count++;
> + _Thread_queue_Boost_priority( &the_mutex->Wait_queue.Queue, the_thread );
> break;
> case CORE_MUTEX_DISCIPLINES_PRIORITY_CEILING:
> _CORE_mutex_Push_priority( the_mutex, the_thread );
> diff --git a/cpukit/score/src/mutex.c b/cpukit/score/src/mutex.c
> index ae637dd..f03bab7 100644
> --- a/cpukit/score/src/mutex.c
> +++ b/cpukit/score/src/mutex.c
> @@ -112,9 +112,7 @@ static void _Mutex_Acquire_slow(
> ISR_lock_Context *lock_context
> )
> {
> - /* Priority inheritance */
> - _Thread_Raise_priority( owner, executing->current_priority );
> -
> + _Thread_Inherit_priority( owner, executing );
> _Thread_queue_Enqueue_critical(
> &mutex->Queue.Queue,
> MUTEX_TQ_OPERATIONS,
> @@ -136,15 +134,22 @@ static void _Mutex_Release_slow(
> {
> if (heads != NULL) {
> const Thread_queue_Operations *operations;
> - Thread_Control *first;
> + Thread_Control *first;
> + bool unblock;
>
> operations = MUTEX_TQ_OPERATIONS;
> first = ( *operations->first )( heads );
>
> mutex->owner = first;
> - _Thread_queue_Extract_critical(
> + unblock = _Thread_queue_Extract_locked(
> &mutex->Queue.Queue,
> operations,
> + first
> + );
> + _Thread_queue_Boost_priority( &mutex->Queue.Queue, first );
> + _Thread_queue_Unblock_critical(
> + unblock,
> + &mutex->Queue.Queue,
> first,
> lock_context
> );
> diff --git a/cpukit/score/src/threadchangepriority.c b/cpukit/score/src/threadchangepriority.c
> index 8f5d14f..35e5e5b 100644
> --- a/cpukit/score/src/threadchangepriority.c
> +++ b/cpukit/score/src/threadchangepriority.c
> @@ -110,6 +110,40 @@ void _Thread_Raise_priority(
> );
> }
>
> +#if defined(RTEMS_SMP)
> +static bool _Thread_Inherit_priority_filter(
> + Thread_Control *inheritor,
> + Priority_Control *new_priority,
> + void *arg
> +)
> +{
> + Thread_Control *ancestor = arg;
> +
> + if ( _Scheduler_Get_own( inheritor ) == _Scheduler_Get_own( ancestor ) ) {
> + *new_priority = ancestor->current_priority;
> + }
> +
> + return _Thread_Priority_less_than(
> + inheritor->current_priority,
> + *new_priority
> + );
> +}
> +
> +void _Thread_Inherit_priority(
> + Thread_Control *inheritor,
> + Thread_Control *ancestor
> +)
> +{
> + _Thread_Change_priority(
> + inheritor,
> + PRIORITY_PSEUDO_ISR,
> + ancestor,
> + _Thread_Inherit_priority_filter,
> + false
> + );
> +}
> +#endif
> +
> static bool _Thread_Restore_priority_filter(
> Thread_Control *the_thread,
> Priority_Control *new_priority,
> diff --git a/cpukit/score/src/threadqops.c b/cpukit/score/src/threadqops.c
> index 07473f5..7b01779 100644
> --- a/cpukit/score/src/threadqops.c
> +++ b/cpukit/score/src/threadqops.c
> @@ -293,6 +293,28 @@ static Thread_Control *_Thread_queue_Priority_first(
> return THREAD_RBTREE_NODE_TO_THREAD( first );
> }
>
> +#if defined(RTEMS_SMP)
> +void _Thread_queue_Boost_priority(
> + Thread_queue_Queue *queue,
> + Thread_Control *the_thread
> +)
> +{
> + Thread_queue_Heads *heads = queue->heads;
> +
> + if (
> + heads != NULL
> + && (
> + !_Chain_Has_only_one_node( &heads->Heads.Fifo )
> + || _RBTree_Is_empty(
> + &_Thread_queue_Priority_queue( heads, the_thread )->Queue
> + )
> + )
> + ) {
> + _Thread_Raise_priority( the_thread, PRIORITY_PSEUDO_ISR );
> + }
> +}
> +#endif
> +
> const Thread_queue_Operations _Thread_queue_Operations_default = {
> .priority_change = _Thread_queue_Do_nothing_priority_change,
> .extract = _Thread_queue_Do_nothing_extract
> diff --git a/doc/user/sem.t b/doc/user/sem.t
> index 6bd22dd..8fbac93 100644
> --- a/doc/user/sem.t
> +++ b/doc/user/sem.t
> @@ -115,6 +115,11 @@ for that resource. Each time a task blocks attempting to obtain
> the resource, the task holding the resource may have its
> priority increased.
>
> +On SMP configurations, in case the task holding the resource and the task that
> +blocks attempting to obtain the resource are in different scheduler instances,
> +the priority of the holder is raised to the pseudo interrupt priority (priority
> +boosting). The pseudo interrupt priority is the highest priority.
> +
> RTEMS supports priority inheritance for local, binary
> semaphores that use the priority task wait queue blocking
> discipline. When a task of higher priority than the task
> diff --git a/doc/user/smp.t b/doc/user/smp.t
> index 53fd7b64..2ab9aaf 100644
> --- a/doc/user/smp.t
> +++ b/doc/user/smp.t
> @@ -164,11 +164,13 @@ and instruction caches. With clustered/partitioned scheduling it is possible to
> honour the cache topology of a system and thus avoid expensive cache
> synchronization traffic. It is easy to implement. The problem is to provide
> synchronization primitives for inter-partition synchronization. In RTEMS there
> -are currently three means available
> +are currently four means available
>
> @itemize @bullet
> @item events,
> - at item message queues, and
> + at item message queues,
> + at item semaphores using the @ref{Semaphore Manager Priority Inheritance}
> +protocol (priority boosting), and
> @item semaphores using the @ref{Semaphore Manager Multiprocessor Resource
> Sharing Protocol} (MrsP).
> @end itemize
> diff --git a/testsuites/smptests/smpmutex01/init.c b/testsuites/smptests/smpmutex01/init.c
> index 1b2a189..80b16fe 100644
> --- a/testsuites/smptests/smpmutex01/init.c
> +++ b/testsuites/smptests/smpmutex01/init.c
> @@ -26,7 +26,7 @@ const char rtems_test_name[] = "SMPMUTEX 1";
>
> #define PART_COUNT 2
>
> -#define TASK_COUNT 8
> +#define TASK_COUNT 9
>
> typedef enum {
> REQ_WAKE_UP_MASTER = RTEMS_EVENT_0,
> @@ -43,7 +43,8 @@ typedef enum {
> B_4,
> B_5_0,
> B_5_1,
> - H,
> + H_A,
> + H_B,
> NONE
> } task_id;
>
> @@ -111,15 +112,21 @@ static rtems_event_set wait_for_events(void)
> return events;
> }
>
> -static void sync_with_helper(test_context *ctx)
> +static void sync_with_helper_by_id(test_context *ctx, task_id id)
> {
> rtems_event_set events;
>
> - send_event(ctx, H, REQ_WAKE_UP_HELPER);
> + send_event(ctx, id, REQ_WAKE_UP_HELPER);
> events = wait_for_events();
> rtems_test_assert(events == REQ_WAKE_UP_MASTER);
> }
>
> +static void sync_with_helper(test_context *ctx)
> +{
> + sync_with_helper_by_id(ctx, H_A);
> + sync_with_helper_by_id(ctx, H_B);
> +}
> +
> static void request(test_context *ctx, task_id id, request_id req)
> {
> send_event(ctx, id, req);
> @@ -159,6 +166,24 @@ static void check_generations(test_context *ctx, task_id a, task_id b)
> }
> }
>
> +static void assert_prio(
> + test_context *ctx,
> + task_id id,
> + rtems_task_priority expected
> +)
> +{
> + rtems_task_priority actual;
> + rtems_status_code sc;
> +
> + sc = rtems_task_set_priority(
> + ctx->tasks[id],
> + RTEMS_CURRENT_PRIORITY,
> + &actual
> + );
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> + rtems_test_assert(expected == actual);
> +}
> +
> static void helper(rtems_task_argument arg)
> {
> test_context *ctx = &test_instance;
> @@ -202,7 +227,8 @@ static void test(void)
> 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);
> + start_task(ctx, H_A, helper, 3, SCHED_A);
> + start_task(ctx, H_B, helper, 6, SCHED_B);
>
> sc = rtems_semaphore_create(
> rtems_build_name(' ', 'M', 'T', 'X'),
> @@ -216,8 +242,10 @@ static void test(void)
> obtain(ctx);
> request(ctx, A_1, REQ_MTX_OBTAIN);
> check_generations(ctx, NONE, NONE);
> + assert_prio(ctx, M, 1);
> release(ctx);
> check_generations(ctx, A_1, NONE);
> + assert_prio(ctx, M, 3);
> request(ctx, A_1, REQ_MTX_RELEASE);
> check_generations(ctx, A_1, NONE);
>
> @@ -226,8 +254,11 @@ static void test(void)
> request(ctx, A_1, REQ_MTX_OBTAIN);
> request(ctx, A_2_1, REQ_MTX_OBTAIN);
> check_generations(ctx, NONE, NONE);
> + assert_prio(ctx, M, 1);
> release(ctx);
> check_generations(ctx, A_1, NONE);
> + assert_prio(ctx, M, 3);
> + assert_prio(ctx, A_1, 1);
> request(ctx, A_1, REQ_MTX_RELEASE);
> check_generations(ctx, A_1, A_2_0);
> request(ctx, A_2_0, REQ_MTX_RELEASE);
> @@ -240,8 +271,10 @@ static void test(void)
> request(ctx, B_4, REQ_MTX_OBTAIN);
> request(ctx, B_5_1, REQ_MTX_OBTAIN);
> check_generations(ctx, NONE, NONE);
> + assert_prio(ctx, M, 0);
> release(ctx);
> sync_with_helper(ctx);
> + assert_prio(ctx, M, 3);
> check_generations(ctx, B_4, NONE);
> request(ctx, B_4, REQ_MTX_RELEASE);
> check_generations(ctx, B_4, B_5_0);
> @@ -252,26 +285,44 @@ static void test(void)
>
> obtain(ctx);
> request(ctx, A_2_0, REQ_MTX_OBTAIN);
> + check_generations(ctx, NONE, NONE);
> + assert_prio(ctx, M, 2);
> request(ctx, B_5_0, REQ_MTX_OBTAIN);
> + check_generations(ctx, NONE, NONE);
> + assert_prio(ctx, M, 0);
> 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);
> + sync_with_helper(ctx);
> check_generations(ctx, A_1, NONE);
> + assert_prio(ctx, M, 3);
> + assert_prio(ctx, A_1, 0);
> request(ctx, A_1, REQ_MTX_RELEASE);
> check_generations(ctx, A_1, B_4);
> + assert_prio(ctx, A_1, 1);
> + assert_prio(ctx, B_4, 0);
> request(ctx, B_4, REQ_MTX_RELEASE);
> check_generations(ctx, B_4, A_2_0);
> + assert_prio(ctx, B_4, 4);
> + assert_prio(ctx, A_2_0, 0);
> request(ctx, A_2_0, REQ_MTX_RELEASE);
> check_generations(ctx, A_2_0, B_5_0);
> + assert_prio(ctx, A_2_0, 2);
> + assert_prio(ctx, B_5_0, 0);
> request(ctx, B_5_0, REQ_MTX_RELEASE);
> check_generations(ctx, B_5_0, A_2_1);
> + assert_prio(ctx, B_5_0, 5);
> + assert_prio(ctx, A_2_1, 0);
> request(ctx, A_2_1, REQ_MTX_RELEASE);
> check_generations(ctx, A_2_1, B_5_1);
> + assert_prio(ctx, A_2_1, 2);
> + assert_prio(ctx, B_5_1, 5);
> request(ctx, B_5_1, REQ_MTX_RELEASE);
> check_generations(ctx, B_5_1, NONE);
> + assert_prio(ctx, B_5_1, 5);
> }
>
> static void Init(rtems_task_argument arg)
> --
> 1.8.4.5
>
> _______________________________________________
> devel mailing list
> devel at rtems.org
> http://lists.rtems.org/mailman/listinfo/devel
More information about the devel
mailing list