score: Rework EDF scheduler

Use inline red-black tree insert.  Do not use shifting priorities since
this is not supported by the thread queues.  Due to the 32-bit
Priority_Control this currently limits the uptime to 49days with a 1ms
clock tick.

Update #2173.
This commit is contained in:
Sebastian Huber
2016-06-09 21:30:40 +02:00
parent 9a78f8a507
commit 99fc1d1d1b
12 changed files with 193 additions and 185 deletions

View File

@@ -35,7 +35,7 @@ extern "C" {
*/ */
/**@{*/ /**@{*/
#define SCHEDULER_EDF_MAXIMUM_PRIORITY 255 #define SCHEDULER_EDF_MAXIMUM_PRIORITY 0x7fffffff
/** /**
* Entry points for the Earliest Deadline First Scheduler. * Entry points for the Earliest Deadline First Scheduler.
@@ -81,18 +81,6 @@ typedef struct {
RBTree_Control Ready; RBTree_Control Ready;
} Scheduler_EDF_Context; } Scheduler_EDF_Context;
/**
* @typedef Scheduler_EDF_Queue_state
*
* This enumeration distiguishes state of a thread with respect to the
* ready queue.
*/
typedef enum {
SCHEDULER_EDF_QUEUE_STATE_NOT_PRESENTLY,
SCHEDULER_EDF_QUEUE_STATE_YES,
SCHEDULER_EDF_QUEUE_STATE_NEVER_HAS_BEEN
} Scheduler_EDF_Queue_state;
/** /**
* @brief Scheduler node specialization for EDF schedulers. * @brief Scheduler node specialization for EDF schedulers.
*/ */
@@ -110,10 +98,17 @@ typedef struct {
* Rbtree node related to this thread. * Rbtree node related to this thread.
*/ */
RBTree_Node Node; RBTree_Node Node;
/** /**
* State of the thread with respect to ready queue. * @brief The thread priority used by this scheduler instance in case no job
* is released.
*/ */
Scheduler_EDF_Queue_state queue_state; Priority_Control background_priority;
/**
* @brief The thread priority currently used by this scheduler instance.
*/
Priority_Control current_priority;
} Scheduler_EDF_Node; } Scheduler_EDF_Node;
/** /**

View File

@@ -44,44 +44,92 @@ RTEMS_INLINE_ROUTINE Scheduler_EDF_Node *_Scheduler_EDF_Thread_get_node(
return (Scheduler_EDF_Node *) _Scheduler_Thread_get_node( the_thread ); return (Scheduler_EDF_Node *) _Scheduler_Thread_get_node( the_thread );
} }
int _Scheduler_EDF_Priority_compare ( RTEMS_INLINE_ROUTINE bool _Scheduler_EDF_Less(
Priority_Control p1, const void *left,
Priority_Control p2 const RBTree_Node *right
);
RBTree_Compare_result _Scheduler_EDF_Compare(
const RBTree_Node* n1,
const RBTree_Node* n2
);
RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Enqueue(
const Scheduler_Control *scheduler,
Thread_Control *the_thread
) )
{ {
Scheduler_EDF_Context *context = const Priority_Control *the_left;
_Scheduler_EDF_Get_context( scheduler ); const Scheduler_EDF_Node *the_right;
Scheduler_EDF_Node *node = _Scheduler_EDF_Thread_get_node( the_thread ); Priority_Control prio_left;
Priority_Control prio_right;
_RBTree_Insert( the_left = left;
the_right = RTEMS_CONTAINER_OF( right, Scheduler_EDF_Node, Node );
prio_left = *the_left;
prio_right = the_right->current_priority;
return prio_left < prio_right;
}
RTEMS_INLINE_ROUTINE bool _Scheduler_EDF_Less_or_equal(
const void *left,
const RBTree_Node *right
)
{
const Priority_Control *the_left;
const Scheduler_EDF_Node *the_right;
Priority_Control prio_left;
Priority_Control prio_right;
the_left = left;
the_right = RTEMS_CONTAINER_OF( right, Scheduler_EDF_Node, Node );
prio_left = *the_left;
prio_right = the_right->current_priority;
return prio_left <= prio_right;
}
RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Enqueue(
Scheduler_EDF_Context *context,
Scheduler_EDF_Node *node,
Priority_Control current_priority
)
{
_RBTree_Insert_inline(
&context->Ready, &context->Ready,
&node->Node, &node->Node,
_Scheduler_EDF_Compare, &current_priority,
false _Scheduler_EDF_Less
);
}
RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Enqueue_first(
Scheduler_EDF_Context *context,
Scheduler_EDF_Node *node,
Priority_Control current_priority
)
{
_RBTree_Insert_inline(
&context->Ready,
&node->Node,
&current_priority,
_Scheduler_EDF_Less_or_equal
); );
node->queue_state = SCHEDULER_EDF_QUEUE_STATE_YES;
} }
RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Extract( RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Extract(
Scheduler_EDF_Context *context,
Scheduler_EDF_Node *node
)
{
_RBTree_Extract( &context->Ready, &node->Node );
}
RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Extract_body(
const Scheduler_Control *scheduler, const Scheduler_Control *scheduler,
Thread_Control *the_thread Thread_Control *the_thread
) )
{ {
Scheduler_EDF_Context *context = Scheduler_EDF_Context *context;
_Scheduler_EDF_Get_context( scheduler ); Scheduler_EDF_Node *node;
Scheduler_EDF_Node *node = _Scheduler_EDF_Thread_get_node( the_thread );
_RBTree_Extract( &context->Ready, &node->Node ); context = _Scheduler_EDF_Get_context( scheduler );
node = _Scheduler_EDF_Thread_get_node( the_thread );
_Scheduler_EDF_Extract( context, node );
} }
RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Schedule_body( RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Schedule_body(
@@ -90,16 +138,17 @@ RTEMS_INLINE_ROUTINE void _Scheduler_EDF_Schedule_body(
bool force_dispatch bool force_dispatch
) )
{ {
Scheduler_EDF_Context *context = Scheduler_EDF_Context *context;
_Scheduler_EDF_Get_context( scheduler ); RBTree_Node *first;
RBTree_Node *first = _RBTree_Minimum( &context->Ready ); Scheduler_EDF_Node *node;
Scheduler_EDF_Node *node =
RTEMS_CONTAINER_OF( first, Scheduler_EDF_Node, Node );
Thread_Control *heir = node->thread;
( void ) the_thread; (void) the_thread;
_Scheduler_Update_heir( heir, force_dispatch ); context = _Scheduler_EDF_Get_context( scheduler );
first = _RBTree_Minimum( &context->Ready );
node = RTEMS_CONTAINER_OF( first, Scheduler_EDF_Node, Node );
_Scheduler_Update_heir( node->thread, force_dispatch );
} }
/**@}*/ /**@}*/

