kernel: Earliest-deadline-first scheduling policy
Very simple implementation of deadline scheduling. Works by storing a single word in each thread containing a deadline, setting it (as a delta from "now") via a single new API call, and using it as extra input to the existing thread priority comparison function when priorities are equal. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
parent
7aa25fa5eb
commit
4a2e50f6b0
5 changed files with 107 additions and 5 deletions
|
@ -466,6 +466,10 @@ struct _thread_base {
|
||||||
u16_t preempt;
|
u16_t preempt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_SCHED_DEADLINE
|
||||||
|
int prio_deadline;
|
||||||
|
#endif
|
||||||
|
|
||||||
u32_t order_key;
|
u32_t order_key;
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
|
@ -1000,6 +1004,38 @@ __syscall int k_thread_priority_get(k_tid_t thread);
|
||||||
*/
|
*/
|
||||||
__syscall void k_thread_priority_set(k_tid_t thread, int prio);
|
__syscall void k_thread_priority_set(k_tid_t thread, int prio);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_SCHED_DEADLINE
|
||||||
|
/**
|
||||||
|
* @brief Set deadline expiration time for scheduler
|
||||||
|
*
|
||||||
|
* This sets the "deadline" expiration as a time delta from the
|
||||||
|
* current time, in the same units used by k_cycle_get_32(). The
|
||||||
|
* scheduler (when deadline scheduling is enabled) will choose the
|
||||||
|
* next expiring thread when selecting between threads at the same
|
||||||
|
* static priority. Threads at different priorities will be scheduled
|
||||||
|
* according to their static priority.
|
||||||
|
*
|
||||||
|
* @note Deadlines that are negative (i.e. in the past) are still seen
|
||||||
|
* as higher priority than others, even if the thread has "finished"
|
||||||
|
* its work. If you don't want it scheduled anymore, you have to
|
||||||
|
* reset the deadline into the future, block/pend the thread, or
|
||||||
|
* modify its priority with k_thread_priority_set().
|
||||||
|
*
|
||||||
|
* @note Despite the API naming, the scheduler makes no guarantees the
|
||||||
|
* the thread WILL be scheduled within that deadline, nor does it take
|
||||||
|
* extra metadata (like e.g. the "runtime" and "period" parameters in
|
||||||
|
* Linux sched_setattr()) that allows the kernel to validate the
|
||||||
|
* scheduling for achievability. Such features could be implemented
|
||||||
|
* above this call, which is simply input to the priority selection
|
||||||
|
* logic.
|
||||||
|
*
|
||||||
|
* @param thread A thread on which to set the deadline
|
||||||
|
* @param deadline A time delta, in cycle units
|
||||||
|
*/
|
||||||
|
__syscall void k_thread_deadline_set(k_tid_t thread, int deadline);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Suspend a thread.
|
* @brief Suspend a thread.
|
||||||
*
|
*
|
||||||
|
|
|
@ -122,6 +122,18 @@ config NUM_METAIRQ_PRIORITIES
|
||||||
for cooperative threads, this tool probably shouldn't be used
|
for cooperative threads, this tool probably shouldn't be used
|
||||||
from application code.
|
from application code.
|
||||||
|
|
||||||
|
config SCHED_DEADLINE
|
||||||
|
bool
|
||||||
|
prompt "Enable earliest-deadline-first scheduling"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This enables a simple "earliest deadline first" scheduling
|
||||||
|
mode where threads can set "deadline" deltas measured in
|
||||||
|
k_cycle_get_32() units. Priority decisions within (!!) a
|
||||||
|
single priority will choose the next expiring deadline and
|
||||||
|
not simply the least recently added thread.
|
||||||
|
|
||||||
|
|
||||||
config MAIN_STACK_SIZE
|
config MAIN_STACK_SIZE
|
||||||
int
|
int
|
||||||
prompt "Size of stack for initialization and main thread"
|
prompt "Size of stack for initialization and main thread"
|
||||||
|
|
|
@ -213,11 +213,7 @@ static inline int _is_prio_lower_or_equal(int prio1, int prio2)
|
||||||
return _is_prio1_lower_than_or_equal_to_prio2(prio1, prio2);
|
return _is_prio1_lower_than_or_equal_to_prio2(prio1, prio2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int _is_t1_higher_prio_than_t2(struct k_thread *t1,
|
int _is_t1_higher_prio_than_t2(struct k_thread *t1, struct k_thread *t2);
|
||||||
struct k_thread *t2)
|
|
||||||
{
|
|
||||||
return _is_prio1_higher_than_prio2(t1->base.prio, t2->base.prio);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int _is_valid_prio(int prio, void *entry_point)
|
static inline int _is_valid_prio(int prio, void *entry_point)
|
||||||
{
|
{
|
||||||
|
|
|
@ -87,6 +87,31 @@ s32_t _ms_to_ticks(s32_t ms)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int _is_t1_higher_prio_than_t2(struct k_thread *t1, struct k_thread *t2)
|
||||||
|
{
|
||||||
|
if (t1->base.prio < t2->base.prio) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SCHED_DEADLINE
|
||||||
|
/* Note that we don't care about wraparound conditions. The
|
||||||
|
* expectation is that the application will have arranged to
|
||||||
|
* block the threads, change their priorities or reset their
|
||||||
|
* deadlines when the job is complete. Letting the deadlines
|
||||||
|
* go negative is fine and in fact prevents aliasing bugs.
|
||||||
|
*/
|
||||||
|
if (t1->base.prio == t2->base.prio) {
|
||||||
|
int now = (int) k_cycle_get_32();
|
||||||
|
int dt1 = t1->base.prio_deadline - now;
|
||||||
|
int dt2 = t2->base.prio_deadline - now;
|
||||||
|
|
||||||
|
return dt1 < dt2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct k_thread *next_up(void)
|
static struct k_thread *next_up(void)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_SMP
|
#ifndef CONFIG_SMP
|
||||||
|
@ -607,6 +632,36 @@ Z_SYSCALL_HANDLER(k_thread_priority_set, thread_p, prio)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SCHED_DEADLINE
|
||||||
|
void _impl_k_thread_deadline_set(k_tid_t tid, int deadline)
|
||||||
|
{
|
||||||
|
struct k_thread *th = tid;
|
||||||
|
|
||||||
|
LOCKED(&sched_lock) {
|
||||||
|
th->base.prio_deadline = k_cycle_get_32() + deadline;
|
||||||
|
if (_is_thread_queued(th)) {
|
||||||
|
_priq_run_remove(&_kernel.ready_q.runq, th);
|
||||||
|
_priq_run_add(&_kernel.ready_q.runq, th);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_USERSPACE
|
||||||
|
Z_SYSCALL_HANDLER(k_thread_deadline_set, thread_p, deadline)
|
||||||
|
{
|
||||||
|
struct k_thread *thread = (struct k_thread *)thread_p;
|
||||||
|
|
||||||
|
Z_OOPS(Z_SYSCALL_OBJ(thread, K_OBJ_THREAD));
|
||||||
|
Z_OOPS(Z_SYSCALL_VERIFY_MSG(deadline > 0,
|
||||||
|
"invalid thread deadline %d",
|
||||||
|
(int)deadline));
|
||||||
|
|
||||||
|
_impl_k_thread_deadline_set((k_tid_t)thread, deadline);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
void _impl_k_yield(void)
|
void _impl_k_yield(void)
|
||||||
{
|
{
|
||||||
__ASSERT(!_is_in_isr(), "");
|
__ASSERT(!_is_in_isr(), "");
|
||||||
|
|
|
@ -313,6 +313,9 @@ void _setup_new_thread(struct k_thread *new_thread,
|
||||||
if (options & K_INHERIT_PERMS) {
|
if (options & K_INHERIT_PERMS) {
|
||||||
_thread_perms_inherit(_current, new_thread);
|
_thread_perms_inherit(_current, new_thread);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SCHED_DEADLINE
|
||||||
|
new_thread->base.prio_deadline = 0;
|
||||||
#endif
|
#endif
|
||||||
new_thread->resource_pool = _current->resource_pool;
|
new_thread->resource_pool = _current->resource_pool;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue