diff options
| author | Glenn Elliott <gelliott@cs.unc.edu> | 2011-01-28 17:29:03 -0500 |
|---|---|---|
| committer | Glenn Elliott <gelliott@cs.unc.edu> | 2011-01-28 19:18:53 -0500 |
| commit | 1a6154cb07727ae9716de118da15dbdb399983b9 (patch) | |
| tree | 73b222136d53fff9564306b6a64204bba6203618 | |
| parent | b8be8fb192541fad88983ef6f9270cec1b51b59a (diff) | |
Implementation of the EDZL scheduler.wip-edzl-final
Implementation of the EDZL scheduler. Zero-laxity points are
tracked by timers while jobs are in the pending state. Locking
primatives are not supported.
| -rw-r--r-- | include/litmus/edzl_common.h | 14 | ||||
| -rw-r--r-- | include/litmus/litmus.h | 21 | ||||
| -rw-r--r-- | include/litmus/rt_param.h | 15 | ||||
| -rw-r--r-- | include/litmus/sched_global_plugin.h | 6 | ||||
| -rw-r--r-- | litmus/Kconfig | 13 | ||||
| -rw-r--r-- | litmus/Makefile | 1 | ||||
| -rw-r--r-- | litmus/edzl_common.c | 57 | ||||
| -rw-r--r-- | litmus/sched_edzl.c | 327 | ||||
| -rw-r--r-- | litmus/sched_global_plugin.c | 106 | ||||
| -rw-r--r-- | litmus/sched_gsn_edf.c | 70 |
10 files changed, 537 insertions, 93 deletions
diff --git a/include/litmus/edzl_common.h b/include/litmus/edzl_common.h new file mode 100644 index 000000000000..d1a89ee08554 --- /dev/null +++ b/include/litmus/edzl_common.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* | ||
| 2 | * EDZL common data structures and utility functions shared by all EDZL | ||
| 3 | * based scheduler plugins | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef __UNC_EDZL_COMMON_H__ | ||
| 7 | #define __UNC_EDZL_COMMON_H__ | ||
| 8 | |||
| 9 | #include <litmus/rt_domain.h> | ||
| 10 | |||
| 11 | int edzl_higher_prio(struct task_struct* first, | ||
| 12 | struct task_struct* second); | ||
| 13 | |||
| 14 | #endif | ||
diff --git a/include/litmus/litmus.h b/include/litmus/litmus.h index 246483783fc0..3203a0809f96 100644 --- a/include/litmus/litmus.h +++ b/include/litmus/litmus.h | |||
| @@ -54,6 +54,12 @@ void litmus_exit_task(struct task_struct *tsk); | |||
| 54 | #define get_release(t) (tsk_rt(t)->job_params.release) | 54 | #define get_release(t) (tsk_rt(t)->job_params.release) |
| 55 | #define get_class(t) (tsk_rt(t)->task_params.cls) | 55 | #define get_class(t) (tsk_rt(t)->task_params.cls) |
| 56 | 56 | ||
| 57 | #ifdef CONFIG_PLUGIN_EDZL | ||
| 58 | #define get_zerolaxity(t) (tsk_rt(t)->job_params.zero_laxity) | ||
| 59 | #define set_zerolaxity(t) (tsk_rt(t)->job_params.zero_laxity=1) | ||
| 60 | #define clear_zerolaxity(t) (tsk_rt(t)->job_params.zero_laxity=0) | ||
| 61 | #endif | ||
| 62 | |||
| 57 | inline static int budget_exhausted(struct task_struct* t) | 63 | inline static int budget_exhausted(struct task_struct* t) |
| 58 | { | 64 | { |
| 59 | return get_exec_time(t) >= get_exec_cost(t); | 65 | return get_exec_time(t) >= get_exec_cost(t); |
| @@ -86,6 +92,21 @@ static inline lt_t litmus_clock(void) | |||
| 86 | return ktime_to_ns(ktime_get()); | 92 | return ktime_to_ns(ktime_get()); |
| 87 | } | 93 | } |
| 88 | 94 | ||
| 95 | #ifdef CONFIG_PLUGIN_EDZL | ||
| 96 | inline static lt_t laxity_remaining(struct task_struct* t) | ||
| 97 | { | ||
| 98 | lt_t now = litmus_clock(); | ||
| 99 | lt_t remaining = budget_remaining(t); | ||
| 100 | lt_t deadline = get_deadline(t); | ||
| 101 | |||
| 102 | if(lt_before(now + remaining, deadline)) | ||
| 103 | return (deadline - (now + remaining)); | ||
| 104 | else | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | #endif | ||
| 108 | |||
| 109 | |||
| 89 | /* A macro to convert from nanoseconds to ktime_t. */ | 110 | /* A macro to convert from nanoseconds to ktime_t. */ |
| 90 | #define ns_to_ktime(t) ktime_add_ns(ktime_set(0, 0), t) | 111 | #define ns_to_ktime(t) ktime_add_ns(ktime_set(0, 0), t) |
| 91 | 112 | ||
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index a7a183f34a80..41768f446436 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h | |||
| @@ -90,6 +90,14 @@ struct rt_job { | |||
| 90 | * Increase this sequence number when a job is released. | 90 | * Increase this sequence number when a job is released. |
| 91 | */ | 91 | */ |
| 92 | unsigned int job_no; | 92 | unsigned int job_no; |
| 93 | |||
| 94 | #ifdef CONFIG_PLUGIN_EDZL | ||
| 95 | /* boolean indicating zero-laxity state. We will | ||
| 96 | set this flag explicitly at zero-laxity detection. | ||
| 97 | This makes priority comparison operations more | ||
| 98 | predictable since laxity varies with time */ | ||
| 99 | unsigned int zero_laxity:1; | ||
| 100 | #endif | ||
| 93 | }; | 101 | }; |
| 94 | 102 | ||
| 95 | struct pfair_param; | 103 | struct pfair_param; |
| @@ -113,6 +121,11 @@ struct rt_param { | |||
| 113 | 121 | ||
| 114 | /* timing parameters */ | 122 | /* timing parameters */ |
| 115 | struct rt_job job_params; | 123 | struct rt_job job_params; |
| 124 | |||
| 125 | #ifdef CONFIG_PLUGIN_EDZL | ||
| 126 | /* used to trigger zero-laxity detection */ | ||
| 127 | struct hrtimer zl_timer; | ||
| 128 | #endif | ||
| 116 | 129 | ||
| 117 | /* task representing the current "inherited" task | 130 | /* task representing the current "inherited" task |
| 118 | * priority, assigned by inherit_priority and | 131 | * priority, assigned by inherit_priority and |
| @@ -120,7 +133,7 @@ struct rt_param { | |||
| 120 | * could point to self if PI does not result in | 133 | * could point to self if PI does not result in |
| 121 | * an increased task priority. | 134 | * an increased task priority. |
| 122 | */ | 135 | */ |
| 123 | struct task_struct* inh_task; | 136 | struct task_struct* inh_task; |
| 124 | 137 | ||
| 125 | #ifdef CONFIG_NP_SECTION | 138 | #ifdef CONFIG_NP_SECTION |
| 126 | /* For the FMLP under PSN-EDF, it is required to make the task | 139 | /* For the FMLP under PSN-EDF, it is required to make the task |
diff --git a/include/litmus/sched_global_plugin.h b/include/litmus/sched_global_plugin.h index cac2de63a3ee..21a91817eac1 100644 --- a/include/litmus/sched_global_plugin.h +++ b/include/litmus/sched_global_plugin.h | |||
| @@ -29,7 +29,7 @@ typedef struct task_struct* (*take_ready_t)(rt_domain_t* rt); | |||
| 29 | typedef void (*add_ready_t)(rt_domain_t* rt, struct task_struct *new); | 29 | typedef void (*add_ready_t)(rt_domain_t* rt, struct task_struct *new); |
| 30 | typedef void (*job_arrival_t)(struct task_struct* task); | 30 | typedef void (*job_arrival_t)(struct task_struct* task); |
| 31 | typedef void (*job_completion_t)(struct task_struct *t, int forced); | 31 | typedef void (*job_completion_t)(struct task_struct *t, int forced); |
| 32 | 32 | typedef int (*preemption_needed_t)(struct task_struct *t); | |
| 33 | 33 | ||
| 34 | struct sched_global_plugin { | 34 | struct sched_global_plugin { |
| 35 | 35 | ||
| @@ -41,6 +41,7 @@ struct sched_global_plugin { | |||
| 41 | add_ready_t add_ready; | 41 | add_ready_t add_ready; |
| 42 | job_arrival_t job_arrival; | 42 | job_arrival_t job_arrival; |
| 43 | job_completion_t job_completion; | 43 | job_completion_t job_completion; |
| 44 | preemption_needed_t preemption_needed; | ||
| 44 | 45 | ||
| 45 | rt_domain_t domain; | 46 | rt_domain_t domain; |
| 46 | 47 | ||
| @@ -60,7 +61,6 @@ extern struct sched_global_plugin* active_gbl_plugin; | |||
| 60 | * | 61 | * |
| 61 | * Use prefix "gbl_" (global) | 62 | * Use prefix "gbl_" (global) |
| 62 | */ | 63 | */ |
| 63 | int gbl_preemption_needed(struct task_struct *t); | ||
| 64 | int gbl_ready_order(struct bheap_node* a, struct bheap_node* b); | 64 | int gbl_ready_order(struct bheap_node* a, struct bheap_node* b); |
| 65 | int gbl_cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b); | 65 | int gbl_cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b); |
| 66 | void gbl_update_cpu_position(cpu_entry_t *entry); | 66 | void gbl_update_cpu_position(cpu_entry_t *entry); |
| @@ -69,6 +69,7 @@ void gbl_link_task_to_cpu(struct task_struct* linked, cpu_entry_t *entry); | |||
| 69 | void gbl_unlink(struct task_struct* t); | 69 | void gbl_unlink(struct task_struct* t); |
| 70 | void gbl_preempt(cpu_entry_t *entry); | 70 | void gbl_preempt(cpu_entry_t *entry); |
| 71 | void gbl_requeue(struct task_struct* task); | 71 | void gbl_requeue(struct task_struct* task); |
| 72 | void gbl_update_queue_position(struct task_struct *task); | ||
| 72 | void gbl_check_for_preemptions(void); | 73 | void gbl_check_for_preemptions(void); |
| 73 | void gbl_release_jobs(rt_domain_t* rt, struct bheap* tasks); | 74 | void gbl_release_jobs(rt_domain_t* rt, struct bheap* tasks); |
| 74 | void gbl_job_completion(struct task_struct *t, int forced); | 75 | void gbl_job_completion(struct task_struct *t, int forced); |
| @@ -87,6 +88,7 @@ long gbl_activate_plugin(void* plugin); | |||
| 87 | * Use prefix "gblv_" (global virtual) | 88 | * Use prefix "gblv_" (global virtual) |
| 88 | */ | 89 | */ |
| 89 | void gblv_job_arrival(struct task_struct* task); | 90 | void gblv_job_arrival(struct task_struct* task); |
| 91 | int gblv_preemption_needed(struct task_struct *t); | ||
| 90 | void gblv_tick(struct task_struct* t); | 92 | void gblv_tick(struct task_struct* t); |
| 91 | struct task_struct* gblv_schedule(struct task_struct * prev); | 93 | struct task_struct* gblv_schedule(struct task_struct * prev); |
| 92 | void gblv_finish_switch(struct task_struct *prev); | 94 | void gblv_finish_switch(struct task_struct *prev); |
diff --git a/litmus/Kconfig b/litmus/Kconfig index a2f267870f29..1e571af45e72 100644 --- a/litmus/Kconfig +++ b/litmus/Kconfig | |||
| @@ -23,6 +23,17 @@ config PLUGIN_PFAIR | |||
| 23 | 23 | ||
| 24 | If unsure, say Yes. | 24 | If unsure, say Yes. |
| 25 | 25 | ||
| 26 | config PLUGIN_EDZL | ||
| 27 | bool "EDZL" | ||
| 28 | depends on X86 && SYSFS | ||
| 29 | default y | ||
| 30 | help | ||
| 31 | Include the EDZL (Earliest Deadline, Zero Laxity) plugin in the kernel. | ||
| 32 | EDZL functions like G-EDF, except jobs with zero laxity are given maximum | ||
| 33 | priority. | ||
| 34 | |||
| 35 | If unsure, say Yes. | ||
| 36 | |||
| 26 | config RELEASE_MASTER | 37 | config RELEASE_MASTER |
| 27 | bool "Release-master Support" | 38 | bool "Release-master Support" |
| 28 | depends on ARCH_HAS_SEND_PULL_TIMERS | 39 | depends on ARCH_HAS_SEND_PULL_TIMERS |
| @@ -32,7 +43,7 @@ config RELEASE_MASTER | |||
| 32 | that services all timer interrupts, but that does not schedule | 43 | that services all timer interrupts, but that does not schedule |
| 33 | real-time tasks. See RTSS'09 paper for details | 44 | real-time tasks. See RTSS'09 paper for details |
| 34 | (http://www.cs.unc.edu/~anderson/papers.html). | 45 | (http://www.cs.unc.edu/~anderson/papers.html). |
| 35 | Currently only supported by GSN-EDF. | 46 | Currently only supported by GSN-EDF and EDZL. |
| 36 | 47 | ||
| 37 | endmenu | 48 | endmenu |
| 38 | 49 | ||
diff --git a/litmus/Makefile b/litmus/Makefile index 820deb7f2263..ec4e21106886 100644 --- a/litmus/Makefile +++ b/litmus/Makefile | |||
| @@ -21,6 +21,7 @@ obj-y = sched_plugin.o litmus.o \ | |||
| 21 | 21 | ||
| 22 | obj-$(CONFIG_PLUGIN_CEDF) += sched_cedf.o | 22 | obj-$(CONFIG_PLUGIN_CEDF) += sched_cedf.o |
| 23 | obj-$(CONFIG_PLUGIN_PFAIR) += sched_pfair.o | 23 | obj-$(CONFIG_PLUGIN_PFAIR) += sched_pfair.o |
| 24 | obj-$(CONFIG_PLUGIN_EDZL) += sched_edzl.o edzl_common.o | ||
| 24 | 25 | ||
| 25 | obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o | 26 | obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o |
| 26 | obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o | 27 | obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o |
diff --git a/litmus/edzl_common.c b/litmus/edzl_common.c new file mode 100644 index 000000000000..9e26304a1ea2 --- /dev/null +++ b/litmus/edzl_common.c | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * kernel/edzl_common.c | ||
| 3 | * | ||
| 4 | * Common functions for EDZL based scheduler. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/percpu.h> | ||
| 8 | #include <linux/sched.h> | ||
| 9 | #include <linux/list.h> | ||
| 10 | |||
| 11 | #include <litmus/litmus.h> | ||
| 12 | #include <litmus/sched_plugin.h> | ||
| 13 | #include <litmus/sched_trace.h> | ||
| 14 | |||
| 15 | #include <litmus/edf_common.h> | ||
| 16 | #include <litmus/edzl_common.h> | ||
| 17 | |||
| 18 | |||
| 19 | int edzl_higher_prio(struct task_struct* first, | ||
| 20 | struct task_struct* second) | ||
| 21 | { | ||
| 22 | struct task_struct *first_task = first; | ||
| 23 | struct task_struct *second_task = second; | ||
| 24 | |||
| 25 | /* There is no point in comparing a task to itself. */ | ||
| 26 | if (first && first == second) { | ||
| 27 | TRACE_TASK(first, | ||
| 28 | "WARNING: pointless edf priority comparison.\n"); | ||
| 29 | return 0; | ||
| 30 | } | ||
| 31 | |||
| 32 | |||
| 33 | /* Check for inherited priorities. Change task | ||
| 34 | * used for comparison in such a case. | ||
| 35 | */ | ||
| 36 | if (first && first->rt_param.inh_task) | ||
| 37 | first_task = first->rt_param.inh_task; | ||
| 38 | if (second && second->rt_param.inh_task) | ||
| 39 | second_task = second->rt_param.inh_task; | ||
| 40 | |||
| 41 | /* null checks & rt checks */ | ||
| 42 | if(!first_task) | ||
| 43 | return 0; | ||
| 44 | else if(!second_task || !is_realtime(second_task)) | ||
| 45 | return 1; | ||
| 46 | |||
| 47 | |||
| 48 | if(likely(get_zerolaxity(first_task) == get_zerolaxity(second_task))) | ||
| 49 | { | ||
| 50 | /* edf order if both tasks have the same laxity state */ | ||
| 51 | return(edf_higher_prio(first_task, second_task)); | ||
| 52 | } | ||
| 53 | else | ||
| 54 | { | ||
| 55 | return(get_zerolaxity(first_task)); | ||
| 56 | } | ||
| 57 | } | ||
diff --git a/litmus/sched_edzl.c b/litmus/sched_edzl.c new file mode 100644 index 000000000000..0664b78e540b --- /dev/null +++ b/litmus/sched_edzl.c | |||
| @@ -0,0 +1,327 @@ | |||
| 1 | /* | ||
| 2 | * litmus/sched_edzl.c | ||
| 3 | * | ||
| 4 | * Implementation of the EDZL scheduling algorithm. | ||
| 5 | * | ||
| 6 | * This version uses the simple approach and serializes all scheduling | ||
| 7 | * decisions by the use of a queue lock. This is probably not the | ||
| 8 | * best way to do it, but it should suffice for now. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/spinlock.h> | ||
| 12 | #include <linux/percpu.h> | ||
| 13 | #include <linux/sched.h> | ||
| 14 | |||
| 15 | #include <litmus/litmus.h> | ||
| 16 | #include <litmus/jobs.h> | ||
| 17 | #include <litmus/sched_global_plugin.h> | ||
| 18 | #include <litmus/edzl_common.h> | ||
| 19 | #include <litmus/sched_trace.h> | ||
| 20 | |||
| 21 | #include <litmus/preempt.h> | ||
| 22 | |||
| 23 | #include <litmus/bheap.h> | ||
| 24 | |||
| 25 | #include <linux/module.h> | ||
| 26 | |||
| 27 | static struct task_struct* __edzl_take_ready(rt_domain_t* rt); | ||
| 28 | static void __edzl_add_ready(rt_domain_t* rt, struct task_struct *new); | ||
| 29 | static void edzl_job_arrival(struct task_struct* task); | ||
| 30 | static void edzl_task_new(struct task_struct * t, int on_rq, int running); | ||
| 31 | static void edzl_task_wake_up(struct task_struct *task); | ||
| 32 | static void edzl_task_exit(struct task_struct * t); | ||
| 33 | static int edzl_preemption_needed(struct task_struct *t); | ||
| 34 | |||
| 35 | |||
| 36 | /* EDZL Plugin object */ | ||
| 37 | static struct sched_global_plugin edzl_plugin __cacheline_aligned_in_smp = { | ||
| 38 | .plugin = { | ||
| 39 | .finish_switch = gblv_finish_switch, | ||
| 40 | .tick = gblv_tick, | ||
| 41 | .complete_job = complete_job, | ||
| 42 | .schedule = gblv_schedule, | ||
| 43 | .task_block = gblv_task_block, | ||
| 44 | .admit_task = gblv_admit_task, | ||
| 45 | .activate_plugin = gbl_activate_plugin, | ||
| 46 | |||
| 47 | .plugin_name = "EDZL", | ||
| 48 | .task_new = edzl_task_new, | ||
| 49 | .task_wake_up = edzl_task_wake_up, | ||
| 50 | .task_exit = edzl_task_exit, | ||
| 51 | }, | ||
| 52 | |||
| 53 | .job_completion = gbl_job_completion, | ||
| 54 | |||
| 55 | .prio_order = edzl_higher_prio, | ||
| 56 | .take_ready = __edzl_take_ready, | ||
| 57 | .add_ready = __edzl_add_ready, | ||
| 58 | .job_arrival = edzl_job_arrival, | ||
| 59 | .preemption_needed = edzl_preemption_needed | ||
| 60 | }; | ||
| 61 | |||
| 62 | |||
| 63 | #define active_gbl_domain (active_gbl_plugin->domain) | ||
| 64 | #define active_gbl_domain_lock (active_gbl_domain.ready_lock) | ||
| 65 | |||
| 66 | DEFINE_PER_CPU(cpu_entry_t, edzl_cpu_entries); | ||
| 67 | |||
| 68 | |||
| 69 | static enum hrtimer_restart on_zero_laxity(struct hrtimer *timer) | ||
| 70 | { | ||
| 71 | unsigned long flags; | ||
| 72 | struct task_struct* t; | ||
| 73 | |||
| 74 | lt_t now = litmus_clock(); | ||
| 75 | |||
| 76 | TRACE("Zero-laxity timer went off!\n"); | ||
| 77 | |||
| 78 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
| 79 | |||
| 80 | t = container_of(container_of(timer, struct rt_param, zl_timer), | ||
| 81 | struct task_struct, | ||
| 82 | rt_param); | ||
| 83 | |||
| 84 | TRACE_TASK(t, "Reached zero-laxity. (now: %llu, zl-pt: %lld, time remaining (now): %lld)\n", | ||
| 85 | now, | ||
| 86 | get_deadline(t) - budget_remaining(t), | ||
| 87 | get_deadline(t) - now); | ||
| 88 | |||
| 89 | set_zerolaxity(t); | ||
| 90 | gbl_update_queue_position(t); | ||
| 91 | |||
| 92 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
| 93 | |||
| 94 | return HRTIMER_NORESTART; | ||
| 95 | } | ||
| 96 | |||
| 97 | /* __edzl_take_ready - call's __take_ready with EDZL timer cancelation side-effect. */ | ||
| 98 | static struct task_struct* __edzl_take_ready(rt_domain_t* rt) | ||
| 99 | { | ||
| 100 | struct task_struct* t = __take_ready(rt); | ||
| 101 | |||
| 102 | if(t) | ||
| 103 | { | ||
| 104 | if(get_zerolaxity(t) == 0) | ||
| 105 | { | ||
| 106 | if(hrtimer_active(&tsk_rt(t)->zl_timer)) | ||
| 107 | { | ||
| 108 | int cancel_ret; | ||
| 109 | |||
| 110 | TRACE_TASK(t, "Canceling zero-laxity timer.\n"); | ||
| 111 | cancel_ret = hrtimer_try_to_cancel(&tsk_rt(t)->zl_timer); | ||
| 112 | WARN_ON(cancel_ret == 0); /* should never be inactive. */ | ||
| 113 | } | ||
| 114 | } | ||
| 115 | else | ||
| 116 | { | ||
| 117 | TRACE_TASK(t, "Task already has zero-laxity flagged.\n"); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | return t; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* __edzl_add_ready - call's __add_ready with EDZL setting timer side-effect. */ | ||
| 125 | static void __edzl_add_ready(rt_domain_t* rt, struct task_struct *new) | ||
| 126 | { | ||
| 127 | __add_ready(rt, new); | ||
| 128 | |||
| 129 | if(get_zerolaxity(new) == 0) | ||
| 130 | { | ||
| 131 | lt_t when_to_fire; | ||
| 132 | |||
| 133 | when_to_fire = get_deadline(new) - budget_remaining(new); | ||
| 134 | |||
| 135 | TRACE_TASK(new, "Setting zero-laxity timer for %llu. (deadline: %llu, remaining: %llu)\n", | ||
| 136 | when_to_fire, | ||
| 137 | get_deadline(new), | ||
| 138 | budget_remaining(new)); | ||
| 139 | |||
| 140 | __hrtimer_start_range_ns(&tsk_rt(new)->zl_timer, | ||
| 141 | ns_to_ktime(when_to_fire), | ||
| 142 | 0, | ||
| 143 | HRTIMER_MODE_ABS_PINNED, | ||
| 144 | 0); | ||
| 145 | } | ||
| 146 | else | ||
| 147 | { | ||
| 148 | TRACE_TASK(new, "Already has zero-laxity when added to ready queue. (deadline: %llu, remaining: %llu))\n", | ||
| 149 | get_deadline(new), | ||
| 150 | budget_remaining(new)); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | /* edzl_job_arrival: task is either resumed or released */ | ||
| 157 | static void edzl_job_arrival(struct task_struct* task) | ||
| 158 | { | ||
| 159 | BUG_ON(!task); | ||
| 160 | |||
| 161 | /* clear old laxity flag or tag zero-laxity upon release */ | ||
| 162 | if(laxity_remaining(task)) | ||
| 163 | clear_zerolaxity(task); | ||
| 164 | else | ||
| 165 | set_zerolaxity(task); | ||
| 166 | |||
| 167 | gbl_requeue(task); | ||
| 168 | gbl_check_for_preemptions(); | ||
| 169 | } | ||
| 170 | |||
| 171 | |||
| 172 | /* Prepare a task for running in RT mode | ||
| 173 | */ | ||
| 174 | static void edzl_task_new(struct task_struct * t, int on_rq, int running) | ||
| 175 | { | ||
| 176 | unsigned long flags; | ||
| 177 | cpu_entry_t* entry; | ||
| 178 | |||
| 179 | TRACE("edzl: task new %d\n", t->pid); | ||
| 180 | |||
| 181 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
| 182 | |||
| 183 | hrtimer_init(&t->rt_param.zl_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
| 184 | t->rt_param.zl_timer.function = on_zero_laxity; | ||
| 185 | |||
| 186 | /* setup job params */ | ||
| 187 | release_at(t, litmus_clock()); | ||
| 188 | |||
| 189 | if (running) { | ||
| 190 | entry = active_gbl_plugin->cpus[task_cpu(t)]; | ||
| 191 | BUG_ON(entry->scheduled); | ||
| 192 | |||
| 193 | #ifdef CONFIG_RELEASE_MASTER | ||
| 194 | if (entry->cpu != active_gbl_domain.release_master) { | ||
| 195 | #endif | ||
| 196 | entry->scheduled = t; | ||
| 197 | tsk_rt(t)->scheduled_on = task_cpu(t); | ||
| 198 | #ifdef CONFIG_RELEASE_MASTER | ||
| 199 | } else { | ||
| 200 | /* do not schedule on release master */ | ||
| 201 | gbl_preempt(entry); /* force resched */ | ||
| 202 | tsk_rt(t)->scheduled_on = NO_CPU; | ||
| 203 | } | ||
| 204 | #endif | ||
| 205 | } else { | ||
| 206 | t->rt_param.scheduled_on = NO_CPU; | ||
| 207 | } | ||
| 208 | t->rt_param.linked_on = NO_CPU; | ||
| 209 | |||
| 210 | active_gbl_plugin->job_arrival(t); | ||
| 211 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
| 212 | } | ||
| 213 | |||
| 214 | |||
| 215 | static void edzl_task_wake_up(struct task_struct *task) | ||
| 216 | { | ||
| 217 | unsigned long flags; | ||
| 218 | lt_t now; | ||
| 219 | |||
| 220 | TRACE_TASK(task, "wake_up at %llu\n", litmus_clock()); | ||
| 221 | |||
| 222 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
| 223 | /* We need to take suspensions because of semaphores into | ||
| 224 | * account! If a job resumes after being suspended due to acquiring | ||
| 225 | * a semaphore, it should never be treated as a new job release. | ||
| 226 | */ | ||
| 227 | if (get_rt_flags(task) == RT_F_EXIT_SEM) { | ||
| 228 | set_rt_flags(task, RT_F_RUNNING); | ||
| 229 | } else { | ||
| 230 | now = litmus_clock(); | ||
| 231 | if (is_tardy(task, now)) { | ||
| 232 | /* new sporadic release */ | ||
| 233 | release_at(task, now); | ||
| 234 | sched_trace_task_release(task); | ||
| 235 | } | ||
| 236 | else { | ||
| 237 | if (task->rt.time_slice) { | ||
| 238 | /* came back in time before deadline | ||
| 239 | */ | ||
| 240 | set_rt_flags(task, RT_F_RUNNING); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | active_gbl_plugin->job_arrival(task); | ||
| 245 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
| 246 | } | ||
| 247 | |||
| 248 | |||
| 249 | static void edzl_task_exit(struct task_struct * t) | ||
| 250 | { | ||
| 251 | unsigned long flags; | ||
| 252 | |||
| 253 | /* unlink if necessary */ | ||
| 254 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
| 255 | gbl_unlink(t); | ||
| 256 | if (tsk_rt(t)->scheduled_on != NO_CPU) { | ||
| 257 | active_gbl_plugin->cpus[tsk_rt(t)->scheduled_on]->scheduled = NULL; | ||
| 258 | tsk_rt(t)->scheduled_on = NO_CPU; | ||
| 259 | } | ||
| 260 | |||
| 261 | if(hrtimer_active(&tsk_rt(t)->zl_timer)) | ||
| 262 | { | ||
| 263 | /* BUG if reached? */ | ||
| 264 | TRACE_TASK(t, "Canceled armed timer while exiting.\n"); | ||
| 265 | hrtimer_cancel(&tsk_rt(t)->zl_timer); | ||
| 266 | } | ||
| 267 | |||
| 268 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
| 269 | |||
| 270 | BUG_ON(!is_realtime(t)); | ||
| 271 | TRACE_TASK(t, "RIP\n"); | ||
| 272 | } | ||
| 273 | |||
| 274 | |||
| 275 | /* need_to_preempt - check whether the task t needs to be preempted | ||
| 276 | * call only with irqs disabled and with ready_lock acquired | ||
| 277 | * THIS DOES NOT TAKE NON-PREEMPTIVE SECTIONS INTO ACCOUNT! | ||
| 278 | */ | ||
| 279 | static int edzl_preemption_needed(struct task_struct *t) | ||
| 280 | { | ||
| 281 | /* we need the read lock for edf_ready_queue */ | ||
| 282 | /* no need to preempt if there is nothing pending */ | ||
| 283 | if (!__jobs_pending(&active_gbl_domain)) | ||
| 284 | return 0; | ||
| 285 | /* we need to reschedule if t doesn't exist */ | ||
| 286 | if (!t) | ||
| 287 | return 1; | ||
| 288 | /* make sure to get non-rt stuff out of the way */ | ||
| 289 | if (!is_realtime(t)) | ||
| 290 | return 1; | ||
| 291 | |||
| 292 | /* NOTE: We cannot check for non-preemptibility since we | ||
| 293 | * don't know what address space we're currently in. | ||
| 294 | */ | ||
| 295 | |||
| 296 | /* Detect zero-laxity as needed. Easier to do it here than in tick. | ||
| 297 | (No timer is used to detect zero-laxity while a job is running.) */ | ||
| 298 | if(unlikely(!get_zerolaxity(t) && laxity_remaining(t) == 0)) | ||
| 299 | { | ||
| 300 | set_zerolaxity(t); | ||
| 301 | } | ||
| 302 | |||
| 303 | return edzl_higher_prio(__next_ready(&active_gbl_domain), t); | ||
| 304 | } | ||
| 305 | |||
| 306 | |||
| 307 | static int __init init_edzl(void) | ||
| 308 | { | ||
| 309 | int cpu; | ||
| 310 | cpu_entry_t *entry; | ||
| 311 | |||
| 312 | bheap_init(&edzl_plugin.cpu_heap); | ||
| 313 | /* initialize CPU state */ | ||
| 314 | for (cpu = 0; cpu < NR_CPUS; cpu++) { | ||
| 315 | entry = &per_cpu(edzl_cpu_entries, cpu); | ||
| 316 | edzl_plugin.cpus[cpu] = entry; | ||
| 317 | entry->cpu = cpu; | ||
| 318 | entry->hn = &edzl_plugin.heap_node[cpu]; | ||
| 319 | bheap_node_init(&entry->hn, entry); | ||
| 320 | } | ||
| 321 | gbl_domain_init(&edzl_plugin, NULL, gbl_release_jobs); | ||
| 322 | |||
| 323 | return register_sched_plugin(&edzl_plugin.plugin); | ||
| 324 | } | ||
| 325 | |||
| 326 | |||
| 327 | module_init(init_edzl); | ||
diff --git a/litmus/sched_global_plugin.c b/litmus/sched_global_plugin.c index 22dffa7d62fc..e94247b66b59 100644 --- a/litmus/sched_global_plugin.c +++ b/litmus/sched_global_plugin.c | |||
| @@ -105,31 +105,11 @@ struct sched_global_plugin* active_gbl_plugin; | |||
| 105 | /*********************************************************************/ | 105 | /*********************************************************************/ |
| 106 | 106 | ||
| 107 | /* Priority-related functions */ | 107 | /* Priority-related functions */ |
| 108 | int gbl_preemption_needed(struct task_struct *t) | ||
| 109 | { | ||
| 110 | /* we need the read lock for active_gbl_domain's ready_queue */ | ||
| 111 | /* no need to preempt if there is nothing pending */ | ||
| 112 | if (!__jobs_pending(&active_gbl_domain)) | ||
| 113 | return 0; | ||
| 114 | /* we need to reschedule if t doesn't exist */ | ||
| 115 | if (!t) | ||
| 116 | return 1; | ||
| 117 | |||
| 118 | /* NOTE: We cannot check for non-preemptibility since we | ||
| 119 | * don't know what address space we're currently in. | ||
| 120 | */ | ||
| 121 | |||
| 122 | /* make sure to get non-rt stuff out of the way */ | ||
| 123 | return !is_realtime(t) || active_gbl_plugin->prio_order(__next_ready(&active_gbl_domain), t); | ||
| 124 | } | ||
| 125 | |||
| 126 | int gbl_ready_order(struct bheap_node* a, struct bheap_node* b) | 108 | int gbl_ready_order(struct bheap_node* a, struct bheap_node* b) |
| 127 | { | 109 | { |
| 128 | return active_gbl_plugin->prio_order(bheap2task(a), bheap2task(b)); | 110 | return active_gbl_plugin->prio_order(bheap2task(a), bheap2task(b)); |
| 129 | } | 111 | } |
| 130 | 112 | ||
| 131 | |||
| 132 | |||
| 133 | int gbl_cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b) | 113 | int gbl_cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b) |
| 134 | { | 114 | { |
| 135 | cpu_entry_t *a, *b; | 115 | cpu_entry_t *a, *b; |
| @@ -243,7 +223,6 @@ void gbl_unlink(struct task_struct* t) | |||
| 243 | } | 223 | } |
| 244 | } | 224 | } |
| 245 | 225 | ||
| 246 | |||
| 247 | /* preempt - force a CPU to reschedule | 226 | /* preempt - force a CPU to reschedule |
| 248 | */ | 227 | */ |
| 249 | void gbl_preempt(cpu_entry_t *entry) | 228 | void gbl_preempt(cpu_entry_t *entry) |
| @@ -268,6 +247,71 @@ void gbl_requeue(struct task_struct* task) | |||
| 268 | } | 247 | } |
| 269 | } | 248 | } |
| 270 | 249 | ||
| 250 | /* | ||
| 251 | * update_queue_position - call after changing the priority of 'task'. | ||
| 252 | */ | ||
| 253 | void gbl_update_queue_position(struct task_struct *task) | ||
| 254 | { | ||
| 255 | /* We don't know whether task is in the ready queue. It should, but | ||
| 256 | * on a budget overrun it may already be in a release queue. Hence, | ||
| 257 | * calling unlink() is not possible since it assumes that the task is | ||
| 258 | * not in a release queue. | ||
| 259 | */ | ||
| 260 | |||
| 261 | /* Assumption: caller holds active_gbl_domain_lock */ | ||
| 262 | |||
| 263 | int check_preempt = 0; | ||
| 264 | |||
| 265 | if (tsk_rt(task)->linked_on != NO_CPU) { | ||
| 266 | TRACE_TASK(task, "%s: linked on %d\n", | ||
| 267 | __FUNCTION__, tsk_rt(task)->linked_on); | ||
| 268 | /* Task is scheduled; need to re-order CPUs. | ||
| 269 | * We can't use heap_decrease() here since | ||
| 270 | * the cpu_heap is ordered in reverse direction, so | ||
| 271 | * it is actually an increase. */ | ||
| 272 | bheap_delete(gbl_cpu_lower_prio, &active_gbl_plugin->cpu_heap, | ||
| 273 | active_gbl_plugin->cpus[tsk_rt(task)->linked_on]->hn); | ||
| 274 | bheap_insert(gbl_cpu_lower_prio, &active_gbl_plugin->cpu_heap, | ||
| 275 | active_gbl_plugin->cpus[tsk_rt(task)->linked_on]->hn); | ||
| 276 | } else { | ||
| 277 | /* task may be queued: first stop queue changes */ | ||
| 278 | raw_spin_lock(&active_gbl_domain.release_lock); | ||
| 279 | if (is_queued(task)) { | ||
| 280 | TRACE_TASK(task, "%s: is queued\n", | ||
| 281 | __FUNCTION__); | ||
| 282 | /* We need to update the position | ||
| 283 | * of task in some heap. Note that this | ||
| 284 | * may be a release heap. */ | ||
| 285 | check_preempt = | ||
| 286 | !bheap_decrease(gbl_ready_order, | ||
| 287 | tsk_rt(task)->heap_node); | ||
| 288 | } else { | ||
| 289 | /* Nothing to do: if it is not queued and not linked | ||
| 290 | * then it is currently being moved by other code | ||
| 291 | * (e.g., a timer interrupt handler) that will use the | ||
| 292 | * correct priority when enqueuing the task. */ | ||
| 293 | TRACE_TASK(task, "%s: is NOT queued => Done.\n", | ||
| 294 | __FUNCTION__); | ||
| 295 | } | ||
| 296 | raw_spin_unlock(&active_gbl_domain.release_lock); | ||
| 297 | |||
| 298 | /* If task was enqueued in a release heap, then the following | ||
| 299 | * preemption check is pointless, but we can't easily detect | ||
| 300 | * that case. If you want to fix this, then consider that | ||
| 301 | * simply adding a state flag requires O(n) time to update when | ||
| 302 | * releasing n tasks, which conflicts with the goal to have | ||
| 303 | * O(log n) merges. */ | ||
| 304 | if (check_preempt) { | ||
| 305 | /* heap_decrease() hit the top level of the heap: make | ||
| 306 | * sure preemption checks get the right task, not the | ||
| 307 | * potentially stale cache. */ | ||
| 308 | bheap_uncache_min(gbl_ready_order, | ||
| 309 | &active_gbl_domain.ready_queue); | ||
| 310 | gbl_check_for_preemptions(); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 271 | 315 | ||
| 272 | /* check for any necessary preemptions */ | 316 | /* check for any necessary preemptions */ |
| 273 | void gbl_check_for_preemptions(void) | 317 | void gbl_check_for_preemptions(void) |
| @@ -276,7 +320,7 @@ void gbl_check_for_preemptions(void) | |||
| 276 | cpu_entry_t* last; | 320 | cpu_entry_t* last; |
| 277 | 321 | ||
| 278 | for(last = lowest_prio_cpu(); | 322 | for(last = lowest_prio_cpu(); |
| 279 | gbl_preemption_needed(last->linked); | 323 | active_gbl_plugin->preemption_needed(last->linked); |
| 280 | last = lowest_prio_cpu()) | 324 | last = lowest_prio_cpu()) |
| 281 | { | 325 | { |
| 282 | /* preemption necessary */ | 326 | /* preemption necessary */ |
| @@ -392,6 +436,24 @@ void gblv_job_arrival(struct task_struct* task) | |||
| 392 | gbl_check_for_preemptions(); | 436 | gbl_check_for_preemptions(); |
| 393 | } | 437 | } |
| 394 | 438 | ||
| 439 | int gblv_preemption_needed(struct task_struct *t) | ||
| 440 | { | ||
| 441 | /* we need the read lock for active_gbl_domain's ready_queue */ | ||
| 442 | /* no need to preempt if there is nothing pending */ | ||
| 443 | if (!__jobs_pending(&active_gbl_domain)) | ||
| 444 | return 0; | ||
| 445 | /* we need to reschedule if t doesn't exist */ | ||
| 446 | if (!t) | ||
| 447 | return 1; | ||
| 448 | |||
| 449 | /* NOTE: We cannot check for non-preemptibility since we | ||
| 450 | * don't know what address space we're currently in. | ||
| 451 | */ | ||
| 452 | |||
| 453 | /* make sure to get non-rt stuff out of the way */ | ||
| 454 | return !is_realtime(t) || active_gbl_plugin->prio_order(__next_ready(&active_gbl_domain), t); | ||
| 455 | } | ||
| 456 | |||
| 395 | /* gbl_tick - this function is called for every local timer interrupt. | 457 | /* gbl_tick - this function is called for every local timer interrupt. |
| 396 | * | 458 | * |
| 397 | * checks whether the current task has expired and checks | 459 | * checks whether the current task has expired and checks |
diff --git a/litmus/sched_gsn_edf.c b/litmus/sched_gsn_edf.c index 7876d707d939..e4d17da0160a 100644 --- a/litmus/sched_gsn_edf.c +++ b/litmus/sched_gsn_edf.c | |||
| @@ -63,76 +63,12 @@ static struct sched_global_plugin gsn_edf_plugin __cacheline_aligned_in_smp = { | |||
| 63 | .take_ready = __take_ready, | 63 | .take_ready = __take_ready, |
| 64 | .add_ready = __add_ready, | 64 | .add_ready = __add_ready, |
| 65 | .job_arrival = gblv_job_arrival, | 65 | .job_arrival = gblv_job_arrival, |
| 66 | .job_completion = gbl_job_completion | 66 | .job_completion = gbl_job_completion, |
| 67 | .preemption_needed = gblv_preemption_needed | ||
| 67 | }; | 68 | }; |
| 68 | 69 | ||
| 69 | 70 | ||
| 70 | #ifdef CONFIG_FMLP | 71 | #ifdef CONFIG_FMLP |
| 71 | /* Update the queue position of a task that got it's priority boosted via | ||
| 72 | * priority inheritance. */ | ||
| 73 | static void update_queue_position(struct task_struct *holder) | ||
| 74 | { | ||
| 75 | /* We don't know whether holder is in the ready queue. It should, but | ||
| 76 | * on a budget overrun it may already be in a release queue. Hence, | ||
| 77 | * calling unlink() is not possible since it assumes that the task is | ||
| 78 | * not in a release queue. However, we can safely check whether | ||
| 79 | * sem->holder is currently in a queue or scheduled after locking both | ||
| 80 | * the release and the ready queue lock. */ | ||
| 81 | |||
| 82 | /* Assumption: caller holds gsnedf_lock */ | ||
| 83 | |||
| 84 | int check_preempt = 0; | ||
| 85 | |||
| 86 | if (tsk_rt(holder)->linked_on != NO_CPU) { | ||
| 87 | TRACE_TASK(holder, "%s: linked on %d\n", | ||
| 88 | __FUNCTION__, tsk_rt(holder)->linked_on); | ||
| 89 | /* Holder is scheduled; need to re-order CPUs. | ||
| 90 | * We can't use heap_decrease() here since | ||
| 91 | * the cpu_heap is ordered in reverse direction, so | ||
| 92 | * it is actually an increase. */ | ||
| 93 | bheap_delete(gbl_cpu_lower_prio, &gsn_edf_plugin.cpu_heap, | ||
| 94 | gsn_edf_plugin.cpus[tsk_rt(holder)->linked_on]->hn); | ||
| 95 | bheap_insert(gbl_cpu_lower_prio, &gsn_edf_plugin.cpu_heap, | ||
| 96 | gsn_edf_plugin.cpus[tsk_rt(holder)->linked_on]->hn); | ||
| 97 | } else { | ||
| 98 | /* holder may be queued: first stop queue changes */ | ||
| 99 | raw_spin_lock(&gsn_edf_plugin.domain.release_lock); | ||
| 100 | if (is_queued(holder)) { | ||
| 101 | TRACE_TASK(holder, "%s: is queued\n", | ||
| 102 | __FUNCTION__); | ||
| 103 | /* We need to update the position | ||
| 104 | * of holder in some heap. Note that this | ||
| 105 | * may be a release heap. */ | ||
| 106 | check_preempt = | ||
| 107 | !bheap_decrease(edf_ready_order, | ||
| 108 | tsk_rt(holder)->heap_node); | ||
| 109 | } else { | ||
| 110 | /* Nothing to do: if it is not queued and not linked | ||
| 111 | * then it is currently being moved by other code | ||
| 112 | * (e.g., a timer interrupt handler) that will use the | ||
| 113 | * correct priority when enqueuing the task. */ | ||
| 114 | TRACE_TASK(holder, "%s: is NOT queued => Done.\n", | ||
| 115 | __FUNCTION__); | ||
| 116 | } | ||
| 117 | raw_spin_unlock(&gsn_edf_plugin.domain.release_lock); | ||
| 118 | |||
| 119 | /* If holder was enqueued in a release heap, then the following | ||
| 120 | * preemption check is pointless, but we can't easily detect | ||
| 121 | * that case. If you want to fix this, then consider that | ||
| 122 | * simply adding a state flag requires O(n) time to update when | ||
| 123 | * releasing n tasks, which conflicts with the goal to have | ||
| 124 | * O(log n) merges. */ | ||
| 125 | if (check_preempt) { | ||
| 126 | /* heap_decrease() hit the top level of the heap: make | ||
| 127 | * sure preemption checks get the right task, not the | ||
| 128 | * potentially stale cache. */ | ||
| 129 | bheap_uncache_min(gbl_ready_order, | ||
| 130 | &gsn_edf_plugin.domain.ready_queue); | ||
| 131 | gbl_check_for_preemptions(); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | static long gsnedf_pi_block(struct pi_semaphore *sem, | 72 | static long gsnedf_pi_block(struct pi_semaphore *sem, |
| 137 | struct task_struct *new_waiter) | 73 | struct task_struct *new_waiter) |
| 138 | { | 74 | { |
| @@ -158,7 +94,7 @@ static long gsnedf_pi_block(struct pi_semaphore *sem, | |||
| 158 | new_waiter->comm, new_waiter->pid); | 94 | new_waiter->comm, new_waiter->pid); |
| 159 | /* let holder inherit */ | 95 | /* let holder inherit */ |
| 160 | sem->holder->rt_param.inh_task = new_waiter; | 96 | sem->holder->rt_param.inh_task = new_waiter; |
| 161 | update_queue_position(sem->holder); | 97 | gbl_update_queue_position(sem->holder); |
| 162 | } | 98 | } |
| 163 | raw_spin_unlock(&gsnedf_lock); | 99 | raw_spin_unlock(&gsnedf_lock); |
| 164 | } | 100 | } |