View File

@@ -25,13 +25,10 @@ void _Scheduler_CBS_Node_initialize(
Thread_Control *the_thread Thread_Control *the_thread
) )
{ {
Scheduler_CBS_Node *node = _Scheduler_CBS_Thread_get_node( the_thread ); Scheduler_CBS_Node *node;
(void) scheduler; _Scheduler_EDF_Node_initialize( scheduler, the_thread );
_Scheduler_Node_do_initialize( &node->Base.Base, the_thread ); node = _Scheduler_CBS_Thread_get_node( the_thread );
node->Base.thread = the_thread;
node->Base.queue_state = SCHEDULER_EDF_QUEUE_STATE_NEVER_HAS_BEEN;
node->cbs_server = NULL; node->cbs_server = NULL;
} }

View File

@@ -30,12 +30,15 @@ Scheduler_Void_or_thread _Scheduler_CBS_Unblock(
Thread_Control *the_thread Thread_Control *the_thread
) )
{ {
Scheduler_CBS_Node *node = _Scheduler_CBS_Thread_get_node( the_thread ); Scheduler_EDF_Context *context;
Scheduler_CBS_Server *serv_info = node->cbs_server; Scheduler_CBS_Node *node;
Priority_Control new_priority; Scheduler_CBS_Server *serv_info;
Priority_Control priority;
_Scheduler_EDF_Enqueue( scheduler, the_thread ); context = _Scheduler_EDF_Get_context( scheduler );
/* TODO: flash critical section? */ node = _Scheduler_CBS_Thread_get_node( the_thread );
serv_info = node->cbs_server;
priority = node->Base.current_priority;
/* /*
* Late unblock rule for deadline-driven tasks. The remaining time to * Late unblock rule for deadline-driven tasks. The remaining time to
@@ -43,29 +46,30 @@ Scheduler_Void_or_thread _Scheduler_CBS_Unblock(
* without increased utilization of this task. It might cause a deadline * without increased utilization of this task. It might cause a deadline
* miss of another task. * miss of another task.
*/ */
if (serv_info) { if ( serv_info != NULL && ( priority & SCHEDULER_EDF_PRIO_MSB ) == 0 ) {
time_t deadline = serv_info->parameters.deadline; time_t deadline = serv_info->parameters.deadline;
time_t budget = serv_info->parameters.budget; time_t budget = serv_info->parameters.budget;
time_t deadline_left = the_thread->cpu_time_budget; uint32_t deadline_left = the_thread->cpu_time_budget;
time_t budget_left = the_thread->real_priority - Priority_Control budget_left = priority - _Watchdog_Ticks_since_boot;
_Watchdog_Ticks_since_boot;
if ( deadline*budget_left > budget*deadline_left ) { if ( deadline * budget_left > budget * deadline_left ) {
/* Put late unblocked task to background until the end of period. */ /* Put late unblocked task to background until the end of period. */
new_priority = the_thread->Start.initial_priority;
the_thread->real_priority = new_priority; priority = node->Base.background_priority;
if ( the_thread->current_priority != new_priority ) { the_thread->real_priority = priority;
the_thread->current_priority = new_priority;
_Scheduler_EDF_Change_priority( if (
scheduler, _Thread_Priority_less_than( the_thread->current_priority, priority )
the_thread, || !_Thread_Owns_resources( the_thread )
new_priority, ) {
true the_thread->current_priority = priority;
);
} }
} }
} }
node->Base.current_priority = priority;
_Scheduler_EDF_Enqueue( context, &node->Base, priority );
/* /*
* If the thread that was unblocked is more important than the heir, * If the thread that was unblocked is more important than the heir,
* then we have a new heir. This may or may not result in a * then we have a new heir. This may or may not result in a
@@ -78,16 +82,8 @@ Scheduler_Void_or_thread _Scheduler_CBS_Unblock(
* Even if the thread isn't preemptible, if the new heir is * Even if the thread isn't preemptible, if the new heir is
* a pseudo-ISR system task, we need to do a context switch. * a pseudo-ISR system task, we need to do a context switch.
*/ */
if ( if ( priority < _Thread_Heir->current_priority ) {
_Scheduler_EDF_Priority_compare( _Scheduler_Update_heir( the_thread, priority == PRIORITY_PSEUDO_ISR );
the_thread->current_priority,
_Thread_Heir->current_priority
) == 1
) {
_Scheduler_Update_heir(
the_thread,
the_thread->current_priority == PRIORITY_PSEUDO_ISR
);
} }
SCHEDULER_RETURN_VOID_OR_NULL; SCHEDULER_RETURN_VOID_OR_NULL;

View File

@@ -20,47 +20,6 @@
#include <rtems/score/scheduleredfimpl.h> #include <rtems/score/scheduleredfimpl.h>
int _Scheduler_EDF_Priority_compare (
Priority_Control p1,
Priority_Control p2
)
{
Watchdog_Interval time = _Watchdog_Ticks_since_boot;
/*
* Reorder priorities to separate deadline driven and background tasks.
*
* The background tasks have p1 or p2 > SCHEDULER_EDF_PRIO_MSB.
* The deadline driven tasks need to have subtracted current time in order
* to see which deadline is closer wrt. current time.
*/
if (!(p1 & SCHEDULER_EDF_PRIO_MSB))
p1 = (p1 - time) & ~SCHEDULER_EDF_PRIO_MSB;
if (!(p2 & SCHEDULER_EDF_PRIO_MSB))
p2 = (p2 - time) & ~SCHEDULER_EDF_PRIO_MSB;
return ((p1<p2) - (p1>p2));
}
RBTree_Compare_result _Scheduler_EDF_Compare(
const RBTree_Node* n1,
const RBTree_Node* n2
)
{
Scheduler_EDF_Node *edf1 =
RTEMS_CONTAINER_OF( n1, Scheduler_EDF_Node, Node );
Scheduler_EDF_Node *edf2 =
RTEMS_CONTAINER_OF( n2, Scheduler_EDF_Node, Node );
Priority_Control value1 = edf1->thread->current_priority;
Priority_Control value2 = edf2->thread->current_priority;
/*
* This function compares only numbers for the red-black tree,
* but priorities have an opposite sense.
*/
return (-1)*_Scheduler_EDF_Priority_compare(value1, value2);
}
void _Scheduler_EDF_Initialize( const Scheduler_Control *scheduler ) void _Scheduler_EDF_Initialize( const Scheduler_Control *scheduler )
{ {
Scheduler_EDF_Context *context = Scheduler_EDF_Context *context =

View File

@@ -29,7 +29,7 @@ void _Scheduler_EDF_Block(
_Scheduler_Generic_block( _Scheduler_Generic_block(
scheduler, scheduler,
the_thread, the_thread,
_Scheduler_EDF_Extract, _Scheduler_EDF_Extract_body,
_Scheduler_EDF_Schedule_body _Scheduler_EDF_Schedule_body
); );
} }

View File

@@ -43,17 +43,25 @@ Scheduler_Void_or_thread _Scheduler_EDF_Change_priority(
bool prepend_it bool prepend_it
) )
{ {
Scheduler_EDF_Context *context = Scheduler_EDF_Context *context;
_Scheduler_EDF_Get_context( scheduler ); Scheduler_EDF_Node *node;
Scheduler_EDF_Node *node = _Scheduler_EDF_Thread_get_node( the_thread );
_RBTree_Extract( &context->Ready, &node->Node ); context = _Scheduler_EDF_Get_context( scheduler );
_RBTree_Insert( node = _Scheduler_EDF_Thread_get_node( the_thread );
&context->Ready,
&node->Node, if ( ( new_priority & SCHEDULER_EDF_PRIO_MSB ) != 0 ) {
_Scheduler_EDF_Compare, node->background_priority = new_priority;
false }
);
node->current_priority = new_priority;
_Scheduler_EDF_Extract( context, node );
if ( prepend_it ) {
_Scheduler_EDF_Enqueue_first( context, node, new_priority );
} else {
_Scheduler_EDF_Enqueue( context, node, new_priority );
}
_Scheduler_EDF_Schedule_body( scheduler, the_thread, false ); _Scheduler_EDF_Schedule_body( scheduler, the_thread, false );

View File

@@ -32,5 +32,4 @@ void _Scheduler_EDF_Node_initialize(
_Scheduler_Node_do_initialize( &node->Base, the_thread ); _Scheduler_Node_do_initialize( &node->Base, the_thread );
node->thread = the_thread; node->thread = the_thread;
node->queue_state = SCHEDULER_EDF_QUEUE_STATE_NEVER_HAS_BEEN;
} }

View File

@@ -18,9 +18,33 @@
#include "config.h" #include "config.h"
#endif #endif
#include <rtems/score/scheduleredf.h> #include <rtems/score/scheduleredfimpl.h>
#include <rtems/score/threadimpl.h>
#include <rtems/score/watchdogimpl.h> static bool _Scheduler_EDF_Priority_filter(
Thread_Control *the_thread,
Priority_Control *new_priority_p,
void *arg
)
{
Scheduler_EDF_Node *node;
Priority_Control current_priority;
Priority_Control new_priority;
node = _Scheduler_EDF_Thread_get_node( the_thread );
current_priority = the_thread->current_priority;
new_priority = *new_priority_p;
if ( new_priority == 0 ) {
new_priority = node->background_priority;
}
node->current_priority = new_priority;
the_thread->real_priority = new_priority;
return _Thread_Priority_less_than( current_priority, new_priority )
|| !_Thread_Owns_resources( the_thread );
}
void _Scheduler_EDF_Release_job( void _Scheduler_EDF_Release_job(
const Scheduler_Control *scheduler, const Scheduler_Control *scheduler,
@@ -28,19 +52,11 @@ void _Scheduler_EDF_Release_job(
uint64_t deadline uint64_t deadline
) )
{ {
Priority_Control new_priority; _Thread_Change_priority(
Priority_Control unused; the_thread,
(Priority_Control) deadline,
(void) scheduler; NULL,
_Scheduler_EDF_Priority_filter,
if (deadline) { true
/* Initializing or shifting deadline. */ );
new_priority = (uint32_t) deadline & ~SCHEDULER_EDF_PRIO_MSB;
}
else {
/* Switch back to background priority. */
new_priority = the_thread->Start.initial_priority;
}
_Thread_Set_priority( the_thread, new_priority, &unused, true );
} }

View File

@@ -27,8 +27,13 @@ Scheduler_Void_or_thread _Scheduler_EDF_Unblock(
Thread_Control *the_thread Thread_Control *the_thread
) )
{ {
_Scheduler_EDF_Enqueue( scheduler, the_thread ); Scheduler_EDF_Context *context;
/* TODO: flash critical section? */ Scheduler_EDF_Node *node;
context = _Scheduler_EDF_Get_context( scheduler );
node = _Scheduler_EDF_Thread_get_node( the_thread );
_Scheduler_EDF_Enqueue( context, node, node->current_priority );
/* /*
* If the thread that was unblocked is more important than the heir, * If the thread that was unblocked is more important than the heir,
@@ -42,12 +47,7 @@ Scheduler_Void_or_thread _Scheduler_EDF_Unblock(
* Even if the thread isn't preemptible, if the new heir is * Even if the thread isn't preemptible, if the new heir is
* a pseudo-ISR system task, we need to do a context switch. * a pseudo-ISR system task, we need to do a context switch.
*/ */
if ( if ( the_thread->current_priority < _Thread_Heir->current_priority ) {
_Scheduler_EDF_Priority_compare(
the_thread->current_priority,
_Thread_Heir->current_priority
) == 1
) {
_Scheduler_Update_heir( _Scheduler_Update_heir(
the_thread, the_thread,
the_thread->current_priority == PRIORITY_PSEUDO_ISR the_thread->current_priority == PRIORITY_PSEUDO_ISR

View File

@@ -26,16 +26,13 @@ void _Scheduler_EDF_Update_priority(
Priority_Control new_priority Priority_Control new_priority
) )
{ {
Scheduler_EDF_Node *node = _Scheduler_EDF_Thread_get_node( the_thread ); Scheduler_EDF_Node *node;
(void) scheduler; node = _Scheduler_EDF_Thread_get_node( the_thread );
(void) new_priority;
if (node->queue_state == SCHEDULER_EDF_QUEUE_STATE_NEVER_HAS_BEEN) { if ( ( new_priority & SCHEDULER_EDF_PRIO_MSB ) != 0 ) {
/* Shifts the priority to the region of background tasks. */ node->background_priority = new_priority;
the_thread->Start.initial_priority |= (SCHEDULER_EDF_PRIO_MSB);
the_thread->real_priority = the_thread->Start.initial_priority;
the_thread->current_priority = the_thread->Start.initial_priority;
node->queue_state = SCHEDULER_EDF_QUEUE_STATE_NOT_PRESENTLY;
} }
node->current_priority = new_priority;
} }

View File

@@ -26,22 +26,14 @@ Scheduler_Void_or_thread _Scheduler_EDF_Yield(
Thread_Control *the_thread Thread_Control *the_thread
) )
{ {
Scheduler_EDF_Context *context = Scheduler_EDF_Context *context;
_Scheduler_EDF_Get_context( scheduler ); Scheduler_EDF_Node *node;
Scheduler_EDF_Node *node = _Scheduler_EDF_Thread_get_node( the_thread );
/* context = _Scheduler_EDF_Get_context( scheduler );
* The RBTree has more than one node, enqueue behind the tasks node = _Scheduler_EDF_Thread_get_node( the_thread );
* with the same priority in case there are such ones.
*/
_RBTree_Extract( &context->Ready, &node->Node );
_RBTree_Insert(
&context->Ready,
&node->Node,
_Scheduler_EDF_Compare,
false
);
_Scheduler_EDF_Extract( context, node );
_Scheduler_EDF_Enqueue( context, node, node->current_priority );
_Scheduler_EDF_Schedule_body( scheduler, the_thread, false ); _Scheduler_EDF_Schedule_body( scheduler, the_thread, false );
SCHEDULER_RETURN_VOID_OR_NULL; SCHEDULER_RETURN_VOID_OR_NULL;