[RTEMS Project] #2412: Improved priority inheritance implementation

RTEMS trac trac at rtems.org
Mon Sep 7 06:01:03 UTC 2015


#2412: Improved priority inheritance implementation
-----------------------------+-------------------
 Reporter:  sebastian.huber  |       Owner:
     Type:  defect           |      Status:  new
 Priority:  normal           |   Milestone:  4.12
Component:  General          |     Version:  4.5
 Severity:  normal           |  Resolution:
 Keywords:                   |
-----------------------------+-------------------
Description changed by sebastian.huber:

Old description:

> = Problem
>
> The RTEMS mutexes implement only a very simple approximation of the
> priority inheritance protocol.  The real priority of a thread is only
> restored once it releases its last mutex.  Lets consider this scenario.
> We have a file system instance protected by one mutex (e.g. JFFS2) and a
> dynamic memory allocator protected by another mutex.  A low priority
> thread performs writes some log data into a file, thus it acquires the
> file system instance mutex.  The file system allocates dynamic memory.
> Now a high priority thread interrupts and tries to allocate dynamic
> memory.  The allocator mutex is already owned, so the priority of the low
> priority thread is raised to the priority of the high priority thread.
> The memory allocation completes and the allocator mutex is released,
> since the low priority thread still owns the file system instance mutex
> it continues to execute with the high priority (the high priority thread
> is not scheduled).  It may now perform complex and long file system
> operations (e.g.  garbage collection, polled flash erase and write
> functions) with a high priority.
>
> = Functional requirements
>
> * The mutex shall use the priority inheritance protocol to prevent
> priority inversion.  On SMP configurations OMIP shall be used.
>
> * The mutex shall allow vertical nesting (a thread owns multiple
> mutexes).
>
> * The mutex shall allow horizontal nesting (a thread waits for ownership
> of a mutex those owner waits for ownership of a mutex, and so on).
>
> * Threads from one scheduler instance shall wait in priority order.  The
> highest priority thread shall be dequeued first.
>
> * The highest priority waiting thread of each scheduler instance shall
> wait in FIFO order.
>
> * The mutex shall provide an acquire operation with timeout.
>
> * In case a mutex is released, then the previous owner shall no longer
> use the priorities inherited by this mutex.
>
> * In case a mutex acquire operation timeout occurs, then the current
> owner of the mutex shall no longer use the priorities inherited by the
> acquiring thread.
>
> * The order of the mutex release operations may differ from the order of
> the mutex acquire operations.
>
> * Priority changes not originating due to the priority inheritance
> protocol shall take place immediately.
>
> = Performance requirements
>
> * The mutex acquire operation shall use only object-specific locks in
> case the mutex is not owned currently.
>
> * The mutex release operation shall use only object-specific locks in
> case no threads wait for ownership of this mutex.
>
> = Invariants
>
> * A mutex shall be owned by at most one thread.
>
> * A thread shall wait for ownership of at most one mutex.
>
> = Possible implementation
>
> Use a recursive data structure to determine the highest priority
> available to a thread for each scheduler instance, e.g.
>
> {{{
> #!c
> typedef struct Thread_Priority_node {
>   Priority_Control current_priority;
>   Priority_Control real_priority;
>   struct Thread_Priority_node *owner;
>   RBTree_Node Node;
>   RBTree_Control Inherited_priorities;
> } Thread_Priority_node;
>
> typedef struct {
>   ...
>   Thread_Priority_node *priority_nodes; /* One per scheduler instances */
>   ...
> } Thread_Control;
> }}}
>
> Initially a thread has a priority node reflecting its real priority.  The
> Thread_Priority_node::owner is NULL.  The
> Thread_Priority_node::current_priority is set to the real priority.  The
> Thread_Priority_node::Inherited_priorities is empty.
>
> In case the thread must wait for ownership of a mutex, then it enqueues
> its priority node in Thread_Priority_node::Inherited_priorities of the
> mutex owner.
>
> In case the thread is dequeued from the wait queue of a mutex, then it
> dequeues its priority node in Thread_Priority_node::Inherited_priorities
> of the previous mutex owner (ownership transfer) or the current mutex
> owner (acquire timeout).
>
> In case the minimum of Thread_Priority_node::real_priority and
> Thread_Priority_node::Inherited_priorities changes, then
> Thread_Priority_node::current_priority is updated.  In case the
> Thread_Priority_node::owner its not NULL, the priority change propagates
> to the owner, and so on.  In case Thread_Priority_node::current_priority
> changes, the corresponding scheduler is notified.
>
> The biggest issue is the locking on SMP configurations in case of
> recursive minimum updates.
>
> Somehow we must connect this to the scheduler helping protocol for OMIP.
> We may have to replace the return value based scheduler operations with a
> pre-context-switch action.  Due to some recent implementation changes the
> run-time of the _Thread_Dispatch() function is no longer average-case
> performance critical.

