aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/litmus/mc2_common.h31
-rw-r--r--include/litmus/rt_param.h4
-rw-r--r--include/litmus/unistd_32.h3
-rw-r--r--include/litmus/unistd_64.h5
-rw-r--r--litmus/Makefile3
-rw-r--r--litmus/mc2_common.c78
-rw-r--r--litmus/reservation.c6
-rw-r--r--litmus/sched_mc2.c1634
8 files changed, 1757 insertions, 7 deletions
diff --git a/include/litmus/mc2_common.h b/include/litmus/mc2_common.h
new file mode 100644
index 000000000000..e3c0af28f1b9
--- /dev/null
+++ b/include/litmus/mc2_common.h
@@ -0,0 +1,31 @@
1/*
2 * MC^2 common data structures
3 */
4
5#ifndef __UNC_MC2_COMMON_H__
6#define __UNC_MC2_COMMON_H__
7
8enum crit_level {
9 CRIT_LEVEL_A = 0,
10 CRIT_LEVEL_B = 1,
11 CRIT_LEVEL_C = 2,
12 NUM_CRIT_LEVELS = 3,
13};
14
15struct mc2_task {
16 enum crit_level crit;
17 unsigned int res_id;
18};
19
20#ifdef __KERNEL__
21
22#include <litmus/reservation.h>
23
24#define tsk_mc2_data(t) (tsk_rt(t)->mc2_data)
25
26long mc2_task_client_init(struct task_client *tc, struct mc2_task *mc2_param, struct task_struct *tsk,
27 struct reservation *res);
28
29#endif /* __KERNEL__ */
30
31#endif \ No newline at end of file
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h
index e626bbbe60d5..26dfa33c1e5e 100644
--- a/include/litmus/rt_param.h
+++ b/include/litmus/rt_param.h
@@ -206,6 +206,7 @@ struct rt_job {
206}; 206};
207 207
208struct pfair_param; 208struct pfair_param;
209struct mc2_task;
209 210
210/* RT task parameters for scheduling extensions 211/* RT task parameters for scheduling extensions
211 * These parameters are inherited during clone and therefore must 212 * These parameters are inherited during clone and therefore must
@@ -322,6 +323,9 @@ struct rt_param {
322 323
323 /* Pointer to the page shared between userspace and kernel. */ 324 /* Pointer to the page shared between userspace and kernel. */
324 struct control_page * ctrl_page; 325 struct control_page * ctrl_page;
326
327 /* Mixed-criticality specific data */
328 struct mc2_task* mc2_data;
325}; 329};
326 330
327#endif 331#endif
diff --git a/include/litmus/unistd_32.h b/include/litmus/unistd_32.h
index 5f6a2749c6a7..202f439a62ae 100644
--- a/include/litmus/unistd_32.h
+++ b/include/litmus/unistd_32.h
@@ -19,5 +19,6 @@
19#define __NR_null_call __LSC(11) 19#define __NR_null_call __LSC(11)
20#define __NR_reservation_create __LSC(12) 20#define __NR_reservation_create __LSC(12)
21#define __NR_reservation_destroy __LSC(13) 21#define __NR_reservation_destroy __LSC(13)
22#define __NR_set_mc2_task_param __LSC(14)
22 23
23#define NR_litmus_syscalls 14 24#define NR_litmus_syscalls 15
diff --git a/include/litmus/unistd_64.h b/include/litmus/unistd_64.h
index 3e6b1d330336..ba2c91c5bf8c 100644
--- a/include/litmus/unistd_64.h
+++ b/include/litmus/unistd_64.h
@@ -33,6 +33,7 @@ __SYSCALL(__NR_null_call, sys_null_call)
33__SYSCALL(__NR_reservation_create, sys_reservation_create) 33__SYSCALL(__NR_reservation_create, sys_reservation_create)
34#define __NR_reservation_destroy __LSC(13) 34#define __NR_reservation_destroy __LSC(13)
35__SYSCALL(__NR_reservation_destroy, sys_reservation_destroy) 35__SYSCALL(__NR_reservation_destroy, sys_reservation_destroy)
36#define __NR_set_mc2_task_param __LSC(14)
37__SYSCALL(__NR_set_mc2_task_param, sys_set_mc2_task_param)
36 38
37 39#define NR_litmus_syscalls 15
38#define NR_litmus_syscalls 14
diff --git a/litmus/Makefile b/litmus/Makefile
index 05021f553eda..70c77b3e9b53 100644
--- a/litmus/Makefile
+++ b/litmus/Makefile
@@ -35,4 +35,5 @@ obj-$(CONFIG_SCHED_OVERHEAD_TRACE) += trace.o
35 35
36obj-y += reservation.o polling_reservations.o 36obj-y += reservation.o polling_reservations.o
37 37
38obj-y += sched_pres.o \ No newline at end of file 38obj-y += sched_pres.o
39obj-y += mc2_common.o sched_mc2.o
diff --git a/litmus/mc2_common.c b/litmus/mc2_common.c
new file mode 100644
index 000000000000..a8ea5d9889f3
--- /dev/null
+++ b/litmus/mc2_common.c
@@ -0,0 +1,78 @@
1/*
2 * litmus/mc2_common.c
3 *
4 * Common functions for MC2 plugin.
5 */
6
7#include <linux/percpu.h>
8#include <linux/sched.h>
9#include <linux/list.h>
10#include <linux/slab.h>
11#include <asm/uaccess.h>
12
13#include <litmus/litmus.h>
14#include <litmus/sched_plugin.h>
15#include <litmus/sched_trace.h>
16
17#include <litmus/mc2_common.h>
18
19long mc2_task_client_init(struct task_client *tc, struct mc2_task *mc2_param, struct task_struct *tsk, struct reservation *res)
20{
21 task_client_init(tc, tsk, res);
22 if ((mc2_param->crit < CRIT_LEVEL_A) ||
23 (mc2_param->crit > CRIT_LEVEL_C))
24 return -EINVAL;
25
26 TRACE_TASK(tsk, "mc2_task_client_init: crit_level = %d\n", mc2_param->crit);
27
28 return 0;
29}
30
31asmlinkage long sys_set_mc2_task_param(pid_t pid, struct mc2_task __user * param)
32{
33 struct task_struct *target;
34 int retval = -EINVAL;
35 struct mc2_task *mp = kzalloc(sizeof(*mp), GFP_KERNEL);
36
37 if (!mp)
38 return -ENOMEM;
39
40 printk("Setting up mc^2 task parameters for process %d.\n", pid);
41
42 if (pid < 0 || param == 0) {
43 goto out;
44 }
45 if (copy_from_user(mp, param, sizeof(*mp))) {
46 retval = -EFAULT;
47 goto out;
48 }
49
50 /* Task search and manipulation must be protected */
51 read_lock_irq(&tasklist_lock);
52 if (!(target = find_task_by_vpid(pid))) {
53 retval = -ESRCH;
54 goto out_unlock;
55 }
56
57 if (is_realtime(target)) {
58 /* The task is already a real-time task.
59 * We cannot not allow parameter changes at this point.
60 */
61 retval = -EBUSY;
62 goto out_unlock;
63 }
64 if (mp->crit < CRIT_LEVEL_A || mp->crit >= NUM_CRIT_LEVELS) {
65 printk(KERN_INFO "litmus: real-time task %d rejected "
66 "because of invalid criticality level\n", pid);
67 goto out_unlock;
68 }
69
70 //target->rt_param.plugin_state = mp;
71 target->rt_param.mc2_data = mp;
72
73 retval = 0;
74out_unlock:
75 read_unlock_irq(&tasklist_lock);
76out:
77 return retval;
78} \ No newline at end of file
diff --git a/litmus/reservation.c b/litmus/reservation.c
index 08c74f9005b3..d11003af279a 100644
--- a/litmus/reservation.c
+++ b/litmus/reservation.c
@@ -217,7 +217,7 @@ static void sup_charge_budget(
217 /* stop at the first ACTIVE reservation */ 217 /* stop at the first ACTIVE reservation */
218 //break; 218 //break;
219 } 219 }
220 TRACE("finished charging budgets\n"); 220 //TRACE("finished charging budgets\n");
221} 221}
222 222
223static void sup_replenish_budgets(struct sup_reservation_environment* sup_env) 223static void sup_replenish_budgets(struct sup_reservation_environment* sup_env)
@@ -234,7 +234,7 @@ static void sup_replenish_budgets(struct sup_reservation_environment* sup_env)
234 break; 234 break;
235 } 235 }
236 } 236 }
237 TRACE("finished replenishing budgets\n"); 237 //TRACE("finished replenishing budgets\n");
238 238
239 /* request a scheduler update at the next replenishment instant */ 239 /* request a scheduler update at the next replenishment instant */
240 res = list_first_entry_or_null(&sup_env->depleted_reservations, 240 res = list_first_entry_or_null(&sup_env->depleted_reservations,
@@ -252,7 +252,7 @@ void sup_update_time(
252 /* If the time didn't advance, there is nothing to do. 252 /* If the time didn't advance, there is nothing to do.
253 * This check makes it safe to call sup_advance_time() potentially 253 * This check makes it safe to call sup_advance_time() potentially
254 * multiple times (e.g., via different code paths. */ 254 * multiple times (e.g., via different code paths. */
255 TRACE("(sup_update_time) now: %llu, current_time: %llu\n", now, sup_env->env.current_time); 255 //TRACE("(sup_update_time) now: %llu, current_time: %llu\n", now, sup_env->env.current_time);
256 if (unlikely(now <= sup_env->env.current_time)) 256 if (unlikely(now <= sup_env->env.current_time))
257 return; 257 return;
258 258
diff --git a/litmus/sched_mc2.c b/litmus/sched_mc2.c
new file mode 100644
index 000000000000..09b5ebed2be5
--- /dev/null
+++ b/litmus/sched_mc2.c
@@ -0,0 +1,1634 @@
1/*
2 * litmus/sched_mc2.c
3 *
4 * Implementation of the Mixed-Criticality on MultiCore scheduler
5 *
6 * Thus plugin implements a scheduling algorithm proposed in
7 * "Mixed-Criticality Real-Time Scheduling for Multicore System" paper.
8 */
9
10#include <linux/percpu.h>
11#include <linux/slab.h>
12#include <asm/uaccess.h>
13
14#include <litmus/sched_plugin.h>
15#include <litmus/preempt.h>
16#include <litmus/debug_trace.h>
17
18#include <litmus/litmus.h>
19#include <litmus/jobs.h>
20#include <litmus/budget.h>
21#include <litmus/litmus_proc.h>
22#include <litmus/sched_trace.h>
23
24#include <litmus/mc2_common.h>
25#include <litmus/reservation.h>
26#include <litmus/polling_reservations.h>
27
28/* _global_env - reservation container for level-C tasks*/
29struct gmp_reservation_environment _global_env;
30
31/* cpu_entry - keep track of a running task on a cpu
32 * This state is used to decide the lowest priority cpu
33 */
34struct cpu_entry {
35 struct task_struct *scheduled;
36 lt_t deadline;
37 int cpu;
38 enum crit_level lv;
39 /* if will_schedule is true, this cpu is already selected and
40 call mc2_schedule() soon. */
41 bool will_schedule;
42};
43
44/* cpu_priority - a global state for choosing the lowest priority CPU */
45struct cpu_priority {
46 raw_spinlock_t lock;
47 struct cpu_entry cpu_entries[NR_CPUS];
48};
49
50struct cpu_priority _lowest_prio_cpu;
51
52/* mc2_task_state - a task state structure */
53struct mc2_task_state {
54 struct task_client res_info;
55 /* if cpu == -1, this task is a global task (level C) */
56 int cpu;
57 bool has_departed;
58 struct mc2_task mc2_param;
59};
60
61/* crit_entry - maintain the logically running job (ghost job) */
62struct crit_entry {
63 enum crit_level level;
64 struct task_struct *running;
65};
66
67/* mc2_cpu_state - maintain the scheduled state and ghost jobs
68 * timer : timer for partitioned tasks (level A and B)
69 * g_timer : timer for global tasks (level C)
70 */
71struct mc2_cpu_state {
72 raw_spinlock_t lock;
73
74 struct sup_reservation_environment sup_env;
75 struct hrtimer timer;
76
77 int cpu;
78 struct task_struct* scheduled;
79 struct crit_entry crit_entries[NUM_CRIT_LEVELS];
80};
81
82static DEFINE_PER_CPU(struct mc2_cpu_state, mc2_cpu_state);
83
84#define cpu_state_for(cpu_id) (&per_cpu(mc2_cpu_state, cpu_id))
85#define local_cpu_state() (&__get_cpu_var(mc2_cpu_state))
86
87/* get_mc2_state - get the task's state */
88static struct mc2_task_state* get_mc2_state(struct task_struct *tsk)
89{
90 struct mc2_task_state* tinfo;
91
92 tinfo = (struct mc2_task_state*)tsk_rt(tsk)->plugin_state;
93
94 if (tinfo)
95 return tinfo;
96 else
97 return NULL;
98}
99
100/* get_task_crit_level - return the criticaility level of a task */
101static enum crit_level get_task_crit_level(struct task_struct *tsk)
102{
103 struct mc2_task *mp;
104
105 if (!tsk || !is_realtime(tsk))
106 return NUM_CRIT_LEVELS;
107
108 mp = tsk_rt(tsk)->mc2_data;
109
110 if (!mp)
111 return NUM_CRIT_LEVELS;
112 else
113 return mp->crit;
114}
115
116/* res_find_by_id - find reservation by id */
117static struct reservation* res_find_by_id(struct mc2_cpu_state *state,
118 unsigned int id)
119{
120 struct reservation *res;
121
122 res = sup_find_by_id(&state->sup_env, id);
123 if (!res)
124 res = gmp_find_by_id(&_global_env, id);
125
126 return res;
127}
128
129/* mc2_update_time - update time for a given criticality level.
130 * caller must hold a proper lock
131 * (cpu_state lock or global lock)
132 */
133static void mc2_update_time(enum crit_level lv,
134 struct mc2_cpu_state *state, lt_t time)
135{
136 if (lv < CRIT_LEVEL_C)
137 sup_update_time(&state->sup_env, time);
138 else if (lv == CRIT_LEVEL_C)
139 gmp_update_time(&_global_env, time);
140 else
141 TRACE("update_time(): Criticality level error!!!!\n");
142}
143
144/* task_depart - remove a task from its reservation
145 * If the job has remaining budget, convert it to a ghost job
146 * and update crit_entries[]
147 *
148 * @job_complete indicate whether job completes or not
149 */
150static void task_departs(struct task_struct *tsk, int job_complete)
151{
152 struct mc2_task_state* tinfo = get_mc2_state(tsk);
153 struct mc2_cpu_state* state = local_cpu_state();
154 struct reservation* res;
155 struct reservation_client *client;
156
157 BUG_ON(!is_realtime(tsk));
158
159 res = tinfo->res_info.client.reservation;
160 client = &tinfo->res_info.client;
161
162 res->ops->client_departs(res, client, job_complete);
163 tinfo->has_departed = true;
164 TRACE_TASK(tsk, "CLIENT DEPART with budget %llu\n", res->cur_budget);
165
166 if (job_complete && res->cur_budget) {
167 struct crit_entry* ce;
168 enum crit_level lv = tinfo->mc2_param.crit;
169
170 ce = &state->crit_entries[lv];
171 ce->running = tsk;
172 res->is_ghost = 1;
173 TRACE_TASK(tsk, "BECOME GHOST at %llu\n", litmus_clock());
174
175 }
176}
177
178/* task_arrive - put a task into its reservation
179 * If the job was a ghost job, remove it from crit_entries[]
180 */
181static void task_arrives(struct mc2_cpu_state *state, struct task_struct *tsk)
182{
183 struct mc2_task_state* tinfo = get_mc2_state(tsk);
184 struct reservation* res;
185 struct reservation_client *client;
186 enum crit_level lv = get_task_crit_level(tsk);
187
188 res = tinfo->res_info.client.reservation;
189 client = &tinfo->res_info.client;
190
191 tinfo->has_departed = false;
192 res->ops->client_arrives(res, client);
193
194 sched_trace_task_release(tsk);
195
196 if (lv != NUM_CRIT_LEVELS) {
197 struct crit_entry *ce;
198 ce = &state->crit_entries[lv];
199 /* if the currrent task is a ghost job, remove it */
200 if (ce->running == tsk)
201 ce->running = NULL;
202 }
203}
204
205/* get_lowest_prio_cpu - return the lowest priority cpu
206 * This will be used for scheduling level-C tasks.
207 * If all CPUs are running tasks which has
208 * higher priority than level C, return NO_CPU.
209 */
210static int get_lowest_prio_cpu(lt_t priority)
211{
212 struct cpu_entry *ce;
213 int cpu, ret = NO_CPU;
214 lt_t latest_deadline = 0;
215
216 raw_spin_lock(&_lowest_prio_cpu.lock);
217 ce = &_lowest_prio_cpu.cpu_entries[local_cpu_state()->cpu];
218 if (!ce->will_schedule && !ce->scheduled) {
219 raw_spin_unlock(&_lowest_prio_cpu.lock);
220 TRACE("CPU %d (local) is the lowest!\n", ce->cpu);
221 return ce->cpu;
222 } else {
223 TRACE("Local CPU will_schedule=%d, scheduled=(%s/%d)\n", ce->will_schedule, ce->scheduled ? (ce->scheduled)->comm : "null", ce->scheduled ? (ce->scheduled)->pid : 0);
224 }
225
226 for_each_online_cpu(cpu) {
227 ce = &_lowest_prio_cpu.cpu_entries[cpu];
228 /* If a CPU will call schedule() in the near future, we don't
229 return that CPU. */
230 TRACE("CPU %d will_schedule=%d, scheduled=(%s/%d:%d)\n", cpu, ce->will_schedule,
231 ce->scheduled ? (ce->scheduled)->comm : "null",
232 ce->scheduled ? (ce->scheduled)->pid : 0,
233 ce->scheduled ? (ce->scheduled)->rt_param.job_params.job_no : 0);
234 if (!ce->will_schedule) {
235 if (!ce->scheduled) {
236 /* Idle cpu, return this. */
237 raw_spin_unlock(&_lowest_prio_cpu.lock);
238 TRACE("CPU %d is the lowest!\n", ce->cpu);
239 return ce->cpu;
240 } else if (ce->lv == CRIT_LEVEL_C &&
241 ce->deadline > latest_deadline) {
242 latest_deadline = ce->deadline;
243 ret = ce->cpu;
244 }
245 }
246 }
247
248 raw_spin_unlock(&_lowest_prio_cpu.lock);
249
250 if (priority >= latest_deadline)
251 ret = NO_CPU;
252
253 TRACE("CPU %d is the lowest!\n", ret);
254
255 return ret;
256}
257
258/* NOTE: drops state->lock */
259/* mc2_update_timer_and_unlock - set a timer and g_timer and unlock
260 * Whenever res_env.current_time is updated,
261 * we check next_scheduler_update and set
262 * a timer.
263 * If there exist a global event which is
264 * not armed on any CPU and g_timer is not
265 * active, set a g_timer for that event.
266 */
267static void mc2_update_timer_and_unlock(struct mc2_cpu_state *state)
268{
269 int local;
270 lt_t update, now;
271 enum crit_level lv = get_task_crit_level(state->scheduled);
272 struct next_timer_event *event, *next;
273
274 //TRACE_TASK(state->scheduled, "update_timer!\n");
275 if (lv != NUM_CRIT_LEVELS)
276 TRACE_TASK(state->scheduled, "UPDATE_TIMER LV = %d\n", lv);
277
278 update = state->sup_env.next_scheduler_update;
279 now = state->sup_env.env.current_time;
280
281 /* Be sure we're actually running on the right core,
282 * as pres_update_timer() is also called from pres_task_resume(),
283 * which might be called on any CPU when a thread resumes.
284 */
285 local = local_cpu_state() == state;
286
287 list_for_each_entry_safe(event, next, &_global_env.next_events, list) {
288 /* If the event time is already passed, we call schedule() on
289 the lowest priority cpu */
290 if (event->next_update >= update) {
291 break;
292 }
293
294 if (event->next_update < litmus_clock()) {
295 if (event->timer_armed_on == NO_CPU) {
296 struct reservation *res = gmp_find_by_id(&_global_env, event->id);
297 int cpu = get_lowest_prio_cpu(res?res->priority:0);
298 TRACE("GLOBAL EVENT PASSED!! poking CPU %d to reschedule\n", cpu);
299 list_del(&event->list);
300 kfree(event);
301 if (cpu != NO_CPU) {
302 raw_spin_lock(&_lowest_prio_cpu.lock);
303 _lowest_prio_cpu.cpu_entries[cpu].will_schedule = true;
304 raw_spin_unlock(&_lowest_prio_cpu.lock);
305 litmus_reschedule(cpu);
306 }
307 }
308 } else if (event->next_update < update && event->timer_armed_on == NO_CPU) {
309 event->timer_armed_on = state->cpu;
310 update = event->next_update;
311 break;
312 }
313 }
314
315 /* Must drop state lock before calling into hrtimer_start(), which
316 * may raise a softirq, which in turn may wake ksoftirqd. */
317 raw_spin_unlock(&state->lock);
318 raw_spin_unlock(&_global_env.lock);
319
320 if (update <= now) {
321 litmus_reschedule(state->cpu);
322 } else if (likely(local && update != SUP_NO_SCHEDULER_UPDATE)) {
323 /* Reprogram only if not already set correctly. */
324 if (!hrtimer_active(&state->timer) ||
325 ktime_to_ns(hrtimer_get_expires(&state->timer)) != update) {
326 TRACE("canceling timer...at %llu\n",
327 ktime_to_ns(hrtimer_get_expires(&state->timer)));
328 hrtimer_cancel(&state->timer);
329 TRACE("setting scheduler timer for %llu\n", update);
330 /* We cannot use hrtimer_start() here because the
331 * wakeup flag must be set to zero. */
332 __hrtimer_start_range_ns(&state->timer,
333 ns_to_ktime(update),
334 0 /* timer coalescing slack */,
335 HRTIMER_MODE_ABS_PINNED,
336 0 /* wakeup */);
337 }
338 } else if (unlikely(!local && update != SUP_NO_SCHEDULER_UPDATE)) {
339 /* Poke remote core only if timer needs to be set earlier than
340 * it is currently set.
341 */
342 TRACE("mc2_update_timer for remote CPU %d (update=%llu, "
343 "active:%d, set:%llu)\n",
344 state->cpu,
345 update,
346 hrtimer_active(&state->timer),
347 ktime_to_ns(hrtimer_get_expires(&state->timer)));
348 if (!hrtimer_active(&state->timer) ||
349 ktime_to_ns(hrtimer_get_expires(&state->timer)) > update) {
350 TRACE("poking CPU %d so that it can update its "
351 "scheduling timer (active:%d, set:%llu)\n",
352 state->cpu,
353 hrtimer_active(&state->timer),
354 ktime_to_ns(hrtimer_get_expires(&state->timer)));
355 litmus_reschedule(state->cpu);
356 }
357 }
358}
359
360/* mc2_update_ghost_state - Update crit_entries[] to track ghost jobs
361 * If the budget of a ghost is exhausted,
362 * clear is_ghost and reschedule
363 */
364static lt_t mc2_update_ghost_state(struct mc2_cpu_state *state)
365{
366 int lv = 0;
367 struct crit_entry* ce;
368 struct reservation *res;
369 struct mc2_task_state *tinfo;
370 lt_t ret = ULLONG_MAX;
371
372 BUG_ON(!state);
373
374 for (lv = 0; lv < NUM_CRIT_LEVELS; lv++) {
375 ce = &state->crit_entries[lv];
376 if (ce->running != NULL) {
377//printk(KERN_ALERT "P%d ce->running : %s/%d\n", state->cpu, ce->running ? (ce->running)->comm : "null", ce->running ? (ce->running)->pid : 0);
378 tinfo = get_mc2_state(ce->running);
379 if (!tinfo)
380 continue;
381
382 res = res_find_by_id(state, tinfo->mc2_param.res_id);
383 BUG_ON(!res);
384//printk(KERN_ALERT "R%d found!\n", res->id);
385 TRACE("LV %d running id %d budget %llu\n",
386 lv, tinfo->mc2_param.res_id, res->cur_budget);
387 /* If the budget is exhausted, clear is_ghost and reschedule */
388 if (!res->cur_budget) {
389 struct sup_reservation_environment* sup_env = &state->sup_env;
390
391 TRACE("GHOST FINISH id %d at %llu\n",
392 tinfo->mc2_param.res_id, litmus_clock());
393 ce->running = NULL;
394 res->is_ghost = 0;
395
396 if (lv < CRIT_LEVEL_C) {
397 res = list_first_entry_or_null(
398 &sup_env->active_reservations,
399 struct reservation, list);
400 if (res)
401 litmus_reschedule_local();
402 } else if (lv == CRIT_LEVEL_C) {
403 res = list_first_entry_or_null(
404 &_global_env.active_reservations,
405 struct reservation, list);
406 if (res)
407 litmus_reschedule(state->cpu);
408 }
409 } else {
410 //TRACE("GHOST NOT FINISH id %d budget %llu\n", res->id, res->cur_budget);
411 //gmp_add_event_after(&_global_env, res->cur_budget, res->id, EVENT_DRAIN);
412 if (ret > res->cur_budget) {
413 ret = res->cur_budget;
414 }
415 }
416 }
417 }
418
419 return ret;
420}
421
422/* update_cpu_prio - Update cpu's priority
423 * When a cpu picks a new task, call this function
424 * to update cpu priorities.
425 */
426static void update_cpu_prio(struct mc2_cpu_state *state)
427{
428 struct cpu_entry *ce = &_lowest_prio_cpu.cpu_entries[state->cpu];
429 enum crit_level lv = get_task_crit_level(state->scheduled);
430
431 if (!state->scheduled) {
432 /* cpu is idle. */
433 ce->scheduled = NULL;
434 ce->deadline = ULLONG_MAX;
435 ce->lv = NUM_CRIT_LEVELS;
436 } else if (lv == CRIT_LEVEL_C) {
437 ce->scheduled = state->scheduled;
438 ce->deadline = get_deadline(state->scheduled);
439 ce->lv = lv;
440 } else if (lv < CRIT_LEVEL_C) {
441 /* If cpu is running level A or B tasks, it is not eligible
442 to run level-C tasks */
443 ce->scheduled = state->scheduled;
444 ce->deadline = 0;
445 ce->lv = lv;
446 }
447};
448
449/* on_scheduling_timer - timer event for partitioned tasks
450 */
451static enum hrtimer_restart on_scheduling_timer(struct hrtimer *timer)
452{
453 unsigned long flags;
454 enum hrtimer_restart restart = HRTIMER_NORESTART;
455 struct mc2_cpu_state *state;
456 lt_t update, now;
457 int global_schedule_now;
458 lt_t remain_budget;
459
460 state = container_of(timer, struct mc2_cpu_state, timer);
461
462 /* The scheduling timer should only fire on the local CPU, because
463 * otherwise deadlocks via timer_cancel() are possible.
464 * Note: this does not interfere with dedicated interrupt handling, as
465 * even under dedicated interrupt handling scheduling timers for
466 * budget enforcement must occur locally on each CPU.
467 */
468 BUG_ON(state->cpu != raw_smp_processor_id());
469
470 TRACE("TIMER FIRED at %llu\n", litmus_clock());
471 raw_spin_lock_irqsave(&_global_env.lock, flags);
472 raw_spin_lock(&state->lock);
473//printk(KERN_ALERT "P%d on_scheduling_timer() hold lock %s/%d\n", state->cpu, current ? (current)->comm : "null", current ? (current)->pid : 0);
474 now = litmus_clock();
475 sup_update_time(&state->sup_env, now);
476 global_schedule_now = gmp_update_time(&_global_env, now);
477//printk(KERN_ALERT "P%d update_time in timer() %s/%d\n", state->cpu, current ? (current)->comm : "null", current ? (current)->pid : 0);
478 remain_budget = mc2_update_ghost_state(state);
479
480 update = state->sup_env.next_scheduler_update;
481 now = state->sup_env.env.current_time;
482
483 if (remain_budget != ULLONG_MAX && update > now + remain_budget) {
484 update = now + remain_budget;
485 }
486
487 //TRACE_CUR("on_scheduling_timer at %llu, upd:%llu (for cpu=%d) g_schedule_now:%d\n", now, update, state->cpu, global_schedule_now);
488//printk(KERN_ALERT "on_scheduling_timer at %llu, upd:%llu (for cpu=%d) g_schedule_now:%d\n", now, update, state->cpu, global_schedule_now);
489 if (update <= now) {
490 litmus_reschedule_local();
491 } else if (update != SUP_NO_SCHEDULER_UPDATE) {
492 hrtimer_set_expires(timer, ns_to_ktime(update));
493 restart = HRTIMER_RESTART;
494 }
495
496 BUG_ON(global_schedule_now < 0 || global_schedule_now > 4);
497
498 /* Find the lowest cpu, and call reschedule */
499 while (global_schedule_now--) {
500 int cpu = get_lowest_prio_cpu(0);
501 if (cpu != NO_CPU) {
502 raw_spin_lock(&_lowest_prio_cpu.lock);
503 _lowest_prio_cpu.cpu_entries[cpu].will_schedule = true;
504 raw_spin_unlock(&_lowest_prio_cpu.lock);
505 //TRACE("LOWEST CPU = P%d\n", cpu);
506 litmus_reschedule(cpu);
507 }
508 }
509
510 raw_spin_unlock(&state->lock);
511 raw_spin_unlock_irqrestore(&_global_env.lock, flags);
512//printk(KERN_ALERT "P%d on_scheduling_timer() release lock %s/%d\n", state->cpu, current ? (current)->comm : "null", current ? (current)->pid : 0);
513 return restart;
514}
515
516/* mc2_dispatch - Select the next task to schedule.
517 */
518struct task_struct* mc2_dispatch(struct sup_reservation_environment* sup_env, struct mc2_cpu_state* state)
519{
520 struct reservation *res, *next;
521 struct task_struct *tsk = NULL;
522 struct crit_entry *ce;
523 enum crit_level lv;
524 lt_t time_slice, cur_priority;
525
526 list_for_each_entry_safe(res, next, &sup_env->active_reservations, list) {
527 if (res->state == RESERVATION_ACTIVE) {
528 tsk = res->ops->dispatch_client(res, &time_slice);
529 if (likely(tsk)) {
530 lv = get_task_crit_level(tsk);
531 if (lv == NUM_CRIT_LEVELS) {
532 sup_scheduler_update_after(sup_env, res->cur_budget);
533 return tsk;
534 } else {
535 ce = &state->crit_entries[lv];
536 if (likely(!ce->running)) {
537 /* If we found the next task, clear all flags */
538 sup_scheduler_update_after(sup_env, res->cur_budget);
539 res->blocked_by_ghost = 0;
540 res->is_ghost = 0;
541 return tsk;
542 } else {
543 /* We cannot schedule the same criticality task
544 because the ghost job exists. Set blocked_by_ghost
545 flag not to charge budget */
546 res->blocked_by_ghost = 1;
547 TRACE_TASK(ce->running, " is GHOST\n");
548 }
549 }
550 }
551 }
552 }
553
554 /* no eligible level A or B tasks exists */
555 /* check the ghost job */
556 ce = &state->crit_entries[CRIT_LEVEL_C];
557 if (ce->running) {
558 TRACE_TASK(ce->running," is GHOST\n");
559 return NULL;
560 }
561
562 cur_priority = _lowest_prio_cpu.cpu_entries[state->cpu].deadline;
563
564 TRACE("****** ACTIVE LIST ******\n");
565 TRACE_TASK(_lowest_prio_cpu.cpu_entries[state->cpu].scheduled, "** CURRENT JOB deadline %llu **\n", cur_priority);
566 list_for_each_entry_safe(res, next, &_global_env.active_reservations, list) {
567 TRACE("R%d deadline=%llu, scheduled_on=%d\n", res->id, res->priority, res->scheduled_on);
568 if (res->state == RESERVATION_ACTIVE && res->scheduled_on == NO_CPU) {
569 tsk = res->ops->dispatch_client(res, &time_slice);
570 if (likely(tsk)) {
571 lv = get_task_crit_level(tsk);
572 if (lv == NUM_CRIT_LEVELS) {
573 gmp_add_event_after(&_global_env, res->cur_budget, res->id, EVENT_DRAIN);
574 //res->event_added = 1;
575 return tsk;
576 } else if (lv == CRIT_LEVEL_C) {
577 //ce = &state->crit_entries[lv];
578 //if (likely(!ce->running)) {
579 gmp_add_event_after(&_global_env, res->cur_budget, res->id, EVENT_DRAIN);
580 res->event_added = 1;
581 res->blocked_by_ghost = 0;
582 res->is_ghost = 0;
583 res->scheduled_on = state->cpu;
584 return tsk;
585 //} else {
586 // res->blocked_by_ghost = 1;
587 // TRACE_TASK(ce->running, " is GHOST\n");
588 // return NULL;
589 //}
590 } else {
591 BUG();
592 }
593 }
594 }
595 }
596
597 return NULL;
598}
599
600/* not used now */
601static void pre_schedule(struct task_struct *prev)
602{
603 enum crit_level lv;
604 if (!is_realtime(prev) || !prev)
605 return;
606
607 lv = get_task_crit_level(prev);
608}
609
610/* not used now */
611static void post_schedule(struct task_struct *next)
612{
613 enum crit_level lv;
614 if (!is_realtime(next) || !next)
615 return;
616
617 lv = get_task_crit_level(next);
618}
619
620/* mc2_schedule - main scheduler function. pick the next task to run
621 */
622static struct task_struct* mc2_schedule(struct task_struct * prev)
623{
624 /* next == NULL means "schedule background work". */
625 lt_t now;
626 struct mc2_cpu_state *state = local_cpu_state();
627
628 pre_schedule(prev);
629
630 raw_spin_lock(&_global_env.lock);
631 raw_spin_lock(&state->lock);
632
633 //BUG_ON(state->scheduled && state->scheduled != prev);
634 //BUG_ON(state->scheduled && !is_realtime(prev));
635 if (state->scheduled && state->scheduled != prev)
636 printk(KERN_ALERT "BUG1!!!!!!!! %s %s\n", state->scheduled ? (state->scheduled)->comm : "null", prev ? (prev)->comm : "null");
637 if (state->scheduled && !is_realtime(prev))
638 printk(KERN_ALERT "BUG2!!!!!!!! \n");
639
640 /* update time */
641 state->sup_env.will_schedule = true;
642
643 now = litmus_clock();
644 sup_update_time(&state->sup_env, now);
645 gmp_update_time(&_global_env, now);
646
647 mc2_update_ghost_state(state);
648
649 /* remove task from reservation if it blocks */
650 if (is_realtime(prev) && !is_running(prev))
651 task_departs(prev, is_completed(prev));
652
653 raw_spin_lock(&_lowest_prio_cpu.lock);
654 _lowest_prio_cpu.cpu_entries[state->cpu].will_schedule = false;
655
656 /* figure out what to schedule next */
657 state->scheduled = mc2_dispatch(&state->sup_env, state);
658 if (state->scheduled && is_realtime(state->scheduled))
659 TRACE_TASK(state->scheduled, "mc2_dispatch picked me!\n");
660
661 update_cpu_prio(state);
662 raw_spin_unlock(&_lowest_prio_cpu.lock);
663
664 /* Notify LITMUS^RT core that we've arrived at a scheduling decision. */
665 sched_state_task_picked();
666
667 /* program scheduler timer */
668 state->sup_env.will_schedule = false;
669
670 /* NOTE: drops state->lock */
671 mc2_update_timer_and_unlock(state);
672
673
674
675 if (prev != state->scheduled && is_realtime(prev)) {
676 struct mc2_task_state* tinfo = get_mc2_state(prev);
677 struct reservation* res = tinfo->res_info.client.reservation;
678 TRACE_TASK(prev, "PREV JOB scheduled_on = P%d\n", res->scheduled_on);
679 res->scheduled_on = NO_CPU;
680 TRACE_TASK(prev, "descheduled.\n");
681 /* if prev is preempted and a global task, find the lowest cpu and reschedule */
682 if (tinfo->has_departed == false && get_task_crit_level(prev) == CRIT_LEVEL_C) {
683 int cpu;
684 raw_spin_lock(&_global_env.lock);
685 cpu = get_lowest_prio_cpu(res?res->priority:0);
686 //TRACE("LEVEL-C TASK PREEMPTED!! poking CPU %d to reschedule\n", cpu);
687 if (cpu != NO_CPU) {
688 raw_spin_lock(&_lowest_prio_cpu.lock);
689 _lowest_prio_cpu.cpu_entries[cpu].will_schedule = true;
690 raw_spin_unlock(&_lowest_prio_cpu.lock);
691 litmus_reschedule(cpu);
692 }
693 raw_spin_unlock(&_global_env.lock);
694 }
695 }
696 if (state->scheduled) {
697 TRACE_TASK(state->scheduled, "scheduled.\n");
698 }
699
700 post_schedule(state->scheduled);
701
702 return state->scheduled;
703}
704
705static void resume_legacy_task_model_updates(struct task_struct *tsk)
706{
707 lt_t now;
708 if (is_sporadic(tsk)) {
709 /* If this sporadic task was gone for a "long" time and woke up past
710 * its deadline, then give it a new budget by triggering a job
711 * release. This is purely cosmetic and has no effect on the
712 * P-RES scheduler. */
713
714 now = litmus_clock();
715 if (is_tardy(tsk, now)) {
716 release_at(tsk, now);
717 sched_trace_task_release(tsk);
718 }
719 }
720}
721
722/* mc2_task_resume - Called when the state of tsk changes back to
723 * TASK_RUNNING. We need to requeue the task.
724 */
725static void mc2_task_resume(struct task_struct *tsk)
726{
727 unsigned long flags;
728 struct mc2_task_state* tinfo = get_mc2_state(tsk);
729 struct mc2_cpu_state *state;
730
731 TRACE_TASK(tsk, "thread wakes up at %llu\n", litmus_clock());
732
733 local_irq_save(flags);
734 if (tinfo->cpu != -1)
735 state = cpu_state_for(tinfo->cpu);
736 else
737 state = local_cpu_state();
738
739 raw_spin_lock(&_global_env.lock);
740//printk(KERN_ALERT "P%d resume() hold lock\n", state->cpu);
741 /* Requeue only if self-suspension was already processed. */
742 if (tinfo->has_departed)
743 {
744 raw_spin_lock(&state->lock);
745 /* Assumption: litmus_clock() is synchronized across cores,
746 * since we might not actually be executing on tinfo->cpu
747 * at the moment. */
748 if (tinfo->cpu != -1) {
749 sup_update_time(&state->sup_env, litmus_clock());
750 } else {
751 //TRACE("RESUME UPDATE ####\n");
752 gmp_update_time(&_global_env, litmus_clock());
753 //TRACE("RESUME UPDATE $$$$\n");
754 }
755
756 mc2_update_ghost_state(state);
757 task_arrives(state, tsk);
758 /* NOTE: drops state->lock */
759 TRACE_TASK(tsk, "mc2_resume()\n");
760 mc2_update_timer_and_unlock(state);
761//printk(KERN_ALERT "P%d resume() dropped lock\n", state->cpu);
762 } else {
763 TRACE_TASK(tsk, "resume event ignored, still scheduled\n");
764 raw_spin_unlock(&_global_env.lock);
765//printk(KERN_ALERT "P%d resume() release lock\n", state->cpu);
766 }
767
768 local_irq_restore(flags);
769
770 resume_legacy_task_model_updates(tsk);
771}
772
773/* mc2_complete_job - syscall backend for job completions
774 */
775static long mc2_complete_job(void)
776{
777 ktime_t next_release;
778 long err;
779
780 TRACE_CUR("mc2_complete_job at %llu (deadline: %llu)\n", litmus_clock(),
781 get_deadline(current));
782
783 tsk_rt(current)->completed = 1;
784
785 /* If this the first job instance, we need to reset replenish
786 time to the next release time */
787 if (tsk_rt(current)->sporadic_release) {
788 struct mc2_cpu_state *state;
789 struct reservation_environment *env;
790 struct mc2_task_state *tinfo;
791 struct reservation *res;
792 unsigned long flags;
793
794 preempt_disable();
795 local_irq_save(flags);
796
797 tinfo = get_mc2_state(current);
798
799 if (get_task_crit_level(current) < CRIT_LEVEL_C)
800 state = cpu_state_for(tinfo->cpu);
801 else
802 state = local_cpu_state();
803
804 raw_spin_lock(&_global_env.lock);
805 raw_spin_lock(&state->lock);
806//printk(KERN_ALERT "P%d complete() hold lock\n", state->cpu);
807 env = &(state->sup_env.env);
808
809 res = res_find_by_id(state, tinfo->mc2_param.res_id);
810
811 if (get_task_crit_level(current) < CRIT_LEVEL_C) {
812 env->time_zero = tsk_rt(current)->sporadic_release_time;
813 } else {
814 _global_env.env.time_zero = tsk_rt(current)->sporadic_release_time;
815 }
816
817 /* set next_replenishtime to synchronous release time */
818 res->next_replenishment = tsk_rt(current)->sporadic_release_time;
819
820 if (get_task_crit_level(current) == CRIT_LEVEL_A) {
821 struct table_driven_reservation *tdres;
822 tdres = container_of(res, struct table_driven_reservation, res);
823 tdres->next_interval = 0;
824 tdres->major_cycle_start = tsk_rt(current)->sporadic_release_time;
825 res->next_replenishment += tdres->intervals[0].start;
826 }
827 res->cur_budget = 0;
828 res->env->change_state(res->env, res, RESERVATION_DEPLETED);
829
830 //TRACE_CUR("CHANGE NEXT_REP = %llu\n NEXT_UPDATE = %llu\n", res->next_replenishment, state->sup_env.next_scheduler_update);
831
832 raw_spin_unlock(&state->lock);
833 raw_spin_unlock(&_global_env.lock);
834//printk(KERN_ALERT "P%d complete() release lock\n", state->cpu);
835 local_irq_restore(flags);
836 preempt_enable();
837 }
838 sched_trace_task_completion(current, 0);
839
840 /* update the next release time and deadline */
841 prepare_for_next_period(current);
842
843 next_release = ns_to_ktime(get_release(current));
844 preempt_disable();
845 TRACE_CUR("next_release=%llu\n", get_release(current));
846 if (get_release(current) > litmus_clock()) {
847 /* sleep until next_release */
848 set_current_state(TASK_INTERRUPTIBLE);
849 preempt_enable_no_resched();
850 err = schedule_hrtimeout(&next_release, HRTIMER_MODE_ABS);
851 if (get_task_crit_level(current) == CRIT_LEVEL_A)
852 sched_trace_task_release(current);
853 } else {
854 /* release the next job immediately */
855 err = 0;
856 TRACE_CUR("TARDY: release=%llu now=%llu\n", get_release(current), litmus_clock());
857 preempt_enable();
858 if (get_task_crit_level(current) == CRIT_LEVEL_A)
859 sched_trace_task_release(current);
860 }
861
862 TRACE_CUR("mc2_complete_job returns at %llu\n", litmus_clock());
863
864 return err;
865}
866
867/* mc2_admit_task - Setup mc2 task parameters
868 */
869static long mc2_admit_task(struct task_struct *tsk)
870{
871 long err = -ESRCH;
872 unsigned long flags;
873 struct reservation *res;
874 struct mc2_cpu_state *state;
875 struct mc2_task_state *tinfo = kzalloc(sizeof(*tinfo), GFP_ATOMIC);
876 struct mc2_task *mp = tsk_rt(tsk)->mc2_data;
877 enum crit_level lv;
878
879 if (!tinfo)
880 return -ENOMEM;
881
882 if (!mp) {
883 printk(KERN_ERR "mc2_admit_task: criticality level has not been set\n");
884 return err;
885 }
886
887 lv = mp->crit;
888 preempt_disable();
889
890 if (lv < CRIT_LEVEL_C) {
891 state = cpu_state_for(task_cpu(tsk));
892 raw_spin_lock_irqsave(&state->lock, flags);
893
894 res = sup_find_by_id(&state->sup_env, mp->res_id);
895
896 /* found the appropriate reservation */
897 if (res) {
898 TRACE_TASK(tsk, "SUP FOUND RES ID\n");
899 tinfo->mc2_param.crit = mp->crit;
900 tinfo->mc2_param.res_id = mp->res_id;
901
902 /* initial values */
903 err = mc2_task_client_init(&tinfo->res_info, &tinfo->mc2_param, tsk, res);
904 tinfo->cpu = task_cpu(tsk);
905 tinfo->has_departed = true;
906 tsk_rt(tsk)->plugin_state = tinfo;
907
908 /* disable LITMUS^RT's per-thread budget enforcement */
909 tsk_rt(tsk)->task_params.budget_policy = NO_ENFORCEMENT;
910 }
911
912 raw_spin_unlock_irqrestore(&state->lock, flags);
913 } else if (lv == CRIT_LEVEL_C) {
914 raw_spin_lock_irqsave(&_global_env.lock, flags);
915//printk(KERN_ALERT "admit() hold lock\n");
916 state = local_cpu_state();
917
918 raw_spin_lock(&state->lock);
919
920 res = gmp_find_by_id(&_global_env, mp->res_id);
921
922 /* found the appropriate reservation (or vCPU) */
923 if (res) {
924 TRACE_TASK(tsk, "GMP FOUND RES ID\n");
925 tinfo->mc2_param.crit = mp->crit;
926 tinfo->mc2_param.res_id = mp->res_id;
927
928 /* initial values */
929 err = mc2_task_client_init(&tinfo->res_info, &tinfo->mc2_param, tsk, res);
930 tinfo->cpu = -1;
931 tinfo->has_departed = true;
932 tsk_rt(tsk)->plugin_state = tinfo;
933
934 /* disable LITMUS^RT's per-thread budget enforcement */
935 tsk_rt(tsk)->task_params.budget_policy = NO_ENFORCEMENT;
936 }
937
938 raw_spin_unlock(&state->lock);
939 raw_spin_unlock_irqrestore(&_global_env.lock, flags);
940//printk(KERN_ALERT "admit() release lock\n");
941 }
942
943 preempt_enable();
944
945 if (err)
946 kfree(tinfo);
947
948 return err;
949}
950
951/* mc2_task_new - A new real-time job is arrived. Release the next job
952 * at the next reservation replenish time
953 */
954static void mc2_task_new(struct task_struct *tsk, int on_runqueue,
955 int is_running)
956{
957 unsigned long flags;
958 struct mc2_task_state* tinfo = get_mc2_state(tsk);
959 struct mc2_cpu_state *state; // = cpu_state_for(tinfo->cpu);
960 struct reservation *res;
961 enum crit_level lv = get_task_crit_level(tsk);
962 lt_t release = 0;
963
964 TRACE_TASK(tsk, "new RT task %llu (on_rq:%d, running:%d)\n",
965 litmus_clock(), on_runqueue, is_running);
966
967 local_irq_save(flags);
968 if (tinfo->cpu == -1)
969 state = local_cpu_state();
970 else
971 state = cpu_state_for(tinfo->cpu);
972
973 /* acquire the lock protecting the state and disable interrupts */
974 raw_spin_lock(&_global_env.lock);
975 raw_spin_lock(&state->lock);
976//printk(KERN_ALERT "new() hold lock R%d\n", tinfo->mc2_param.res_id);
977 if (is_running) {
978 state->scheduled = tsk;
979 /* make sure this task should actually be running */
980 litmus_reschedule_local();
981 }
982
983 res = res_find_by_id(state, tinfo->mc2_param.res_id);
984 release = res->next_replenishment;
985
986 if (on_runqueue || is_running) {
987 /* Assumption: litmus_clock() is synchronized across cores
988 * [see comment in pres_task_resume()] */
989 mc2_update_time(lv, state, litmus_clock());
990 mc2_update_ghost_state(state);
991 task_arrives(state, tsk);
992 /* NOTE: drops state->lock */
993 TRACE("mc2_new()\n");
994
995 mc2_update_timer_and_unlock(state);
996//printk(KERN_ALERT "new() dropped lock R%d\n",tinfo->mc2_param.res_id);
997 } else {
998 raw_spin_unlock(&state->lock);
999 raw_spin_unlock(&_global_env.lock);
1000//printk(KERN_ALERT "new() release lock R%d\n",tinfo->mc2_param.res_id);
1001 }
1002 local_irq_restore(flags);
1003
1004 if (!release) {
1005 TRACE_TASK(tsk, "mc2_task_new() next_release = %llu\n", release);
1006 release_at(tsk, release);
1007 }
1008 else
1009 TRACE_TASK(tsk, "mc2_task_new() next_release = NULL\n");
1010}
1011
1012/* mc2_reservation_destroy - reservation_destroy system call backend
1013 */
1014static long mc2_reservation_destroy(unsigned int reservation_id, int cpu)
1015{
1016 long ret = -EINVAL;
1017 struct mc2_cpu_state *state;
1018 struct reservation *res = NULL, *next;
1019 struct sup_reservation_environment *sup_env;
1020 int found = 0;
1021 enum crit_level lv = get_task_crit_level(current);
1022 unsigned long flags;
1023
1024 if (cpu == -1) {
1025 /* if the reservation is global reservation */
1026 local_irq_save(flags);
1027 state = local_cpu_state();
1028 raw_spin_lock(&_global_env.lock);
1029 raw_spin_lock(&state->lock);
1030
1031 list_for_each_entry_safe(res, next, &_global_env.depleted_reservations, list) {
1032 if (res->id == reservation_id) {
1033 TRACE("DESTROY RES FOUND!!!\n");
1034 list_del(&res->list);
1035 kfree(res);
1036 found = 1;
1037 ret = 0;
1038 }
1039 }
1040 if (!found) {
1041 list_for_each_entry_safe(res, next, &_global_env.inactive_reservations, list) {
1042 if (res->id == reservation_id) {
1043 TRACE("DESTROY RES FOUND!!!\n");
1044 list_del(&res->list);
1045 kfree(res);
1046 found = 1;
1047 ret = 0;
1048 }
1049 }
1050 }
1051 if (!found) {
1052 list_for_each_entry_safe(res, next, &_global_env.active_reservations, list) {
1053 if (res->id == reservation_id) {
1054 TRACE("DESTROY RES FOUND!!!\n");
1055 list_del(&res->list);
1056 kfree(res);
1057 found = 1;
1058 ret = 0;
1059 }
1060 }
1061 }
1062
1063 raw_spin_unlock(&state->lock);
1064 raw_spin_unlock(&_global_env.lock);
1065 local_irq_restore(flags);
1066 } else {
1067 /* if the reservation is partitioned reservation */
1068 state = cpu_state_for(cpu);
1069 raw_spin_lock_irqsave(&state->lock, flags);
1070
1071 // res = sup_find_by_id(&state->sup_env, reservation_id);
1072 sup_env = &state->sup_env;
1073 list_for_each_entry_safe(res, next, &sup_env->depleted_reservations, list) {
1074 if (res->id == reservation_id) {
1075 if (lv == CRIT_LEVEL_A) {
1076 struct table_driven_reservation *tdres;
1077 tdres = container_of(res, struct table_driven_reservation, res);
1078 kfree(tdres->intervals);
1079 }
1080 list_del(&res->list);
1081 kfree(res);
1082 found = 1;
1083 ret = 0;
1084 }
1085 }
1086 if (!found) {
1087 list_for_each_entry_safe(res, next, &sup_env->inactive_reservations, list) {
1088 if (res->id == reservation_id) {
1089 if (lv == CRIT_LEVEL_A) {
1090 struct table_driven_reservation *tdres;
1091 tdres = container_of(res, struct table_driven_reservation, res);
1092 kfree(tdres->intervals);
1093 }
1094 list_del(&res->list);
1095 kfree(res);
1096 found = 1;
1097 ret = 0;
1098 }
1099 }
1100 }
1101 if (!found) {
1102 list_for_each_entry_safe(res, next, &sup_env->active_reservations, list) {
1103 if (res->id == reservation_id) {
1104 if (lv == CRIT_LEVEL_A) {
1105 struct table_driven_reservation *tdres;
1106 tdres = container_of(res, struct table_driven_reservation, res);
1107 kfree(tdres->intervals);
1108 }
1109 list_del(&res->list);
1110 kfree(res);
1111 found = 1;
1112 ret = 0;
1113 }
1114 }
1115 }
1116
1117 raw_spin_unlock_irqrestore(&state->lock, flags);
1118 }
1119
1120 TRACE("RESERVATION_DESTROY ret = %d\n", ret);
1121 return ret;
1122}
1123
1124/* mc2_task_exit - Task became a normal task (not real-time task)
1125 */
1126static void mc2_task_exit(struct task_struct *tsk)
1127{
1128 unsigned long flags;
1129 struct mc2_task_state* tinfo = get_mc2_state(tsk);
1130 struct mc2_cpu_state *state;
1131 enum crit_level lv = tinfo->mc2_param.crit;
1132 struct crit_entry* ce;
1133 int cpu;
1134
1135 local_irq_save(flags);
1136 if (tinfo->cpu != -1)
1137 state = cpu_state_for(tinfo->cpu);
1138 else
1139 state = local_cpu_state();
1140
1141 raw_spin_lock(&_global_env.lock);
1142 raw_spin_lock(&state->lock);
1143
1144 if (state->scheduled == tsk)
1145 state->scheduled = NULL;
1146
1147 ce = &state->crit_entries[lv];
1148 if (ce->running == tsk)
1149 ce->running = NULL;
1150
1151 /* remove from queues */
1152 if (is_running(tsk)) {
1153 /* Assumption: litmus_clock() is synchronized across cores
1154 * [see comment in pres_task_resume()] */
1155
1156 /* update both global and partitioned */
1157 mc2_update_time(lv, state, litmus_clock());
1158 mc2_update_ghost_state(state);
1159 task_departs(tsk, 0);
1160
1161 /* NOTE: drops state->lock */
1162 TRACE("mc2_exit()\n");
1163
1164 mc2_update_timer_and_unlock(state);
1165 } else {
1166 raw_spin_unlock(&state->lock);
1167 raw_spin_unlock(&_global_env.lock);
1168 }
1169
1170 for_each_online_cpu(cpu) {
1171 state = cpu_state_for(cpu);
1172 if (state == local_cpu_state())
1173 continue;
1174 raw_spin_lock(&state->lock);
1175
1176 if (state->scheduled == tsk)
1177 state->scheduled = NULL;
1178
1179 ce = &state->crit_entries[lv];
1180 if (ce->running == tsk)
1181 ce->running = NULL;
1182
1183 raw_spin_unlock(&state->lock);
1184 }
1185
1186 local_irq_restore(flags);
1187
1188 kfree(tsk_rt(tsk)->plugin_state);
1189 tsk_rt(tsk)->plugin_state = NULL;
1190 kfree(tsk_rt(tsk)->mc2_data);
1191 tsk_rt(tsk)->mc2_data = NULL;
1192}
1193
1194/* create_polling_reservation - create a new polling reservation
1195 */
1196static long create_polling_reservation(
1197 int res_type,
1198 struct reservation_config *config)
1199{
1200 struct mc2_cpu_state *state;
1201 struct reservation* res;
1202 struct polling_reservation *pres;
1203 unsigned long flags;
1204 int use_edf = config->priority == LITMUS_NO_PRIORITY;
1205 int periodic = res_type == PERIODIC_POLLING;
1206 long err = -EINVAL;
1207
1208 /* sanity checks */
1209 if (config->polling_params.budget >
1210 config->polling_params.period) {
1211 printk(KERN_ERR "invalid polling reservation (%u): "
1212 "budget > period\n", config->id);
1213 return -EINVAL;
1214 }
1215 if (config->polling_params.budget >
1216 config->polling_params.relative_deadline
1217 && config->polling_params.relative_deadline) {
1218 printk(KERN_ERR "invalid polling reservation (%u): "
1219 "budget > deadline\n", config->id);
1220 return -EINVAL;
1221 }
1222 if (config->polling_params.offset >
1223 config->polling_params.period) {
1224 printk(KERN_ERR "invalid polling reservation (%u): "
1225 "offset > period\n", config->id);
1226 return -EINVAL;
1227 }
1228
1229 /* Allocate before we grab a spin lock.
1230 * Todo: would be nice to use a core-local allocation.
1231 */
1232 pres = kzalloc(sizeof(*pres), GFP_KERNEL);
1233 if (!pres)
1234 return -ENOMEM;
1235
1236 if (config->cpu != -1) {
1237
1238 raw_spin_lock_irqsave(&_global_env.lock, flags);
1239 state = cpu_state_for(config->cpu);
1240 raw_spin_lock(&state->lock);
1241
1242 res = sup_find_by_id(&state->sup_env, config->id);
1243 if (!res) {
1244 polling_reservation_init(pres, use_edf, periodic,
1245 config->polling_params.budget,
1246 config->polling_params.period,
1247 config->polling_params.relative_deadline,
1248 config->polling_params.offset);
1249 pres->res.id = config->id;
1250 pres->res.blocked_by_ghost = 0;
1251 pres->res.is_ghost = 0;
1252 if (!use_edf)
1253 pres->res.priority = config->priority;
1254 sup_add_new_reservation(&state->sup_env, &pres->res);
1255 err = config->id;
1256 } else {
1257 err = -EEXIST;
1258 }
1259
1260 raw_spin_unlock(&state->lock);
1261 raw_spin_unlock_irqrestore(&_global_env.lock, flags);
1262
1263 } else {
1264 raw_spin_lock_irqsave(&_global_env.lock, flags);
1265
1266 res = gmp_find_by_id(&_global_env, config->id);
1267 if (!res) {
1268 polling_reservation_init(pres, use_edf, periodic,
1269 config->polling_params.budget,
1270 config->polling_params.period,
1271 config->polling_params.relative_deadline,
1272 config->polling_params.offset);
1273 pres->res.id = config->id;
1274 pres->res.blocked_by_ghost = 0;
1275 pres->res.scheduled_on = NO_CPU;
1276 pres->res.is_ghost = 0;
1277 if (!use_edf)
1278 pres->res.priority = config->priority;
1279 gmp_add_new_reservation(&_global_env, &pres->res);
1280 TRACE("GMP_ADD_NEW_RESERVATION R%d\n", pres->res.id);
1281 err = config->id;
1282 } else {
1283 err = -EEXIST;
1284 }
1285 raw_spin_unlock_irqrestore(&_global_env.lock, flags);
1286 }
1287
1288 if (err < 0)
1289 kfree(pres);
1290
1291 return err;
1292}
1293
1294#define MAX_INTERVALS 1024
1295
1296/* create_table_driven_reservation - create a table_driven reservation
1297 */
1298static long create_table_driven_reservation(
1299 struct reservation_config *config)
1300{
1301 struct mc2_cpu_state *state;
1302 struct reservation* res;
1303 struct table_driven_reservation *td_res = NULL;
1304 struct lt_interval *slots = NULL;
1305 size_t slots_size;
1306 unsigned int i, num_slots;
1307 unsigned long flags;
1308 long err = -EINVAL;
1309
1310
1311 if (!config->table_driven_params.num_intervals) {
1312 printk(KERN_ERR "invalid table-driven reservation (%u): "
1313 "no intervals\n", config->id);
1314 return -EINVAL;
1315 }
1316
1317 if (config->table_driven_params.num_intervals > MAX_INTERVALS) {
1318 printk(KERN_ERR "invalid table-driven reservation (%u): "
1319 "too many intervals (max: %d)\n", config->id, MAX_INTERVALS);
1320 return -EINVAL;
1321 }
1322
1323 num_slots = config->table_driven_params.num_intervals;
1324 slots_size = sizeof(slots[0]) * num_slots;
1325 slots = kzalloc(slots_size, GFP_KERNEL);
1326 if (!slots)
1327 return -ENOMEM;
1328
1329 td_res = kzalloc(sizeof(*td_res), GFP_KERNEL);
1330 if (!td_res)
1331 err = -ENOMEM;
1332 else
1333 err = copy_from_user(slots,
1334 config->table_driven_params.intervals, slots_size);
1335
1336 if (!err) {
1337 /* sanity checks */
1338 for (i = 0; !err && i < num_slots; i++)
1339 if (slots[i].end <= slots[i].start) {
1340 printk(KERN_ERR
1341 "invalid table-driven reservation (%u): "
1342 "invalid interval %u => [%llu, %llu]\n",
1343 config->id, i,
1344 slots[i].start, slots[i].end);
1345 err = -EINVAL;
1346 }
1347
1348 for (i = 0; !err && i + 1 < num_slots; i++)
1349 if (slots[i + 1].start <= slots[i].end) {
1350 printk(KERN_ERR
1351 "invalid table-driven reservation (%u): "
1352 "overlapping intervals %u, %u\n",
1353 config->id, i, i + 1);
1354 err = -EINVAL;
1355 }
1356
1357 if (slots[num_slots - 1].end >
1358 config->table_driven_params.major_cycle_length) {
1359 printk(KERN_ERR
1360 "invalid table-driven reservation (%u): last "
1361 "interval ends past major cycle %llu > %llu\n",
1362 config->id,
1363 slots[num_slots - 1].end,
1364 config->table_driven_params.major_cycle_length);
1365 err = -EINVAL;
1366 }
1367 }
1368
1369 if (!err) {
1370 state = cpu_state_for(config->cpu);
1371 raw_spin_lock_irqsave(&state->lock, flags);
1372
1373 res = sup_find_by_id(&state->sup_env, config->id);
1374 if (!res) {
1375 table_driven_reservation_init(td_res,
1376 config->table_driven_params.major_cycle_length,
1377 slots, num_slots);
1378 td_res->res.id = config->id;
1379 td_res->res.priority = config->priority;
1380 td_res->res.blocked_by_ghost = 0;
1381 sup_add_new_reservation(&state->sup_env, &td_res->res);
1382 err = config->id;
1383 } else {
1384 err = -EEXIST;
1385 }
1386
1387 raw_spin_unlock_irqrestore(&state->lock, flags);
1388 }
1389
1390 if (err < 0) {
1391 kfree(slots);
1392 kfree(td_res);
1393 }
1394
1395 return err;
1396}
1397
1398/* mc2_reservation_create - reservation_create system call backend
1399 */
1400static long mc2_reservation_create(int res_type, void* __user _config)
1401{
1402 long ret = -EINVAL;
1403 struct reservation_config config;
1404
1405 TRACE("Attempt to create reservation (%d)\n", res_type);
1406
1407 if (copy_from_user(&config, _config, sizeof(config)))
1408 return -EFAULT;
1409
1410 if (config.cpu != -1) {
1411 if (config.cpu < 0 || !cpu_online(config.cpu)) {
1412 printk(KERN_ERR "invalid polling reservation (%u): "
1413 "CPU %d offline\n", config.id, config.cpu);
1414 return -EINVAL;
1415 }
1416 }
1417
1418 switch (res_type) {
1419 case PERIODIC_POLLING:
1420 case SPORADIC_POLLING:
1421 ret = create_polling_reservation(res_type, &config);
1422 break;
1423
1424 case TABLE_DRIVEN:
1425 ret = create_table_driven_reservation(&config);
1426 break;
1427
1428 default:
1429 return -EINVAL;
1430 };
1431
1432 return ret;
1433}
1434
1435static struct domain_proc_info mc2_domain_proc_info;
1436
1437static long mc2_get_domain_proc_info(struct domain_proc_info **ret)
1438{
1439 *ret = &mc2_domain_proc_info;
1440 return 0;
1441}
1442
1443static void mc2_setup_domain_proc(void)
1444{
1445 int i, cpu;
1446 int num_rt_cpus = num_online_cpus();
1447
1448 struct cd_mapping *cpu_map, *domain_map;
1449
1450 memset(&mc2_domain_proc_info, sizeof(mc2_domain_proc_info), 0);
1451 init_domain_proc_info(&mc2_domain_proc_info, num_rt_cpus, num_rt_cpus);
1452 mc2_domain_proc_info.num_cpus = num_rt_cpus;
1453 mc2_domain_proc_info.num_domains = num_rt_cpus;
1454
1455 i = 0;
1456 for_each_online_cpu(cpu) {
1457 cpu_map = &mc2_domain_proc_info.cpu_to_domains[i];
1458 domain_map = &mc2_domain_proc_info.domain_to_cpus[i];
1459
1460 cpu_map->id = cpu;
1461 domain_map->id = i;
1462 cpumask_set_cpu(i, cpu_map->mask);
1463 cpumask_set_cpu(cpu, domain_map->mask);
1464 ++i;
1465 }
1466}
1467
1468static long mc2_activate_plugin(void)
1469{
1470 int cpu, lv;
1471 struct mc2_cpu_state *state;
1472 struct cpu_entry *ce;
1473
1474 gmp_init(&_global_env);
1475 raw_spin_lock_init(&_lowest_prio_cpu.lock);
1476
1477 for_each_online_cpu(cpu) {
1478 TRACE("Initializing CPU%d...\n", cpu);
1479
1480 state = cpu_state_for(cpu);
1481 ce = &_lowest_prio_cpu.cpu_entries[cpu];
1482
1483 ce->cpu = cpu;
1484 ce->scheduled = NULL;
1485 ce->deadline = ULLONG_MAX;
1486 ce->lv = NUM_CRIT_LEVELS;
1487 ce->will_schedule = false;
1488
1489 raw_spin_lock_init(&state->lock);
1490 state->cpu = cpu;
1491 state->scheduled = NULL;
1492 for (lv = 0; lv < NUM_CRIT_LEVELS; lv++) {
1493 struct crit_entry *cr_entry = &state->crit_entries[lv];
1494 cr_entry->level = lv;
1495 cr_entry->running = NULL;
1496 }
1497 sup_init(&state->sup_env);
1498
1499 hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
1500 state->timer.function = on_scheduling_timer;
1501 }
1502
1503 mc2_setup_domain_proc();
1504
1505 return 0;
1506}
1507
1508static void mc2_finish_switch(struct task_struct *prev)
1509{
1510 struct mc2_cpu_state *state = local_cpu_state();
1511
1512 state->scheduled = is_realtime(current) ? current : NULL;
1513 TRACE("FINISH CXS! from %s/%d to %s/%d\n", prev ? (prev)->comm : "null", prev ? (prev)->pid : 0, current ? (current)->comm : "null", current ? (current)->pid : 0);
1514}
1515
1516static long mc2_deactivate_plugin(void)
1517{
1518 int cpu;
1519 struct mc2_cpu_state *state;
1520 struct reservation *res;
1521 struct next_timer_event *event;
1522 struct cpu_entry *ce;
1523
1524 raw_spin_lock(&_global_env.lock);
1525
1526 for_each_online_cpu(cpu) {
1527 state = cpu_state_for(cpu);
1528 raw_spin_lock(&state->lock);
1529
1530 hrtimer_cancel(&state->timer);
1531
1532 ce = &_lowest_prio_cpu.cpu_entries[cpu];
1533
1534 ce->cpu = cpu;
1535 ce->scheduled = NULL;
1536 ce->deadline = ULLONG_MAX;
1537 ce->lv = NUM_CRIT_LEVELS;
1538 ce->will_schedule = false;
1539
1540 /* Delete all reservations --- assumes struct reservation
1541 * is prefix of containing struct. */
1542
1543 while (!list_empty(&state->sup_env.active_reservations)) {
1544 res = list_first_entry(
1545 &state->sup_env.active_reservations,
1546 struct reservation, list);
1547 list_del(&res->list);
1548 kfree(res);
1549 }
1550
1551 while (!list_empty(&state->sup_env.inactive_reservations)) {
1552 res = list_first_entry(
1553 &state->sup_env.inactive_reservations,
1554 struct reservation, list);
1555 list_del(&res->list);
1556 kfree(res);
1557 }
1558
1559 while (!list_empty(&state->sup_env.depleted_reservations)) {
1560 res = list_first_entry(
1561 &state->sup_env.depleted_reservations,
1562 struct reservation, list);
1563 list_del(&res->list);
1564 kfree(res);
1565 }
1566
1567 raw_spin_unlock(&state->lock);
1568 }
1569
1570
1571 while (!list_empty(&_global_env.active_reservations)) {
1572 TRACE("RES FOUND!!!\n");
1573 res = list_first_entry(
1574 &_global_env.active_reservations,
1575 struct reservation, list);
1576 list_del(&res->list);
1577 kfree(res);
1578 }
1579
1580 while (!list_empty(&_global_env.inactive_reservations)) {
1581 TRACE("RES FOUND!!!\n");
1582 res = list_first_entry(
1583 &_global_env.inactive_reservations,
1584 struct reservation, list);
1585 list_del(&res->list);
1586 kfree(res);
1587 }
1588
1589 while (!list_empty(&_global_env.depleted_reservations)) {
1590 TRACE("RES FOUND!!!\n");
1591 res = list_first_entry(
1592 &_global_env.depleted_reservations,
1593 struct reservation, list);
1594 list_del(&res->list);
1595 kfree(res);
1596 }
1597
1598 while (!list_empty(&_global_env.next_events)) {
1599 TRACE("EVENT FOUND!!!\n");
1600 event = list_first_entry(
1601 &_global_env.next_events,
1602 struct next_timer_event, list);
1603 list_del(&event->list);
1604 kfree(event);
1605 }
1606
1607 raw_spin_unlock(&_global_env.lock);
1608
1609 destroy_domain_proc_info(&mc2_domain_proc_info);
1610 return 0;
1611}
1612
1613static struct sched_plugin mc2_plugin = {
1614 .plugin_name = "MC2",
1615 .schedule = mc2_schedule,
1616 .finish_switch = mc2_finish_switch,
1617 .task_wake_up = mc2_task_resume,
1618 .admit_task = mc2_admit_task,
1619 .task_new = mc2_task_new,
1620 .task_exit = mc2_task_exit,
1621 .complete_job = mc2_complete_job,
1622 .get_domain_proc_info = mc2_get_domain_proc_info,
1623 .activate_plugin = mc2_activate_plugin,
1624 .deactivate_plugin = mc2_deactivate_plugin,
1625 .reservation_create = mc2_reservation_create,
1626 .reservation_destroy = mc2_reservation_destroy,
1627};
1628
1629static int __init init_mc2(void)
1630{
1631 return register_sched_plugin(&mc2_plugin);
1632}
1633
1634module_init(init_mc2);