[PATCH 1/2] score: Implement scheduler helping protocol

Gedare Bloom gedare at rtems.org
Tue Jul 8 15:22:21 UTC 2014


On Tue, Jul 8, 2014 at 11:05 AM, Sebastian Huber
<sebastian.huber at embedded-brains.de> wrote:
> The following scheduler operations return a thread in need for help
>
>     - unblock,
>     - change priority, and
>     - yield.
>
> A thread in need for help is a thread that encounters a scheduler state
> change from scheduled to ready or a thread that cannot be scheduled in
> an unblock operation.  Such a thread can ask threads which depend on
> resources owned by this thread for help.
>
> Add a new ask for help scheduler operation.  This operation is used by
> _Scheduler_Ask_for_help() to help threads in need for help returned by
> the operations mentioned above.  This operation is also used by
> _Scheduler_Thread_change_resource_root() in case the root of a resource
> sub-tree changes.  A use case is the ownership change of a resource.
>
> In case it is not possible to schedule a thread in need for help, then
> the corresponding scheduler node will be placed into the set of ready
> scheduler nodes of the scheduler instance.  Once a state change from
> ready to scheduled happens for this scheduler node it may be used to
> schedule the thread in need for help.
> ---
>  cpukit/score/Makefile.am                           |    4 +-
>  cpukit/score/include/rtems/score/mrspimpl.h        |   30 +-
>  cpukit/score/include/rtems/score/scheduler.h       |   47 ++
>  cpukit/score/include/rtems/score/schedulercbs.h    |    1 +
>  cpukit/score/include/rtems/score/scheduleredf.h    |    1 +
>  cpukit/score/include/rtems/score/schedulerimpl.h   |  578 +++++++++++++++++++-
>  .../score/include/rtems/score/schedulerpriority.h  |    1 +
>  .../rtems/score/schedulerpriorityaffinitysmp.h     |    7 +
>  .../include/rtems/score/schedulerprioritysmp.h     |    7 +
>  .../include/rtems/score/schedulerprioritysmpimpl.h |   22 +
>  cpukit/score/include/rtems/score/schedulersimple.h |    1 +
>  .../score/include/rtems/score/schedulersimplesmp.h |    7 +
>  cpukit/score/include/rtems/score/schedulersmp.h    |    5 +
>  .../score/include/rtems/score/schedulersmpimpl.h   |  369 ++++++++++---
>  cpukit/score/include/rtems/score/thread.h          |   53 ++
>  cpukit/score/include/rtems/score/threadimpl.h      |   10 +
>  cpukit/score/src/schedulerchangeroot.c             |   85 +++
>  cpukit/score/src/schedulerdefaultaskforhelp.c      |   26 +
>  cpukit/score/src/schedulerpriorityaffinitysmp.c    |   38 ++-
>  cpukit/score/src/schedulerprioritysmp.c            |   31 +-
>  cpukit/score/src/schedulersimplesmp.c              |   53 ++-
>  cpukit/score/src/schedulersmpdebug.c               |   54 ++
>  cpukit/score/src/schedulersmpstartidle.c           |    1 +
>  cpukit/score/src/schedulersmpvalidstatechanges.c   |   38 --
>  cpukit/score/src/threadinitialize.c                |    3 +
>  doc/user/smp.t                                     |   73 +++
>  26 files changed, 1391 insertions(+), 154 deletions(-)
>  create mode 100644 cpukit/score/src/schedulerchangeroot.c
>  create mode 100644 cpukit/score/src/schedulerdefaultaskforhelp.c
>  create mode 100644 cpukit/score/src/schedulersmpdebug.c
>  delete mode 100644 cpukit/score/src/schedulersmpvalidstatechanges.c
>
> diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am
> index 6caefb5..e4c373c 100644
> --- a/cpukit/score/Makefile.am
> +++ b/cpukit/score/Makefile.am
> @@ -133,13 +133,15 @@ endif
>  if HAS_SMP
>  libscore_a_SOURCES += src/percpustatewait.c
>  libscore_a_SOURCES += src/profilingsmplock.c
> -libscore_a_SOURCES += src/schedulersmpvalidstatechanges.c
> +libscore_a_SOURCES += src/schedulerchangeroot.c
>  libscore_a_SOURCES += src/schedulerpriorityaffinitysmp.c
>  libscore_a_SOURCES += src/schedulerprioritysmp.c
>  libscore_a_SOURCES += src/schedulersimplesmp.c
> +libscore_a_SOURCES += src/schedulersmpdebug.c
>  libscore_a_SOURCES += src/smp.c
>  libscore_a_SOURCES += src/cpuset.c
>  libscore_a_SOURCES += src/cpusetprintsupport.c
> +libscore_a_SOURCES += src/schedulerdefaultaskforhelp.c
>  libscore_a_SOURCES += src/schedulerdefaultgetaffinity.c
>  libscore_a_SOURCES += src/schedulerdefaultsetaffinity.c
>  libscore_a_SOURCES += src/schedulersmpstartidle.c
> diff --git a/cpukit/score/include/rtems/score/mrspimpl.h b/cpukit/score/include/rtems/score/mrspimpl.h
> index 6aa45a8..4aaa50b 100644
> --- a/cpukit/score/include/rtems/score/mrspimpl.h
> +++ b/cpukit/score/include/rtems/score/mrspimpl.h
> @@ -42,25 +42,6 @@ extern "C" {
>
>  #define MRSP_RIVAL_STATE_TIMEOUT 0x2U
>
> -RTEMS_INLINE_ROUTINE bool _MRSP_Set_root_visitor(
> -  Resource_Node *node,
> -  void *arg
> -)
> -{
> -  _Resource_Node_set_root( node, arg );
> -
> -  return false;
> -}
> -
> -RTEMS_INLINE_ROUTINE void _MRSP_Set_root(
> -  Resource_Node *top,
> -  Resource_Node *root
> -)
> -{
> -  _Resource_Node_set_root( top, root );
> -  _Resource_Iterate( top, _MRSP_Set_root_visitor, root );
> -}
> -
>  RTEMS_INLINE_ROUTINE void _MRSP_Elevate_priority(
>    MRSP_Control     *mrsp,
>    Thread_Control   *new_owner,
> @@ -197,9 +178,10 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Wait_for_ownership(
>    _Resource_Node_set_dependency( &executing->Resource_node, &mrsp->Resource );
>    previous_help_state =
>      _Scheduler_Thread_change_help_state( executing, SCHEDULER_HELP_ACTIVE_RIVAL );
> -  _MRSP_Set_root(
> -    &executing->Resource_node,
> -    _Resource_Node_get_root( owner )
> +
> +  _Scheduler_Thread_change_resource_root(
> +    executing,
> +    _Thread_Resource_node_to_thread( _Resource_Node_get_root( owner ) )
>    );
>
>    if ( timeout > 0 ) {
> @@ -241,7 +223,7 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Wait_for_ownership(
>      _Resource_Node_extract( &executing->Resource_node );
>      _Resource_Node_set_dependency( &executing->Resource_node, NULL );
>      _Scheduler_Thread_change_help_state( executing, previous_help_state );
> -    _MRSP_Set_root( &executing->Resource_node, &executing->Resource_node );
> +    _Scheduler_Thread_change_resource_root( executing, executing );
>      _MRSP_Restore_priority( mrsp, executing, initial_priority );
>
>      status = MRSP_TIMEOUT;
> @@ -334,7 +316,7 @@ RTEMS_INLINE_ROUTINE MRSP_Status _MRSP_Release(
>      _Resource_Node_add_resource( &new_owner->Resource_node, &mrsp->Resource );
>      _Resource_Set_owner( &mrsp->Resource, &new_owner->Resource_node );
>      _Scheduler_Thread_change_help_state( new_owner, SCHEDULER_HELP_ACTIVE_OWNER );
> -    _MRSP_Set_root( &new_owner->Resource_node, &new_owner->Resource_node );
> +    _Scheduler_Thread_change_resource_root( new_owner, new_owner );
>      _MRSP_Add_state( rival, MRSP_RIVAL_STATE_NEW_OWNER );
>    }
>
> diff --git a/cpukit/score/include/rtems/score/scheduler.h b/cpukit/score/include/rtems/score/scheduler.h
> index 993ae55..8da988f 100644
> --- a/cpukit/score/include/rtems/score/scheduler.h
> +++ b/cpukit/score/include/rtems/score/scheduler.h
> @@ -90,6 +90,31 @@ typedef struct {
>      bool
>    );
>
> +#if defined(RTEMS_SMP)
> +  /**
> +   * Ask for help operation.
> +   *
> +   * @param[in] scheduler The scheduler of the thread offering help.
> +   * @param[in] offers_help The thread offering help.
> +   * @param[in] needs_help The thread needing help.
> +   *
> +   * @retval needs_help It was not possible to schedule the thread needing
> +   *   help, so it is returned to continue the search for help.
> +   * @retval next_needs_help It was possible to schedule the thread needing
> +   *   help, but this displaced another thread eligible to ask for help.  So
> +   *   this thread is returned to start a new search for help.
> +   * @retval NULL It was possible to schedule the thread needing help, and no
> +   *   other thread needs help as a result.
> +   *
> +   * @see _Scheduler_Ask_for_help().
> +   */
> +  Thread_Control *( *ask_for_help )(
> +    const Scheduler_Control *scheduler,
> +    Thread_Control          *offers_help,
> +    Thread_Control          *needs_help
> +  );
> +#endif
> +
>    /** @see _Scheduler_Node_initialize() */
>    void ( *node_initialize )( const Scheduler_Control *, Thread_Control * );
>
> @@ -375,6 +400,28 @@ extern const Scheduler_Control _Scheduler_Table[];
>    extern const Scheduler_Assignment _Scheduler_Assignments[];
>  #endif
>
> +#if defined(RTEMS_SMP)
> +  /**
> +   * @brief Does nothing.
> +   *
> +   * @param[in] scheduler Unused.
> +   * @param[in] offers_help Unused.
> +   * @param[in] needs_help Unused.
> +   *
> +   * @retval NULL Always.
> +   */
> +  Thread_Control *_Scheduler_default_Ask_for_help(
> +    const Scheduler_Control *scheduler,
> +    Thread_Control          *offers_help,
> +    Thread_Control          *needs_help
> +  );
> +
> +  #define SCHEDULER_OPERATION_DEFAULT_ASK_FOR_HELP \
> +    _Scheduler_default_Ask_for_help,
> +#else
> +  #define SCHEDULER_OPERATION_DEFAULT_ASK_FOR_HELP
> +#endif
> +
>  /**
>   * @brief Does nothing.
>   *
> diff --git a/cpukit/score/include/rtems/score/schedulercbs.h b/cpukit/score/include/rtems/score/schedulercbs.h
> index 008cc91..b3381e0 100644
> --- a/cpukit/score/include/rtems/score/schedulercbs.h
> +++ b/cpukit/score/include/rtems/score/schedulercbs.h
> @@ -53,6 +53,7 @@ extern "C" {
>      _Scheduler_EDF_Block,            /* block entry point */ \
>      _Scheduler_CBS_Unblock,          /* unblock entry point */ \
>      _Scheduler_EDF_Change_priority,  /* change priority entry point */ \
> +    SCHEDULER_OPERATION_DEFAULT_ASK_FOR_HELP \
>      _Scheduler_CBS_Node_initialize,  /* node initialize entry point */ \
>      _Scheduler_default_Node_destroy, /* node destroy entry point */ \
>      _Scheduler_EDF_Update_priority,  /* update priority entry point */ \
> diff --git a/cpukit/score/include/rtems/score/scheduleredf.h b/cpukit/score/include/rtems/score/scheduleredf.h
> index 1dda767..e695691 100644
> --- a/cpukit/score/include/rtems/score/scheduleredf.h
> +++ b/cpukit/score/include/rtems/score/scheduleredf.h
> @@ -46,6 +46,7 @@ extern "C" {
>      _Scheduler_EDF_Block,            /* block entry point */ \
>      _Scheduler_EDF_Unblock,          /* unblock entry point */ \
>      _Scheduler_EDF_Change_priority,  /* change priority entry point */ \
> +    SCHEDULER_OPERATION_DEFAULT_ASK_FOR_HELP \
>      _Scheduler_EDF_Node_initialize,  /* node initialize entry point */ \
>      _Scheduler_default_Node_destroy, /* node destroy entry point */ \
>      _Scheduler_EDF_Update_priority,  /* update priority entry point */ \
> diff --git a/cpukit/score/include/rtems/score/schedulerimpl.h b/cpukit/score/include/rtems/score/schedulerimpl.h
> index 5e4e509..face5aa 100644
> --- a/cpukit/score/include/rtems/score/schedulerimpl.h
> +++ b/cpukit/score/include/rtems/score/schedulerimpl.h
> @@ -10,6 +10,7 @@
>  /*
>   *  Copyright (C) 2010 Gedare Bloom.
>   *  Copyright (C) 2011 On-Line Applications Research Corporation (OAR).
> + *  Copyright (c) 2014 embedded brains GmbH
>   *
>   *  The license and distribution terms for this file may be
>   *  found in the file LICENSE in this distribution or at
> @@ -42,6 +43,13 @@ extern "C" {
>   */
>  void _Scheduler_Handler_initialization( void );
>
> +RTEMS_INLINE_ROUTINE Scheduler_Context *_Scheduler_Get_context(
> +  const Scheduler_Control *scheduler
> +)
> +{
> +  return scheduler->context;
> +}
> +
>  RTEMS_INLINE_ROUTINE const Scheduler_Control *_Scheduler_Get(
>    const Thread_Control *the_thread
>  )
> @@ -55,6 +63,19 @@ RTEMS_INLINE_ROUTINE const Scheduler_Control *_Scheduler_Get(
>  #endif
>  }
>
> +RTEMS_INLINE_ROUTINE const Scheduler_Control *_Scheduler_Get_own(
> +  const Thread_Control *the_thread
> +)
> +{
> +#if defined(RTEMS_SMP)
> +  return the_thread->Scheduler.own_control;
> +#else
> +  (void) the_thread;
> +
> +  return &_Scheduler_Table[ 0 ];
> +#endif
> +}
> +
>  RTEMS_INLINE_ROUTINE const Scheduler_Control *_Scheduler_Get_by_CPU_index(
>    uint32_t cpu_index
>  )
> @@ -78,6 +99,13 @@ RTEMS_INLINE_ROUTINE const Scheduler_Control *_Scheduler_Get_by_CPU(
>  }
>
>  #if defined(RTEMS_SMP)
> +RTEMS_INLINE_ROUTINE Scheduler_Node *_Scheduler_Thread_get_own_node(
> +  const Thread_Control *the_thread
> +)
> +{
> +  return the_thread->Scheduler.own_node;
> +}
> +
>  RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Node_get_user(
>    const Scheduler_Node *node
>  )
> @@ -117,6 +145,39 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Schedule( Thread_Control *the_thread )
>  }
>
>  #if defined(RTEMS_SMP)
> +typedef struct {
> +  Thread_Control *needs_help;
> +  Thread_Control *next_needs_help;
> +} Scheduler_Ask_for_help_context ;
> +
> +RTEMS_INLINE_ROUTINE bool _Scheduler_Ask_for_help_visitor(
> +  Resource_Node *resource_node,
> +  void          *arg
> +)
> +{
> +  bool done;
> +  Scheduler_Ask_for_help_context *help_context = arg;
> +  Thread_Control *previous_needs_help = help_context->needs_help;
> +  Thread_Control *next_needs_help;
> +  Thread_Control *offers_help =
> +    _Thread_Resource_node_to_thread( resource_node );
> +  const Scheduler_Control *scheduler = _Scheduler_Get_own( offers_help );
> +
> +  next_needs_help = ( *scheduler->Operations.ask_for_help )(
> +    scheduler,
> +    offers_help,
> +    previous_needs_help
> +  );
> +
> +  done = next_needs_help != previous_needs_help;
> +
> +  if ( done ) {
> +    help_context->next_needs_help = next_needs_help;
> +  }
> +
> +  return done;
> +}
> +
>  /**
>   * @brief Ask threads depending on resources owned by the thread for help.
>   *
> @@ -126,11 +187,50 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Schedule( Thread_Control *the_thread )
>   *
>   * @param[in] needs_help The thread needing help.
>   */
> +RTEMS_INLINE_ROUTINE void _Scheduler_Ask_for_help(
> +  Thread_Control *needs_help
> +)
> +{
> +  do {
> +    const Scheduler_Control *scheduler = _Scheduler_Get_own( needs_help );
> +
> +    needs_help = ( *scheduler->Operations.ask_for_help )(
> +      scheduler,
> +      needs_help,
> +      needs_help
> +    );
> +
> +    if ( needs_help != NULL ) {
> +      Scheduler_Ask_for_help_context help_context = { needs_help, NULL };
> +
> +      _Resource_Iterate(
> +        &needs_help->Resource_node,
> +        _Scheduler_Ask_for_help_visitor,
> +        &help_context
> +      );
> +
> +      needs_help = help_context.next_needs_help;
> +    }
> +  } while ( needs_help != NULL );
> +}
> +
>  RTEMS_INLINE_ROUTINE void _Scheduler_Ask_for_help_if_necessary(
>    Thread_Control *needs_help
>  )
>  {
> -  (void) needs_help;
> +  if (
> +    needs_help != NULL
> +      && _Resource_Node_owns_resources( &needs_help->Resource_node )
> +  ) {
> +    Scheduler_Node *node = _Scheduler_Thread_get_own_node( needs_help );
> +
> +    if (
> +      node->help_state != SCHEDULER_HELP_ACTIVE_RIVAL
> +        || _Scheduler_Node_get_user( node ) != needs_help
> +    ) {
> +      _Scheduler_Ask_for_help( needs_help );
> +    }
> +  }
>  }
>  #endif
>
> @@ -218,7 +318,7 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Change_priority(
>    bool                     prepend_it
>  )
>  {
> -  const Scheduler_Control *scheduler = _Scheduler_Get( the_thread );
> +  const Scheduler_Control *scheduler = _Scheduler_Get_own( the_thread );
>  #if defined(RTEMS_SMP)
>    Thread_Control *needs_help;
>
> @@ -426,6 +526,7 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Set(
>    if ( current_scheduler != scheduler ) {
>      _Thread_Set_state( the_thread, STATES_MIGRATING );
>      _Scheduler_Node_destroy( current_scheduler, the_thread );
> +    the_thread->Scheduler.own_control = scheduler;
>      the_thread->Scheduler.control = scheduler;
>      _Scheduler_Node_initialize( scheduler, the_thread );
>      _Scheduler_Update_priority( the_thread, the_thread->current_priority );
> @@ -628,13 +729,6 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Change_priority_if_higher(
>    }
>  }
>
> -RTEMS_INLINE_ROUTINE Scheduler_Context *_Scheduler_Get_context(
> -  const Scheduler_Control *scheduler
> -)
> -{
> -  return scheduler->context;
> -}
> -
>  RTEMS_INLINE_ROUTINE uint32_t _Scheduler_Get_processor_count(
>    const Scheduler_Control *scheduler
>  )
> @@ -721,6 +815,29 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Node_do_initialize(
>  }
>
>  #if defined(RTEMS_SMP)
> +/**
> + * @brief Gets an idle thread from the scheduler instance.
> + *
> + * @param[in] context The scheduler instance context.
> + *
> + * @retval idle An idle thread for use.  This function must always return an
> + * idle thread.  If none is available, then this is a fatal error.
> + */
> +typedef Thread_Control *( *Scheduler_Get_idle_thread )(
> +  Scheduler_Context *context
> +);
> +
> +/**
> + * @brief Releases an idle thread to the scheduler instance for reuse.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] idle The idle thread to release
> + */
> +typedef void ( *Scheduler_Release_idle_thread )(
> +  Scheduler_Context *context,
> +  Thread_Control    *idle
> +);
> +
>  RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Node_get_owner(
>    const Scheduler_Node *node
>  )
> @@ -735,6 +852,50 @@ RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Node_get_idle(
>    return node->idle;
>  }
>
> +RTEMS_INLINE_ROUTINE void _Scheduler_Node_set_user(
> +  Scheduler_Node *node,
> +  Thread_Control *user
> +)
> +{
> +  node->user = user;
> +}
> +
> +RTEMS_INLINE_ROUTINE void _Scheduler_Thread_set_node(
> +  Thread_Control *the_thread,
> +  Scheduler_Node *node
> +)
> +{
> +  the_thread->Scheduler.node = node;
> +}
> +
> +RTEMS_INLINE_ROUTINE void _Scheduler_Thread_set_scheduler_and_node(
> +  Thread_Control       *the_thread,
> +  Scheduler_Node       *node,
> +  const Thread_Control *previous_user_of_node
> +)
> +{
> +  const Scheduler_Control *scheduler =
> +    _Scheduler_Get_own( previous_user_of_node );
> +
> +  the_thread->Scheduler.control = scheduler;
> +  _Scheduler_Thread_set_node( the_thread, node );
> +}
> +
> +extern const bool _Scheduler_Thread_state_valid_state_changes[ 3 ][ 3 ];
> +
> +RTEMS_INLINE_ROUTINE void _Scheduler_Thread_change_state(
> +  Thread_Control         *the_thread,
> +  Thread_Scheduler_state  new_state
> +)
> +{
> +  _Assert(
> +    _Scheduler_Thread_state_valid_state_changes
> +      [ the_thread->Scheduler.state ][ new_state ]
> +  );
> +
> +  the_thread->Scheduler.state = new_state;
> +}
> +
>  /**
>   * @brief Changes the scheduler help state of a thread.
>   *
> @@ -748,13 +909,410 @@ RTEMS_INLINE_ROUTINE Scheduler_Help_state _Scheduler_Thread_change_help_state(
>    Scheduler_Help_state  new_help_state
>  )
>  {
> -  Scheduler_Node *node = _Scheduler_Thread_get_node( the_thread );
> +  Scheduler_Node *node = _Scheduler_Thread_get_own_node( the_thread );
>    Scheduler_Help_state previous_help_state = node->help_state;
>
>    node->help_state = new_help_state;
>
>    return previous_help_state;
>  }
> +
> +/**
> + * @brief Changes the resource tree root of a thread.
> + *
> + * For each node of the resource sub-tree specified by the top thread the
> + * scheduler asks for help.  So the root thread gains access to all scheduler
> + * nodes corresponding to the resource sub-tree.  In case a thread previously
> + * granted help is displaced by this operation, then the scheduler asks for
> + * help using its remaining resource tree.
> + *
> + * @param[in] top The thread specifying the resource sub-tree top.
> + * @param[in] root The thread specifying the new resource sub-tree root.
> + */
> +void _Scheduler_Thread_change_resource_root(
> +  Thread_Control *top,
> +  Thread_Control *root
> +);
> +
> +/**
> + * @brief Use an idle thread for this scheduler node.
> + *
> + * A thread in the SCHEDULER_HELP_ACTIVE_OWNER owner state may use an idle
> + * thread for the scheduler node owned by itself in case it executes currently
> + * using another scheduler node or in case it is in a blocking state.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] node The node which wants to use the idle thread.
> + * @param[in] get_idle_thread Function to get an idle thread.
> + */
> +RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Use_idle_thread(
> +  Scheduler_Context         *context,
> +  Scheduler_Node            *node,
> +  Scheduler_Get_idle_thread  get_idle_thread
> +)
> +{
> +  Thread_Control *idle = ( *get_idle_thread )( context );
> +
> +  _Assert( node->help_state == SCHEDULER_HELP_ACTIVE_OWNER );
> +  _Assert( _Scheduler_Node_get_idle( node ) == NULL );
> +  _Assert(
> +    _Scheduler_Node_get_owner( node ) == _Scheduler_Node_get_user( node )
> +  );
> +
> +  _Scheduler_Thread_set_node( idle, node );
> +
> +  _Scheduler_Node_set_user( node, idle );
> +  node->idle = idle;
> +
> +  return idle;
> +}
> +
> +/**
> + * @brief Try to schedule this scheduler node.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] node The node which wants to get scheduled.
> + * @param[in] get_idle_thread Function to get an idle thread.
> + *
> + * @retval true This node can be scheduled.
> + * @retval false Otherwise.
> + */
> +RTEMS_INLINE_ROUTINE bool _Scheduler_Try_to_schedule_node(
> +  Scheduler_Context         *context,
> +  Scheduler_Node            *node,
> +  Scheduler_Get_idle_thread  get_idle_thread
> +)
> +{
> +  bool schedule;
> +  Thread_Control *owner;
> +  Thread_Control *user;
> +
> +  if ( node->help_state == SCHEDULER_HELP_YOURSELF ) {
> +    return true;
> +  }
> +
> +  owner = _Scheduler_Node_get_owner( node );
> +  user = _Scheduler_Node_get_user( node );
> +
> +  if ( node->help_state == SCHEDULER_HELP_ACTIVE_RIVAL) {
> +    if ( user->Scheduler.state == THREAD_SCHEDULER_READY ) {
> +      _Scheduler_Thread_set_scheduler_and_node( user, node, owner );
> +    } else {
> +      _Scheduler_Node_set_user( node, owner );
> +    }
> +
> +    schedule = true;
> +  } else if ( node->help_state == SCHEDULER_HELP_ACTIVE_OWNER ) {
> +    if ( user->Scheduler.state == THREAD_SCHEDULER_READY ) {
> +      _Scheduler_Thread_set_scheduler_and_node( user, node, owner );
> +    } else {
> +      _Scheduler_Use_idle_thread( context, node, get_idle_thread );
> +    }
> +
> +    schedule = true;
> +  } else {
> +    _Assert( node->help_state == SCHEDULER_HELP_PASSIVE );
> +
> +    if ( user->Scheduler.state == THREAD_SCHEDULER_READY ) {
> +      _Scheduler_Thread_set_scheduler_and_node( user, node, owner );
> +      schedule = true;
> +    } else {
> +      schedule = false;
> +    }
> +  }
> +
> +  return schedule;
> +}
> +
> +/**
> + * @brief Release an idle thread using this scheduler node.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] node The node which may have an idle thread as user.
> + * @param[in] release_idle_thread Function to release an idle thread.
> + *
> + * @retval idle The idle thread which used this node.
> + * @retval NULL This node had no idle thread as an user.
> + */
> +RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Release_idle_thread(
> +  Scheduler_Context             *context,
> +  Scheduler_Node                *node,
> +  Scheduler_Release_idle_thread  release_idle_thread
> +)
> +{
> +  Thread_Control *idle = _Scheduler_Node_get_idle( node );
> +
> +  if ( idle != NULL ) {
> +    Thread_Control *owner = _Scheduler_Node_get_owner( node );
> +
> +    node->idle = NULL;
> +    _Scheduler_Node_set_user( node, owner );
> +    _Scheduler_Thread_change_state( idle, THREAD_SCHEDULER_READY );
> +    _Scheduler_Thread_set_node( idle, idle->Scheduler.own_node );
> +
> +    ( *release_idle_thread )( context, idle );
> +  }
> +
> +  return idle;
> +}
> +
> +/**
> + * @brief Block this scheduler node.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] node The node which wants to get blocked.
> + * @param[in] is_scheduled This node is scheduled.
> + * @param[in] get_idle_thread Function to get an idle thread.
> + *
> + * @retval true Continue with the blocking operation.
> + * @retval false Otherwise.
> + */
> +RTEMS_INLINE_ROUTINE bool _Scheduler_Block_node(
> +  Scheduler_Context         *context,
> +  Scheduler_Node            *node,
> +  bool                       is_scheduled,
> +  Scheduler_Get_idle_thread  get_idle_thread
> +)
> +{
> +  bool block;
> +  Thread_Control *old_user = _Scheduler_Node_get_user( node );
> +  Thread_Control *new_user;
> +
> +  _Scheduler_Thread_change_state( old_user, THREAD_SCHEDULER_BLOCKED );
> +
> +  if ( node->help_state == SCHEDULER_HELP_ACTIVE_RIVAL ) {
> +    new_user = _Scheduler_Node_get_owner( node );
> +
> +    _Assert( new_user != old_user );
> +    _Scheduler_Node_set_user( node, new_user );
> +  } else if (
> +    node->help_state == SCHEDULER_HELP_ACTIVE_OWNER
> +      && is_scheduled
> +  ) {
> +    new_user = _Scheduler_Use_idle_thread( context, node, get_idle_thread );
> +  } else {
> +    new_user = NULL;
> +  }
> +
> +  if ( new_user != NULL && is_scheduled ) {
> +    Per_CPU_Control *cpu = _Thread_Get_CPU( old_user );
> +
> +    _Scheduler_Thread_change_state( new_user, THREAD_SCHEDULER_SCHEDULED );
> +    _Thread_Set_CPU( new_user, cpu );
> +    _Thread_Dispatch_update_heir( _Per_CPU_Get(), cpu, new_user );
> +
> +    block = false;
> +  } else {
> +    block = true;
> +  }
> +
> +  return block;
> +}
> +
> +/**
> + * @brief Unblock this scheduler node.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] the_thread The thread which wants to get unblocked.
> + * @param[in] node The node which wants to get unblocked.
> + * @param[in] is_scheduled This node is scheduled.
> + * @param[in] release_idle_thread Function to release an idle thread.
> + *
> + * @retval true Continue with the unblocking operation.
> + * @retval false Otherwise.
> + */
> +RTEMS_INLINE_ROUTINE bool _Scheduler_Unblock_node(
> +  Scheduler_Context             *context,
> +  Thread_Control                *the_thread,
> +  Scheduler_Node                *node,
> +  bool                           is_scheduled,
> +  Scheduler_Release_idle_thread  release_idle_thread
> +)
> +{
> +  bool unblock;
> +
> +  if ( is_scheduled ) {
> +    Thread_Control *old_user = _Scheduler_Node_get_user( node );
> +    Per_CPU_Control *cpu = _Thread_Get_CPU( old_user );
> +
> +    if ( node->help_state == SCHEDULER_HELP_ACTIVE_OWNER ) {
> +      Thread_Control *idle = _Scheduler_Release_idle_thread(
> +        context,
> +        node,
> +        release_idle_thread
> +      );
> +
> +      _Assert( idle != NULL );
> +      (void) idle;
> +    } else {
> +      _Assert( node->help_state == SCHEDULER_HELP_ACTIVE_RIVAL );
> +
> +      _Scheduler_Thread_change_state( old_user, THREAD_SCHEDULER_READY );
> +      _Scheduler_Node_set_user( node, the_thread );
> +    }
> +
> +    _Scheduler_Thread_change_state( the_thread, THREAD_SCHEDULER_SCHEDULED );
> +    _Thread_Set_CPU( the_thread, cpu );
> +    _Thread_Dispatch_update_heir( _Per_CPU_Get(), cpu, the_thread );
> +
> +    unblock = false;
> +  } else {
> +    _Scheduler_Thread_change_state( the_thread, THREAD_SCHEDULER_READY );
> +
> +    unblock = true;
> +  }
> +
> +  return unblock;
> +}
> +
> +/**
> + * @brief Asks a ready scheduler node for help.
> + *
> + * @param[in] node The ready node offering help.
> + * @param[in] needs_help The thread needing help.
> + *
> + * @retval needs_help The thread needing help.
> + */
> +RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Ask_ready_node_for_help(
> +  Scheduler_Node *node,
> +  Thread_Control *needs_help
> +)
> +{
> +  _Scheduler_Node_set_user( node, needs_help );
> +
> +  return needs_help;
> +}
> +
> +/**
> + * @brief Asks a scheduled scheduler node for help.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] node The scheduled node offering help.
> + * @param[in] offers_help The thread offering help.
> + * @param[in] needs_help The thread needing help.
> + * @param[in] previous_accepts_help The previous thread accepting help by this
> + *   scheduler node.
> + * @param[in] release_idle_thread Function to release an idle thread.
> + *
> + * @retval needs_help The previous thread accepting help by this scheduler node
> + *   which was displaced by the thread needing help.
> + * @retval NULL There are no more threads needing help.
> + */
> +RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Ask_scheduled_node_for_help(
> +  Scheduler_Context             *context,
> +  Scheduler_Node                *node,
> +  Thread_Control                *offers_help,
> +  Thread_Control                *needs_help,
> +  Thread_Control                *previous_accepts_help,
> +  Scheduler_Release_idle_thread  release_idle_thread
> +)
> +{
> +  Thread_Control *next_needs_help = NULL;
> +  Thread_Control *old_user = NULL;
> +  Thread_Control *new_user = NULL;
> +
> +  if (
> +    previous_accepts_help != needs_help
> +      && _Scheduler_Thread_get_node( previous_accepts_help ) == node
> +  ) {
> +    Thread_Control *idle = _Scheduler_Release_idle_thread(
> +      context,
> +      node,
> +      release_idle_thread
> +    );
> +
> +    if ( idle != NULL ) {
> +      old_user = idle;
> +    } else {
> +      _Assert( _Scheduler_Node_get_user( node ) == previous_accepts_help );
> +      old_user = previous_accepts_help;
> +    }
> +
> +    if ( needs_help->Scheduler.state == THREAD_SCHEDULER_READY ) {
> +      new_user = needs_help;
> +    } else {
> +      _Assert( node->help_state == SCHEDULER_HELP_ACTIVE_RIVAL );
> +      _Assert( offers_help->Scheduler.node == offers_help->Scheduler.own_node );
> +
> +      new_user = offers_help;
> +    }
> +
> +    if ( previous_accepts_help != offers_help ) {
> +      next_needs_help = previous_accepts_help;
> +    }
> +  } else if ( needs_help->Scheduler.state == THREAD_SCHEDULER_READY ) {
> +    Thread_Control *idle = _Scheduler_Release_idle_thread(
> +      context,
> +      node,
> +      release_idle_thread
> +    );
> +
> +    if ( idle != NULL ) {
> +      old_user = idle;
> +    } else {
> +      old_user = _Scheduler_Node_get_user( node );
> +    }
> +
> +    new_user = needs_help;
> +  } else {
> +    _Assert( needs_help->Scheduler.state == THREAD_SCHEDULER_SCHEDULED );
> +  }
> +
> +  if ( new_user != old_user ) {
> +    Per_CPU_Control *cpu_self = _Per_CPU_Get();
> +    Per_CPU_Control *cpu = _Thread_Get_CPU( old_user );
> +
> +    _Scheduler_Thread_change_state( old_user, THREAD_SCHEDULER_READY );
> +    _Scheduler_Thread_set_scheduler_and_node(
> +      old_user,
> +      _Scheduler_Thread_get_own_node( old_user ),
> +      old_user
> +    );
> +
> +    _Scheduler_Thread_change_state( new_user, THREAD_SCHEDULER_SCHEDULED );
> +    _Scheduler_Thread_set_scheduler_and_node( new_user, node, offers_help );
> +
> +    _Scheduler_Node_set_user( node, new_user );
> +    _Thread_Set_CPU( new_user, cpu );
> +    _Thread_Dispatch_update_heir( cpu_self, cpu, new_user );
> +  }
> +
> +  return next_needs_help;
> +}
> +
> +/**
> + * @brief Asks a blocked scheduler node for help.
> + *
> + * @param[in] context The scheduler instance context.
> + * @param[in] node The scheduled node offering help.
> + * @param[in] offers_help The thread offering help.
> + * @param[in] needs_help The thread needing help.
> + *
> + * @retval true Enqueue this scheduler node.
> + * @retval false Otherwise.
> + */
> +RTEMS_INLINE_ROUTINE bool _Scheduler_Ask_blocked_node_for_help(
> +  Scheduler_Context *context,
> +  Scheduler_Node    *node,
> +  Thread_Control    *offers_help,
> +  Thread_Control    *needs_help
> +)
> +{
> +  bool enqueue;
> +
> +  _Assert( node->help_state == SCHEDULER_HELP_PASSIVE );
> +
> +  if ( needs_help->Scheduler.state == THREAD_SCHEDULER_READY ) {
> +    _Scheduler_Node_set_user( node, needs_help );
> +    _Scheduler_Thread_set_scheduler_and_node( needs_help, node, offers_help );
> +
> +    enqueue = true;
> +  } else {
> +    enqueue = false;
> +  }
> +
> +  return enqueue;
> +}
>  #endif
>
>  /** @} */
> diff --git a/cpukit/score/include/rtems/score/schedulerpriority.h b/cpukit/score/include/rtems/score/schedulerpriority.h
> index 805e302..de051a8 100644
> --- a/cpukit/score/include/rtems/score/schedulerpriority.h
> +++ b/cpukit/score/include/rtems/score/schedulerpriority.h
> @@ -45,6 +45,7 @@ extern "C" {
>      _Scheduler_priority_Block,            /* block entry point */ \
>      _Scheduler_priority_Unblock,          /* unblock entry point */ \
>      _Scheduler_priority_Change_priority,  /* change priority entry point */ \
> +    SCHEDULER_OPERATION_DEFAULT_ASK_FOR_HELP \
>      _Scheduler_default_Node_initialize,   /* node initialize entry point */ \
>      _Scheduler_default_Node_destroy,      /* node destroy entry point */ \
>      _Scheduler_priority_Update_priority,  /* update priority entry point */ \
> diff --git a/cpukit/score/include/rtems/score/schedulerpriorityaffinitysmp.h b/cpukit/score/include/rtems/score/schedulerpriorityaffinitysmp.h
> index 3a23510..91ffcd2 100644
> --- a/cpukit/score/include/rtems/score/schedulerpriorityaffinitysmp.h
> +++ b/cpukit/score/include/rtems/score/schedulerpriorityaffinitysmp.h
> @@ -55,6 +55,7 @@ extern "C" {
>      _Scheduler_priority_affinity_SMP_Block, \
>      _Scheduler_priority_affinity_SMP_Unblock, \
>      _Scheduler_priority_affinity_SMP_Change_priority, \
> +    _Scheduler_priority_affinity_SMP_Ask_for_help, \
>      _Scheduler_priority_affinity_SMP_Node_initialize, \
>      _Scheduler_default_Node_destroy, \
>      _Scheduler_priority_SMP_Update_priority, \
> @@ -139,6 +140,12 @@ Thread_Control *_Scheduler_priority_affinity_SMP_Change_priority(
>    bool                     prepend_it
>  );
>
> +Thread_Control *_Scheduler_priority_affinity_SMP_Ask_for_help(
> +  const Scheduler_Control *scheduler,
> +  Thread_Control          *offers_help,
> +  Thread_Control          *needs_help
> +);
> +
>  /**
>   * @brief Set affinity for the priority affinity SMP scheduler.
>   *
> diff --git a/cpukit/score/include/rtems/score/schedulerprioritysmp.h b/cpukit/score/include/rtems/score/schedulerprioritysmp.h
> index a1a1481..d8ce7dc 100644
> --- a/cpukit/score/include/rtems/score/schedulerprioritysmp.h
> +++ b/cpukit/score/include/rtems/score/schedulerprioritysmp.h
> @@ -84,6 +84,7 @@ typedef struct {
>      _Scheduler_priority_SMP_Block, \
>      _Scheduler_priority_SMP_Unblock, \
>      _Scheduler_priority_SMP_Change_priority, \
> +    _Scheduler_priority_SMP_Ask_for_help, \
>      _Scheduler_priority_SMP_Node_initialize, \
>      _Scheduler_default_Node_destroy, \
>      _Scheduler_priority_SMP_Update_priority, \
> @@ -118,6 +119,12 @@ Thread_Control *_Scheduler_priority_SMP_Change_priority(
>    bool                     prepend_it
>  );
>
> +Thread_Control *_Scheduler_priority_SMP_Ask_for_help(
> +  const Scheduler_Control *scheduler,
> +  Thread_Control          *needs_help,
> +  Thread_Control          *offers_help
> +);
> +
>  void _Scheduler_priority_SMP_Update_priority(
>    const Scheduler_Control *scheduler,
>    Thread_Control *thread,
> diff --git a/cpukit/score/include/rtems/score/schedulerprioritysmpimpl.h b/cpukit/score/include/rtems/score/schedulerprioritysmpimpl.h
> index 9ae0103..bb200b4 100644
> --- a/cpukit/score/include/rtems/score/schedulerprioritysmpimpl.h
> +++ b/cpukit/score/include/rtems/score/schedulerprioritysmpimpl.h
> @@ -148,6 +148,28 @@ static inline void _Scheduler_priority_SMP_Extract_from_ready(
>    );
>  }
>
> +static inline Thread_Control *_Scheduler_priority_SMP_Get_idle_thread(
> +  Scheduler_Context *context
> +)
> +{
> +  return _Scheduler_SMP_Get_idle_thread(
> +    context,
> +    _Scheduler_priority_SMP_Extract_from_ready
> +  );
> +}
> +
> +static void _Scheduler_priority_SMP_Release_idle_thread(
> +  Scheduler_Context *context,
> +  Thread_Control    *idle
> +)
> +{
> +  _Scheduler_SMP_Release_idle_thread(
> +    context,
> +    idle,
> +    _Scheduler_priority_SMP_Insert_ready_fifo
> +  );
> +}
> +
>  static inline void _Scheduler_priority_SMP_Do_update(
>    Scheduler_Context *context,
>    Scheduler_Node *node_to_update,
> diff --git a/cpukit/score/include/rtems/score/schedulersimple.h b/cpukit/score/include/rtems/score/schedulersimple.h
> index c97ad2f..82b8c3d 100644
> --- a/cpukit/score/include/rtems/score/schedulersimple.h
> +++ b/cpukit/score/include/rtems/score/schedulersimple.h
> @@ -43,6 +43,7 @@ extern "C" {
>      _Scheduler_simple_Block,              /* block entry point */ \
>      _Scheduler_simple_Unblock,            /* unblock entry point */ \
>      _Scheduler_simple_Change_priority,    /* change priority entry point */ \
> +    SCHEDULER_OPERATION_DEFAULT_ASK_FOR_HELP \
>      _Scheduler_default_Node_initialize,   /* node initialize entry point */ \
>      _Scheduler_default_Node_destroy,      /* node destroy entry point */ \
>      _Scheduler_default_Update_priority,   /* update priority entry point */ \
> diff --git a/cpukit/score/include/rtems/score/schedulersimplesmp.h b/cpukit/score/include/rtems/score/schedulersimplesmp.h
> index de338ab..11310c6 100644
> --- a/cpukit/score/include/rtems/score/schedulersimplesmp.h
> +++ b/cpukit/score/include/rtems/score/schedulersimplesmp.h
> @@ -65,6 +65,7 @@ typedef struct {
>      _Scheduler_simple_SMP_Block, \
>      _Scheduler_simple_SMP_Unblock, \
>      _Scheduler_simple_SMP_Change_priority, \
> +    _Scheduler_simple_SMP_Ask_for_help, \
>      _Scheduler_simple_SMP_Node_initialize, \
>      _Scheduler_default_Node_destroy, \
>      _Scheduler_simple_SMP_Update_priority, \
> @@ -99,6 +100,12 @@ Thread_Control *_Scheduler_simple_SMP_Change_priority(
>    bool                     prepend_it
>  );
>
> +Thread_Control *_Scheduler_simple_SMP_Ask_for_help(
> +  const Scheduler_Control *scheduler,
> +  Thread_Control          *offers_help,
> +  Thread_Control          *needs_help
> +);
> +
>  void _Scheduler_simple_SMP_Update_priority(
>    const Scheduler_Control *scheduler,
>    Thread_Control          *thread,
> diff --git a/cpukit/score/include/rtems/score/schedulersmp.h b/cpukit/score/include/rtems/score/schedulersmp.h
> index 0c51a14..a58417a 100644
> --- a/cpukit/score/include/rtems/score/schedulersmp.h
> +++ b/cpukit/score/include/rtems/score/schedulersmp.h
> @@ -51,6 +51,11 @@ typedef struct {
>     * @brief The chain of scheduled nodes.
>     */
>    Chain_Control Scheduled;
> +
> +  /**
> +   * @brief Chain of the available idle threads.
> +   */
> +  Chain_Control Idle_threads;
>  } Scheduler_SMP_Context;
>
>  /**
> diff --git a/cpukit/score/include/rtems/score/schedulersmpimpl.h b/cpukit/score/include/rtems/score/schedulersmpimpl.h
> index 55d0697..3cf7861 100644
> --- a/cpukit/score/include/rtems/score/schedulersmpimpl.h
> +++ b/cpukit/score/include/rtems/score/schedulersmpimpl.h
> @@ -361,6 +361,7 @@ static inline void _Scheduler_SMP_Initialize(
>  )
>  {
>    _Chain_Initialize_empty( &self->Scheduled );
> +  _Chain_Initialize_empty( &self->Idle_threads );
>  }
>
>  static inline Scheduler_SMP_Node *_Scheduler_SMP_Thread_get_node(
> @@ -370,6 +371,13 @@ static inline Scheduler_SMP_Node *_Scheduler_SMP_Thread_get_node(
>    return (Scheduler_SMP_Node *) _Scheduler_Thread_get_node( thread );
>  }
>
> +static inline Scheduler_SMP_Node *_Scheduler_SMP_Thread_get_own_node(
> +  Thread_Control *thread
> +)
> +{
> +  return (Scheduler_SMP_Node *) _Scheduler_Thread_get_own_node( thread );
> +}
> +
>  static inline Scheduler_SMP_Node *_Scheduler_SMP_Node_downcast(
>    Scheduler_Node *node
>  )
> @@ -416,6 +424,36 @@ static inline bool _Scheduler_SMP_Is_processor_owned_by_us(
>    return cpu->scheduler_context == context;
>  }
>
> +static inline Thread_Control *_Scheduler_SMP_Get_idle_thread(
> +  Scheduler_Context     *context,
> +  Scheduler_SMP_Extract  extract_from_ready
> +)
> +{
> +  Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
> +  Thread_Control *idle = (Thread_Control *)
> +    _Chain_Get_first_unprotected( &self->Idle_threads );
> +  Scheduler_Node *own_node = _Scheduler_Thread_get_own_node( idle );
> +
> +  ( *extract_from_ready )( &self->Base, own_node );
> +
> +  _Assert( &idle->Object.Node != _Chain_Tail( &self->Idle_threads ) );
> +
> +  return idle;
> +}
> +
> +static inline void _Scheduler_SMP_Release_idle_thread(
> +  Scheduler_Context    *context,
> +  Thread_Control       *idle,
> +  Scheduler_SMP_Insert  insert_ready
> +)
> +{
> +  Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
> +  Scheduler_Node *own_node = _Scheduler_Thread_get_own_node( idle );
> +
> +  _Chain_Prepend_unprotected( &self->Idle_threads, &idle->Object.Node );
> +  ( *insert_ready )( context, own_node );
> +}
> +
>  static inline void _Scheduler_SMP_Allocate_processor_lazy(
>    Scheduler_Context *context,
>    Thread_Control    *scheduled_thread,
> @@ -468,6 +506,7 @@ static inline void _Scheduler_SMP_Allocate_processor(
>      _Scheduler_SMP_Node_downcast( scheduled ),
>      SCHEDULER_SMP_NODE_SCHEDULED
>    );
> +  _Scheduler_Thread_change_state( scheduled_thread, THREAD_SCHEDULER_SCHEDULED );
>
>    ( *allocate_processor )( context, scheduled_thread, victim_thread );
>  }
> @@ -491,6 +530,57 @@ static inline Scheduler_Node *_Scheduler_SMP_Get_lowest_scheduled(
>    return lowest_scheduled;
>  }
>
> +static inline Thread_Control *_Scheduler_SMP_Enqueue_to_scheduled(
> +  Scheduler_Context                *context,
> +  Scheduler_Node                   *node,
> +  Scheduler_Node                   *lowest_scheduled,
> +  Scheduler_SMP_Insert              insert_scheduled,
> +  Scheduler_SMP_Move                move_from_scheduled_to_ready,
> +  Scheduler_SMP_Allocate_processor  allocate_processor,
> +  Scheduler_Release_idle_thread     release_idle_thread
> +)
> +{
> +  Thread_Control *user = _Scheduler_Node_get_user( node );
> +  Thread_Control *lowest_scheduled_user =
> +    _Scheduler_Node_get_user( lowest_scheduled );
> +  Thread_Control *needs_help;
> +  Thread_Control *idle;
> +
> +  _Scheduler_SMP_Node_change_state(
> +    _Scheduler_SMP_Node_downcast( lowest_scheduled ),
> +    SCHEDULER_SMP_NODE_READY
> +  );
> +  _Scheduler_Thread_change_state(
> +    lowest_scheduled_user,
> +    THREAD_SCHEDULER_READY
> +  );
> +
> +  _Scheduler_Thread_set_node( user, node );
> +
> +  _Scheduler_SMP_Allocate_processor(
> +    context,
> +    node,
> +    lowest_scheduled,
> +    allocate_processor
> +  );
> +
> +  ( *insert_scheduled )( context, node );
> +  ( *move_from_scheduled_to_ready )( context, lowest_scheduled );
> +
> +  idle = _Scheduler_Release_idle_thread(
> +    context,
> +    lowest_scheduled,
> +    release_idle_thread
> +  );
> +  if ( idle == NULL ) {
> +    needs_help = lowest_scheduled_user;
> +  } else {
> +    needs_help = NULL;
> +  }
> +
> +  return needs_help;
> +}
> +
>  /**
>   * @brief Enqueues a node according to the specified order function.
>   *
> @@ -513,6 +603,7 @@ static inline Scheduler_Node *_Scheduler_SMP_Get_lowest_scheduled(
>   *   if this pointer is passed as the second argument to the order function.
>   * @param[in] allocate_processor Function to allocate a processor to a node
>   *   based on the rules of the scheduler.
> + * @param[in] release_idle_thread Function to release an idle thread.
>   */
>  static inline Thread_Control *_Scheduler_SMP_Enqueue_ordered(
>    Scheduler_Context                  *context,
> @@ -523,29 +614,23 @@ static inline Thread_Control *_Scheduler_SMP_Enqueue_ordered(
>    Scheduler_SMP_Insert                insert_scheduled,
>    Scheduler_SMP_Move                  move_from_scheduled_to_ready,
>    Scheduler_SMP_Get_lowest_scheduled  get_lowest_scheduled,
> -  Scheduler_SMP_Allocate_processor    allocate_processor
> +  Scheduler_SMP_Allocate_processor    allocate_processor,
> +  Scheduler_Release_idle_thread       release_idle_thread
>  )
>  {
>    Scheduler_Node *lowest_scheduled =
>      ( *get_lowest_scheduled )( context, node, order );
>
>    if ( ( *order )( &node->Node, &lowest_scheduled->Node ) ) {
> -    _Scheduler_SMP_Node_change_state(
> -      _Scheduler_SMP_Node_downcast( lowest_scheduled ),
> -      SCHEDULER_SMP_NODE_READY
> -    );
> -
> -    _Scheduler_SMP_Allocate_processor(
> +    needs_help = _Scheduler_SMP_Enqueue_to_scheduled(
>        context,
>        node,
>        lowest_scheduled,
> -      allocate_processor
> +      insert_scheduled,
> +      move_from_scheduled_to_ready,
> +      allocate_processor,
> +      release_idle_thread
>      );
> -
> -    ( *insert_scheduled )( context, node );
> -    ( *move_from_scheduled_to_ready )( context, lowest_scheduled );
> -
> -    needs_help = _Scheduler_Node_get_user( lowest_scheduled );
>    } else {
>      ( *insert_ready )( context, node );
>    }
> @@ -560,6 +645,8 @@ static inline Thread_Control *_Scheduler_SMP_Enqueue_ordered(
>   * @param[in] context The scheduler instance context.
>   * @param[in] node The node to enqueue.
>   * @param[in] order The order function.
> + * @param[in] extract_from_ready Function to extract a node from the set of
> + *   ready nodes.
>   * @param[in] get_highest_ready Function to get the highest ready node.
>   * @param[in] insert_ready Function to insert a node into the set of ready
>   *   nodes.
> @@ -569,48 +656,86 @@ static inline Thread_Control *_Scheduler_SMP_Enqueue_ordered(
>   *   of ready nodes to the set of scheduled nodes.
>   * @param[in] allocate_processor Function to allocate a processor to a node
>   *   based on the rules of the scheduler.
> + * @param[in] get_idle_thread Function to get an idle thread.
> + * @param[in] release_idle_thread Function to release an idle thread.
>   */
>  static inline Thread_Control *_Scheduler_SMP_Enqueue_scheduled_ordered(
>    Scheduler_Context                *context,
>    Scheduler_Node                   *node,
>    Chain_Node_order                  order,
> +  Scheduler_SMP_Extract             extract_from_ready,
>    Scheduler_SMP_Get_highest_ready   get_highest_ready,
>    Scheduler_SMP_Insert              insert_ready,
>    Scheduler_SMP_Insert              insert_scheduled,
>    Scheduler_SMP_Move                move_from_ready_to_scheduled,
> -  Scheduler_SMP_Allocate_processor  allocate_processor
> +  Scheduler_SMP_Allocate_processor  allocate_processor,
> +  Scheduler_Get_idle_thread         get_idle_thread,
> +  Scheduler_Release_idle_thread     release_idle_thread
>  )
>  {
> -  Scheduler_Node *highest_ready = ( *get_highest_ready )( context, node );
>    Thread_Control *needs_help;
>
> -  _Assert( highest_ready != NULL );
> +  while ( true ) {
> +    Scheduler_Node *highest_ready = ( *get_highest_ready )( context, node );
>
> -  /*
> -   * The node has been extracted from the scheduled chain.  We have to place
> -   * it now on the scheduled or ready set.
> -   */
> -  if ( ( *order )( &node->Node, &highest_ready->Node ) ) {
> -    ( *insert_scheduled )( context, node );
> +    _Assert( highest_ready != NULL );
>
> -    needs_help = NULL;
> -  } else {
> -    _Scheduler_SMP_Node_change_state(
> -      _Scheduler_SMP_Node_downcast( node ),
> -      SCHEDULER_SMP_NODE_READY
> -    );
> +    /*
> +     * The node has been extracted from the scheduled chain.  We have to place
> +     * it now on the scheduled or ready set.
> +     */
> +    if ( ( *order )( &node->Node, &highest_ready->Node ) ) {
> +      ( *insert_scheduled )( context, node );
>
> -    _Scheduler_SMP_Allocate_processor(
> -      context,
> -      highest_ready,
> -      node,
> -      allocate_processor
> -    );
> +      needs_help = NULL;
>
> -    ( *insert_ready )( context, node );
> -    ( *move_from_ready_to_scheduled )( context, highest_ready );
> +      break;
> +    } else if (
> +      _Scheduler_Try_to_schedule_node(
> +        context,
> +        highest_ready,
> +        get_idle_thread
> +      )
> +    ) {
> +      Thread_Control *user = _Scheduler_Node_get_user( node );
> +      Thread_Control *idle;
> +
> +      _Scheduler_SMP_Node_change_state(
> +        _Scheduler_SMP_Node_downcast( node ),
> +        SCHEDULER_SMP_NODE_READY
> +      );
> +      _Scheduler_Thread_change_state( user, THREAD_SCHEDULER_READY );
>
> -    needs_help = _Scheduler_Node_get_user( node );
> +      _Scheduler_SMP_Allocate_processor(
> +        context,
> +        highest_ready,
> +        node,
> +        allocate_processor
> +      );
> +
> +      ( *insert_ready )( context, node );
> +      ( *move_from_ready_to_scheduled )( context, highest_ready );
> +
> +      idle = _Scheduler_Release_idle_thread(
> +        context,
> +        node,
> +        release_idle_thread
> +      );
> +      if ( idle == NULL ) {
> +        needs_help = user;
> +      } else {
> +        needs_help = NULL;
> +      }
> +
> +      break;
> +    } else {
> +      _Scheduler_SMP_Node_change_state(
> +        _Scheduler_SMP_Node_downcast( highest_ready ),
> +        SCHEDULER_SMP_NODE_BLOCKED
> +      );
> +
> +      ( *extract_from_ready )( context, highest_ready );
> +    }
>    }
>
>    return needs_help;
> @@ -626,21 +751,44 @@ static inline void _Scheduler_SMP_Extract_from_scheduled(
>  static inline void _Scheduler_SMP_Schedule_highest_ready(
>    Scheduler_Context                *context,
>    Scheduler_Node                   *victim,
> +  Scheduler_SMP_Extract             extract_from_ready,
>    Scheduler_SMP_Get_highest_ready   get_highest_ready,
>    Scheduler_SMP_Move                move_from_ready_to_scheduled,
> -  Scheduler_SMP_Allocate_processor  allocate_processor
> +  Scheduler_SMP_Allocate_processor  allocate_processor,
> +  Scheduler_Get_idle_thread         get_idle_thread
>  )
>  {
> -  Scheduler_Node *highest_ready = ( *get_highest_ready )( context, victim );
> +  while ( true ) {
> +    Scheduler_Node *highest_ready = ( *get_highest_ready )( context, victim );
>
> -  _Scheduler_SMP_Allocate_processor(
> -    context,
> -    highest_ready,
> -    victim,
> -    allocate_processor
> -  );
> +    _Assert( highest_ready != NULL );
> +
> +    if (
> +      _Scheduler_Try_to_schedule_node(
> +        context,
> +        highest_ready,
> +        get_idle_thread
> +      )
> +    ) {
> +      _Scheduler_SMP_Allocate_processor(
> +        context,
> +        highest_ready,
> +        victim,
> +        allocate_processor
> +      );
> +
> +      ( *move_from_ready_to_scheduled )( context, highest_ready );
> +
> +      break;
> +    } else {
> +      _Scheduler_SMP_Node_change_state(
> +        _Scheduler_SMP_Node_downcast( highest_ready ),
> +        SCHEDULER_SMP_NODE_BLOCKED
> +      );
>
> -  ( *move_from_ready_to_scheduled )( context, highest_ready );
> +      ( *extract_from_ready )( context, highest_ready );
> +    }
> +  }
>  }
>
>  /**
> @@ -649,10 +797,11 @@ static inline void _Scheduler_SMP_Schedule_highest_ready(
>   * @param[in] context The scheduler instance context.
>   * @param[in] thread The thread of the scheduling operation.
>   * @param[in] extract_from_ready Function to extract a node from the set of
> - * ready nodes.
> + *   ready nodes.
>   * @param[in] get_highest_ready Function to get the highest ready node.
>   * @param[in] move_from_ready_to_scheduled Function to move a node from the set
> - * of ready nodes to the set of scheduled nodes.
> + *   of ready nodes to the set of scheduled nodes.
> + * @param[in] get_idle_thread Function to get an idle thread.
>   */
>  static inline void _Scheduler_SMP_Block(
>    Scheduler_Context                *context,
> @@ -660,40 +809,67 @@ static inline void _Scheduler_SMP_Block(
>    Scheduler_SMP_Extract             extract_from_ready,
>    Scheduler_SMP_Get_highest_ready   get_highest_ready,
>    Scheduler_SMP_Move                move_from_ready_to_scheduled,
> -  Scheduler_SMP_Allocate_processor  allocate_processor
> +  Scheduler_SMP_Allocate_processor  allocate_processor,
> +  Scheduler_Get_idle_thread         get_idle_thread
>  )
>  {
>    Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_node( thread );
>    bool is_scheduled = node->state == SCHEDULER_SMP_NODE_SCHEDULED;
> +  bool block = _Scheduler_Block_node(
> +    context,
> +    &node->Base,
> +    is_scheduled,
> +    get_idle_thread
> +  );
>
> -  _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_BLOCKED );
> +  if ( block ) {
> +    _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_BLOCKED );
>
> -  if ( is_scheduled ) {
> -    _Scheduler_SMP_Extract_from_scheduled( &node->Base );
> +    if ( is_scheduled ) {
> +      _Scheduler_SMP_Extract_from_scheduled( &node->Base );
>
> -    _Scheduler_SMP_Schedule_highest_ready(
> -      context,
> -      &node->Base,
> -      get_highest_ready,
> -      move_from_ready_to_scheduled,
> -      allocate_processor
> -    );
> -  } else {
> -    ( *extract_from_ready )( context, &node->Base );
> +      _Scheduler_SMP_Schedule_highest_ready(
> +        context,
> +        &node->Base,
> +        extract_from_ready,
> +        get_highest_ready,
> +        move_from_ready_to_scheduled,
> +        allocate_processor,
> +        get_idle_thread
> +      );
> +    } else {
> +      ( *extract_from_ready )( context, &node->Base );
> +    }
>    }
>  }
>
>  static inline Thread_Control *_Scheduler_SMP_Unblock(
> -  Scheduler_Context     *context,
> -  Thread_Control        *thread,
> -  Scheduler_SMP_Enqueue  enqueue_fifo
> +  Scheduler_Context             *context,
> +  Thread_Control                *thread,
> +  Scheduler_SMP_Enqueue          enqueue_fifo,
> +  Scheduler_Release_idle_thread  release_idle_thread
>  )
>  {
>    Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_node( thread );
> +  bool is_scheduled = node->state == SCHEDULER_SMP_NODE_SCHEDULED;
> +  bool unblock = _Scheduler_Unblock_node(
> +    context,
> +    thread,
> +    &node->Base,
> +    is_scheduled,
> +    release_idle_thread
> +  );
> +  Thread_Control *needs_help;
>
> -  _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );
> +  if ( unblock ) {
> +    _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );
>
> -  return ( *enqueue_fifo )( context, &node->Base, thread );
> +    needs_help = ( *enqueue_fifo )( context, &node->Base, thread );
> +  } else {
> +    needs_help = NULL;
> +  }
> +
> +  return needs_help;
>  }
>
>  static inline Thread_Control *_Scheduler_SMP_Change_priority(
> @@ -709,7 +885,7 @@ static inline Thread_Control *_Scheduler_SMP_Change_priority(
>    Scheduler_SMP_Enqueue_scheduled  enqueue_scheduled_lifo
>  )
>  {
> -  Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_node( thread );
> +  Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_own_node( thread );
>    Thread_Control *needs_help;
>
>    if ( node->state == SCHEDULER_SMP_NODE_SCHEDULED ) {
> @@ -722,7 +898,7 @@ static inline Thread_Control *_Scheduler_SMP_Change_priority(
>      } else {
>        needs_help = ( *enqueue_scheduled_fifo )( context, &node->Base );
>      }
> -  } else {
> +  } else if ( node->state == SCHEDULER_SMP_NODE_READY ) {
>      ( *extract_from_ready )( context, &node->Base );
>
>      ( *update )( context, &node->Base, new_priority );
> @@ -732,11 +908,68 @@ static inline Thread_Control *_Scheduler_SMP_Change_priority(
>      } else {
>        needs_help = ( *enqueue_fifo )( context, &node->Base, NULL );
>      }
> +  } else {
> +    ( *update )( context, &node->Base, new_priority );
> +
> +    needs_help = NULL;
>    }
>
>    return needs_help;
>  }
>
> +static inline Thread_Control *_Scheduler_SMP_Ask_for_help(
> +  Scheduler_Context                  *context,
> +  Thread_Control                     *offers_help,
> +  Thread_Control                     *needs_help,
> +  Scheduler_SMP_Enqueue               enqueue_fifo,
> +  Scheduler_Release_idle_thread       release_idle_thread
> +)
> +{
> +  Scheduler_SMP_Node *node = _Scheduler_SMP_Thread_get_own_node( offers_help );
> +  Thread_Control *next_needs_help = NULL;
> +  Thread_Control *previous_accepts_help;
> +
> +  previous_accepts_help = node->Base.accepts_help;
> +  node->Base.accepts_help = needs_help;
> +
> +  switch ( node->state ) {
> +    case SCHEDULER_SMP_NODE_READY:
> +      next_needs_help =
> +        _Scheduler_Ask_ready_node_for_help( &node->Base, needs_help );
> +      break;
> +    case SCHEDULER_SMP_NODE_SCHEDULED:
> +      next_needs_help = _Scheduler_Ask_scheduled_node_for_help(
> +        context,
> +        &node->Base,
> +        offers_help,
> +        needs_help,
> +        previous_accepts_help,
> +        release_idle_thread
> +      );
> +      break;
> +    case SCHEDULER_SMP_NODE_BLOCKED:
> +      if (
> +        _Scheduler_Ask_blocked_node_for_help(
> +          context,
> +          &node->Base,
> +          offers_help,
> +          needs_help
> +        )
> +      ) {
> +        _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );
> +
> +        next_needs_help = ( *enqueue_fifo )(
> +          context,
> +          &node->Base,
> +          needs_help
> +        );
> +      }
> +      break;
> +  }
> +
> +  return next_needs_help;
> +}
> +
>  static inline Thread_Control *_Scheduler_SMP_Yield(
>    Scheduler_Context               *context,
>    Thread_Control                  *thread,
> diff --git a/cpukit/score/include/rtems/score/thread.h b/cpukit/score/include/rtems/score/thread.h
> index a9a3f9f..4d758fb 100644
> --- a/cpukit/score/include/rtems/score/thread.h
> +++ b/cpukit/score/include/rtems/score/thread.h
> @@ -461,19 +461,72 @@ typedef struct {
>    Thread_Control    *terminator;
>  } Thread_Life_control;
>
> +#if defined(RTEMS_SMP)
> +/**
> + * @brief The thread state with respect to the scheduler.
> + */
> +typedef enum {
> +  /**
> +   * @brief This thread is blocked with respect to the scheduler.
> +   *
> +   * This thread uses no scheduler nodes.
> +   */
> +  THREAD_SCHEDULER_BLOCKED,
> +
> +  /**
> +   * @brief This thread is scheduled with respect to the scheduler.
> +   *
> +   * This thread executes using one of its scheduler nodes.  This could be its
> +   * own scheduler node or in case it owns resources taking part in the
> +   * scheduler helping protocol a scheduler node of another thread.
> +   */
> +  THREAD_SCHEDULER_SCHEDULED,
> +
> +  /**
> +   * @brief This thread is ready with respect to the scheduler.
> +   *
> +   * None of the scheduler nodes of this thread is scheduled.
> +   */
> +  THREAD_SCHEDULER_READY
> +} Thread_Scheduler_state;
> +#endif
> +
>  /**
>   * @brief Thread scheduler control.
>   */
>  typedef struct {
>  #if defined(RTEMS_SMP)
>    /**
> +   * @brief The current scheduler state of this thread.
> +   */
> +  Thread_Scheduler_state state;
Should be named "State" for an embedded structure.

> +
> +  /**
> +   * @brief The own scheduler control of this thread.
> +   *
> +   * This field is constant after initialization.
> +   */
> +  const struct Scheduler_Control *own_control;
> +
> +  /**
>     * @brief The current scheduler control of this thread.
> +   *
> +   * The scheduler helping protocol may change this field.
>     */
>    const struct Scheduler_Control *control;
Perhaps current_control is better now that there are two of these pointers?

> +
> +  /**
> +   * @brief The own scheduler node of this thread.
> +   *
> +   * This field is constant after initialization.
> +   */
> +  struct Scheduler_Node *own_node;
>  #endif
>
>    /**
>     * @brief The current scheduler node of this thread.
> +   *
> +   * The scheduler helping protocol may change this field.
but only for SMP schedulers? There is no helping protocol for UP right?

>     */
>    struct Scheduler_Node *node;
Again current_node might be better now.

>
> diff --git a/cpukit/score/include/rtems/score/threadimpl.h b/cpukit/score/include/rtems/score/threadimpl.h
> index 4971e9d..cb7d5fe 100644
> --- a/cpukit/score/include/rtems/score/threadimpl.h
> +++ b/cpukit/score/include/rtems/score/threadimpl.h
> @@ -828,6 +828,16 @@ RTEMS_INLINE_ROUTINE bool _Thread_Owns_resources(
>    return owns_resources;
>  }
>
> +#if defined(RTEMS_SMP)
> +RTEMS_INLINE_ROUTINE Thread_Control *_Thread_Resource_node_to_thread(
> +  Resource_Node *node
> +)
> +{
> +  return (Thread_Control *)
> +    ( (char *) node - offsetof( Thread_Control, Resource_node ) );
> +}
We should include some generic container_of function in rtems instead
of reproducing it multiple places.

> +#endif
> +
>  RTEMS_INLINE_ROUTINE void _Thread_Debug_set_real_processor(
>    Thread_Control  *the_thread,
>    Per_CPU_Control *cpu
> diff --git a/cpukit/score/src/schedulerchangeroot.c b/cpukit/score/src/schedulerchangeroot.c
> new file mode 100644
> index 0000000..bdb7b30
> --- /dev/null
> +++ b/cpukit/score/src/schedulerchangeroot.c
> @@ -0,0 +1,85 @@
> +/*
> + * Copyright (c) 2014 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.
> + */
> +
> +#if HAVE_CONFIG_H
> +  #include "config.h"
> +#endif
> +
> +#include <rtems/score/schedulerimpl.h>
> +
> +typedef struct {
> +  Thread_Control *root;
> +  Thread_Control *needs_help;
> +} Scheduler_Set_root_context;
> +
> +RTEMS_INLINE_ROUTINE bool _Scheduler_Set_root_visitor(
> +  Resource_Node *resource_node,
> +  void          *arg
> +)
> +{
> +  Scheduler_Set_root_context *ctx = arg;
> +  Thread_Control *root = ctx->root;
> +  Thread_Control *needs_help = root;
> +  Thread_Control *offers_help =
> +    _Thread_Resource_node_to_thread( resource_node );
> +  const Scheduler_Control *scheduler = _Scheduler_Get_own( offers_help );
> +  Thread_Control *needs_also_help;
Minor nit: Better English grammar should not split the verb, and also
is often used before the verb. Equivalentely, "too" can be used, and
it usually comes after the verb. So it would be better to use either:
"also_needs_help", "needs_help_also", or "needs_help_too". I like the
"needs_help_too" form, it matches variable names a little nicer.


> +
> +  _Resource_Node_set_root( resource_node, &root->Resource_node );
> +
> +  needs_also_help = ( *scheduler->Operations.ask_for_help )(
> +    scheduler,
> +    offers_help,
> +    needs_help
> +  );
> +
> +  if ( needs_also_help != needs_help && needs_also_help != NULL ) {
> +    _Assert( ctx->needs_help == NULL );
> +    ctx->needs_help = needs_also_help;
> +  }
> +
> +  return false;
> +}
> +
> +void _Scheduler_Thread_change_resource_root(
> +  Thread_Control *top,
> +  Thread_Control *root
> +)
> +{
> +  Scheduler_Set_root_context ctx = { root, NULL };
> +  Thread_Control *offers_help = top;
> +  Scheduler_Node *offers_help_node;
> +  Thread_Control *offers_also_help;
> +  ISR_Level level;
> +
> +  _ISR_Disable( level );
> +
> +  offers_help_node = _Scheduler_Thread_get_node( offers_help );
> +  offers_also_help = _Scheduler_Node_get_owner( offers_help_node );
> +
> +  if ( offers_help != offers_also_help ) {
> +    _Scheduler_Set_root_visitor( &offers_also_help->Resource_node, &ctx );
> +    _Assert( ctx.needs_help == offers_help );
> +    ctx.needs_help = NULL;
> +  }
> +
> +  _Scheduler_Set_root_visitor( &top->Resource_node, &ctx );
> +  _Resource_Iterate( &top->Resource_node, _Scheduler_Set_root_visitor, &ctx );
> +
Does this iterate() with disabled interrupts have bad implications for
schedulability / worst-case latency?

> +  if ( ctx.needs_help != NULL ) {
> +    _Scheduler_Ask_for_help( ctx.needs_help );
> +  }
> +
> +  _ISR_Enable( level );
> +}
> diff --git a/cpukit/score/src/schedulerdefaultaskforhelp.c b/cpukit/score/src/schedulerdefaultaskforhelp.c
> new file mode 100644
> index 0000000..b695248
> --- /dev/null
> +++ b/cpukit/score/src/schedulerdefaultaskforhelp.c
> @@ -0,0 +1,26 @@
> +/*
> + * Copyright (c) 2014 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>
> +
> +Thread_Control *_Scheduler_default_Ask_for_help(
> +  const Scheduler_Control *scheduler,
> +  Thread_Control          *offers_help,
> +  Thread_Control          *needs_help
> +)
> +{
> +  (void) scheduler;
> +  (void) offers_help;
> +  (void) needs_help;
> +
> +  return NULL;
> +}
> diff --git a/cpukit/score/src/schedulerpriorityaffinitysmp.c b/cpukit/score/src/schedulerpriorityaffinitysmp.c
> index 14a022e..5c80213 100644
> --- a/cpukit/score/src/schedulerpriorityaffinitysmp.c
> +++ b/cpukit/score/src/schedulerpriorityaffinitysmp.c
> @@ -221,7 +221,8 @@ void _Scheduler_priority_affinity_SMP_Block(
>      _Scheduler_priority_SMP_Extract_from_ready,
>      _Scheduler_priority_affinity_SMP_Get_highest_ready,
>      _Scheduler_priority_SMP_Move_from_ready_to_scheduled,
> -    _Scheduler_SMP_Allocate_processor_exact
> +    _Scheduler_SMP_Allocate_processor_exact,
> +    _Scheduler_priority_SMP_Get_idle_thread
>    );
>
>    /*
> @@ -303,7 +304,8 @@ static Thread_Control *_Scheduler_priority_affinity_SMP_Enqueue_fifo(
>      _Scheduler_SMP_Insert_scheduled_fifo,
>      _Scheduler_priority_SMP_Move_from_scheduled_to_ready,
>      _Scheduler_priority_affinity_SMP_Get_lowest_scheduled,
> -    _Scheduler_SMP_Allocate_processor_exact
> +    _Scheduler_SMP_Allocate_processor_exact,
> +    _Scheduler_priority_SMP_Release_idle_thread
>    );
>  }
>
> @@ -387,7 +389,8 @@ Thread_Control *_Scheduler_priority_affinity_SMP_Unblock(
>    needs_help = _Scheduler_SMP_Unblock(
>      context,
>      thread,
> -    _Scheduler_priority_affinity_SMP_Enqueue_fifo
> +    _Scheduler_priority_affinity_SMP_Enqueue_fifo,
> +    _Scheduler_priority_SMP_Release_idle_thread
>    );
>
>    /*
> @@ -420,7 +423,8 @@ static Thread_Control *_Scheduler_priority_affinity_SMP_Enqueue_ordered(
>      insert_scheduled,
>      _Scheduler_priority_SMP_Move_from_scheduled_to_ready,
>      _Scheduler_priority_affinity_SMP_Get_lowest_scheduled,
> -    _Scheduler_SMP_Allocate_processor_exact
> +    _Scheduler_SMP_Allocate_processor_exact,
> +    _Scheduler_priority_SMP_Release_idle_thread
>    );
>  }
>
> @@ -463,11 +467,14 @@ _Scheduler_priority_affinity_SMP_Enqueue_scheduled_ordered(
>      context,
>      node,
>      order,
> +    _Scheduler_priority_SMP_Extract_from_ready,
>      _Scheduler_priority_affinity_SMP_Get_highest_ready,
>      insert_ready,
>      insert_scheduled,
>      _Scheduler_priority_SMP_Move_from_ready_to_scheduled,
> -    _Scheduler_SMP_Allocate_processor_exact
> +    _Scheduler_SMP_Allocate_processor_exact,
> +    _Scheduler_priority_SMP_Get_idle_thread,
> +    _Scheduler_priority_SMP_Release_idle_thread
>    );
>  }
>
> @@ -543,6 +550,27 @@ Thread_Control *_Scheduler_priority_affinity_SMP_Change_priority(
>    return displaced;
>  }
>
> +Thread_Control *_Scheduler_priority_affinity_SMP_Ask_for_help(
> +  const Scheduler_Control *scheduler,
> +  Thread_Control          *offers_help,
> +  Thread_Control          *needs_help
> +)
> +{
> +  Scheduler_Context *context = _Scheduler_Get_context( scheduler );
> +
> +  needs_help = _Scheduler_SMP_Ask_for_help(
> +    context,
> +    offers_help,
> +    needs_help,
> +    _Scheduler_priority_affinity_SMP_Enqueue_fifo,
> +    _Scheduler_priority_SMP_Release_idle_thread
> +  );
> +
> +  _Scheduler_priority_affinity_SMP_Check_for_migrations( context );
> +
> +  return needs_help;
> +}
> +
>  /*
>   * This is the public scheduler specific Change Priority operation.
>   */
> diff --git a/cpukit/score/src/schedulerprioritysmp.c b/cpukit/score/src/schedulerprioritysmp.c
> index b642c5d..6d6f055 100644
> --- a/cpukit/score/src/schedulerprioritysmp.c
> +++ b/cpukit/score/src/schedulerprioritysmp.c
> @@ -93,7 +93,8 @@ void _Scheduler_priority_SMP_Block(
>      _Scheduler_priority_SMP_Extract_from_ready,
>      _Scheduler_priority_SMP_Get_highest_ready,
>      _Scheduler_priority_SMP_Move_from_ready_to_scheduled,
> -    _Scheduler_SMP_Allocate_processor_lazy
> +    _Scheduler_SMP_Allocate_processor_lazy,
> +    _Scheduler_priority_SMP_Get_idle_thread
>    );
>  }
>
> @@ -115,7 +116,8 @@ static Thread_Control *_Scheduler_priority_SMP_Enqueue_ordered(
>      insert_scheduled,
>      _Scheduler_priority_SMP_Move_from_scheduled_to_ready,
>      _Scheduler_SMP_Get_lowest_scheduled,
> -    _Scheduler_SMP_Allocate_processor_lazy
> +    _Scheduler_SMP_Allocate_processor_lazy,
> +    _Scheduler_priority_SMP_Release_idle_thread
>    );
>  }
>
> @@ -163,11 +165,14 @@ static Thread_Control *_Scheduler_priority_SMP_Enqueue_scheduled_ordered(
>      context,
>      node,
>      order,
> +    _Scheduler_priority_SMP_Extract_from_ready,
>      _Scheduler_priority_SMP_Get_highest_ready,
>      insert_ready,
>      insert_scheduled,
>      _Scheduler_priority_SMP_Move_from_ready_to_scheduled,
> -    _Scheduler_SMP_Allocate_processor_lazy
> +    _Scheduler_SMP_Allocate_processor_lazy,
> +    _Scheduler_priority_SMP_Get_idle_thread,
> +    _Scheduler_priority_SMP_Release_idle_thread
>    );
>  }
>
> @@ -209,7 +214,8 @@ Thread_Control *_Scheduler_priority_SMP_Unblock(
>    return _Scheduler_SMP_Unblock(
>      context,
>      thread,
> -    _Scheduler_priority_SMP_Enqueue_fifo
> +    _Scheduler_priority_SMP_Enqueue_fifo,
> +    _Scheduler_priority_SMP_Release_idle_thread
>    );
>  }
>
> @@ -236,6 +242,23 @@ Thread_Control *_Scheduler_priority_SMP_Change_priority(
>    );
>  }
>
> +Thread_Control *_Scheduler_priority_SMP_Ask_for_help(
> +  const Scheduler_Control *scheduler,
> +  Thread_Control          *offers_help,
> +  Thread_Control          *needs_help
> +)
> +{
> +  Scheduler_Context *context = _Scheduler_Get_context( scheduler );
> +
> +  return _Scheduler_SMP_Ask_for_help(
> +    context,
> +    offers_help,
> +    needs_help,
> +    _Scheduler_priority_SMP_Enqueue_fifo,
> +    _Scheduler_priority_SMP_Release_idle_thread
> +  );
> +}
> +
>  Thread_Control *_Scheduler_priority_SMP_Yield(
>    const Scheduler_Control *scheduler,
>    Thread_Control *thread
> diff --git a/cpukit/score/src/schedulersimplesmp.c b/cpukit/score/src/schedulersimplesmp.c
> index ee540be..99dbbef 100644
> --- a/cpukit/score/src/schedulersimplesmp.c
> +++ b/cpukit/score/src/schedulersimplesmp.c
> @@ -162,6 +162,28 @@ static void _Scheduler_simple_SMP_Extract_from_ready(
>    _Chain_Extract_unprotected( &node_to_extract->Node );
>  }
>
> +static Thread_Control *_Scheduler_simple_SMP_Get_idle_thread(
> +  Scheduler_Context *context
> +)
> +{
> +  return _Scheduler_SMP_Get_idle_thread(
> +    context,
> +    _Scheduler_simple_SMP_Extract_from_ready
> +  );
> +}
> +
> +static void _Scheduler_simple_SMP_Release_idle_thread(
> +  Scheduler_Context *context,
> +  Thread_Control    *idle
> +)
> +{
> +  _Scheduler_SMP_Release_idle_thread(
> +    context,
> +    idle,
> +    _Scheduler_simple_SMP_Insert_ready_fifo
> +  );
> +}
> +
>  void _Scheduler_simple_SMP_Block(
>    const Scheduler_Control *scheduler,
>    Thread_Control *thread
> @@ -175,7 +197,8 @@ void _Scheduler_simple_SMP_Block(
>      _Scheduler_simple_SMP_Extract_from_ready,
>      _Scheduler_simple_SMP_Get_highest_ready,
>      _Scheduler_simple_SMP_Move_from_ready_to_scheduled,
> -    _Scheduler_SMP_Allocate_processor_lazy
> +    _Scheduler_SMP_Allocate_processor_lazy,
> +    _Scheduler_simple_SMP_Get_idle_thread
>    );
>  }
>
> @@ -197,7 +220,8 @@ static Thread_Control *_Scheduler_simple_SMP_Enqueue_ordered(
>      insert_scheduled,
>      _Scheduler_simple_SMP_Move_from_scheduled_to_ready,
>      _Scheduler_SMP_Get_lowest_scheduled,
> -    _Scheduler_SMP_Allocate_processor_lazy
> +    _Scheduler_SMP_Allocate_processor_lazy,
> +    _Scheduler_simple_SMP_Release_idle_thread
>    );
>  }
>
> @@ -245,11 +269,14 @@ static Thread_Control *_Scheduler_simple_SMP_Enqueue_scheduled_ordered(
>      context,
>      node,
>      order,
> +    _Scheduler_simple_SMP_Extract_from_ready,
>      _Scheduler_simple_SMP_Get_highest_ready,
>      insert_ready,
>      insert_scheduled,
>      _Scheduler_simple_SMP_Move_from_ready_to_scheduled,
> -    _Scheduler_SMP_Allocate_processor_lazy
> +    _Scheduler_SMP_Allocate_processor_lazy,
> +    _Scheduler_simple_SMP_Get_idle_thread,
> +    _Scheduler_simple_SMP_Release_idle_thread
>    );
>  }
>
> @@ -291,7 +318,8 @@ Thread_Control *_Scheduler_simple_SMP_Unblock(
>    return _Scheduler_SMP_Unblock(
>      context,
>      thread,
> -    _Scheduler_simple_SMP_Enqueue_fifo
> +    _Scheduler_simple_SMP_Enqueue_fifo,
> +    _Scheduler_simple_SMP_Release_idle_thread
>    );
>  }
>
> @@ -318,6 +346,23 @@ Thread_Control *_Scheduler_simple_SMP_Change_priority(
>    );
>  }
>
> +Thread_Control *_Scheduler_simple_SMP_Ask_for_help(
> +  const Scheduler_Control *scheduler,
> +  Thread_Control          *offers_help,
> +  Thread_Control          *needs_help
> +)
> +{
> +  Scheduler_Context *context = _Scheduler_Get_context( scheduler );
> +
> +  return _Scheduler_SMP_Ask_for_help(
> +    context,
> +    offers_help,
> +    needs_help,
> +    _Scheduler_simple_SMP_Enqueue_fifo,
> +    _Scheduler_simple_SMP_Release_idle_thread
> +  );
> +}
> +
>  Thread_Control *_Scheduler_simple_SMP_Yield(
>    const Scheduler_Control *scheduler,
>    Thread_Control *thread
> diff --git a/cpukit/score/src/schedulersmpdebug.c b/cpukit/score/src/schedulersmpdebug.c
> new file mode 100644
> index 0000000..4a45d20
> --- /dev/null
> +++ b/cpukit/score/src/schedulersmpdebug.c
> @@ -0,0 +1,54 @@
> +/**
> + * @file
> + *
> + * @ingroup ScoreScheduler
> + *
> + * @brief Scheduler SMP Debug Implementation
> + */
> +
> +/*
> + * Copyright (c) 2014 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.
> + */
> +
> +#if HAVE_CONFIG_H
> +  #include "config.h"
> +#endif
> +
> +#include <rtems/score/schedulerimpl.h>
> +#include <rtems/score/schedulerpriorityimpl.h>
> +
> +#if defined(RTEMS_DEBUG)
> +
> +/*
> + * Table with all valid state transitions for _Scheduler_Thread_change_state()
> + * in case RTEMS_DEBUG is defined.
> + */
> +const bool _Scheduler_Thread_state_valid_state_changes[ 3 ][ 3 ] = {
> +  /* FROM / TO       BLOCKED SCHEDULED READY */
> +  /* BLOCKED    */ { false,  true,     true },
> +  /* SCHEDULED  */ { true,   false,    true },
> +  /* READY      */ { true,   true,     true }
> +};
> +
> +/*
> + * Table with all valid state transitions for
> + * _Scheduler_SMP_Node_change_state() in case RTEMS_DEBUG is defined.
> + */
> +const bool _Scheduler_SMP_Node_valid_state_changes[ 3 ][ 3 ] = {
> +  /* FROM / TO       BLOCKED SCHEDULED READY */
> +  /* BLOCKED    */ { false,  true,     true },
> +  /* SCHEDULED  */ { true,   false,    true },
> +  /* READY      */ { true,   true,     false }
> +};
> +
> +#endif
> diff --git a/cpukit/score/src/schedulersmpstartidle.c b/cpukit/score/src/schedulersmpstartidle.c
> index 6809fd8..de125d3 100644
> --- a/cpukit/score/src/schedulersmpstartidle.c
> +++ b/cpukit/score/src/schedulersmpstartidle.c
> @@ -26,4 +26,5 @@ void _Scheduler_SMP_Start_idle(
>
>    _Thread_Set_CPU( thread, cpu );
>    _Chain_Append_unprotected( &self->Scheduled, &node->Base.Node );
> +  _Chain_Prepend_unprotected( &self->Idle_threads, &thread->Object.Node );
>  }
> diff --git a/cpukit/score/src/schedulersmpvalidstatechanges.c b/cpukit/score/src/schedulersmpvalidstatechanges.c
> deleted file mode 100644
> index 6a5dcc6..0000000
> --- a/cpukit/score/src/schedulersmpvalidstatechanges.c
> +++ /dev/null
> @@ -1,38 +0,0 @@
> -/**
> - * @file
> - *
> - * @ingroup ScoreSchedulerSMP
> - *
> - * @brief SMP Scheduler Implementation
> - */
> -
> -/*
> - * Copyright (c) 2014 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.
> - */
> -
> -#if HAVE_CONFIG_H
> -  #include "config.h"
> -#endif
> -
> -#include <rtems/score/schedulerpriorityimpl.h>
> -
> -/*
> - * Table with all valid state transitions.  It is used in
> - * _Scheduler_SMP_Node_change_state() in case RTEMS_DEBUG is defined.
> - */
> -const bool _Scheduler_SMP_Node_valid_state_changes[ 3 ][ 3 ] = {
> -  /* FROM / TO       BLOCKED SCHEDULED READY */
> -  /* BLOCKED    */ { false,  true,     true },
> -  /* SCHEDULED  */ { true,   false,    true },
> -  /* READY      */ { true,   true,     false }
> -};
> diff --git a/cpukit/score/src/threadinitialize.c b/cpukit/score/src/threadinitialize.c
> index e6c4985..e56e4e6 100644
> --- a/cpukit/score/src/threadinitialize.c
> +++ b/cpukit/score/src/threadinitialize.c
> @@ -181,7 +181,10 @@ bool _Thread_Initialize(
>    }
>
>  #if defined(RTEMS_SMP)
> +  the_thread->Scheduler.state = THREAD_SCHEDULER_BLOCKED;
> +  the_thread->Scheduler.own_control = scheduler;
>    the_thread->Scheduler.control = scheduler;
> +  the_thread->Scheduler.own_node = the_thread->Scheduler.node;
>    _Resource_Node_initialize( &the_thread->Resource_node );
>    _CPU_Context_Set_is_executing( &the_thread->Registers, false );
>  #endif
> diff --git a/doc/user/smp.t b/doc/user/smp.t
> index dd84c37..239a544 100644
> --- a/doc/user/smp.t
> +++ b/doc/user/smp.t
> @@ -147,6 +147,79 @@ another processor.  So if we enable interrupts during this transition we have
>  to provide an alternative task independent stack for this time frame.  This
>  issue needs further investigation.
>
> + at subsection Scheduler Helping Protocol
> +
> +The scheduler provides a helping protocol to support locking protocols like
> + at cite{Migratory Priority Inheritance} or the @cite{Multiprocessor Resource
> +Sharing Protocol}.  Each ready task can use at least one scheduler node at a
> +time to gain access to a processor.  Each scheduler node has an owner, a user
> +and an optional idle task.  The owner of a scheduler node is determined a task
> +creation and never changes during the life time of a scheduler node.  The user
> +of a scheduler node may change due to the scheduler helping protocol.  A
> +scheduler node is in one of the four scheduler help states:
> +
> + at table @dfn
> +
> + at item help yourself
> +
> +This scheduler node is solely used by the owner task.  This task owns no
> +resources using a helping protocol and thus does not take part in the scheduler
> +helping protocol.  No help will be provided for other tasks.
> +
> + at item help active owner
> +
> +This scheduler node is owned by a task actively owning a resource and can be
> +used to help out tasks.
> +
> +In case this scheduler node changes its state from ready to scheduled and the
> +task executes using another node, then an idle task will be provided as a user
> +of this node to temporarily execute on behalf of the owner task.  Thus lower
> +priority tasks are denied access to the processors of this scheduler instance.
> +
> +In case a task actively owning a resource performs a blocking operation, then
> +an idle task will be used also in case this node is in the scheduled state.
> +
> + at item help active rival
> +
> +This scheduler node is owned by a task actively obtaining a resource currently
> +owned by another task and can be used to help out tasks.
> +
> +The task owning this node is ready and will give away its processor in case the
> +task owning the resource asks for help.
> +
> + at item help passive
> +
> +This scheduler node is owned by a task obtaining a resource currently owned by
> +another task and can be used to help out tasks.
> +
> +The task owning this node is blocked.
> +
> + at end table
> +
> +The following scheduler operations return a task in need for help
> +
> + at itemize @bullet
> + at item unblock,
> + at item change priority,
> + at item yield, and
> + at item ask for help.
> + at end itemize
> +
> +A task in need for help is a task that encounters a scheduler state change from
> +scheduled to ready or a task that cannot be scheduled in an unblock operation.
> +Such a task can ask tasks which depend on resources owned by this task for
> +help.
> +
> +In case it is not possible to schedule a task in need for help, then
> +the corresponding scheduler node will be placed into the set of ready
> +scheduler nodes of the scheduler instance.  Once a state change from
> +ready to scheduled happens for this scheduler node it may be used to
> +schedule the task in need for help.
> +
> +The ask for help scheduler operation is used to help tasks in need for help
> +returned by the operations mentioned above.  This operation is also used in
> +case the root of a resource sub-tree owned by a task changes.
> +
>  @subsection Critical Section Techniques and SMP
>
>  As discussed earlier, SMP systems have opportunities for true parallelism
> --
> 1.7.7
>
> _______________________________________________
> devel mailing list
> devel at rtems.org
> http://lists.rtems.org/mailman/listinfo/devel



More information about the devel mailing list