New description:

 = Problem

 The RTEMS mutexes implement only a very simple approximation of the
 priority inheritance protocol.  The real priority of a thread is only
 restored once it releases its last mutex.  Lets consider this scenario.
 We have a file system instance protected by one mutex (e.g. JFFS2) and a
 dynamic memory allocator protected by another mutex.  A low priority
 thread performs writes some log data into a file, thus it acquires the
 file system instance mutex.  The file system allocates dynamic memory.
 Now a high priority thread interrupts and tries to allocate dynamic
 memory.  The allocator mutex is already owned, so the priority of the low
 priority thread is raised to the priority of the high priority thread.
 The memory allocation completes and the allocator mutex is released, since
 the low priority thread still owns the file system instance mutex it
 continues to execute with the high priority (the high priority thread is
 not scheduled).  It may now perform complex and long file system
 operations (e.g.  garbage collection, polled flash erase and write
 functions) with a high priority.

 = Functional requirements

 * The mutex shall use the priority inheritance protocol to prevent
 priority inversion.  On SMP configurations OMIP shall be used.

 * The mutex shall allow vertical nesting (a thread owns multiple mutexes).

 * The mutex shall allow horizontal nesting (a thread waits for ownership
 of a mutex those owner waits for ownership of a mutex, and so on).

 * Threads from one scheduler instance shall wait in priority order.  The
 highest priority thread shall be dequeued first.

 * The highest priority waiting thread of each scheduler instance shall
 wait in FIFO order.

 * The mutex shall provide an acquire operation with timeout.

 * In case a mutex is released, then the previous owner shall no longer use
 the priorities inherited by this mutex.

 * In case a mutex acquire operation timeout occurs, then the current owner
 of the mutex shall no longer use the priorities inherited by the acquiring
 thread.

 * The order of the mutex release operations may differ from the order of
 the mutex acquire operations.

 * Priority changes not originating due to the priority inheritance
 protocol shall take place immediately.

 * Deadlock shall be detected.  In case a deadlock would occur an error
 status shall be returned or a fatal error shall be generated.

 * Deadlocks at application level shall not lead to a deadlock at operating
 system level.

 = Performance requirements

 * The mutex acquire operation shall use only object-specific locks in case
 the mutex is not owned currently.

 * The mutex release operation shall use only object-specific locks in case
 no threads wait for ownership of this mutex.

 = Invariants

 * A mutex shall be owned by at most one thread.

 * A thread shall wait for ownership of at most one mutex.

 = Possible implementation

 Use a recursive data structure to determine the highest priority available
 to a thread for each scheduler instance, e.g.

 {{{
 #!c
 typedef struct Thread_Priority_node {
   Priority_Control current_priority;
   Priority_Control real_priority;
   struct Thread_Priority_node *owner;
   RBTree_Node Node;
   RBTree_Control Inherited_priorities;
 } Thread_Priority_node;

 typedef struct {
   ...
   Thread_Priority_node *priority_nodes; /* One per scheduler instances */
   ...
 } Thread_Control;
 }}}

 Initially a thread has a priority node reflecting its real priority.  The
 Thread_Priority_node::owner is NULL.  The
 Thread_Priority_node::current_priority is set to the real priority.  The
 Thread_Priority_node::Inherited_priorities is empty.

 In case the thread must wait for ownership of a mutex, then it enqueues
 its priority node in Thread_Priority_node::Inherited_priorities of the
 mutex owner.

 In case the thread is dequeued from the wait queue of a mutex, then it
 dequeues its priority node in Thread_Priority_node::Inherited_priorities
 of the previous mutex owner (ownership transfer) or the current mutex
 owner (acquire timeout).

 In case the minimum of Thread_Priority_node::real_priority and
 Thread_Priority_node::Inherited_priorities changes, then
 Thread_Priority_node::current_priority is updated.  In case the
 Thread_Priority_node::owner its not NULL, the priority change propagates
 to the owner, and so on.  In case Thread_Priority_node::current_priority
 changes, the corresponding scheduler is notified.

 The biggest issue is the locking on SMP configurations in case of
 recursive minimum updates.

 Somehow we must connect this to the scheduler helping protocol for OMIP.
 We may have to replace the return value based scheduler operations with a
 pre-context-switch action.  Due to some recent implementation changes the
 run-time of the _Thread_Dispatch() function is no longer average-case
 performance critical.

--

--
Ticket URL: <http://devel.rtems.org/ticket/2412#comment:1>
RTEMS Project <http://www.rtems.org/>
RTEMS Project


More information about the bugs mailing list