aboutsummaryrefslogtreecommitdiffstats
path: root/litmus/sched_mc_edf.c
diff options
context:
space:
mode:
Diffstat (limited to 'litmus/sched_mc_edf.c')
-rw-r--r--litmus/sched_mc_edf.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/litmus/sched_mc_edf.c b/litmus/sched_mc_edf.c
new file mode 100644
index 000000000000..bdd518523cff
--- /dev/null
+++ b/litmus/sched_mc_edf.c
@@ -0,0 +1,678 @@
1/*
2 * kernel/sched_mc_edf.c
3 *
4 * Implementation of the MC-EDF scheduler plugin.
5 * Based on kern/sched_part_edf.c and kern/sched_gsn_edf.c.
6 *
7 * Suspensions and non-preemptable sections are supported.
8 * Priority inheritance is not supported.
9 */
10
11#include <linux/percpu.h>
12#include <linux/sched.h>
13#include <linux/list.h>
14#include <linux/spinlock.h>
15#include <linux/module.h>
16
17#include <litmus/litmus.h>
18#include <litmus/jobs.h>
19#include <litmus/preempt.h>
20#include <litmus/sched_plugin.h>
21#include <litmus/edf_common.h>
22#include <litmus/sched_trace.h>
23#include <litmus/trace.h>
24#include <litmus/budget.h>
25
26typedef struct {
27 int curr_cri; // mc-bipasa
28 rt_domain_t high_domain; // mc-bipasa
29 rt_domain_t domain;
30 int cpu;
31 struct task_struct* scheduled; /* only RT tasks */
32/*
33 * scheduling lock slock
34 * protects the domain and serializes scheduling decisions
35 */
36#define slock domain.ready_lock
37
38} mcedf_domain_t;
39
40DEFINE_PER_CPU(mcedf_domain_t, mcedf_domains);
41
42#define local_edf (&__get_cpu_var(mcedf_domains).domain)
43#define local_mcedf (&__get_cpu_var(mcedf_domains))
44#define remote_edf(cpu) (&per_cpu(mcedf_domains, cpu).domain)
45#define remote_mcedf(cpu) (&per_cpu(mcedf_domains, cpu))
46#define task_edf(task) remote_edf(get_partition(task))
47#define task_mcedf(task) remote_mcedf(get_partition(task))//mc-bipasa always 0
48
49
50static void mcedf_domain_init(mcedf_domain_t* mcedf,
51 check_resched_needed_t check,
52 release_jobs_t release,
53 int cpu)
54{
55 // high >= 1, low = 0
56 mcedf->curr_cri = 0;
57 edf_mc_domain_init(&mcedf->high_domain, check, release, 1); // mc-bipasa
58 edf_mc_domain_init(&mcedf->domain, check, release, 0); // mc-bipasa
59 mcedf->cpu = cpu;
60 mcedf->scheduled = NULL;
61}
62
63static void requeue(struct task_struct* t, rt_domain_t *edf)
64{
65 if (t->state != TASK_RUNNING)
66 TRACE_TASK(t, "requeue: !TASK_RUNNING\n");
67
68 set_rt_flags(t, RT_F_RUNNING);
69 if (is_released(t, litmus_clock()))
70 __add_ready(edf, t);
71 else
72 add_release(edf, t); /* it has got to wait */
73}
74
75/* we assume the lock is being held */
76static void preempt(mcedf_domain_t *mcedf)
77{
78 preempt_if_preemptable(mcedf->scheduled, mcedf->cpu);
79}
80
81#ifdef CONFIG_LITMUS_LOCKING
82
83static void boost_priority(struct task_struct* t)
84{
85 unsigned long flags;
86 mcedf_domain_t* mcedf = task_mcedf(t);
87 lt_t now;
88
89 raw_spin_lock_irqsave(&mcedf->slock, flags);
90 now = litmus_clock();
91
92 TRACE_TASK(t, "priority boosted at %llu\n", now);
93
94 tsk_rt(t)->priority_boosted = 1;
95 tsk_rt(t)->boost_start_time = now;
96
97 if (mcedf->scheduled != t) {
98 /* holder may be queued: first stop queue changes */
99 raw_spin_lock(&mcedf->domain.release_lock);
100 if (is_queued(t) &&
101 /* If it is queued, then we need to re-order. */
102 bheap_decrease(edf_ready_order, tsk_rt(t)->heap_node) &&
103 /* If we bubbled to the top, then we need to check for preemptions. */
104 edf_preemption_needed(&mcedf->domain, mcedf->scheduled))
105 preempt(mcedf);
106 raw_spin_unlock(&mcedf->domain.release_lock);
107 } /* else: nothing to do since the job is not queued while scheduled */
108
109 raw_spin_unlock_irqrestore(&mcedf->slock, flags);
110}
111
112static void unboost_priority(struct task_struct* t)
113{
114 unsigned long flags;
115 mcedf_domain_t* mcedf = task_mcedf(t);
116 lt_t now;
117
118 raw_spin_lock_irqsave(&mcedf->slock, flags);
119 now = litmus_clock();
120
121 /* assumption: this only happens when the job is scheduled */
122 BUG_ON(mcedf->scheduled != t);
123
124 TRACE_TASK(t, "priority restored at %llu\n", now);
125
126 /* priority boosted jobs must be scheduled */
127 BUG_ON(mcedf->scheduled != t);
128
129 tsk_rt(t)->priority_boosted = 0;
130 tsk_rt(t)->boost_start_time = 0;
131
132 /* check if this changes anything */
133 if (edf_preemption_needed(&mcedf->domain, mcedf->scheduled))
134 preempt(mcedf);
135
136 raw_spin_unlock_irqrestore(&mcedf->slock, flags);
137}
138
139#endif
140
141/* This check is trivial in partioned systems as we only have to consider
142 * the CPU of the partition.
143 */
144static int mcedf_check_resched(rt_domain_t *edf)
145{
146 mcedf_domain_t *mcedf = container_of(edf, mcedf_domain_t, domain);
147
148 /* because this is a callback from rt_domain_t we already hold
149 * the necessary lock for the ready queue
150 */
151 if (edf_preemption_needed(edf, mcedf->scheduled)) {
152 preempt(mcedf);
153 return 1;
154 } else
155 return 0;
156}
157
158static void job_completion(struct task_struct* t, int forced)
159{
160 sched_trace_task_completion(t,forced);
161 TRACE_TASK(t, "job_completion().\n");
162
163 set_rt_flags(t, RT_F_SLEEP);
164
165 prepare_for_next_period(t); // check
166 /*if (forced) {
167 server_release(t);
168 } else {
169 task_release(t);
170 }*/
171}
172
173static void mcedf_tick(struct task_struct *t)
174{
175 mcedf_domain_t *mcedf = local_mcedf;
176
177 /* Check for inconsistency. We don't need the lock for this since
178 * ->scheduled is only changed in schedule, which obviously is not
179 * executing in parallel on this CPU
180 */
181 BUG_ON(is_realtime(t) && t != mcedf->scheduled);
182
183 if (is_realtime(t) && budget_enforced(t) && budget_exhausted(t)) {
184 if (!is_np(t)) {
185 litmus_reschedule_local();
186 TRACE("mcedf_scheduler_tick: "
187 "%d is preemptable "
188 " => FORCE_RESCHED\n", t->pid);
189 } else if (is_user_np(t)) {
190 TRACE("mcedf_scheduler_tick: "
191 "%d is non-preemptable, "
192 "preemption delayed.\n", t->pid);
193 request_exit_np(t);
194 }
195 }
196}
197
198static struct task_struct* mcedf_schedule(struct task_struct * prev)
199{
200 mcedf_domain_t* mcedf = local_mcedf;
201 rt_domain_t* edf = &mcedf->domain;
202 struct task_struct* next;
203
204 int out_of_time, sleep, preempt,
205 np, exists, blocks, resched;
206
207 raw_spin_lock(&mcedf->slock);
208
209 /* sanity checking
210 * differently from gedf, when a task exits (dead)
211 * mcedf->schedule may be null and prev _is_ realtime
212 */
213 BUG_ON(mcedf->scheduled && mcedf->scheduled != prev);
214 BUG_ON(mcedf->scheduled && !is_realtime(prev));
215
216 /* (0) Determine state */
217 exists = mcedf->scheduled != NULL;
218 blocks = exists && !is_running(mcedf->scheduled);
219 out_of_time = exists &&
220 budget_enforced(mcedf->scheduled) &&
221 budget_exhausted(mcedf->scheduled);
222 np = exists && is_np(mcedf->scheduled);
223 sleep = exists && get_rt_flags(mcedf->scheduled) == RT_F_SLEEP;
224 preempt = edf_preemption_needed(edf, prev);
225
226 /* If we need to preempt do so.
227 * The following checks set resched to 1 in case of special
228 * circumstances.
229 */
230 resched = preempt;
231
232 /* If a task blocks we have no choice but to reschedule.
233 */
234 if (blocks)
235 resched = 1;
236
237 /* Request a sys_exit_np() call if we would like to preempt but cannot.
238 * Multiple calls to request_exit_np() don't hurt.
239 */
240 if (np && (out_of_time || preempt || sleep))
241 request_exit_np(mcedf->scheduled);
242
243 // prev code
244 /* Any task that is preemptable and either exhausts its execution
245 * budget or wants to sleep completes. We may have to reschedule after
246 * this.
247 */
248 /*if (!np && (out_of_time || sleep) && !blocks) {
249 job_completion(mcedf->scheduled, !sleep);
250 resched = 1;
251 }*/
252
253 /* Any task that is premptable and either exhausts its execution
254 * budget or wants to sleep. We have to reschedule after this.
255 */
256
257 if (!np && (out_of_time || sleep) && !blocks){
258 /* This causes a switch to high criticality*/
259 if (out_of_time){
260 TRACE_TASK(prev, "Ran out of budget at %llu. Switching to high criticality\n", litmus_clock());
261 mcedf->curr_cri = 1;
262 TRACE("After this point no low criticality jobs must be scheduled\n");
263 }
264 //if (sleep){
265 job_completion(mcedf->scheduled, !sleep);
266 //}
267 resched = 1;
268 }
269
270 /* The final scheduling decision. Do we need to switch for some reason?
271 * Switch if we are in RT mode and have no task or if we need to
272 * resched.
273 */
274 next = NULL;
275 if ((!np || blocks) && (resched || !exists)) {
276 /* When preempting a task that does not block, then
277 * re-insert it into either the ready queue or the
278 * release queue (if it completed). requeue() picks
279 * the appropriate queue.
280 */
281 if (mcedf->scheduled && !blocks)
282 requeue(mcedf->scheduled, edf);
283 next = __take_ready(edf);
284 } else
285 /* Only override Linux scheduler if we have a real-time task
286 * scheduled that needs to continue.
287 */
288 if (exists)
289 next = prev;
290
291 if (next) {
292 TRACE_TASK(next, "scheduled at %llu\n", litmus_clock());
293 set_rt_flags(next, RT_F_RUNNING);
294 } else {
295 TRACE("becoming idle at %llu\n", litmus_clock());
296 }
297
298 mcedf->scheduled = next;
299 sched_state_task_picked();
300 raw_spin_unlock(&mcedf->slock);
301
302 return next;
303}
304
305
306/* Prepare a task for running in RT mode
307 */
308static void mcedf_task_new(struct task_struct * t, int on_rq, int running)
309{
310 rt_domain_t* edf = task_edf(t);
311 mcedf_domain_t* mcedf = task_mcedf(t);
312 unsigned long flags;
313
314 TRACE_TASK(t, "mc edf: task new, cpu = %d\n",
315 t->rt_param.task_params.cpu);
316 TRACE_TASK(t, "mc edf: task new, criticality = %d\n",
317 t->rt_param.task_params.cri); // mc-bipasa
318
319 /* setup job parameters */
320 release_at(t, litmus_clock());
321
322 /* The task should be running in the queue, otherwise signal
323 * code will try to wake it up with fatal consequences.
324 */
325 raw_spin_lock_irqsave(&mcedf->slock, flags);
326 if (running) {
327 /* there shouldn't be anything else running at the time */
328 BUG_ON(mcedf->scheduled);
329 mcedf->scheduled = t;
330 } else {
331 requeue(t, edf);
332 /* maybe we have to reschedule */
333 preempt(mcedf);
334 }
335 raw_spin_unlock_irqrestore(&mcedf->slock, flags);
336}
337
338static void mcedf_task_wake_up(struct task_struct *task)
339{
340 unsigned long flags;
341 mcedf_domain_t* mcedf = task_mcedf(task);
342 rt_domain_t* edf = task_edf(task);
343 lt_t now;
344
345 TRACE_TASK(task, "wake_up at %llu\n", litmus_clock());
346 raw_spin_lock_irqsave(&mcedf->slock, flags);
347 BUG_ON(is_queued(task));
348 now = litmus_clock();
349 if (is_tardy(task, now)
350#ifdef CONFIG_LITMUS_LOCKING
351 /* We need to take suspensions because of semaphores into
352 * account! If a job resumes after being suspended due to acquiring
353 * a semaphore, it should never be treated as a new job release.
354 */
355 && !is_priority_boosted(task)
356#endif
357 ) {
358 /* new sporadic release */
359 release_at(task, now);
360 sched_trace_task_release(task);
361 }
362
363 /* Only add to ready queue if it is not the currently-scheduled
364 * task. This could be the case if a task was woken up concurrently
365 * on a remote CPU before the executing CPU got around to actually
366 * de-scheduling the task, i.e., wake_up() raced with schedule()
367 * and won.
368 */
369 if (mcedf->scheduled != task)
370 requeue(task, edf);
371
372 raw_spin_unlock_irqrestore(&mcedf->slock, flags);
373 TRACE_TASK(task, "wake up done\n");
374}
375
376static void mcedf_task_block(struct task_struct *t)
377{
378 /* only running tasks can block, thus t is in no queue */
379 TRACE_TASK(t, "block at %llu, state=%d\n", litmus_clock(), t->state);
380
381 BUG_ON(!is_realtime(t));
382 BUG_ON(is_queued(t));
383}
384
385static void mcedf_task_exit(struct task_struct * t)
386{
387 unsigned long flags;
388 mcedf_domain_t* mcedf = task_mcedf(t);
389 rt_domain_t* edf;
390
391 raw_spin_lock_irqsave(&mcedf->slock, flags);
392 if (is_queued(t)) {
393 /* dequeue */
394 edf = task_edf(t);
395 remove(edf, t);
396 }
397 if (mcedf->scheduled == t)
398 mcedf->scheduled = NULL;
399
400 TRACE_TASK(t, "RIP, now reschedule\n");
401
402 preempt(mcedf);
403 raw_spin_unlock_irqrestore(&mcedf->slock, flags);
404}
405// I don't need to following for my work
406#ifdef CONFIG_LITMUS_LOCKING
407
408#include <litmus/fdso.h>
409#include <litmus/srp.h>
410
411/* ******************** SRP support ************************ */
412
413static unsigned int mcedf_get_srp_prio(struct task_struct* t)
414{
415 /* assumes implicit deadlines */
416 return get_rt_period(t);
417}
418
419/* ******************** FMLP support ********************** */
420
421/* struct for semaphore with priority inheritance */
422struct fmlp_semaphore {
423 struct litmus_lock litmus_lock;
424
425 /* current resource holder */
426 struct task_struct *owner;
427
428 /* FIFO queue of waiting tasks */
429 wait_queue_head_t wait;
430};
431
432static inline struct fmlp_semaphore* fmlp_from_lock(struct litmus_lock* lock)
433{
434 return container_of(lock, struct fmlp_semaphore, litmus_lock);
435}
436int mcedf_fmlp_lock(struct litmus_lock* l)
437{
438 struct task_struct* t = current;
439 struct fmlp_semaphore *sem = fmlp_from_lock(l);
440 wait_queue_t wait;
441 unsigned long flags;
442
443 if (!is_realtime(t))
444 return -EPERM;
445
446 spin_lock_irqsave(&sem->wait.lock, flags);
447
448 if (sem->owner) {
449 /* resource is not free => must suspend and wait */
450
451 init_waitqueue_entry(&wait, t);
452
453 /* FIXME: interruptible would be nice some day */
454 set_task_state(t, TASK_UNINTERRUPTIBLE);
455
456 __add_wait_queue_tail_exclusive(&sem->wait, &wait);
457
458 TS_LOCK_SUSPEND;
459
460 /* release lock before sleeping */
461 spin_unlock_irqrestore(&sem->wait.lock, flags);
462
463 /* We depend on the FIFO order. Thus, we don't need to recheck
464 * when we wake up; we are guaranteed to have the lock since
465 * there is only one wake up per release.
466 */
467
468 schedule();
469
470 TS_LOCK_RESUME;
471
472 /* Since we hold the lock, no other task will change
473 * ->owner. We can thus check it without acquiring the spin
474 * lock. */
475 BUG_ON(sem->owner != t);
476 } else {
477 /* it's ours now */
478 sem->owner = t;
479
480 /* mark the task as priority-boosted. */
481 boost_priority(t);
482
483 spin_unlock_irqrestore(&sem->wait.lock, flags);
484 }
485
486 return 0;
487}
488
489int mcedf_fmlp_unlock(struct litmus_lock* l)
490{
491 struct task_struct *t = current, *next;
492 struct fmlp_semaphore *sem = fmlp_from_lock(l);
493 unsigned long flags;
494 int err = 0;
495
496 spin_lock_irqsave(&sem->wait.lock, flags);
497
498 if (sem->owner != t) {
499 err = -EINVAL;
500 goto out;
501 }
502
503 /* we lose the benefit of priority boosting */
504
505 unboost_priority(t);
506
507 /* check if there are jobs waiting for this resource */
508 next = __waitqueue_remove_first(&sem->wait);
509 if (next) {
510 /* boost next job */
511 boost_priority(next);
512
513 /* next becomes the resouce holder */
514 sem->owner = next;
515
516 /* wake up next */
517 wake_up_process(next);
518 } else
519 /* resource becomes available */
520 sem->owner = NULL;
521
522out:
523 spin_unlock_irqrestore(&sem->wait.lock, flags);
524 return err;
525}
526
527int mcedf_fmlp_close(struct litmus_lock* l)
528{
529 struct task_struct *t = current;
530 struct fmlp_semaphore *sem = fmlp_from_lock(l);
531 unsigned long flags;
532
533 int owner;
534
535 spin_lock_irqsave(&sem->wait.lock, flags);
536
537 owner = sem->owner == t;
538
539 spin_unlock_irqrestore(&sem->wait.lock, flags);
540
541 if (owner)
542 mcedf_fmlp_unlock(l);
543
544 return 0;
545}
546
547void mcedf_fmlp_free(struct litmus_lock* lock)
548{
549 kfree(fmlp_from_lock(lock));
550}
551
552static struct litmus_lock_ops mcedf_fmlp_lock_ops = {
553 .close = mcedf_fmlp_close,
554 .lock = mcedf_fmlp_lock,
555 .unlock = mcedf_fmlp_unlock,
556 .deallocate = mcedf_fmlp_free,
557};
558
559static struct litmus_lock* mcedf_new_fmlp(void)
560{
561 struct fmlp_semaphore* sem;
562
563 sem = kmalloc(sizeof(*sem), GFP_KERNEL);
564 if (!sem)
565 return NULL;
566
567 sem->owner = NULL;
568 init_waitqueue_head(&sem->wait);
569 sem->litmus_lock.ops = &mcedf_fmlp_lock_ops;
570
571 return &sem->litmus_lock;
572}
573
574/* **** lock constructor **** */
575
576
577static long mcedf_allocate_lock(struct litmus_lock **lock, int type,
578 void* __user unused)
579{
580 int err = -ENXIO;
581 struct srp_semaphore* srp;
582
583 /* MC-EDF currently supports the SRP for local resources and the FMLP
584 * for global resources. */
585 switch (type) {
586 case FMLP_SEM:
587 /* Flexible Multiprocessor Locking Protocol */
588 *lock = mcedf_new_fmlp();
589 if (*lock)
590 err = 0;
591 else
592 err = -ENOMEM;
593 break;
594
595 case SRP_SEM:
596 /* Baker's Stack Resource Policy */
597 srp = allocate_srp_semaphore();
598 if (srp) {
599 *lock = &srp->litmus_lock;
600 err = 0;
601 } else
602 err = -ENOMEM;
603 break;
604 };
605
606 return err;
607}
608
609#endif
610
611
612static long mcedf_activate_plugin(void)
613{
614#ifdef CONFIG_RELEASE_MASTER
615 int cpu;
616
617 for_each_online_cpu(cpu) {
618 remote_edf(cpu)->release_master = atomic_read(&release_master_cpu);
619 }
620#endif
621
622#ifdef CONFIG_LITMUS_LOCKING
623 get_srp_prio = mcedf_get_srp_prio;
624#endif
625
626 return 0;
627}
628
629static long mcedf_admit_task(struct task_struct* tsk)
630{
631 if (task_cpu(tsk) == tsk->rt_param.task_params.cpu
632#ifdef CONFIG_RELEASE_MASTER
633 /* don't allow tasks on release master CPU */
634 && task_cpu(tsk) != remote_edf(task_cpu(tsk))->release_master
635#endif
636 )
637 return 0;
638 else
639 return -EINVAL;
640}
641
642/* Plugin object */
643static struct sched_plugin mc_edf_plugin __cacheline_aligned_in_smp = {
644 .plugin_name = "MC-EDF",
645 .tick = mcedf_tick,
646 .task_new = mcedf_task_new,
647 .complete_job = complete_job,
648 .task_exit = mcedf_task_exit,
649 .schedule = mcedf_schedule,
650 .task_wake_up = mcedf_task_wake_up,
651 .task_block = mcedf_task_block,
652 .admit_task = mcedf_admit_task,
653 .activate_plugin = mcedf_activate_plugin,
654#ifdef CONFIG_LITMUS_LOCKING
655 .allocate_lock = mcedf_allocate_lock,
656#endif
657};
658
659
660static int __init init_mc_edf(void)
661{
662 int i;
663
664 /* We do not really want to support cpu hotplug, do we? ;)
665 * However, if we are so crazy to do so,
666 * we cannot use num_online_cpu()
667 */
668 printk("Hello Mixed Criticality\n");
669 for (i = 0; i < num_online_cpus(); i++) {
670 mcedf_domain_init(remote_mcedf(i),
671 mcedf_check_resched,
672 NULL, i);
673 }
674 return register_sched_plugin(&mc_edf_plugin);
675}
676
677module_init(init_mc_edf);
678