diff options
-rw-r--r-- | include/litmus/ce_domain.h | 23 | ||||
-rw-r--r-- | include/litmus/sched_mc.h | 37 | ||||
-rw-r--r-- | litmus/ce_domain.c | 89 | ||||
-rw-r--r-- | litmus/sched_mc.c | 87 | ||||
-rw-r--r-- | litmus/sched_mc_ce.c | 351 |
5 files changed, 339 insertions, 248 deletions
diff --git a/include/litmus/ce_domain.h b/include/litmus/ce_domain.h new file mode 100644 index 000000000000..373f3f5f78d3 --- /dev/null +++ b/include/litmus/ce_domain.h | |||
@@ -0,0 +1,23 @@ | |||
1 | #ifndef _LITMUS_CE_DOMAIN_H | ||
2 | #define _LITMUS_CE_DOMAIN_H | ||
3 | |||
4 | /* | ||
5 | * Functions that the MC plugin needs to call through a domain pointer. | ||
6 | */ | ||
7 | void ce_requeue(domain_t*, struct task_struct*); | ||
8 | struct task_struct* ce_peek_and_take_ready(domain_t*); | ||
9 | int ce_higher_prio(struct task_struct*, struct task_struct*); | ||
10 | |||
11 | typedef enum hrtimer_restart (*ce_timer_callback_t)(struct hrtimer*); | ||
12 | |||
13 | void ce_domain_init(domain_t*, | ||
14 | raw_spinlock_t*, | ||
15 | requeue_t, | ||
16 | peek_ready_t, | ||
17 | take_ready_t, | ||
18 | preempt_needed_t, | ||
19 | task_prio_t, | ||
20 | struct ce_dom_data*, | ||
21 | const int, | ||
22 | ce_timer_callback_t); | ||
23 | #endif | ||
diff --git a/include/litmus/sched_mc.h b/include/litmus/sched_mc.h index ad5d097b3d61..384e65e4151d 100644 --- a/include/litmus/sched_mc.h +++ b/include/litmus/sched_mc.h | |||
@@ -21,7 +21,10 @@ struct mc_job { | |||
21 | }; | 21 | }; |
22 | 22 | ||
23 | #ifdef __KERNEL__ | 23 | #ifdef __KERNEL__ |
24 | /* only used in the kernel (no user space) */ | 24 | /* |
25 | * These are used only in the kernel. Userspace programs like RTSpin won't see | ||
26 | * them. | ||
27 | */ | ||
25 | 28 | ||
26 | struct mc_data { | 29 | struct mc_data { |
27 | struct mc_task mc_task; | 30 | struct mc_task mc_task; |
@@ -33,18 +36,8 @@ struct mc_data { | |||
33 | #define is_ghost(t) (tsk_mc_data(t)->mc_job.is_ghost) | 36 | #define is_ghost(t) (tsk_mc_data(t)->mc_job.is_ghost) |
34 | 37 | ||
35 | /* | 38 | /* |
36 | * Cache the budget along with the struct PID for a task so that we don't need | 39 | * The MC-CE scheduler uses this as domain data. |
37 | * to fetch its task_struct every time we check to see what should be | ||
38 | * scheduled. | ||
39 | */ | 40 | */ |
40 | struct ce_dom_pid_entry { | ||
41 | struct pid *pid; | ||
42 | lt_t budget; | ||
43 | /* accumulated (summed) budgets, including this one */ | ||
44 | lt_t acc_time; | ||
45 | int expected_job; | ||
46 | }; | ||
47 | |||
48 | struct ce_dom_data { | 41 | struct ce_dom_data { |
49 | int cpu; | 42 | int cpu; |
50 | struct task_struct *scheduled, *should_schedule; | 43 | struct task_struct *scheduled, *should_schedule; |
@@ -52,9 +45,6 @@ struct ce_dom_data { | |||
52 | * Each CPU needs a mapping of level A ID (integer) to struct pid so | 45 | * Each CPU needs a mapping of level A ID (integer) to struct pid so |
53 | * that we can get its task struct. | 46 | * that we can get its task struct. |
54 | */ | 47 | */ |
55 | struct ce_dom_pid_entry pid_entries[CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS]; | ||
56 | int num_pid_entries; | ||
57 | lt_t cycle_time; | ||
58 | struct hrtimer_start_on_info timer_info; | 48 | struct hrtimer_start_on_info timer_info; |
59 | struct hrtimer timer; | 49 | struct hrtimer timer; |
60 | }; | 50 | }; |
@@ -90,6 +80,23 @@ typedef struct { | |||
90 | crit_entry_t* crit_entry; | 80 | crit_entry_t* crit_entry; |
91 | } domain_data_t; | 81 | } domain_data_t; |
92 | 82 | ||
83 | /* | ||
84 | * Functions that are used with the MC-CE plugin. | ||
85 | */ | ||
86 | long mc_ce_set_domains(const int, domain_data_t*[]); | ||
87 | unsigned int mc_ce_get_expected_job(const int, const int); | ||
88 | |||
89 | /* | ||
90 | * These functions are (lazily) inserted into the MC plugin code so that it | ||
91 | * manipulates the MC-CE state. | ||
92 | */ | ||
93 | long mc_ce_admit_task_common(struct task_struct*); | ||
94 | void mc_ce_task_exit_common(struct task_struct*); | ||
95 | void mc_ce_timer_callback_common(domain_t*, struct hrtimer*); | ||
96 | void mc_ce_release_at_common(struct task_struct*, lt_t); | ||
97 | long mc_ce_activate_plugin_common(void); | ||
98 | long mc_ce_deactivate_plugin_common(void); | ||
99 | |||
93 | #endif /* __KERNEL__ */ | 100 | #endif /* __KERNEL__ */ |
94 | 101 | ||
95 | #endif | 102 | #endif |
diff --git a/litmus/ce_domain.c b/litmus/ce_domain.c index 8797c05d9392..c00feaf45a5c 100644 --- a/litmus/ce_domain.c +++ b/litmus/ce_domain.c | |||
@@ -19,35 +19,25 @@ void ce_requeue(domain_t *dom, struct task_struct *ts) | |||
19 | { | 19 | { |
20 | const struct ce_dom_data *ce_data = dom->data; | 20 | const struct ce_dom_data *ce_data = dom->data; |
21 | const int idx = tsk_mc_data(ts)->mc_task.lvl_a_id; | 21 | const int idx = tsk_mc_data(ts)->mc_task.lvl_a_id; |
22 | const struct ce_dom_pid_entry *pid_entry = | 22 | const unsigned int just_finished = tsk_rt(ts)->job_params.job_no; |
23 | &ce_data->pid_entries[idx]; | 23 | const unsigned int expected_job = |
24 | const int just_finished = tsk_rt(ts)->job_params.job_no; | 24 | mc_ce_get_expected_job(ce_data->cpu, idx); |
25 | const int expected_job = pid_entry->expected_job; | ||
26 | const int asleep = RT_F_SLEEP == get_rt_flags(ts); | 25 | const int asleep = RT_F_SLEEP == get_rt_flags(ts); |
27 | 26 | ||
28 | TRACE_TASK(ts, "entered ce_requeue. asleep: %d just_finished: %4d " | 27 | TRACE_TASK(ts, "entered ce_requeue. asleep: %d just_finished: %3u " |
29 | "expected_job: %4d\n", | 28 | "expected_job: %3u\n", |
30 | asleep, just_finished, expected_job); | 29 | asleep, just_finished, expected_job); |
31 | 30 | ||
32 | /* When coming from job completion, the task will be asleep. */ | 31 | /* When coming from job completion, the task will be asleep. */ |
33 | if (asleep && just_finished < expected_job) { | 32 | if (asleep && just_finished < expected_job) { |
34 | TRACE_TASK(ts, "appears behind\n"); | 33 | TRACE_TASK(ts, "appears behind\n"); |
35 | } else if (asleep && expected_job < just_finished) { | 34 | } else if (asleep && expected_job < just_finished) { |
36 | TRACE_TASK(ts, "job %d completed in expected job %d which " | 35 | TRACE_TASK(ts, "job %u completed in expected job %u which " |
37 | "seems too early\n", just_finished, | 36 | "seems too early\n", just_finished, |
38 | expected_job); | 37 | expected_job); |
39 | } | 38 | } |
40 | } | 39 | } |
41 | 40 | ||
42 | void mc_ce_task_exit(struct task_struct*); | ||
43 | /* | ||
44 | * Called when a task exits the system. | ||
45 | */ | ||
46 | void ce_task_exit(domain_t *dom, struct task_struct *ts) | ||
47 | { | ||
48 | mc_ce_task_exit(ts); | ||
49 | } | ||
50 | |||
51 | /* | 41 | /* |
52 | * ce_take_ready and ce_peek_ready | 42 | * ce_take_ready and ce_peek_ready |
53 | */ | 43 | */ |
@@ -74,8 +64,7 @@ struct task_struct* ce_peek_and_take_ready(domain_t *dom) | |||
74 | (t) ? t->rt_param.job_params.job_no : 1, \ | 64 | (t) ? t->rt_param.job_params.job_no : 1, \ |
75 | (t && get_task_domain(t)) ? get_task_domain(t)->name : "" | 65 | (t && get_task_domain(t)) ? get_task_domain(t)->name : "" |
76 | 66 | ||
77 | int ce_higher_prio(struct task_struct *_a, | 67 | int ce_higher_prio(struct task_struct *_a, struct task_struct *_b) |
78 | struct task_struct *_b) | ||
79 | { | 68 | { |
80 | const struct task_struct *a = _a; | 69 | const struct task_struct *a = _a; |
81 | const domain_t *dom = get_task_domain(a); | 70 | const domain_t *dom = get_task_domain(a); |
@@ -87,52 +76,22 @@ int ce_higher_prio(struct task_struct *_a, | |||
87 | return (a == ce_data->should_schedule); | 76 | return (a == ce_data->should_schedule); |
88 | } | 77 | } |
89 | 78 | ||
90 | void __mc_ce_timer_callback(struct hrtimer *timer); | 79 | void ce_domain_init(domain_t *dom, |
91 | domain_data_t *ce_domain_for(int); | 80 | raw_spinlock_t *lock, |
92 | void mc_check_for_preempt(domain_t*); | 81 | requeue_t requeue, |
93 | static enum hrtimer_restart ce_timer_function(struct hrtimer *timer) | 82 | peek_ready_t peek_ready, |
94 | { | 83 | take_ready_t take_ready, |
95 | struct ce_dom_data *ce_data; | 84 | preempt_needed_t preempt_needed, |
96 | domain_data_t *dom_data; | 85 | task_prio_t task_prio, |
97 | unsigned long flags; | 86 | struct ce_dom_data *dom_data, |
98 | 87 | const int cpu, | |
99 | TRACE("timer callback\n"); | 88 | ce_timer_callback_t ce_timer_callback) |
100 | |||
101 | ce_data = container_of(timer, struct ce_dom_data, timer); | ||
102 | dom_data = ce_domain_for(ce_data->cpu); | ||
103 | raw_spin_lock_irqsave(dom_data->domain.lock, flags); | ||
104 | __mc_ce_timer_callback(timer); | ||
105 | mc_check_for_preempt(&dom_data->domain); | ||
106 | raw_spin_unlock_irqrestore(dom_data->domain.lock, flags); | ||
107 | return HRTIMER_RESTART; | ||
108 | } | ||
109 | |||
110 | void mc_ce_release_at(struct task_struct*, lt_t); | ||
111 | void ce_start(struct task_struct *ts, lt_t start) | ||
112 | { | ||
113 | mc_ce_release_at(ts, start); | ||
114 | } | ||
115 | |||
116 | domain_data_t *ce_domain_for(int); | ||
117 | long mc_ce_activate_plugin(void); | ||
118 | long ce_activate_plugin(void) | ||
119 | { | ||
120 | domain_data_t *dom_data; | ||
121 | struct ce_dom_data *ce_data; | ||
122 | int cpu; | ||
123 | |||
124 | /* first change the timer callback function */ | ||
125 | for_each_online_cpu(cpu) { | ||
126 | dom_data = ce_domain_for(cpu); | ||
127 | ce_data = dom_data->domain.data; | ||
128 | ce_data->timer.function = ce_timer_function; | ||
129 | } | ||
130 | /* then run the regular CE activate plugin */ | ||
131 | return mc_ce_activate_plugin(); | ||
132 | } | ||
133 | |||
134 | long mc_ce_deactivate_plugin(void); | ||
135 | long ce_deactivate_plugin(void) | ||
136 | { | 89 | { |
137 | return mc_ce_deactivate_plugin(); | 90 | domain_init(dom, lock, requeue, peek_ready, take_ready, preempt_needed, |
91 | task_prio); | ||
92 | dom->data = dom_data; | ||
93 | dom_data->cpu = cpu; | ||
94 | hrtimer_start_on_info_init(&dom_data->timer_info); | ||
95 | hrtimer_init(&dom_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
96 | dom_data->timer.function = ce_timer_callback; | ||
138 | } | 97 | } |
diff --git a/litmus/sched_mc.c b/litmus/sched_mc.c index 3b98a93511ab..11ba10a54f4d 100644 --- a/litmus/sched_mc.c +++ b/litmus/sched_mc.c | |||
@@ -507,7 +507,7 @@ static enum hrtimer_restart mc_ghost_exhausted(struct hrtimer *timer) | |||
507 | { | 507 | { |
508 | unsigned long flags; | 508 | unsigned long flags; |
509 | struct task_struct *tmp = NULL; | 509 | struct task_struct *tmp = NULL; |
510 | crit_entry_t *ce = container_of(timer, crit_entry_t, timer);; | 510 | crit_entry_t *ce = container_of(timer, crit_entry_t, timer); |
511 | 511 | ||
512 | local_irq_save(flags); | 512 | local_irq_save(flags); |
513 | TRACE_CRIT_ENTRY(ce, "Ghost exhausted firing"); | 513 | TRACE_CRIT_ENTRY(ce, "Ghost exhausted firing"); |
@@ -534,22 +534,38 @@ static enum hrtimer_restart mc_ghost_exhausted(struct hrtimer *timer) | |||
534 | return HRTIMER_NORESTART; | 534 | return HRTIMER_NORESTART; |
535 | } | 535 | } |
536 | 536 | ||
537 | void __mc_ce_timer_callback(struct hrtimer *timer); | ||
538 | domain_data_t *ce_domain_for(int); | ||
539 | static enum hrtimer_restart ce_timer_function(struct hrtimer *timer) | 537 | static enum hrtimer_restart ce_timer_function(struct hrtimer *timer) |
540 | { | 538 | { |
541 | struct ce_dom_data *ce_data; | 539 | struct ce_dom_data *ce_data = |
542 | domain_data_t *dom_data; | 540 | container_of(timer, struct ce_dom_data, timer); |
541 | crit_entry_t *ce = &cpus[ce_data->cpu]->crit_entries[CRIT_LEVEL_A]; | ||
542 | domain_t *dom = ce->domain; | ||
543 | struct task_struct *old_link = NULL; | ||
543 | unsigned long flags; | 544 | unsigned long flags; |
544 | 545 | ||
545 | TRACE("timer callback\n"); | 546 | TRACE("MC level-A timer callback for CPU %d\n", ce_data->cpu); |
547 | |||
548 | raw_spin_lock_irqsave(dom->lock, flags); | ||
549 | |||
550 | raw_spin_lock(&crit_cpu(ce)->lock); | ||
551 | if (ce->linked && | ||
552 | ce->linked == ce_data->should_schedule && | ||
553 | is_ghost(ce->linked)) | ||
554 | { | ||
555 | update_ghost_time(ce->linked); | ||
556 | if (tsk_mc_data(ce->linked)->mc_job.ghost_budget == 0) { | ||
557 | old_link = ce->linked; | ||
558 | link_task_to_crit(ce, NULL); | ||
559 | } | ||
560 | } | ||
561 | raw_spin_unlock(&crit_cpu(ce)->lock); | ||
562 | |||
563 | if (NULL != old_link) | ||
564 | job_completion(old_link, 0); | ||
546 | 565 | ||
547 | ce_data = container_of(timer, struct ce_dom_data, timer); | 566 | mc_ce_timer_callback_common(dom, timer); |
548 | dom_data = ce_domain_for(ce_data->cpu); | 567 | mc_check_for_preempt(dom); |
549 | raw_spin_lock_irqsave(dom_data->domain.lock, flags); | 568 | raw_spin_unlock_irqrestore(dom->lock, flags); |
550 | __mc_ce_timer_callback(timer); | ||
551 | mc_check_for_preempt(&dom_data->domain); | ||
552 | raw_spin_unlock_irqrestore(dom_data->domain.lock, flags); | ||
553 | return HRTIMER_RESTART; | 569 | return HRTIMER_RESTART; |
554 | } | 570 | } |
555 | 571 | ||
@@ -658,12 +674,11 @@ static void mc_task_exit(struct task_struct *task) | |||
658 | } | 674 | } |
659 | 675 | ||
660 | if (CRIT_LEVEL_A == tsk_mc_crit(task)) | 676 | if (CRIT_LEVEL_A == tsk_mc_crit(task)) |
661 | ce_task_exit(get_task_domain(task), task); | 677 | mc_ce_task_exit_common(task); |
662 | 678 | ||
663 | local_irq_restore(flags); | 679 | local_irq_restore(flags); |
664 | } | 680 | } |
665 | 681 | ||
666 | long __mc_ce_admit_task(struct task_struct*); | ||
667 | /** | 682 | /** |
668 | * mc_admit_task() - Return true if the task is valid. | 683 | * mc_admit_task() - Return true if the task is valid. |
669 | * Assumes there are no partitioned levels after level B. | 684 | * Assumes there are no partitioned levels after level B. |
@@ -685,7 +700,7 @@ static long mc_admit_task(struct task_struct* task) | |||
685 | goto out; | 700 | goto out; |
686 | } | 701 | } |
687 | if (crit == CRIT_LEVEL_A) { | 702 | if (crit == CRIT_LEVEL_A) { |
688 | ret = __mc_ce_admit_task(task); | 703 | ret = mc_ce_admit_task_common(task); |
689 | if (ret) | 704 | if (ret) |
690 | goto out; | 705 | goto out; |
691 | } | 706 | } |
@@ -806,12 +821,30 @@ static struct task_struct* mc_schedule(struct task_struct * prev) | |||
806 | 821 | ||
807 | static long mc_activate_plugin(void) | 822 | static long mc_activate_plugin(void) |
808 | { | 823 | { |
824 | domain_data_t *dom_data; | ||
825 | domain_t *dom; | ||
826 | domain_data_t *our_domains[NR_CPUS]; | ||
827 | int cpu, n = 0; | ||
828 | long ret; | ||
829 | |||
809 | #ifdef CONFIG_RELEASE_MASTER | 830 | #ifdef CONFIG_RELEASE_MASTER |
810 | interrupt_cpu = atomic_read(&release_master_cpu); | 831 | interrupt_cpu = atomic_read(&release_master_cpu); |
811 | if (interrupt_cpu == NO_CPU) | 832 | if (interrupt_cpu == NO_CPU) |
812 | interrupt_cpu = 0; | 833 | interrupt_cpu = 0; |
813 | #endif | 834 | #endif |
814 | return ce_activate_plugin(); | 835 | for_each_online_cpu(cpu) { |
836 | BUG_ON(NR_CPUS <= n); | ||
837 | dom = cpus[cpu]->crit_entries[CRIT_LEVEL_A].domain; | ||
838 | dom_data = domain_data(dom); | ||
839 | our_domains[cpu] = dom_data; | ||
840 | n++; | ||
841 | } | ||
842 | ret = mc_ce_set_domains(n, our_domains); | ||
843 | if (ret) | ||
844 | goto out; | ||
845 | ret = mc_ce_activate_plugin_common(); | ||
846 | out: | ||
847 | return ret; | ||
815 | } | 848 | } |
816 | 849 | ||
817 | /* | 850 | /* |
@@ -823,14 +856,14 @@ void mc_release_at(struct task_struct *ts, lt_t start) | |||
823 | { | 856 | { |
824 | /* hack so that we can have CE timers start at the right time */ | 857 | /* hack so that we can have CE timers start at the right time */ |
825 | if (CRIT_LEVEL_A == tsk_mc_crit(ts)) | 858 | if (CRIT_LEVEL_A == tsk_mc_crit(ts)) |
826 | ce_start(ts, start); | 859 | mc_ce_release_at_common(ts, start); |
827 | else | 860 | else |
828 | release_at(ts, start); | 861 | release_at(ts, start); |
829 | } | 862 | } |
830 | 863 | ||
831 | long mc_deactivate_plugin(void) | 864 | long mc_deactivate_plugin(void) |
832 | { | 865 | { |
833 | return ce_deactivate_plugin(); | 866 | return mc_ce_deactivate_plugin_common(); |
834 | } | 867 | } |
835 | 868 | ||
836 | /* ************************************************************************** | 869 | /* ************************************************************************** |
@@ -843,7 +876,8 @@ long mc_deactivate_plugin(void) | |||
843 | DEFINE_PER_CPU(cpu_entry_t, _mc_cpus); | 876 | DEFINE_PER_CPU(cpu_entry_t, _mc_cpus); |
844 | /* LVL-A */ | 877 | /* LVL-A */ |
845 | DEFINE_PER_CPU(domain_data_t, _mc_crit_a); | 878 | DEFINE_PER_CPU(domain_data_t, _mc_crit_a); |
846 | DEFINE_PER_CPU(rt_domain_t, _mc_crit_a_rt); | 879 | DEFINE_PER_CPU(raw_spinlock_t, _mc_crit_a_lock); |
880 | DEFINE_PER_CPU(struct ce_dom_data, _mc_crit_a_ce_data); | ||
847 | /* LVL-B */ | 881 | /* LVL-B */ |
848 | DEFINE_PER_CPU(domain_data_t, _mc_crit_b); | 882 | DEFINE_PER_CPU(domain_data_t, _mc_crit_b); |
849 | DEFINE_PER_CPU(rt_domain_t, _mc_crit_b_rt); | 883 | DEFINE_PER_CPU(rt_domain_t, _mc_crit_b_rt); |
@@ -925,7 +959,8 @@ static int __init init_mc(void) | |||
925 | cpu_entry_t *entry; | 959 | cpu_entry_t *entry; |
926 | rt_domain_t *rt; | 960 | rt_domain_t *rt; |
927 | domain_data_t *dom_data; | 961 | domain_data_t *dom_data; |
928 | raw_spinlock_t *a_dom, *b_dom, *c_dom; /* For lock debugger */ | 962 | raw_spinlock_t *a_dom_lock, *b_dom, *c_dom; /* For lock debugger */ |
963 | struct ce_dom_data *ce_data; | ||
929 | 964 | ||
930 | for_each_online_cpu(cpu) { | 965 | for_each_online_cpu(cpu) { |
931 | entry = &per_cpu(_mc_cpus, cpu); | 966 | entry = &per_cpu(_mc_cpus, cpu); |
@@ -942,10 +977,16 @@ static int __init init_mc(void) | |||
942 | #endif | 977 | #endif |
943 | 978 | ||
944 | /* CRIT_LEVEL_A */ | 979 | /* CRIT_LEVEL_A */ |
945 | dom_data = ce_domain_for(cpu); | 980 | dom_data = &per_cpu(_mc_crit_a, cpu); |
946 | ce_domain_init(/* TODO */); | 981 | ce_data = &per_cpu(_mc_crit_a_ce_data, cpu); |
982 | a_dom_lock = &per_cpu(_mc_crit_a_lock, cpu); | ||
983 | raw_spin_lock_init(a_dom_lock); | ||
984 | ce_domain_init(&dom_data->domain, | ||
985 | a_dom_lock, ce_requeue, ce_peek_and_take_ready, | ||
986 | ce_peek_and_take_ready, mc_preempt_needed, | ||
987 | ce_higher_prio, ce_data, cpu, | ||
988 | ce_timer_function); | ||
947 | init_local_domain(entry, dom_data, CRIT_LEVEL_A); | 989 | init_local_domain(entry, dom_data, CRIT_LEVEL_A); |
948 | a_dom = dom_data->domain.lock; | ||
949 | dom_data->domain.name = "LVL-A"; | 990 | dom_data->domain.name = "LVL-A"; |
950 | 991 | ||
951 | /* CRIT_LEVEL_B */ | 992 | /* CRIT_LEVEL_B */ |
diff --git a/litmus/sched_mc_ce.c b/litmus/sched_mc_ce.c index 77acc67d05bd..8cac7ea58f66 100644 --- a/litmus/sched_mc_ce.c +++ b/litmus/sched_mc_ce.c | |||
@@ -27,25 +27,81 @@ | |||
27 | 27 | ||
28 | static struct sched_plugin mc_ce_plugin __cacheline_aligned_in_smp; | 28 | static struct sched_plugin mc_ce_plugin __cacheline_aligned_in_smp; |
29 | 29 | ||
30 | #define is_active_plugin() (litmus == &mc_ce_plugin) | 30 | #define using_linux_plugin() (litmus == &linux_sched_plugin) |
31 | #define get_ce_data(dom_data_ref) (dom_data_ref->domain.data) | 31 | |
32 | /* get a reference to domain_t for a CPU */ | ||
33 | #define get_domain_for(cpu) (&domains[cpu]->domain) | ||
34 | |||
35 | #define get_pid_table(cpu) (&per_cpu(ce_pid_table, cpu)) | ||
36 | #define get_pid_entry(cpu, idx) (&(get_pid_table(cpu)->entries[idx])) | ||
32 | 37 | ||
33 | static atomic_t start_time_set = ATOMIC_INIT(-1); | 38 | static atomic_t start_time_set = ATOMIC_INIT(-1); |
34 | static atomic64_t start_time = ATOMIC64_INIT(0); | 39 | static atomic64_t start_time = ATOMIC64_INIT(0); |
35 | static struct proc_dir_entry *mc_ce_dir = NULL, *ce_file = NULL; | 40 | static struct proc_dir_entry *mc_ce_dir = NULL, *ce_file = NULL; |
36 | 41 | ||
42 | /* | ||
43 | * Cache the budget along with the struct PID for a task so that we don't need | ||
44 | * to fetch its task_struct every time we check to see what should be | ||
45 | * scheduled. | ||
46 | */ | ||
47 | struct ce_pid_entry { | ||
48 | struct pid *pid; | ||
49 | lt_t budget; | ||
50 | /* accumulated (summed) budgets, including this one */ | ||
51 | lt_t acc_time; | ||
52 | unsigned int expected_job; | ||
53 | }; | ||
54 | |||
55 | struct ce_pid_table { | ||
56 | struct ce_pid_entry entries[CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS]; | ||
57 | int num_pid_entries; | ||
58 | lt_t cycle_time; | ||
59 | }; | ||
60 | |||
61 | DEFINE_PER_CPU(struct ce_pid_table, ce_pid_table); | ||
37 | 62 | ||
38 | DEFINE_PER_CPU(domain_data_t, mc_ce_doms); | 63 | /* |
64 | * How we get the domain for a given CPU locally. Set with the | ||
65 | * mc_ce_set_domains function. Must be done before activating plugins. Be | ||
66 | * careful when using domains as a variable elsewhere in this file. | ||
67 | */ | ||
68 | static domain_data_t *domains[NR_CPUS] __cacheline_aligned_in_smp; | ||
69 | |||
70 | /* | ||
71 | * The domains and other data used by the MC-CE plugin when it runs alone. | ||
72 | */ | ||
73 | DEFINE_PER_CPU(domain_data_t, _mc_ce_doms); | ||
39 | DEFINE_PER_CPU(struct ce_dom_data, _mc_ce_dom_data); | 74 | DEFINE_PER_CPU(struct ce_dom_data, _mc_ce_dom_data); |
40 | DEFINE_PER_CPU(raw_spinlock_t, _dom_locks); | 75 | DEFINE_PER_CPU(raw_spinlock_t, _mc_ce_dom_locks); |
41 | 76 | ||
42 | /* Return the address of the domain_t for this CPU, used by the | 77 | long mc_ce_set_domains(const int n, domain_data_t *domains_in[]) |
43 | * mixed-criticality plugin. */ | ||
44 | domain_data_t *ce_domain_for(int cpu) | ||
45 | { | 78 | { |
46 | return &per_cpu(mc_ce_doms, cpu); | 79 | const int max = (NR_CPUS < n) ? NR_CPUS : n; |
80 | domain_data_t *new_dom = NULL; | ||
81 | int i, ret; | ||
82 | if (!using_linux_plugin()) { | ||
83 | printk(KERN_WARNING "can't set MC-CE domains when not using " | ||
84 | "Linux scheduler.\n"); | ||
85 | ret = -EINVAL; | ||
86 | goto out; | ||
87 | } | ||
88 | for (i = 0; i < max; ++i) { | ||
89 | new_dom = domains_in[i]; | ||
90 | domains[i] = new_dom; | ||
91 | } | ||
92 | ret = 0; | ||
93 | out: | ||
94 | return ret; | ||
47 | } | 95 | } |
48 | 96 | ||
97 | unsigned int mc_ce_get_expected_job(const int cpu, const int idx) | ||
98 | { | ||
99 | const struct ce_pid_table *pid_table = get_pid_table(cpu); | ||
100 | BUG_ON(0 > cpu); | ||
101 | BUG_ON(0 > idx); | ||
102 | BUG_ON(pid_table->num_pid_entries <= idx); | ||
103 | return pid_table->entries[idx].expected_job; | ||
104 | } | ||
49 | 105 | ||
50 | /* | 106 | /* |
51 | * Get the offset into the cycle taking the start time into account. | 107 | * Get the offset into the cycle taking the start time into account. |
@@ -65,14 +121,12 @@ static inline lt_t get_cycle_offset(const lt_t when, const lt_t cycle_time) | |||
65 | * | 121 | * |
66 | * Do not call prepare_for_next_period on Level-A tasks! | 122 | * Do not call prepare_for_next_period on Level-A tasks! |
67 | */ | 123 | */ |
68 | static void mc_ce_job_completion(struct task_struct *ts) | 124 | static void mc_ce_job_completion(domain_t *dom, struct task_struct *ts) |
69 | { | 125 | { |
70 | const domain_data_t *dom_data = &per_cpu(mc_ce_doms, smp_processor_id()); | 126 | const int cpu = task_cpu(ts); |
71 | const struct ce_dom_data *ce_data = get_ce_data(dom_data); | ||
72 | const int idx = tsk_mc_data(ts)->mc_task.lvl_a_id; | 127 | const int idx = tsk_mc_data(ts)->mc_task.lvl_a_id; |
73 | const struct ce_dom_pid_entry *pid_entry = | 128 | const struct ce_pid_entry *pid_entry = get_pid_entry(cpu, idx); |
74 | &ce_data->pid_entries[idx]; | 129 | unsigned int just_finished; |
75 | int just_finished; | ||
76 | 130 | ||
77 | TRACE_TASK(ts, "completed\n"); | 131 | TRACE_TASK(ts, "completed\n"); |
78 | 132 | ||
@@ -87,11 +141,11 @@ static void mc_ce_job_completion(struct task_struct *ts) | |||
87 | if (just_finished < pid_entry->expected_job) { | 141 | if (just_finished < pid_entry->expected_job) { |
88 | /* this job is already released because it's running behind */ | 142 | /* this job is already released because it's running behind */ |
89 | set_rt_flags(ts, RT_F_RUNNING); | 143 | set_rt_flags(ts, RT_F_RUNNING); |
90 | TRACE_TASK(ts, "appears behind: the expected job is %d but " | 144 | TRACE_TASK(ts, "appears behind: the expected job is %u but " |
91 | "job %d just completed\n", | 145 | "job %u just completed\n", |
92 | pid_entry->expected_job, just_finished); | 146 | pid_entry->expected_job, just_finished); |
93 | } else if (pid_entry->expected_job < just_finished) { | 147 | } else if (pid_entry->expected_job < just_finished) { |
94 | printk(KERN_CRIT "job %d completed in expected job %d which " | 148 | printk(KERN_CRIT "job %u completed in expected job %u which " |
95 | "seems too early\n", just_finished, | 149 | "seems too early\n", just_finished, |
96 | pid_entry->expected_job); | 150 | pid_entry->expected_job); |
97 | BUG(); | 151 | BUG(); |
@@ -109,30 +163,30 @@ static void mc_ce_job_completion(struct task_struct *ts) | |||
109 | static int mc_ce_schedule_at(const domain_t *dom, lt_t offset) | 163 | static int mc_ce_schedule_at(const domain_t *dom, lt_t offset) |
110 | { | 164 | { |
111 | const struct ce_dom_data *ce_data = dom->data; | 165 | const struct ce_dom_data *ce_data = dom->data; |
112 | const struct ce_dom_pid_entry *pid_entry = NULL; | 166 | struct ce_pid_table *pid_table = get_pid_table(ce_data->cpu); |
113 | int i; | 167 | const struct ce_pid_entry *pid_entry = NULL; |
168 | int idx; | ||
114 | 169 | ||
115 | BUG_ON(ce_data->cycle_time < 1); | 170 | BUG_ON(pid_table->cycle_time < 1); |
116 | BUG_ON(ce_data->num_pid_entries < 1); | 171 | BUG_ON(pid_table->num_pid_entries < 1); |
117 | 172 | ||
118 | for (i = 0; i < ce_data->num_pid_entries; ++i) { | 173 | for (idx = 0; idx < pid_table->num_pid_entries; ++idx) { |
119 | pid_entry = &ce_data->pid_entries[i]; | 174 | pid_entry = &pid_table->entries[idx]; |
120 | if (offset < pid_entry->acc_time) { | 175 | if (offset < pid_entry->acc_time) { |
121 | /* found task to schedule in this window */ | 176 | /* found task to schedule in this window */ |
122 | break; | 177 | break; |
123 | } | 178 | } |
124 | } | 179 | } |
125 | /* can only happen if cycle_time is not right */ | 180 | /* can only happen if cycle_time is not right */ |
126 | BUG_ON(pid_entry->acc_time > ce_data->cycle_time); | 181 | BUG_ON(pid_entry->acc_time > pid_table->cycle_time); |
127 | TRACE("schedule at returned task %d for CPU %d\n", i, ce_data->cpu); | 182 | TRACE("schedule at returning task %d for CPU %d\n", idx, ce_data->cpu); |
128 | return i; | 183 | return idx; |
129 | } | 184 | } |
130 | 185 | ||
131 | static struct task_struct *mc_ce_schedule(struct task_struct *prev) | 186 | static struct task_struct *mc_ce_schedule(struct task_struct *prev) |
132 | { | 187 | { |
133 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, smp_processor_id()); | 188 | domain_t *dom = get_domain_for(smp_processor_id()); |
134 | domain_t *dom = &dom_data->domain; | 189 | struct ce_dom_data *ce_data = dom->data; |
135 | struct ce_dom_data *ce_data = get_ce_data(dom_data); | ||
136 | struct task_struct *next = NULL; | 190 | struct task_struct *next = NULL; |
137 | int exists, sleep, should_sched_exists, should_sched_blocked, | 191 | int exists, sleep, should_sched_exists, should_sched_blocked, |
138 | should_sched_asleep; | 192 | should_sched_asleep; |
@@ -150,7 +204,7 @@ static struct task_struct *mc_ce_schedule(struct task_struct *prev) | |||
150 | TRACE("exists: %d, sleep: %d\n", exists, sleep); | 204 | TRACE("exists: %d, sleep: %d\n", exists, sleep); |
151 | 205 | ||
152 | if (sleep) | 206 | if (sleep) |
153 | mc_ce_job_completion(ce_data->scheduled); | 207 | mc_ce_job_completion(dom, ce_data->scheduled); |
154 | 208 | ||
155 | /* these checks must go after the call to mc_ce_job_completion in case | 209 | /* these checks must go after the call to mc_ce_job_completion in case |
156 | * a late task needs to be scheduled again right away and its the only | 210 | * a late task needs to be scheduled again right away and its the only |
@@ -181,8 +235,8 @@ static struct task_struct *mc_ce_schedule(struct task_struct *prev) | |||
181 | 235 | ||
182 | static void mc_ce_finish_switch(struct task_struct *prev) | 236 | static void mc_ce_finish_switch(struct task_struct *prev) |
183 | { | 237 | { |
184 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, smp_processor_id()); | 238 | domain_t *dom = get_domain_for(smp_processor_id()); |
185 | struct ce_dom_data *ce_data = get_ce_data(dom_data); | 239 | struct ce_dom_data *ce_data = dom->data; |
186 | 240 | ||
187 | TRACE("finish switch\n"); | 241 | TRACE("finish switch\n"); |
188 | 242 | ||
@@ -197,14 +251,17 @@ static void mc_ce_finish_switch(struct task_struct *prev) | |||
197 | * Here we look up the task's PID structure and save it in the proper slot on | 251 | * Here we look up the task's PID structure and save it in the proper slot on |
198 | * the CPU this task will run on. | 252 | * the CPU this task will run on. |
199 | */ | 253 | */ |
200 | long __mc_ce_admit_task(struct task_struct *ts) | 254 | long mc_ce_admit_task_common(struct task_struct *ts) |
201 | { | 255 | { |
202 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, get_partition(ts)); | 256 | domain_t *dom = get_domain_for(get_partition(ts)); |
203 | struct ce_dom_data *ce_data = get_ce_data(dom_data); | 257 | struct ce_dom_data *ce_data = dom->data; |
204 | struct mc_data *mcd = tsk_mc_data(ts); | 258 | struct mc_data *mcd = tsk_mc_data(ts); |
205 | struct pid *pid = NULL; | 259 | struct pid *pid = NULL; |
206 | long retval = -EINVAL; | 260 | long retval = -EINVAL; |
207 | const int lvl_a_id = mcd->mc_task.lvl_a_id; | 261 | const int lvl_a_id = mcd->mc_task.lvl_a_id; |
262 | struct ce_pid_table *pid_table = get_pid_table(ce_data->cpu); | ||
263 | |||
264 | BUG_ON(get_partition(ts) != ce_data->cpu); | ||
208 | 265 | ||
209 | /* check the task has migrated to the right CPU (like in sched_cedf) */ | 266 | /* check the task has migrated to the right CPU (like in sched_cedf) */ |
210 | if (task_cpu(ts) != get_partition(ts)) { | 267 | if (task_cpu(ts) != get_partition(ts)) { |
@@ -228,26 +285,26 @@ long __mc_ce_admit_task(struct task_struct *ts) | |||
228 | goto out; | 285 | goto out; |
229 | } | 286 | } |
230 | 287 | ||
231 | if (lvl_a_id >= ce_data->num_pid_entries) { | 288 | if (lvl_a_id >= pid_table->num_pid_entries) { |
232 | printk(KERN_INFO "litmus: level A id greater than expected " | 289 | printk(KERN_INFO "litmus: level A id greater than expected " |
233 | "number of tasks %d for %d cpu %d\n", | 290 | "number of tasks %d for %d cpu %d\n", |
234 | ce_data->num_pid_entries, ts->pid, | 291 | pid_table->num_pid_entries, ts->pid, |
235 | get_partition(ts)); | 292 | get_partition(ts)); |
236 | goto out_put_pid; | 293 | goto out_put_pid; |
237 | } | 294 | } |
238 | if (ce_data->pid_entries[lvl_a_id].pid) { | 295 | if (pid_table->entries[lvl_a_id].pid) { |
239 | printk(KERN_INFO "litmus: have saved pid info id: %d cpu: %d\n", | 296 | printk(KERN_INFO "litmus: have saved pid info id: %d cpu: %d\n", |
240 | lvl_a_id, get_partition(ts)); | 297 | lvl_a_id, get_partition(ts)); |
241 | goto out_put_pid; | 298 | goto out_put_pid; |
242 | } | 299 | } |
243 | if (get_exec_cost(ts) >= ce_data->pid_entries[lvl_a_id].budget) { | 300 | if (get_exec_cost(ts) >= pid_table->entries[lvl_a_id].budget) { |
244 | printk(KERN_INFO "litmus: execution cost %llu is larger than " | 301 | printk(KERN_INFO "litmus: execution cost %llu is larger than " |
245 | "the budget %llu\n", | 302 | "the budget %llu\n", |
246 | get_exec_cost(ts), | 303 | get_exec_cost(ts), |
247 | ce_data->pid_entries[lvl_a_id].budget); | 304 | pid_table->entries[lvl_a_id].budget); |
248 | goto out_put_pid; | 305 | goto out_put_pid; |
249 | } | 306 | } |
250 | ce_data->pid_entries[lvl_a_id].pid = pid; | 307 | pid_table->entries[lvl_a_id].pid = pid; |
251 | retval = 0; | 308 | retval = 0; |
252 | /* don't call put_pid if we are successful */ | 309 | /* don't call put_pid if we are successful */ |
253 | goto out; | 310 | goto out; |
@@ -260,11 +317,10 @@ out: | |||
260 | 317 | ||
261 | static long mc_ce_admit_task(struct task_struct *ts) | 318 | static long mc_ce_admit_task(struct task_struct *ts) |
262 | { | 319 | { |
263 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, get_partition(ts)); | 320 | domain_t *dom = get_domain_for(get_partition(ts)); |
264 | domain_t *dom = &dom_data->domain; | ||
265 | unsigned long flags, retval; | 321 | unsigned long flags, retval; |
266 | raw_spin_lock_irqsave(dom->lock, flags); | 322 | raw_spin_lock_irqsave(dom->lock, flags); |
267 | retval = __mc_ce_admit_task(ts); | 323 | retval = mc_ce_admit_task_common(ts); |
268 | raw_spin_unlock_irqrestore(dom->lock, flags); | 324 | raw_spin_unlock_irqrestore(dom->lock, flags); |
269 | return retval; | 325 | return retval; |
270 | } | 326 | } |
@@ -276,27 +332,26 @@ static long mc_ce_admit_task(struct task_struct *ts) | |||
276 | */ | 332 | */ |
277 | static void mc_ce_task_new(struct task_struct *ts, int on_rq, int running) | 333 | static void mc_ce_task_new(struct task_struct *ts, int on_rq, int running) |
278 | { | 334 | { |
279 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, task_cpu(ts)); | 335 | const int cpu = task_cpu(ts); |
280 | domain_t *dom = &dom_data->domain; | 336 | domain_t *dom = get_domain_for(cpu); |
281 | struct ce_dom_data *ce_data = get_ce_data(dom_data); | 337 | struct ce_dom_data *ce_data = dom->data; |
338 | struct ce_pid_table *pid_table = get_pid_table(cpu); | ||
282 | struct pid *pid_should_be_running; | 339 | struct pid *pid_should_be_running; |
283 | struct ce_dom_pid_entry *pid_entry; | 340 | struct ce_pid_entry *pid_entry; |
284 | unsigned long flags; | 341 | unsigned long flags; |
285 | int idx, should_be_running; | 342 | int idx, should_be_running; |
286 | lt_t offset; | 343 | lt_t offset; |
287 | 344 | ||
288 | /* have to call mc_ce_schedule_at because the task only gets a PID | ||
289 | * entry after calling admit_task */ | ||
290 | |||
291 | raw_spin_lock_irqsave(dom->lock, flags); | 345 | raw_spin_lock_irqsave(dom->lock, flags); |
292 | pid_entry = &ce_data->pid_entries[tsk_mc_data(ts)->mc_task.lvl_a_id]; | 346 | pid_entry = get_pid_entry(cpu, tsk_mc_data(ts)->mc_task.lvl_a_id); |
293 | /* initialize some task state */ | 347 | /* initialize some task state */ |
294 | set_rt_flags(ts, RT_F_RUNNING); | 348 | set_rt_flags(ts, RT_F_RUNNING); |
295 | tsk_rt(ts)->job_params.job_no = 1; | ||
296 | 349 | ||
297 | offset = get_cycle_offset(litmus_clock(), ce_data->cycle_time); | 350 | /* have to call mc_ce_schedule_at because the task only gets a PID |
351 | * entry after calling admit_task */ | ||
352 | offset = get_cycle_offset(litmus_clock(), pid_table->cycle_time); | ||
298 | idx = mc_ce_schedule_at(dom, offset); | 353 | idx = mc_ce_schedule_at(dom, offset); |
299 | pid_should_be_running = ce_data->pid_entries[idx].pid; | 354 | pid_should_be_running = get_pid_entry(cpu, idx)->pid; |
300 | rcu_read_lock(); | 355 | rcu_read_lock(); |
301 | should_be_running = (ts == pid_task(pid_should_be_running, PIDTYPE_PID)); | 356 | should_be_running = (ts == pid_task(pid_should_be_running, PIDTYPE_PID)); |
302 | rcu_read_unlock(); | 357 | rcu_read_unlock(); |
@@ -323,9 +378,8 @@ static void mc_ce_task_new(struct task_struct *ts, int on_rq, int running) | |||
323 | */ | 378 | */ |
324 | static void mc_ce_task_wake_up(struct task_struct *ts) | 379 | static void mc_ce_task_wake_up(struct task_struct *ts) |
325 | { | 380 | { |
326 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, smp_processor_id()); | 381 | domain_t *dom = get_domain_for(get_partition(ts)); |
327 | domain_t *dom = &dom_data->domain; | 382 | struct ce_dom_data *ce_data = dom->data; |
328 | struct ce_dom_data *ce_data = get_ce_data(dom_data); | ||
329 | unsigned long flags; | 383 | unsigned long flags; |
330 | 384 | ||
331 | TRACE_TASK(ts, "wake up\n"); | 385 | TRACE_TASK(ts, "wake up\n"); |
@@ -349,26 +403,27 @@ static void mc_ce_task_block(struct task_struct *ts) | |||
349 | /* | 403 | /* |
350 | * Called when a task switches from RT mode back to normal mode. | 404 | * Called when a task switches from RT mode back to normal mode. |
351 | */ | 405 | */ |
352 | void mc_ce_task_exit(struct task_struct *ts) | 406 | void mc_ce_task_exit_common(struct task_struct *ts) |
353 | { | 407 | { |
354 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, get_partition(ts)); | 408 | domain_t *dom = get_domain_for(get_partition(ts)); |
355 | domain_t *dom = &dom_data->domain; | 409 | struct ce_dom_data *ce_data = dom->data; |
356 | struct ce_dom_data *ce_data = get_ce_data(dom_data); | ||
357 | unsigned long flags; | 410 | unsigned long flags; |
358 | struct pid *pid; | 411 | struct pid *pid; |
359 | const int lvl_a_id = tsk_mc_data(ts)->mc_task.lvl_a_id; | 412 | const int lvl_a_id = tsk_mc_data(ts)->mc_task.lvl_a_id; |
413 | struct ce_pid_table *pid_table = get_pid_table(ce_data->cpu); | ||
360 | 414 | ||
361 | TRACE_TASK(ts, "exited\n"); | 415 | TRACE_TASK(ts, "exited\n"); |
362 | 416 | ||
363 | BUG_ON(task_cpu(ts) != get_partition(ts)); | 417 | BUG_ON(task_cpu(ts) != get_partition(ts)); |
364 | BUG_ON(CRIT_LEVEL_A != tsk_mc_crit(ts)); | 418 | BUG_ON(CRIT_LEVEL_A != tsk_mc_crit(ts)); |
365 | BUG_ON(lvl_a_id >= ce_data->num_pid_entries); | 419 | BUG_ON(lvl_a_id >= pid_table->num_pid_entries); |
420 | BUG_ON(ce_data->cpu != task_cpu(ts)); | ||
366 | 421 | ||
367 | raw_spin_lock_irqsave(dom->lock, flags); | 422 | raw_spin_lock_irqsave(dom->lock, flags); |
368 | pid = ce_data->pid_entries[lvl_a_id].pid; | 423 | pid = pid_table->entries[lvl_a_id].pid; |
369 | BUG_ON(!pid); | 424 | BUG_ON(!pid); |
370 | put_pid(pid); | 425 | put_pid(pid); |
371 | ce_data->pid_entries[lvl_a_id].pid = NULL; | 426 | pid_table->entries[lvl_a_id].pid = NULL; |
372 | if (ce_data->scheduled == ts) | 427 | if (ce_data->scheduled == ts) |
373 | ce_data->scheduled = NULL; | 428 | ce_data->scheduled = NULL; |
374 | if (ce_data->should_schedule == ts) | 429 | if (ce_data->should_schedule == ts) |
@@ -380,29 +435,27 @@ void mc_ce_task_exit(struct task_struct *ts) | |||
380 | * Timer stuff | 435 | * Timer stuff |
381 | **********************************************************/ | 436 | **********************************************************/ |
382 | 437 | ||
383 | void __mc_ce_timer_callback(struct hrtimer *timer) | 438 | void mc_ce_timer_callback_common(domain_t *dom, struct hrtimer *timer) |
384 | { | 439 | { |
385 | /* relative and absolute times for cycles */ | 440 | /* relative and absolute times for cycles */ |
386 | lt_t now, offset_rel, cycle_start_abs, next_timer_abs; | 441 | lt_t now, offset_rel, cycle_start_abs, next_timer_abs; |
387 | struct task_struct *should_schedule; | 442 | struct task_struct *should_schedule; |
388 | struct ce_dom_pid_entry *pid_entry; | 443 | struct ce_pid_table *pid_table; |
444 | struct ce_pid_entry *pid_entry; | ||
389 | struct ce_dom_data *ce_data; | 445 | struct ce_dom_data *ce_data; |
390 | domain_data_t *dom_data; | ||
391 | domain_t *dom; | ||
392 | int idx, budget_overrun; | 446 | int idx, budget_overrun; |
393 | 447 | ||
394 | ce_data = container_of(timer, struct ce_dom_data, timer); | 448 | ce_data = dom->data; |
395 | dom_data = &per_cpu(mc_ce_doms, ce_data->cpu); | 449 | pid_table = get_pid_table(ce_data->cpu); |
396 | dom = &dom_data->domain; | ||
397 | 450 | ||
398 | /* Based off of the current time, figure out the offset into the cycle | 451 | /* Based off of the current time, figure out the offset into the cycle |
399 | * and the cycle's start time, and determine what should be scheduled. | 452 | * and the cycle's start time, and determine what should be scheduled. |
400 | */ | 453 | */ |
401 | now = litmus_clock(); | 454 | now = litmus_clock(); |
402 | offset_rel = get_cycle_offset(now, ce_data->cycle_time); | 455 | offset_rel = get_cycle_offset(now, pid_table->cycle_time); |
403 | cycle_start_abs = now - offset_rel; | 456 | cycle_start_abs = now - offset_rel; |
404 | idx = mc_ce_schedule_at(dom, offset_rel); | 457 | idx = mc_ce_schedule_at(dom, offset_rel); |
405 | pid_entry = &ce_data->pid_entries[idx]; | 458 | pid_entry = get_pid_entry(ce_data->cpu, idx); |
406 | /* set the timer to fire at the next cycle start */ | 459 | /* set the timer to fire at the next cycle start */ |
407 | next_timer_abs = cycle_start_abs + pid_entry->acc_time; | 460 | next_timer_abs = cycle_start_abs + pid_entry->acc_time; |
408 | hrtimer_set_expires(timer, ns_to_ktime(next_timer_abs)); | 461 | hrtimer_set_expires(timer, ns_to_ktime(next_timer_abs)); |
@@ -426,8 +479,8 @@ void __mc_ce_timer_callback(struct hrtimer *timer) | |||
426 | budget_overrun = pid_entry->expected_job != | 479 | budget_overrun = pid_entry->expected_job != |
427 | tsk_rt(should_schedule)->job_params.job_no; | 480 | tsk_rt(should_schedule)->job_params.job_no; |
428 | if (budget_overrun) | 481 | if (budget_overrun) |
429 | TRACE_TASK(should_schedule, "timer expected job number: %d " | 482 | TRACE_TASK(should_schedule, "timer expected job number: %u " |
430 | "but current job: %d\n", | 483 | "but current job: %u\n", |
431 | pid_entry->expected_job, | 484 | pid_entry->expected_job, |
432 | tsk_rt(should_schedule)->job_params.job_no); | 485 | tsk_rt(should_schedule)->job_params.job_no); |
433 | } | 486 | } |
@@ -452,17 +505,15 @@ static enum hrtimer_restart mc_ce_timer_callback(struct hrtimer *timer) | |||
452 | { | 505 | { |
453 | struct ce_dom_data *ce_data; | 506 | struct ce_dom_data *ce_data; |
454 | unsigned long flags; | 507 | unsigned long flags; |
455 | domain_data_t *dom_data; | ||
456 | domain_t *dom; | 508 | domain_t *dom; |
457 | 509 | ||
458 | ce_data = container_of(timer, struct ce_dom_data, timer); | 510 | ce_data = container_of(timer, struct ce_dom_data, timer); |
459 | dom_data = &per_cpu(mc_ce_doms, ce_data->cpu); | 511 | dom = get_domain_for(ce_data->cpu); |
460 | dom = &dom_data->domain; | ||
461 | 512 | ||
462 | TRACE("timer callback on CPU %d (before lock)\n", ce_data->cpu); | 513 | TRACE("timer callback on CPU %d (before lock)\n", ce_data->cpu); |
463 | 514 | ||
464 | raw_spin_lock_irqsave(dom->lock, flags); | 515 | raw_spin_lock_irqsave(dom->lock, flags); |
465 | __mc_ce_timer_callback(timer); | 516 | mc_ce_timer_callback_common(dom, timer); |
466 | 517 | ||
467 | if (ce_data->scheduled != ce_data->should_schedule) | 518 | if (ce_data->scheduled != ce_data->should_schedule) |
468 | preempt_if_preemptable(ce_data->scheduled, ce_data->cpu); | 519 | preempt_if_preemptable(ce_data->scheduled, ce_data->cpu); |
@@ -478,14 +529,14 @@ static enum hrtimer_restart mc_ce_timer_callback(struct hrtimer *timer) | |||
478 | static int cancel_all_timers(void) | 529 | static int cancel_all_timers(void) |
479 | { | 530 | { |
480 | struct ce_dom_data *ce_data; | 531 | struct ce_dom_data *ce_data; |
481 | domain_data_t *dom_data; | 532 | domain_t *dom; |
482 | int cpu, ret = 0, cancel_res; | 533 | int cpu, cancel_res, ret = 0; |
483 | 534 | ||
484 | TRACE("cancel all timers\n"); | 535 | TRACE("cancel all timers\n"); |
485 | 536 | ||
486 | for_each_online_cpu(cpu) { | 537 | for_each_online_cpu(cpu) { |
487 | dom_data = &per_cpu(mc_ce_doms, cpu); | 538 | dom = get_domain_for(cpu); |
488 | ce_data = get_ce_data(dom_data); | 539 | ce_data = dom->data; |
489 | ce_data->should_schedule = NULL; | 540 | ce_data->should_schedule = NULL; |
490 | cancel_res = hrtimer_cancel(&ce_data->timer); | 541 | cancel_res = hrtimer_cancel(&ce_data->timer); |
491 | atomic_set(&ce_data->timer_info.state, | 542 | atomic_set(&ce_data->timer_info.state, |
@@ -502,20 +553,22 @@ static int cancel_all_timers(void) | |||
502 | */ | 553 | */ |
503 | static void arm_all_timers(void) | 554 | static void arm_all_timers(void) |
504 | { | 555 | { |
556 | domain_t *dom; | ||
505 | struct ce_dom_data *ce_data; | 557 | struct ce_dom_data *ce_data; |
506 | domain_data_t *dom_data; | 558 | struct ce_pid_table *pid_table; |
507 | int cpu, idx; | 559 | int cpu, idx; |
508 | const lt_t start = atomic64_read(&start_time); | 560 | const lt_t start = atomic64_read(&start_time); |
509 | 561 | ||
510 | TRACE("arm all timers\n"); | 562 | TRACE("arm all timers\n"); |
511 | 563 | ||
512 | for_each_online_cpu(cpu) { | 564 | for_each_online_cpu(cpu) { |
513 | dom_data = &per_cpu(mc_ce_doms, cpu); | 565 | dom = get_domain_for(cpu); |
514 | ce_data = get_ce_data(dom_data); | 566 | ce_data = dom->data; |
515 | if (0 == ce_data->num_pid_entries) | 567 | pid_table = get_pid_table(cpu); |
568 | if (0 == pid_table->num_pid_entries) | ||
516 | continue; | 569 | continue; |
517 | for (idx = 0; idx < ce_data->num_pid_entries; idx++) { | 570 | for (idx = 0; idx < pid_table->num_pid_entries; idx++) { |
518 | ce_data->pid_entries[idx].expected_job = 0; | 571 | pid_table->entries[idx].expected_job = 1; |
519 | } | 572 | } |
520 | TRACE("arming timer for CPU %d\n", cpu); | 573 | TRACE("arming timer for CPU %d\n", cpu); |
521 | hrtimer_start_on(cpu, &ce_data->timer_info, &ce_data->timer, | 574 | hrtimer_start_on(cpu, &ce_data->timer_info, &ce_data->timer, |
@@ -528,7 +581,7 @@ static void arm_all_timers(void) | |||
528 | * call this. We can re-set our notion of the CE period start to make | 581 | * call this. We can re-set our notion of the CE period start to make |
529 | * the schedule look pretty. | 582 | * the schedule look pretty. |
530 | */ | 583 | */ |
531 | void mc_ce_release_at(struct task_struct *ts, lt_t start) | 584 | void mc_ce_release_at_common(struct task_struct *ts, lt_t start) |
532 | { | 585 | { |
533 | TRACE_TASK(ts, "release at\n"); | 586 | TRACE_TASK(ts, "release at\n"); |
534 | if (atomic_inc_and_test(&start_time_set)) { | 587 | if (atomic_inc_and_test(&start_time_set)) { |
@@ -540,15 +593,15 @@ void mc_ce_release_at(struct task_struct *ts, lt_t start) | |||
540 | atomic_dec(&start_time_set); | 593 | atomic_dec(&start_time_set); |
541 | } | 594 | } |
542 | 595 | ||
543 | long mc_ce_activate_plugin(void) | 596 | long mc_ce_activate_plugin_common(void) |
544 | { | 597 | { |
545 | struct ce_dom_data *ce_data; | 598 | struct ce_dom_data *ce_data; |
546 | domain_data_t *dom_data; | 599 | domain_t *dom; |
547 | int cpu; | 600 | int cpu; |
548 | 601 | ||
549 | for_each_online_cpu(cpu) { | 602 | for_each_online_cpu(cpu) { |
550 | dom_data = &per_cpu(mc_ce_doms, cpu); | 603 | dom = get_domain_for(cpu); |
551 | ce_data = get_ce_data(dom_data); | 604 | ce_data = dom->data; |
552 | ce_data->scheduled = NULL; | 605 | ce_data->scheduled = NULL; |
553 | ce_data->should_schedule = NULL; | 606 | ce_data->should_schedule = NULL; |
554 | } | 607 | } |
@@ -560,33 +613,54 @@ long mc_ce_activate_plugin(void) | |||
560 | return 0; | 613 | return 0; |
561 | } | 614 | } |
562 | 615 | ||
616 | static long mc_ce_activate_plugin(void) | ||
617 | { | ||
618 | domain_data_t *our_domains[NR_CPUS]; | ||
619 | int cpu, n = 0; | ||
620 | long ret; | ||
621 | |||
622 | for_each_online_cpu(cpu) { | ||
623 | BUG_ON(NR_CPUS <= n); | ||
624 | our_domains[cpu] = &per_cpu(_mc_ce_doms, cpu); | ||
625 | n++; | ||
626 | } | ||
627 | ret = mc_ce_set_domains(n, our_domains); | ||
628 | if (ret) | ||
629 | goto out; | ||
630 | ret = mc_ce_activate_plugin_common(); | ||
631 | out: | ||
632 | return ret; | ||
633 | } | ||
634 | |||
563 | static void clear_pid_entries(void) | 635 | static void clear_pid_entries(void) |
564 | { | 636 | { |
637 | struct ce_pid_table *pid_table = NULL; | ||
565 | int cpu, entry; | 638 | int cpu, entry; |
566 | domain_data_t *dom_data; | ||
567 | struct ce_dom_data *ce_data; | ||
568 | 639 | ||
569 | for_each_online_cpu(cpu) { | 640 | for_each_online_cpu(cpu) { |
570 | dom_data = &per_cpu(mc_ce_doms, cpu); | 641 | pid_table = get_pid_table(cpu); |
571 | ce_data = get_ce_data(dom_data); | 642 | pid_table->num_pid_entries = 0; |
572 | ce_data->num_pid_entries = 0; | 643 | pid_table->cycle_time = 0; |
573 | ce_data->cycle_time = 0; | ||
574 | for (entry = 0; entry < CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS; | 644 | for (entry = 0; entry < CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS; |
575 | ++entry) { | 645 | ++entry) { |
576 | if (NULL != ce_data->pid_entries[entry].pid) { | 646 | if (NULL != pid_table->entries[entry].pid) { |
577 | put_pid(ce_data->pid_entries[entry].pid); | 647 | put_pid(pid_table->entries[entry].pid); |
578 | ce_data->pid_entries[entry].pid = NULL; | 648 | pid_table->entries[entry].pid = NULL; |
579 | } | 649 | } |
580 | ce_data->pid_entries[entry].budget = 0; | 650 | pid_table->entries[entry].budget = 0; |
581 | ce_data->pid_entries[entry].acc_time = 0; | 651 | pid_table->entries[entry].acc_time = 0; |
582 | ce_data->pid_entries[entry].expected_job = 0; | 652 | pid_table->entries[entry].expected_job = 1; |
583 | } | 653 | } |
584 | } | 654 | } |
585 | } | 655 | } |
586 | 656 | ||
587 | long mc_ce_deactivate_plugin(void) | 657 | long mc_ce_deactivate_plugin_common(void) |
588 | { | 658 | { |
659 | int cpu; | ||
589 | cancel_all_timers(); | 660 | cancel_all_timers(); |
661 | for_each_online_cpu(cpu) { | ||
662 | domains[cpu] = NULL; | ||
663 | } | ||
590 | return 0; | 664 | return 0; |
591 | } | 665 | } |
592 | 666 | ||
@@ -596,43 +670,33 @@ static struct sched_plugin mc_ce_plugin __cacheline_aligned_in_smp = { | |||
596 | .admit_task = mc_ce_admit_task, | 670 | .admit_task = mc_ce_admit_task, |
597 | .task_new = mc_ce_task_new, | 671 | .task_new = mc_ce_task_new, |
598 | .complete_job = complete_job, | 672 | .complete_job = complete_job, |
599 | .release_at = mc_ce_release_at, | 673 | .release_at = mc_ce_release_at_common, |
600 | .task_exit = mc_ce_task_exit, | 674 | .task_exit = mc_ce_task_exit_common, |
601 | .schedule = mc_ce_schedule, | 675 | .schedule = mc_ce_schedule, |
602 | .finish_switch = mc_ce_finish_switch, | 676 | .finish_switch = mc_ce_finish_switch, |
603 | .task_wake_up = mc_ce_task_wake_up, | 677 | .task_wake_up = mc_ce_task_wake_up, |
604 | .task_block = mc_ce_task_block, | 678 | .task_block = mc_ce_task_block, |
605 | .activate_plugin = mc_ce_activate_plugin, | 679 | .activate_plugin = mc_ce_activate_plugin, |
606 | .deactivate_plugin = mc_ce_deactivate_plugin, | 680 | .deactivate_plugin = mc_ce_deactivate_plugin_common, |
607 | }; | 681 | }; |
608 | 682 | ||
609 | int mc_preempt_needed(domain_t*, struct task_struct*); | ||
610 | static int setup_proc(void); | 683 | static int setup_proc(void); |
611 | static int __init init_sched_mc_ce(void) | 684 | static int __init init_sched_mc_ce(void) |
612 | { | 685 | { |
613 | struct ce_dom_data *ce_data; | ||
614 | raw_spinlock_t *ce_lock; | 686 | raw_spinlock_t *ce_lock; |
615 | domain_data_t *dom_data; | 687 | domain_data_t *dom_data; |
616 | domain_t *dom; | 688 | domain_t *dom; |
617 | int cpu, err; | 689 | int cpu, err; |
618 | 690 | ||
619 | for_each_online_cpu(cpu) { | 691 | for_each_online_cpu(cpu) { |
620 | ce_lock = &per_cpu(_dom_locks, cpu); | 692 | domains[cpu] = NULL; |
693 | ce_lock = &per_cpu(_mc_ce_dom_locks, cpu); | ||
621 | raw_spin_lock_init(ce_lock); | 694 | raw_spin_lock_init(ce_lock); |
622 | dom_data = &per_cpu(mc_ce_doms, cpu); | 695 | dom_data = &per_cpu(_mc_ce_doms, cpu); |
623 | dom = &dom_data->domain; | 696 | dom = &dom_data->domain; |
624 | /* initialize the domain. the ce_ functions are for the MC | 697 | ce_domain_init(dom, ce_lock, NULL, NULL, NULL, NULL, NULL, |
625 | * plugin */ | 698 | &per_cpu(_mc_ce_dom_data, cpu), cpu, |
626 | /* move into ce_domain_init */ | 699 | mc_ce_timer_callback); |
627 | domain_init(dom, ce_lock, ce_requeue, ce_peek_and_take_ready, | ||
628 | ce_peek_and_take_ready, mc_preempt_needed, | ||
629 | ce_higher_prio); | ||
630 | dom->data = &per_cpu(_mc_ce_dom_data, cpu); | ||
631 | ce_data = get_ce_data(dom_data); | ||
632 | hrtimer_init(&ce_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
633 | hrtimer_start_on_info_init(&ce_data->timer_info); | ||
634 | ce_data->cpu = cpu; | ||
635 | ce_data->timer.function = mc_ce_timer_callback; | ||
636 | } | 700 | } |
637 | clear_pid_entries(); | 701 | clear_pid_entries(); |
638 | err = setup_proc(); | 702 | err = setup_proc(); |
@@ -674,7 +738,7 @@ out: | |||
674 | #define PID_SPACE 15 | 738 | #define PID_SPACE 15 |
675 | #define TASK_INFO_BUF (PID_SPACE + TASK_COMM_LEN) | 739 | #define TASK_INFO_BUF (PID_SPACE + TASK_COMM_LEN) |
676 | static int write_pid_entry(char *page, const int count, const int cpu, | 740 | static int write_pid_entry(char *page, const int count, const int cpu, |
677 | const int task, struct ce_dom_pid_entry *pid_entry) | 741 | const int task, struct ce_pid_entry *pid_entry) |
678 | { | 742 | { |
679 | static char task_info[TASK_INFO_BUF]; | 743 | static char task_info[TASK_INFO_BUF]; |
680 | struct task_struct *ts; | 744 | struct task_struct *ts; |
@@ -731,8 +795,7 @@ static int proc_read_ce_file(char *page, char **start, off_t off, int count, | |||
731 | int *eof, void *data) | 795 | int *eof, void *data) |
732 | { | 796 | { |
733 | int n = 0, err, cpu, t; | 797 | int n = 0, err, cpu, t; |
734 | struct ce_dom_data *ce_data; | 798 | struct ce_pid_table *pid_table; |
735 | domain_data_t *dom_data; | ||
736 | 799 | ||
737 | if (off > 0) { | 800 | if (off > 0) { |
738 | printk(KERN_INFO "litmus: MC-CE called read with off > 0\n"); | 801 | printk(KERN_INFO "litmus: MC-CE called read with off > 0\n"); |
@@ -740,11 +803,10 @@ static int proc_read_ce_file(char *page, char **start, off_t off, int count, | |||
740 | } | 803 | } |
741 | 804 | ||
742 | for_each_online_cpu(cpu) { | 805 | for_each_online_cpu(cpu) { |
743 | dom_data = &per_cpu(mc_ce_doms, cpu); | 806 | pid_table = get_pid_table(cpu); |
744 | ce_data = get_ce_data(dom_data); | 807 | for (t = 0; t < pid_table->num_pid_entries; ++t) { |
745 | for (t = 0; t < ce_data->num_pid_entries; ++t) { | ||
746 | err = write_pid_entry(page + n, count - n, | 808 | err = write_pid_entry(page + n, count - n, |
747 | cpu, t, &ce_data->pid_entries[t]); | 809 | cpu, t, get_pid_entry(cpu, t)); |
748 | if (err < 0) { | 810 | if (err < 0) { |
749 | n = -ENOSPC; | 811 | n = -ENOSPC; |
750 | goto out; | 812 | goto out; |
@@ -781,9 +843,8 @@ static int skip_comment(const char *buf, const unsigned long max) | |||
781 | #define BUDGET_THRESHOLD 5000000ULL | 843 | #define BUDGET_THRESHOLD 5000000ULL |
782 | static int setup_pid_entry(const int cpu, const int task, const lt_t budget) | 844 | static int setup_pid_entry(const int cpu, const int task, const lt_t budget) |
783 | { | 845 | { |
784 | domain_data_t *dom_data = &per_cpu(mc_ce_doms, cpu); | 846 | struct ce_pid_table *pid_table = get_pid_table(cpu); |
785 | struct ce_dom_data *ce_data = get_ce_data(dom_data); | 847 | struct ce_pid_entry *new_entry = NULL; |
786 | struct ce_dom_pid_entry *new_entry; | ||
787 | int err = 0; | 848 | int err = 0; |
788 | 849 | ||
789 | /* check the inputs */ | 850 | /* check the inputs */ |
@@ -801,20 +862,20 @@ static int setup_pid_entry(const int cpu, const int task, const lt_t budget) | |||
801 | "MC-CE task; that might be an issue.\n"); | 862 | "MC-CE task; that might be an issue.\n"); |
802 | } | 863 | } |
803 | /* check that we have space for a new entry */ | 864 | /* check that we have space for a new entry */ |
804 | if (CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS <= ce_data->num_pid_entries) { | 865 | if (CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS <= pid_table->num_pid_entries) { |
805 | printk(KERN_INFO "litmus: too many MC-CE tasks for cpu " | 866 | printk(KERN_INFO "litmus: too many MC-CE tasks for cpu " |
806 | "%d\n", cpu); | 867 | "%d\n", cpu); |
807 | err = -EINVAL; | 868 | err = -EINVAL; |
808 | goto out; | 869 | goto out; |
809 | } | 870 | } |
810 | /* add the new entry */ | 871 | /* add the new entry */ |
811 | new_entry = &ce_data->pid_entries[ce_data->num_pid_entries]; | 872 | new_entry = get_pid_entry(cpu, pid_table->num_pid_entries); |
812 | BUG_ON(NULL != new_entry->pid); | 873 | BUG_ON(NULL != new_entry->pid); |
813 | new_entry->budget = budget; | 874 | new_entry->budget = budget; |
814 | new_entry->acc_time = ce_data->cycle_time + budget; | 875 | new_entry->acc_time = pid_table->cycle_time + budget; |
815 | /* update the domain entry */ | 876 | /* update the domain entry */ |
816 | ce_data->cycle_time += budget; | 877 | pid_table->cycle_time += budget; |
817 | ce_data->num_pid_entries++; | 878 | pid_table->num_pid_entries++; |
818 | out: | 879 | out: |
819 | return err; | 880 | return err; |
820 | } | 881 | } |
@@ -835,9 +896,9 @@ static int proc_write_ce_file(struct file *file, const char __user *buffer, | |||
835 | int cpu, task, cnt = 0, chars_read, converted, err; | 896 | int cpu, task, cnt = 0, chars_read, converted, err; |
836 | lt_t budget; | 897 | lt_t budget; |
837 | 898 | ||
838 | if (is_active_plugin()) { | 899 | if (!using_linux_plugin()) { |
839 | printk(KERN_INFO "litmus: can't edit MC-CE proc when plugin " | 900 | printk(KERN_INFO "litmus: can only edit MC-CE proc under Linux " |
840 | "active\n"); | 901 | "plugin\n"); |
841 | cnt = -EINVAL; | 902 | cnt = -EINVAL; |
842 | goto out; | 903 | goto out; |
843 | } | 904 | } |