diff options
author | Christopher Kenna <cjk@cs.unc.edu> | 2011-09-30 01:23:20 -0400 |
---|---|---|
committer | Christopher Kenna <cjk@cs.unc.edu> | 2011-09-30 01:23:20 -0400 |
commit | cd5685b6483df2f1ba8affc0ff8a0679f4044db8 (patch) | |
tree | b2c15c6f04fdfd96a738900d8e822057847ea641 /litmus/sched_mc.c | |
parent | 23a00b911b968c6290251913ecc34171836b4d32 (diff) |
Refactor timer merging and add it to CE plugin.
THIS CODE IS UNTESTED
We now initialize one event group for each cpu on system start. We can
get the event group for a CPU via a function in event_group.c
Another change is that an event now stores what group it is in when it
add_event() is called on it. This lets us cancel it without knowing what
event group it is in.
The above is important because Level-C events (like releases) have a
NULL event group. When calling add_event(), it will get the event group
of the current CPU. If the event needs to be canceled later, we need
that saved group in the event so we know where to remove it from.
Diffstat (limited to 'litmus/sched_mc.c')
-rw-r--r-- | litmus/sched_mc.c | 135 |
1 files changed, 75 insertions, 60 deletions
diff --git a/litmus/sched_mc.c b/litmus/sched_mc.c index 7b74958d1f4..17f84c9eba7 100644 --- a/litmus/sched_mc.c +++ b/litmus/sched_mc.c | |||
@@ -27,6 +27,8 @@ | |||
27 | #include <litmus/sched_mc.h> | 27 | #include <litmus/sched_mc.h> |
28 | #include <litmus/ce_domain.h> | 28 | #include <litmus/ce_domain.h> |
29 | 29 | ||
30 | /* XXX TODO Do we ever want to move level-A timers? */ | ||
31 | |||
30 | /** | 32 | /** |
31 | * struct cpu_entry - State of a CPU for the entire MC system | 33 | * struct cpu_entry - State of a CPU for the entire MC system |
32 | * @cpu CPU id | 34 | * @cpu CPU id |
@@ -34,6 +36,9 @@ | |||
34 | * @linked Task that should be running / is logically running | 36 | * @linked Task that should be running / is logically running |
35 | * @lock For serialization | 37 | * @lock For serialization |
36 | * @crit_entries Array of CPU state per criticality level | 38 | * @crit_entries Array of CPU state per criticality level |
39 | * @redir List of redirected work for this CPU. | ||
40 | * @redir_lock Lock for @redir. | ||
41 | * @event_group Event group for timer merging. | ||
37 | */ | 42 | */ |
38 | struct cpu_entry { | 43 | struct cpu_entry { |
39 | int cpu; | 44 | int cpu; |
@@ -45,18 +50,12 @@ struct cpu_entry { | |||
45 | struct list_head redir; | 50 | struct list_head redir; |
46 | raw_spinlock_t redir_lock; | 51 | raw_spinlock_t redir_lock; |
47 | #endif | 52 | #endif |
48 | #ifdef CONFIG_MERGE_TIMERS | ||
49 | struct event_group* event_group; | ||
50 | #endif | ||
51 | }; | 53 | }; |
52 | 54 | ||
53 | DEFINE_PER_CPU(struct cpu_entry, cpus); | 55 | DEFINE_PER_CPU(struct cpu_entry, cpus); |
54 | #ifdef CONFIG_RELEASE_MASTER | 56 | #ifdef CONFIG_RELEASE_MASTER |
55 | static int interrupt_cpu; | 57 | static int interrupt_cpu; |
56 | #endif | 58 | #endif |
57 | #ifdef CONFIG_MERGE_TIMERS | ||
58 | static struct event_group* global_group; | ||
59 | #endif | ||
60 | 59 | ||
61 | #define domain_data(dom) (container_of(dom, struct domain_data, domain)) | 60 | #define domain_data(dom) (container_of(dom, struct domain_data, domain)) |
62 | #define is_global(dom) (domain_data(dom)->heap) | 61 | #define is_global(dom) (domain_data(dom)->heap) |
@@ -67,6 +66,7 @@ static struct event_group* global_group; | |||
67 | (((e)->linked) ? tsk_mc_crit((e)->linked) : NUM_CRIT_LEVELS - 1) | 66 | (((e)->linked) ? tsk_mc_crit((e)->linked) : NUM_CRIT_LEVELS - 1) |
68 | #define crit_cpu(ce) \ | 67 | #define crit_cpu(ce) \ |
69 | (container_of((void*)((ce) - (ce)->level), struct cpu_entry, crit_entries)) | 68 | (container_of((void*)((ce) - (ce)->level), struct cpu_entry, crit_entries)) |
69 | #define get_crit_entry_for(cpu, level) (&per_cpu(cpus, cpu).crit_entries[level]) | ||
70 | #define TRACE_ENTRY(e, fmt, args...) \ | 70 | #define TRACE_ENTRY(e, fmt, args...) \ |
71 | STRACE("P%d, linked=" TS " " fmt "\n", e->cpu, TA(e->linked), ##args) | 71 | STRACE("P%d, linked=" TS " " fmt "\n", e->cpu, TA(e->linked), ##args) |
72 | #define TRACE_CRIT_ENTRY(ce, fmt, args...) \ | 72 | #define TRACE_CRIT_ENTRY(ce, fmt, args...) \ |
@@ -129,7 +129,7 @@ static inline struct crit_entry* lowest_prio_cpu(struct domain *dom) | |||
129 | static inline void cancel_ghost(struct crit_entry *ce) | 129 | static inline void cancel_ghost(struct crit_entry *ce) |
130 | { | 130 | { |
131 | #ifdef CONFIG_MERGE_TIMERS | 131 | #ifdef CONFIG_MERGE_TIMERS |
132 | cancel_event(crit_cpu(ce)->event_group, &ce->event); | 132 | cancel_event(&ce->event); |
133 | #else | 133 | #else |
134 | hrtimer_try_to_cancel(&ce->timer); | 134 | hrtimer_try_to_cancel(&ce->timer); |
135 | #endif | 135 | #endif |
@@ -141,10 +141,10 @@ static inline void cancel_ghost(struct crit_entry *ce) | |||
141 | static inline void arm_ghost(struct crit_entry *ce, lt_t fire) | 141 | static inline void arm_ghost(struct crit_entry *ce, lt_t fire) |
142 | { | 142 | { |
143 | #ifdef CONFIG_MERGE_TIMERS | 143 | #ifdef CONFIG_MERGE_TIMERS |
144 | add_event(crit_cpu(ce)->event_group, &ce->event, fire); | 144 | add_event(get_event_group_for(crit_cpu(ce)->cpu), &ce->event, fire); |
145 | #else | 145 | #else |
146 | __hrtimer_start_range_ns(&ce->timer, | 146 | __hrtimer_start_range_ns(&ce->timer, |
147 | ns_to_ktime(when_to_fire), | 147 | ns_to_ktime(fire), |
148 | 0 /* delta */, | 148 | 0 /* delta */, |
149 | HRTIMER_MODE_ABS_PINNED, | 149 | HRTIMER_MODE_ABS_PINNED, |
150 | 0 /* no wakeup */); | 150 | 0 /* no wakeup */); |
@@ -270,10 +270,8 @@ static void low_prio_arrival(struct task_struct *task) | |||
270 | if (!can_requeue(task)) return; | 270 | if (!can_requeue(task)) return; |
271 | 271 | ||
272 | #ifdef CONFIG_PLUGIN_MC_REDIRECT | 272 | #ifdef CONFIG_PLUGIN_MC_REDIRECT |
273 | #ifndef CONFIG_PLUGIN_MC_REDIRECT_ALL | ||
274 | if (!is_global_task(task)) | 273 | if (!is_global_task(task)) |
275 | goto arrive; | 274 | goto arrive; |
276 | #endif | ||
277 | if (smp_processor_id() != interrupt_cpu) { | 275 | if (smp_processor_id() != interrupt_cpu) { |
278 | entry = &__get_cpu_var(cpus); | 276 | entry = &__get_cpu_var(cpus); |
279 | raw_spin_lock(&entry->redir_lock); | 277 | raw_spin_lock(&entry->redir_lock); |
@@ -284,7 +282,7 @@ static void low_prio_arrival(struct task_struct *task) | |||
284 | } else | 282 | } else |
285 | #endif | 283 | #endif |
286 | { | 284 | { |
287 | arrive: | 285 | arrive: |
288 | job_arrival(task); | 286 | job_arrival(task); |
289 | } | 287 | } |
290 | } | 288 | } |
@@ -576,19 +574,19 @@ static enum hrtimer_restart mc_ghost_exhausted(struct hrtimer *timer) | |||
576 | #endif | 574 | #endif |
577 | } | 575 | } |
578 | 576 | ||
579 | static enum hrtimer_restart ce_timer_function(struct hrtimer *timer) | 577 | /* |
578 | * The MC-CE common timer callback code for merged and non-merged timers. | ||
579 | * Returns the next time the timer should fire. | ||
580 | */ | ||
581 | static lt_t __ce_timer_function(struct ce_dom_data *ce_data) | ||
580 | { | 582 | { |
581 | struct ce_dom_data *ce_data = | 583 | struct crit_entry *ce = get_crit_entry_for(ce_data->cpu, CRIT_LEVEL_A); |
582 | container_of(timer, struct ce_dom_data, timer); | ||
583 | struct crit_entry *ce = &per_cpu(cpus, ce_data->cpu).crit_entries[CRIT_LEVEL_A]; | ||
584 | struct domain *dom = ce->domain; | 584 | struct domain *dom = ce->domain; |
585 | struct task_struct *old_link = NULL; | 585 | struct task_struct *old_link = NULL; |
586 | unsigned long flags; | 586 | lt_t next_timer_abs; |
587 | 587 | ||
588 | TRACE("MC level-A timer callback for CPU %d\n", ce_data->cpu); | 588 | TRACE("MC level-A timer callback for CPU %d\n", ce_data->cpu); |
589 | 589 | ||
590 | local_irq_save(flags); | ||
591 | |||
592 | raw_spin_lock(dom->lock); | 590 | raw_spin_lock(dom->lock); |
593 | 591 | ||
594 | raw_spin_lock(&crit_cpu(ce)->lock); | 592 | raw_spin_lock(&crit_cpu(ce)->lock); |
@@ -602,7 +600,7 @@ static enum hrtimer_restart ce_timer_function(struct hrtimer *timer) | |||
602 | } | 600 | } |
603 | raw_spin_unlock(&crit_cpu(ce)->lock); | 601 | raw_spin_unlock(&crit_cpu(ce)->lock); |
604 | 602 | ||
605 | mc_ce_timer_callback_common(dom, timer); | 603 | next_timer_abs = mc_ce_timer_callback_common(dom); |
606 | 604 | ||
607 | /* job completion will check for preemptions by means of calling job | 605 | /* job completion will check for preemptions by means of calling job |
608 | * arrival if the task is not blocked */ | 606 | * arrival if the task is not blocked */ |
@@ -615,11 +613,38 @@ static enum hrtimer_restart ce_timer_function(struct hrtimer *timer) | |||
615 | raw_spin_unlock(dom->lock); | 613 | raw_spin_unlock(dom->lock); |
616 | check_for_preempt(dom); | 614 | check_for_preempt(dom); |
617 | } | 615 | } |
616 | return next_timer_abs; | ||
617 | } | ||
618 | 618 | ||
619 | #ifdef CONFIG_MERGE_TIMERS | ||
620 | static void ce_timer_function(struct rt_event *e) | ||
621 | { | ||
622 | struct ce_dom_data *ce_data = | ||
623 | container_of(e, struct ce_dom_data, event); | ||
624 | struct event_group *event_group = get_event_group_for(ce_data->cpu); | ||
625 | unsigned long flags; | ||
626 | lt_t next_timer_abs; | ||
627 | |||
628 | local_irq_save(flags); | ||
629 | next_timer_abs = __ce_timer_function(ce_data); | ||
630 | add_event(event_group, e, next_timer_abs); | ||
619 | local_irq_restore(flags); | 631 | local_irq_restore(flags); |
632 | } | ||
633 | #else /* else to CONFIG_MERGE_TIMERS */ | ||
634 | static enum hrtimer_restart ce_timer_function(struct hrtimer *timer) | ||
635 | { | ||
636 | struct ce_dom_data *ce_data = | ||
637 | container_of(timer, struct ce_dom_data, timer); | ||
638 | unsigned long flags; | ||
639 | lt_t next_timer_abs; | ||
620 | 640 | ||
641 | local_irq_save(flags); | ||
642 | next_timer_abs = __ce_timer_function(ce_data); | ||
643 | hrtimer_set_expires(timer, ns_to_ktime(next_timer_abs)); | ||
644 | local_irq_restore(flags); | ||
621 | return HRTIMER_RESTART; | 645 | return HRTIMER_RESTART; |
622 | } | 646 | } |
647 | #endif /* CONFIG_MERGE_TIMERS */ | ||
623 | 648 | ||
624 | 649 | ||
625 | /** | 650 | /** |
@@ -915,14 +940,6 @@ static rt_domain_t _mc_crit_c_rt; | |||
915 | struct bheap _mc_heap_c; | 940 | struct bheap _mc_heap_c; |
916 | struct bheap_node _mc_nodes_c[NR_CPUS]; | 941 | struct bheap_node _mc_nodes_c[NR_CPUS]; |
917 | 942 | ||
918 | #ifdef CONFIG_MERGE_TIMERS | ||
919 | #ifdef CONFIG_PLUGIN_MC_RELEASE_MASTER | ||
920 | struct event_group _mc_group; | ||
921 | #else | ||
922 | DEFINE_PER_CPU(struct event_group, _mc_groups); | ||
923 | #endif | ||
924 | #endif | ||
925 | |||
926 | static long mc_activate_plugin(void) | 943 | static long mc_activate_plugin(void) |
927 | { | 944 | { |
928 | struct domain_data *dom_data; | 945 | struct domain_data *dom_data; |
@@ -933,8 +950,14 @@ static long mc_activate_plugin(void) | |||
933 | 950 | ||
934 | #ifdef CONFIG_RELEASE_MASTER | 951 | #ifdef CONFIG_RELEASE_MASTER |
935 | interrupt_cpu = atomic_read(&release_master_cpu); | 952 | interrupt_cpu = atomic_read(&release_master_cpu); |
936 | if (interrupt_cpu == NO_CPU) | 953 | #if defined(CONFIG_PLUGIN_MC_REDIRECT) || \ |
937 | interrupt_cpu = 0; | 954 | (defined(CONFIG_PLUGIN_MC_RELEASE_MASTER) && defined(CONFIG_MERGE_TIMERS)) |
955 | if (NO_CPU == interrupt_cpu) { | ||
956 | printk(KERN_ERR "LITMUS-MC: need a release master\n"); | ||
957 | ret = -EINVAL; | ||
958 | goto out; | ||
959 | } | ||
960 | #endif | ||
938 | #endif | 961 | #endif |
939 | 962 | ||
940 | for_each_online_cpu(cpu) { | 963 | for_each_online_cpu(cpu) { |
@@ -1016,20 +1039,30 @@ static void init_global_domain(struct domain_data *dom_data, enum crit_level lev | |||
1016 | } | 1039 | } |
1017 | 1040 | ||
1018 | static inline void init_edf_domain(struct domain *dom, rt_domain_t *rt, | 1041 | static inline void init_edf_domain(struct domain *dom, rt_domain_t *rt, |
1019 | int timer_cpu, int prio) | 1042 | int prio, int is_partitioned, int cpu) |
1020 | { | 1043 | { |
1021 | pd_domain_init(dom, rt, edf_ready_order, NULL, | 1044 | pd_domain_init(dom, rt, edf_ready_order, NULL, |
1022 | mc_release_jobs, mc_preempt_needed, | 1045 | mc_release_jobs, mc_preempt_needed, |
1023 | edf_higher_prio); | 1046 | edf_higher_prio); |
1024 | #ifdef CONFIG_PLUGIN_MC_RELEASE_MASTER | 1047 | #if defined(CONFIG_PLUGIN_MC_RELEASE_MASTER) && defined(CONFIG_MERGE_TIMERS) |
1025 | #ifdef CONFIG_MERGE_TIMERS | 1048 | /* All timers are on one CPU and release-master is using the event |
1026 | rt->event_group = &_mc_group; | 1049 | * merging interface as well. */ |
1050 | BUG_ON(NO_CPU == interrupt_cpu); | ||
1051 | rt->event_group = get_event_group_for(interrupt_cpu); | ||
1027 | rt->prio = prio; | 1052 | rt->prio = prio; |
1028 | #else | 1053 | #elif defined(CONFIG_PLUGIN_MC_RELEASE_MASTER) && !defined(CONFIG_MERGE_TIMERS) |
1054 | /* Using release master, but not merging timers. */ | ||
1029 | rt->release_master = interrupt_cpu; | 1055 | rt->release_master = interrupt_cpu; |
1030 | #endif | 1056 | #elif !defined(CONFIG_PLUGIN_MC_RELEASE_MASTER) && defined(CONFIG_MERGE_TIMERS) |
1031 | #elif CONFIG_MERGE_TIMERS | 1057 | /* Merge the timers, but don't move them to the release master. */ |
1032 | rt->event_group = &_mc_groups[timer_cpu]; | 1058 | if (is_partitioned) { |
1059 | rt->event_group = get_event_group_for(cpu); | ||
1060 | } else { | ||
1061 | /* Global timers will be added to the event groups that code is | ||
1062 | * executing on when add_event() is called. | ||
1063 | */ | ||
1064 | rt->event_group = NULL; | ||
1065 | } | ||
1033 | rt->prio = prio; | 1066 | rt->prio = prio; |
1034 | #endif | 1067 | #endif |
1035 | } | 1068 | } |
@@ -1058,16 +1091,6 @@ static int __init init_mc(void) | |||
1058 | INIT_LIST_HEAD(&entry->redir); | 1091 | INIT_LIST_HEAD(&entry->redir); |
1059 | #endif | 1092 | #endif |
1060 | 1093 | ||
1061 | #ifdef CONFIG_MERGE_TIMERS | ||
1062 | #ifdef CONFIG_PLUGIN_MC_RELEASE_MASTER | ||
1063 | entry->event_group = &_mc_group; | ||
1064 | #else | ||
1065 | init_event_group(&_mc_groups[cpu], | ||
1066 | CONFIG_MERGE_TIMERS_WINDOW, cpu); | ||
1067 | entry->event_group = &_mc_groups[cpu]; | ||
1068 | #endif | ||
1069 | #endif | ||
1070 | |||
1071 | /* CRIT_LEVEL_A */ | 1094 | /* CRIT_LEVEL_A */ |
1072 | dom_data = &per_cpu(_mc_crit_a, cpu); | 1095 | dom_data = &per_cpu(_mc_crit_a, cpu); |
1073 | ce_data = &per_cpu(_mc_crit_a_ce_data, cpu); | 1096 | ce_data = &per_cpu(_mc_crit_a_ce_data, cpu); |
@@ -1085,25 +1108,17 @@ static int __init init_mc(void) | |||
1085 | dom_data = &per_cpu(_mc_crit_b, cpu); | 1108 | dom_data = &per_cpu(_mc_crit_b, cpu); |
1086 | rt = &per_cpu(_mc_crit_b_rt, cpu); | 1109 | rt = &per_cpu(_mc_crit_b_rt, cpu); |
1087 | init_local_domain(entry, dom_data, CRIT_LEVEL_B); | 1110 | init_local_domain(entry, dom_data, CRIT_LEVEL_B); |
1088 | init_edf_domain(&dom_data->domain, rt, cpu, CRIT_LEVEL_B); | 1111 | init_edf_domain(&dom_data->domain, rt, CRIT_LEVEL_B, 1, cpu); |
1089 | b_dom_lock = dom_data->domain.lock; | 1112 | b_dom_lock = dom_data->domain.lock; |
1090 | raw_spin_lock_init(b_dom_lock); | 1113 | raw_spin_lock_init(b_dom_lock); |
1091 | dom_data->domain.name = "LVL-B"; | 1114 | dom_data->domain.name = "LVL-B"; |
1092 | } | 1115 | } |
1093 | 1116 | ||
1094 | #ifdef CONFIG_MERGE_TIMERS | ||
1095 | #ifdef CONFIG_PLUGIN_MC_RELEASE_MASTER | ||
1096 | init_event_group(&_mc_group, CONFIG_MERGE_TIMERS_WINDOW, interrupt_cpu); | ||
1097 | global_group = &_mc_group; | ||
1098 | #else | ||
1099 | global_group = &_mc_groups[0]; | ||
1100 | #endif | ||
1101 | #endif | ||
1102 | |||
1103 | /* CRIT_LEVEL_C */ | 1117 | /* CRIT_LEVEL_C */ |
1104 | init_global_domain(&_mc_crit_c, CRIT_LEVEL_C, | 1118 | init_global_domain(&_mc_crit_c, CRIT_LEVEL_C, |
1105 | &_mc_heap_c, _mc_nodes_c); | 1119 | &_mc_heap_c, _mc_nodes_c); |
1106 | init_edf_domain(&_mc_crit_c.domain, &_mc_crit_c_rt, 0, CRIT_LEVEL_C); | 1120 | init_edf_domain(&_mc_crit_c.domain, &_mc_crit_c_rt, CRIT_LEVEL_C, |
1121 | 0, NO_CPU); | ||
1107 | c_dom_lock = _mc_crit_c.domain.lock; | 1122 | c_dom_lock = _mc_crit_c.domain.lock; |
1108 | raw_spin_lock_init(c_dom_lock); | 1123 | raw_spin_lock_init(c_dom_lock); |
1109 | _mc_crit_c.domain.name = "LVL-C"; | 1124 | _mc_crit_c.domain.name = "LVL-C"; |