Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:27

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSScoreThread
0007  *
0008  * @brief This source file contains the implementation of
0009  *   _Thread_Priority_add(), _Thread_Priority_and_sticky_update(),
0010  *   _Thread_Priority_changed(), _Thread_Priority_perform_actions(),
0011  *   _Thread_Priority_remove(), _Thread_Priority_replace(), and
0012  *   _Thread_Priority_update().
0013  */
0014 
0015 /*
0016  *  COPYRIGHT (c) 1989-2014.
0017  *  On-Line Applications Research Corporation (OAR).
0018  *
0019  *  Copyright (C) 2013, 2016 embedded brains GmbH & Co. KG
0020  *
0021  * Redistribution and use in source and binary forms, with or without
0022  * modification, are permitted provided that the following conditions
0023  * are met:
0024  * 1. Redistributions of source code must retain the above copyright
0025  *    notice, this list of conditions and the following disclaimer.
0026  * 2. Redistributions in binary form must reproduce the above copyright
0027  *    notice, this list of conditions and the following disclaimer in the
0028  *    documentation and/or other materials provided with the distribution.
0029  *
0030  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0031  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0032  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0033  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0034  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0035  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0036  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0037  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0038  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0039  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0040  * POSSIBILITY OF SUCH DAMAGE.
0041  */
0042 
0043 #ifdef HAVE_CONFIG_H
0044 #include "config.h"
0045 #endif
0046 
0047 #include <rtems/score/threadimpl.h>
0048 #include <rtems/score/assert.h>
0049 #include <rtems/score/schedulerimpl.h>
0050 
0051 static void _Thread_Set_scheduler_node_priority(
0052   Priority_Aggregation *priority_aggregation,
0053   Priority_Group_order  priority_group_order
0054 )
0055 {
0056   _Scheduler_Node_set_priority(
0057     SCHEDULER_NODE_OF_WAIT_PRIORITY_NODE( priority_aggregation ),
0058     _Priority_Get_priority( priority_aggregation ),
0059     priority_group_order
0060   );
0061 }
0062 
0063 #if defined(RTEMS_SMP)
0064 static void _Thread_Priority_action_add(
0065   Priority_Aggregation *priority_aggregation,
0066   Priority_Actions     *priority_actions,
0067   void                 *arg
0068 )
0069 {
0070   Scheduler_Node *scheduler_node;
0071   Thread_Control *the_thread;
0072 
0073   scheduler_node = SCHEDULER_NODE_OF_WAIT_PRIORITY( priority_aggregation );
0074   the_thread = arg;
0075 
0076   _Thread_Scheduler_add_wait_node( the_thread, scheduler_node );
0077   _Thread_Set_scheduler_node_priority(
0078     priority_aggregation,
0079     PRIORITY_GROUP_LAST
0080   );
0081   _Priority_Set_action_type( priority_aggregation, PRIORITY_ACTION_ADD );
0082   _Priority_Actions_add( priority_actions, priority_aggregation );
0083 }
0084 
0085 static void _Thread_Priority_action_remove(
0086   Priority_Aggregation *priority_aggregation,
0087   Priority_Actions     *priority_actions,
0088   void                 *arg
0089 )
0090 {
0091   Scheduler_Node *scheduler_node;
0092   Thread_Control *the_thread;
0093 
0094   scheduler_node = SCHEDULER_NODE_OF_WAIT_PRIORITY( priority_aggregation );
0095   the_thread = arg;
0096 
0097   _Thread_Scheduler_remove_wait_node( the_thread, scheduler_node );
0098   _Thread_Set_scheduler_node_priority(
0099     priority_aggregation,
0100     PRIORITY_GROUP_FIRST
0101   );
0102   _Priority_Set_action_type( priority_aggregation, PRIORITY_ACTION_REMOVE );
0103   _Priority_Actions_add( priority_actions, priority_aggregation );
0104 }
0105 #endif
0106 
0107 static void _Thread_Priority_action_change(
0108   Priority_Aggregation *priority_aggregation,
0109   Priority_Group_order  priority_group_order,
0110   Priority_Actions     *priority_actions,
0111   void                 *arg
0112 )
0113 {
0114   (void) arg;
0115   _Thread_Set_scheduler_node_priority(
0116     priority_aggregation,
0117     priority_group_order
0118   );
0119 #if defined(RTEMS_SMP) || defined(RTEMS_DEBUG)
0120   _Priority_Set_action_type( priority_aggregation, PRIORITY_ACTION_CHANGE );
0121 #endif
0122   _Priority_Actions_add( priority_actions, priority_aggregation );
0123 }
0124 
0125 static void _Thread_Priority_do_perform_actions(
0126   Thread_Control                *the_thread,
0127   Thread_queue_Queue            *queue,
0128   const Thread_queue_Operations *operations,
0129   Priority_Group_order           priority_group_order,
0130   Thread_queue_Context          *queue_context
0131 )
0132 {
0133   Priority_Aggregation *priority_aggregation;
0134 
0135   _Assert( !_Priority_Actions_is_empty( &queue_context->Priority.Actions ) );
0136   priority_aggregation = _Priority_Actions_move( &queue_context->Priority.Actions );
0137 
0138   do {
0139 #if defined(RTEMS_SMP)
0140     Priority_Aggregation *next_aggregation;
0141 #endif
0142     Priority_Node        *priority_action_node;
0143     Priority_Action_type  priority_action_type;
0144 
0145 #if defined(RTEMS_SMP)
0146     next_aggregation = _Priority_Get_next_action( priority_aggregation );
0147 #endif
0148 
0149     priority_action_node = priority_aggregation->Action.node;
0150     priority_action_type = priority_aggregation->Action.type;
0151 
0152     switch ( priority_action_type ) {
0153       case PRIORITY_ACTION_ADD:
0154 #if defined(RTEMS_SMP)
0155         _Priority_Insert(
0156           priority_aggregation,
0157           priority_action_node,
0158           &queue_context->Priority.Actions,
0159           _Thread_Priority_action_add,
0160           _Thread_Priority_action_change,
0161           the_thread
0162         );
0163 #else
0164         _Priority_Non_empty_insert(
0165           priority_aggregation,
0166           priority_action_node,
0167           &queue_context->Priority.Actions,
0168           _Thread_Priority_action_change,
0169           NULL
0170         );
0171 #endif
0172         break;
0173       case PRIORITY_ACTION_REMOVE:
0174 #if defined(RTEMS_SMP)
0175         _Priority_Extract(
0176           priority_aggregation,
0177           priority_action_node,
0178           &queue_context->Priority.Actions,
0179           _Thread_Priority_action_remove,
0180           _Thread_Priority_action_change,
0181           the_thread
0182         );
0183 #else
0184         _Priority_Extract_non_empty(
0185           priority_aggregation,
0186           priority_action_node,
0187           &queue_context->Priority.Actions,
0188           _Thread_Priority_action_change,
0189           NULL
0190         );
0191 #endif
0192         break;
0193       default:
0194         _Assert( priority_action_type == PRIORITY_ACTION_CHANGE );
0195         _Priority_Changed(
0196           priority_aggregation,
0197           priority_action_node,
0198           priority_group_order,
0199           &queue_context->Priority.Actions,
0200           _Thread_Priority_action_change,
0201           NULL
0202         );
0203         break;
0204     }
0205 
0206 #if defined(RTEMS_SMP)
0207     priority_aggregation = next_aggregation;
0208   } while ( priority_aggregation != NULL );
0209 #else
0210   } while ( false );
0211 #endif
0212 
0213   if ( !_Priority_Actions_is_empty( &queue_context->Priority.Actions ) ) {
0214     _Thread_queue_Context_add_priority_update( queue_context, the_thread );
0215 
0216     ( *operations->priority_actions )(
0217       queue,
0218       &queue_context->Priority.Actions
0219     );
0220   }
0221 }
0222 
0223 void _Thread_Priority_perform_actions(
0224   Thread_Control       *start_of_path,
0225   Thread_queue_Context *queue_context
0226 )
0227 {
0228   Thread_Control *the_thread;
0229   size_t          update_count;
0230 
0231   _Assert( start_of_path != NULL );
0232 
0233   /*
0234    * This function is tricky on SMP configurations.  Please note that we do not
0235    * use the thread queue path available via the thread queue context.  Instead
0236    * we directly use the thread wait information to traverse the thread queue
0237    * path.  Thus, we do not necessarily acquire all thread queue locks on our
0238    * own.  In case of a deadlock, we use locks acquired by other processors
0239    * along the path.
0240    */
0241 
0242   the_thread = start_of_path;
0243   update_count = _Thread_queue_Context_get_priority_updates( queue_context );
0244 
0245   while ( true ) {
0246     Thread_queue_Queue *queue;
0247 
0248     queue = the_thread->Wait.queue;
0249 
0250     _Thread_Priority_do_perform_actions(
0251       the_thread,
0252       queue,
0253       the_thread->Wait.operations,
0254       PRIORITY_GROUP_LAST,
0255       queue_context
0256     );
0257 
0258     if ( _Priority_Actions_is_empty( &queue_context->Priority.Actions ) ) {
0259       return;
0260     }
0261 
0262     _Assert( queue != NULL );
0263     the_thread = queue->owner;
0264     _Assert( the_thread != NULL );
0265 
0266     /*
0267      * In case the priority action list is non-empty, then the current thread
0268      * is enqueued on a thread queue.  There is no need to notify the scheduler
0269      * about a priority change, since it will pick up the new priority once it
0270      * is unblocked.  Restore the previous set of threads bound to update the
0271      * priority.
0272      */
0273     _Thread_queue_Context_restore_priority_updates(
0274       queue_context,
0275       update_count
0276     );
0277   }
0278 }
0279 
0280 static void _Thread_Priority_apply(
0281   Thread_Control       *the_thread,
0282   Priority_Node        *priority_action_node,
0283   Thread_queue_Context *queue_context,
0284   Priority_Group_order  priority_group_order,
0285   Priority_Action_type  priority_action_type
0286 )
0287 {
0288   Scheduler_Node     *scheduler_node;
0289   Thread_queue_Queue *queue;
0290 
0291   scheduler_node = _Thread_Scheduler_get_home_node( the_thread );
0292   _Priority_Actions_initialize_one(
0293     &queue_context->Priority.Actions,
0294     &scheduler_node->Wait.Priority,
0295     priority_action_node,
0296     priority_action_type
0297   );
0298   queue = the_thread->Wait.queue;
0299 
0300 #if defined(RTEMS_SMP)
0301   if ( queue != NULL ) {
0302     /*
0303      * If the thread waits on a thread queue, then we have to acquire the path
0304      * before the priority action prepared above is carried out.  For example,
0305      * when a priority inheritance queue is involved, the priority action may
0306      * produce more actions using scheduler nodes of other threads.  Using
0307      * these nodes is only safe once the thread queue path is acquired.
0308      */
0309     (void) _Thread_queue_Path_acquire( queue, the_thread, queue_context );
0310   }
0311 #endif
0312 
0313   _Thread_Priority_do_perform_actions(
0314     the_thread,
0315     queue,
0316     the_thread->Wait.operations,
0317     priority_group_order,
0318     queue_context
0319   );
0320 
0321   if ( !_Priority_Actions_is_empty( &queue_context->Priority.Actions ) ) {
0322     _Thread_Priority_perform_actions( queue->owner, queue_context );
0323   }
0324 
0325 #if defined(RTEMS_SMP)
0326   if (queue != NULL ) {
0327     _Thread_queue_Path_release( queue_context );
0328   }
0329 #endif
0330 }
0331 
0332 void _Thread_Priority_add(
0333   Thread_Control       *the_thread,
0334   Priority_Node        *priority_node,
0335   Thread_queue_Context *queue_context
0336 )
0337 {
0338   _Thread_Priority_apply(
0339     the_thread,
0340     priority_node,
0341     queue_context,
0342     PRIORITY_GROUP_LAST,
0343     PRIORITY_ACTION_ADD
0344   );
0345 }
0346 
0347 void _Thread_Priority_remove(
0348   Thread_Control       *the_thread,
0349   Priority_Node        *priority_node,
0350   Thread_queue_Context *queue_context
0351 )
0352 {
0353   _Thread_Priority_apply(
0354     the_thread,
0355     priority_node,
0356     queue_context,
0357     PRIORITY_GROUP_FIRST,
0358     PRIORITY_ACTION_REMOVE
0359   );
0360 }
0361 
0362 void _Thread_Priority_changed(
0363   Thread_Control       *the_thread,
0364   Priority_Node        *priority_node,
0365   Priority_Group_order  priority_group_order,
0366   Thread_queue_Context *queue_context
0367 )
0368 {
0369   _Thread_Priority_apply(
0370     the_thread,
0371     priority_node,
0372     queue_context,
0373     priority_group_order,
0374     PRIORITY_ACTION_CHANGE
0375   );
0376 }
0377 
0378 #if defined(RTEMS_SMP)
0379 void _Thread_Priority_replace(
0380   Thread_Control *the_thread,
0381   Priority_Node  *victim_node,
0382   Priority_Node  *replacement_node
0383 )
0384 {
0385   Scheduler_Node *scheduler_node;
0386 
0387   scheduler_node = _Thread_Scheduler_get_home_node( the_thread );
0388   _Priority_Replace(
0389     &scheduler_node->Wait.Priority,
0390     victim_node,
0391     replacement_node
0392   );
0393 }
0394 #endif
0395 
0396 void _Thread_Priority_update( Thread_queue_Context *queue_context )
0397 {
0398   size_t i;
0399   size_t n;
0400 
0401   n = queue_context->Priority.update_count;
0402 
0403   /*
0404    * Update the priority of all threads of the set.  Do not care to clear the
0405    * set, since the thread queue context will soon get destroyed anyway.
0406    */
0407   for ( i = 0; i < n ; ++i ) {
0408     Thread_Control   *the_thread;
0409     ISR_lock_Context  lock_context;
0410 
0411     the_thread = queue_context->Priority.update[ i ];
0412     _Thread_State_acquire( the_thread, &lock_context );
0413     _Scheduler_Update_priority( the_thread );
0414     _Thread_State_release( the_thread, &lock_context );
0415   }
0416 }
0417 
0418 #if defined(RTEMS_SMP)
0419 static void _Thread_Priority_update_helping(
0420   Thread_Control *the_thread,
0421   Chain_Node     *first_node
0422 )
0423 {
0424   const Chain_Node *tail;
0425   Chain_Node       *node;
0426 
0427   tail = _Chain_Immutable_tail( &the_thread->Scheduler.Scheduler_nodes );
0428   node = _Chain_Next( first_node );
0429 
0430   while ( node != tail ) {
0431     Scheduler_Node          *scheduler_node;
0432     const Scheduler_Control *scheduler;
0433     ISR_lock_Context         lock_context;
0434 
0435     scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node );
0436     scheduler = _Scheduler_Node_get_scheduler( scheduler_node );
0437 
0438     _Scheduler_Acquire_critical( scheduler, &lock_context );
0439     ( *scheduler->Operations.update_priority )(
0440       scheduler,
0441       the_thread,
0442       scheduler_node
0443     );
0444     _Scheduler_Release_critical( scheduler, &lock_context );
0445 
0446     node = _Chain_Next( node );
0447   }
0448 }
0449 
0450 void _Thread_Priority_update_and_make_sticky( Thread_Control *the_thread )
0451 {
0452   ISR_lock_Context         lock_context;
0453   ISR_lock_Context         lock_context_2;
0454   Chain_Node              *node;
0455   Scheduler_Node          *scheduler_node;
0456   const Scheduler_Control *scheduler;
0457   int                      new_sticky_level;
0458   int                      make_sticky_level;
0459 
0460   _Thread_State_acquire( the_thread, &lock_context );
0461   _Thread_Scheduler_process_requests( the_thread );
0462 
0463   node = _Chain_First( &the_thread->Scheduler.Scheduler_nodes );
0464   scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node );
0465   scheduler = _Scheduler_Node_get_scheduler( scheduler_node );
0466 
0467   _Scheduler_Acquire_critical( scheduler, &lock_context_2 );
0468 
0469   new_sticky_level = scheduler_node->sticky_level + 1;
0470   scheduler_node->sticky_level = new_sticky_level;
0471   _Assert( new_sticky_level >= 1 );
0472 
0473   /*
0474    * The sticky level is incremented by the scheduler block operation, so for a
0475    * ready thread, the change to sticky happens at a level of two.
0476    */
0477   make_sticky_level = 1 + (int) _Thread_Is_ready( the_thread );
0478 
0479   if ( new_sticky_level == make_sticky_level ) {
0480     ( *scheduler->Operations.make_sticky )(
0481       scheduler,
0482       the_thread,
0483       scheduler_node
0484     );
0485   }
0486 
0487   ( *scheduler->Operations.update_priority )(
0488     scheduler,
0489     the_thread,
0490     scheduler_node
0491   );
0492 
0493   _Scheduler_Release_critical( scheduler, &lock_context_2 );
0494   _Thread_Priority_update_helping( the_thread, node );
0495   _Thread_State_release( the_thread, &lock_context );
0496 }
0497 
0498 void _Thread_Priority_update_and_clean_sticky( Thread_Control *the_thread )
0499 {
0500   ISR_lock_Context         lock_context;
0501   ISR_lock_Context         lock_context_2;
0502   Chain_Node              *node;
0503   Scheduler_Node          *scheduler_node;
0504   const Scheduler_Control *scheduler;
0505   int                      new_sticky_level;
0506   int                      clean_sticky_level;
0507 
0508   _Thread_State_acquire( the_thread, &lock_context );
0509   _Thread_Scheduler_process_requests( the_thread );
0510 
0511   node = _Chain_First( &the_thread->Scheduler.Scheduler_nodes );
0512   scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node );
0513   scheduler = _Scheduler_Node_get_scheduler( scheduler_node );
0514 
0515   _Scheduler_Acquire_critical( scheduler, &lock_context_2 );
0516 
0517   new_sticky_level = scheduler_node->sticky_level - 1;
0518   scheduler_node->sticky_level = new_sticky_level;
0519   _Assert( new_sticky_level >= 0 );
0520 
0521   /*
0522    * The sticky level is incremented by the scheduler block operation, so for a
0523    * ready thread, the change to sticky happens at a level of one.
0524    */
0525   clean_sticky_level = (int) _Thread_Is_ready( the_thread );
0526 
0527   if ( new_sticky_level == clean_sticky_level ) {
0528     ( *scheduler->Operations.clean_sticky )(
0529       scheduler,
0530       the_thread,
0531       scheduler_node
0532     );
0533   }
0534 
0535   ( *scheduler->Operations.update_priority )(
0536     scheduler,
0537     the_thread,
0538     scheduler_node
0539   );
0540 
0541   _Scheduler_Release_critical( scheduler, &lock_context_2 );
0542   _Thread_Priority_update_helping( the_thread, node );
0543   _Thread_State_release( the_thread, &lock_context );
0544 }
0545 
0546 void _Thread_Priority_update_ignore_sticky( Thread_Control *the_thread )
0547 {
0548   ISR_lock_Context lock_context;
0549 
0550   _Thread_State_acquire( the_thread, &lock_context );
0551   _Thread_Scheduler_process_requests( the_thread );
0552   _Scheduler_Update_priority( the_thread );
0553   _Thread_State_release( the_thread, &lock_context );
0554 }
0555 #endif