aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Brandenburg <bbb@mpi-sws.org>2014-06-14 11:16:06 -0400
committerBjoern Brandenburg <bbb@mpi-sws.org>2014-07-21 09:11:12 -0400
commit7a597c5c855bbf8efd9859ab23d9604fac59624e (patch)
tree313c2f385766c97b4ad88348d157b7ad6545fdb2
parent99ca08e2bd92b7957c9a22a8ec6ecad4d7f4afd3 (diff)
Add partitioned reservation-based scheduler plugin (P-RES)
A simple partitioned scheduler that provides a reservation environment on each core, based on the generic reservations code. Hierarchical scheduling is not supported in this version.
-rw-r--r--litmus/Makefile2
-rw-r--r--litmus/sched_pres.c631
2 files changed, 633 insertions, 0 deletions
diff --git a/litmus/Makefile b/litmus/Makefile
index e3439c88e1b1..05021f553eda 100644
--- a/litmus/Makefile
+++ b/litmus/Makefile
@@ -34,3 +34,5 @@ obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o
34obj-$(CONFIG_SCHED_OVERHEAD_TRACE) += trace.o 34obj-$(CONFIG_SCHED_OVERHEAD_TRACE) += trace.o
35 35
36obj-y += reservation.o polling_reservations.o 36obj-y += reservation.o polling_reservations.o
37
38obj-y += sched_pres.o \ No newline at end of file
diff --git a/litmus/sched_pres.c b/litmus/sched_pres.c
new file mode 100644
index 000000000000..6779ffd16dae
--- /dev/null
+++ b/litmus/sched_pres.c
@@ -0,0 +1,631 @@
1#include <linux/percpu.h>
2#include <linux/slab.h>
3#include <asm/uaccess.h>
4
5#include <litmus/sched_plugin.h>
6#include <litmus/preempt.h>
7#include <litmus/debug_trace.h>
8
9#include <litmus/litmus.h>
10#include <litmus/jobs.h>
11#include <litmus/budget.h>
12#include <litmus/litmus_proc.h>
13
14#include <litmus/reservation.h>
15#include <litmus/polling_reservations.h>
16
17struct pres_task_state {
18 struct task_client res_info;
19 int cpu;
20};
21
22struct pres_cpu_state {
23 raw_spinlock_t lock;
24
25 struct sup_reservation_environment sup_env;
26 struct hrtimer timer;
27
28 int cpu;
29 struct task_struct* scheduled;
30};
31
32static DEFINE_PER_CPU(struct pres_cpu_state, pres_cpu_state);
33
34#define cpu_state_for(cpu_id) (&per_cpu(pres_cpu_state, cpu_id))
35#define local_cpu_state() (&__get_cpu_var(pres_cpu_state))
36
37static struct pres_task_state* get_pres_state(struct task_struct *tsk)
38{
39 return (struct pres_task_state*) tsk_rt(tsk)->plugin_state;
40}
41
42static void task_departs(struct task_struct *tsk, int job_complete)
43{
44 struct pres_task_state* state = get_pres_state(tsk);
45 struct reservation* res;
46 struct reservation_client *client;
47
48 res = state->res_info.reservation;
49 client = &state->res_info.client;
50
51 res->ops->client_departs(res, client, job_complete);
52}
53
54static void task_arrives(struct task_struct *tsk)
55{
56 struct pres_task_state* state = get_pres_state(tsk);
57 struct reservation* res;
58 struct reservation_client *client;
59
60 res = state->res_info.reservation;
61 client = &state->res_info.client;
62
63 res->ops->client_arrives(res, client);
64}
65
66static void pres_update_timer(struct pres_cpu_state *state)
67{
68 lt_t update, now;
69
70 update = state->sup_env.next_scheduler_update;
71 now = state->sup_env.env.current_time;
72 if (update <= now) {
73 litmus_reschedule(state->cpu);
74 } else if (update != SUP_NO_SCHEDULER_UPDATE) {
75 /* reprogram only if not already set correctly */
76 if (!hrtimer_active(&state->timer) ||
77 ktime_to_ns(hrtimer_get_expires(&state->timer)) != update) {
78 TRACE("canceling timer...\n");
79 hrtimer_cancel(&state->timer);
80 TRACE("setting scheduler timer for %llu\n", update);
81 hrtimer_start(&state->timer, ns_to_ktime(update),
82 HRTIMER_MODE_ABS_PINNED);
83 }
84 }
85}
86
87static enum hrtimer_restart on_scheduling_timer(struct hrtimer *timer)
88{
89 unsigned long flags;
90 enum hrtimer_restart restart = HRTIMER_NORESTART;
91 struct pres_cpu_state *state = local_cpu_state();
92 lt_t update, now;
93
94 raw_spin_lock_irqsave(&state->lock, flags);
95 sup_update_time(&state->sup_env, litmus_clock());
96
97 update = state->sup_env.next_scheduler_update;
98 now = state->sup_env.env.current_time;
99
100 TRACE_CUR("on_scheduling_timer at %llu, upd:%llu\n", now, update);
101
102 if (update <= now) {
103 litmus_reschedule_local();
104 } else if (update != SUP_NO_SCHEDULER_UPDATE) {
105 hrtimer_set_expires(timer, ns_to_ktime(update));
106 restart = HRTIMER_RESTART;
107 }
108
109 raw_spin_unlock_irqrestore(&state->lock, flags);
110
111 return restart;
112}
113
114static struct task_struct* pres_schedule(struct task_struct * prev)
115{
116 /* next == NULL means "schedule background work". */
117 struct pres_cpu_state *state = local_cpu_state();
118
119 raw_spin_lock(&state->lock);
120
121 BUG_ON(state->scheduled && state->scheduled != prev);
122 BUG_ON(state->scheduled && !is_realtime(prev));
123
124 /* update time */
125 state->sup_env.will_schedule = true;
126 sup_update_time(&state->sup_env, litmus_clock());
127
128 /* remove task from reservation if it blocks */
129 if (is_realtime(prev) && !is_running(prev))
130 task_departs(prev, is_completed(prev));
131
132 /* figure out what to schedule next */
133 state->scheduled = sup_dispatch(&state->sup_env);
134
135 /* program scheduler timer */
136 state->sup_env.will_schedule = false;
137 pres_update_timer(state);
138
139 /* Notify LITMUS^RT core that we've arrived at a scheduling decision. */
140 sched_state_task_picked();
141
142 raw_spin_unlock(&state->lock);
143
144 if (prev != state->scheduled && is_realtime(prev))
145 TRACE_TASK(prev, "descheduled.\n");
146 if (state->scheduled)
147 TRACE_TASK(state->scheduled, "scheduled.\n");
148
149 return state->scheduled;
150}
151
152static void resume_legacy_task_model_updates(struct task_struct *tsk)
153{
154 lt_t now;
155 if (is_sporadic(tsk)) {
156 /* If this sporadic task was gone for a "long" time and woke up past
157 * its deadline, then give it a new budget by triggering a job
158 * release. This is purely cosmetic and has no effect on the
159 * P-RES scheduler. */
160
161 now = litmus_clock();
162 if (is_tardy(tsk, now))
163 release_at(tsk, now);
164 }
165}
166
167/* Called when the state of tsk changes back to TASK_RUNNING.
168 * We need to requeue the task.
169 */
170static void pres_task_resume(struct task_struct *tsk)
171{
172 unsigned long flags;
173 struct pres_task_state* tinfo = get_pres_state(tsk);
174 struct pres_cpu_state *state = cpu_state_for(tinfo->cpu);
175
176 TRACE_TASK(tsk, "wake_up at %llu\n", litmus_clock());
177
178 raw_spin_lock_irqsave(&state->lock, flags);
179 /* Requeue if self-suspension was already processed. */
180 if (state->scheduled != tsk)
181 {
182 sup_update_time(&state->sup_env, litmus_clock());
183 task_arrives(tsk);
184 pres_update_timer(state);
185 }
186 raw_spin_unlock_irqrestore(&state->lock, flags);
187
188 resume_legacy_task_model_updates(tsk);
189}
190
191/* syscall backend for job completions */
192static long pres_complete_job(void)
193{
194 ktime_t next_release;
195 long err;
196
197 TRACE_CUR("pres_complete_job at %llu\n", litmus_clock());
198
199 tsk_rt(current)->completed = 1;
200 prepare_for_next_period(current);
201 next_release = ns_to_ktime(get_release(current));
202 set_current_state(TASK_INTERRUPTIBLE);
203 err = schedule_hrtimeout(&next_release, HRTIMER_MODE_ABS);
204
205 TRACE_CUR("pres_complete_job returns at %llu\n", litmus_clock());
206 return err;
207}
208
209static long pres_admit_task(struct task_struct *tsk)
210{
211 long err = -ESRCH;
212 unsigned long flags;
213 struct reservation *res;
214 struct pres_cpu_state *state;
215 struct pres_task_state *tinfo = kzalloc(sizeof(*tinfo), GFP_KERNEL);
216
217 if (!tinfo)
218 return -ENOMEM;
219
220 preempt_disable();
221
222 state = cpu_state_for(task_cpu(tsk));
223 raw_spin_lock_irqsave(&state->lock, flags);
224
225 res = sup_find_by_id(&state->sup_env, tsk_rt(tsk)->task_params.cpu);
226
227 /* found the appropriate reservation (or vCPU) */
228 if (res) {
229 task_client_init(&tinfo->res_info, tsk, res);
230 tinfo->cpu = task_cpu(tsk);
231 tsk_rt(tsk)->plugin_state = tinfo;
232 err = 0;
233 }
234
235 raw_spin_unlock_irqrestore(&state->lock, flags);
236
237 preempt_enable();
238
239 if (err)
240 kfree(tinfo);
241
242 return err;
243}
244
245static void task_new_legacy_task_model_updates(struct task_struct *tsk)
246{
247 lt_t now = litmus_clock();
248
249 /* the first job exists starting as of right now */
250 release_at(tsk, now);
251}
252
253static void pres_task_new(struct task_struct *tsk, int on_runqueue,
254 int is_running)
255{
256 unsigned long flags;
257 struct pres_task_state* tinfo = get_pres_state(tsk);
258 struct pres_cpu_state *state = cpu_state_for(tinfo->cpu);
259
260 TRACE_TASK(tsk, "new RT task %llu (on_rq:%d, running:%d)\n",
261 litmus_clock(), on_runqueue, is_running);
262
263 /* acquire the lock protecting the state and disable interrupts */
264 raw_spin_lock_irqsave(&state->lock, flags);
265
266 if (is_running) {
267 state->scheduled = tsk;
268 /* make sure this task should actually be running */
269 litmus_reschedule_local();
270 }
271
272 if (on_runqueue || is_running) {
273 sup_update_time(&state->sup_env, litmus_clock());
274 task_arrives(tsk);
275 pres_update_timer(state);
276 }
277
278 raw_spin_unlock_irqrestore(&state->lock, flags);
279
280 task_new_legacy_task_model_updates(tsk);
281}
282
283static void pres_task_exit(struct task_struct *tsk)
284{
285 unsigned long flags;
286 struct pres_task_state* tinfo = get_pres_state(tsk);
287 struct pres_cpu_state *state = cpu_state_for(tinfo->cpu);
288
289 raw_spin_lock_irqsave(&state->lock, flags);
290
291 if (state->scheduled == tsk)
292 state->scheduled = NULL;
293
294 /* remove from queues */
295 if (is_running(tsk)) {
296 sup_update_time(&state->sup_env, litmus_clock());
297 task_departs(tsk, 0);
298 pres_update_timer(state);
299 }
300
301 raw_spin_unlock_irqrestore(&state->lock, flags);
302
303 kfree(tsk_rt(tsk)->plugin_state);
304 tsk_rt(tsk)->plugin_state = NULL;
305}
306
307static long create_polling_reservation(
308 int res_type,
309 struct reservation_config *config)
310{
311 struct pres_cpu_state *state;
312 struct reservation* res;
313 struct polling_reservation *pres;
314 unsigned long flags;
315 int use_edf = config->priority == LITMUS_NO_PRIORITY;
316 int periodic = res_type == PERIODIC_POLLING;
317 long err = -EINVAL;
318
319 if (config->polling_params.budget >
320 config->polling_params.period) {
321 printk(KERN_ERR "invalid polling reservation (%u): "
322 "budget > period\n", config->id);
323 return -EINVAL;
324 }
325 if (config->polling_params.budget >
326 config->polling_params.relative_deadline
327 && config->polling_params.relative_deadline) {
328 printk(KERN_ERR "invalid polling reservation (%u): "
329 "budget > deadline\n", config->id);
330 return -EINVAL;
331 }
332 if (config->polling_params.offset >
333 config->polling_params.period) {
334 printk(KERN_ERR "invalid polling reservation (%u): "
335 "offset > period\n", config->id);
336 return -EINVAL;
337 }
338
339 /* Allocate before we grab a spin lock.
340 * Todo: would be nice to use a core-local allocation.
341 */
342 pres = kzalloc(sizeof(*pres), GFP_KERNEL);
343 if (!pres)
344 return -ENOMEM;
345
346 state = cpu_state_for(config->cpu);
347 raw_spin_lock_irqsave(&state->lock, flags);
348
349 res = sup_find_by_id(&state->sup_env, config->id);
350 if (!res) {
351 polling_reservation_init(pres, use_edf, periodic,
352 config->polling_params.budget,
353 config->polling_params.period,
354 config->polling_params.relative_deadline,
355 config->polling_params.offset);
356 pres->res.id = config->id;
357 if (!use_edf)
358 pres->res.priority = config->priority;
359 sup_add_new_reservation(&state->sup_env, &pres->res);
360 err = config->id;
361 } else {
362 err = -EEXIST;
363 }
364
365 raw_spin_unlock_irqrestore(&state->lock, flags);
366
367 if (err < 0)
368 kfree(pres);
369
370 return err;
371}
372
373#define MAX_INTERVALS 1024
374
375static long create_table_driven_reservation(
376 struct reservation_config *config)
377{
378 struct pres_cpu_state *state;
379 struct reservation* res;
380 struct table_driven_reservation *td_res = NULL;
381 struct lt_interval *slots = NULL;
382 size_t slots_size;
383 unsigned int i, num_slots;
384 unsigned long flags;
385 long err = -EINVAL;
386
387
388 if (!config->table_driven_params.num_intervals) {
389 printk(KERN_ERR "invalid table-driven reservation (%u): "
390 "no intervals\n", config->id);
391 return -EINVAL;
392 }
393
394 if (config->table_driven_params.num_intervals > MAX_INTERVALS) {
395 printk(KERN_ERR "invalid table-driven reservation (%u): "
396 "too many intervals (max: %d)\n", config->id, MAX_INTERVALS);
397 return -EINVAL;
398 }
399
400 num_slots = config->table_driven_params.num_intervals;
401 slots_size = sizeof(slots[0]) * num_slots;
402 slots = kzalloc(slots_size, GFP_KERNEL);
403 if (!slots)
404 return -ENOMEM;
405
406 td_res = kzalloc(sizeof(*td_res), GFP_KERNEL);
407 if (!td_res)
408 err = -ENOMEM;
409 else
410 err = copy_from_user(slots,
411 config->table_driven_params.intervals, slots_size);
412
413 if (!err) {
414 /* sanity checks */
415 for (i = 0; !err && i < num_slots; i++)
416 if (slots[i].end <= slots[i].start) {
417 printk(KERN_ERR
418 "invalid table-driven reservation (%u): "
419 "invalid interval %u => [%llu, %llu]\n",
420 config->id, i,
421 slots[i].start, slots[i].end);
422 err = -EINVAL;
423 }
424
425 for (i = 0; !err && i + 1 < num_slots; i++)
426 if (slots[i + 1].start <= slots[i].end) {
427 printk(KERN_ERR
428 "invalid table-driven reservation (%u): "
429 "overlapping intervals %u, %u\n",
430 config->id, i, i + 1);
431 err = -EINVAL;
432 }
433
434 if (slots[num_slots - 1].end >
435 config->table_driven_params.major_cycle_length) {
436 printk(KERN_ERR
437 "invalid table-driven reservation (%u): last "
438 "interval ends past major cycle %llu > %llu\n",
439 config->id,
440 slots[num_slots - 1].end,
441 config->table_driven_params.major_cycle_length);
442 err = -EINVAL;
443 }
444 }
445
446 if (!err) {
447 state = cpu_state_for(config->cpu);
448 raw_spin_lock_irqsave(&state->lock, flags);
449
450 res = sup_find_by_id(&state->sup_env, config->id);
451 if (!res) {
452 table_driven_reservation_init(td_res,
453 config->table_driven_params.major_cycle_length,
454 slots, num_slots);
455 td_res->res.id = config->id;
456 td_res->res.priority = config->priority;
457 sup_add_new_reservation(&state->sup_env, &td_res->res);
458 err = config->id;
459 } else {
460 err = -EEXIST;
461 }
462
463 raw_spin_unlock_irqrestore(&state->lock, flags);
464 }
465
466 if (err < 0) {
467 kfree(slots);
468 kfree(td_res);
469 }
470
471 return err;
472}
473
474static long pres_reservation_create(int res_type, void* __user _config)
475{
476 long ret = -EINVAL;
477 struct reservation_config config;
478
479 TRACE("Attempt to create reservation (%d)\n", res_type);
480
481 if (copy_from_user(&config, _config, sizeof(config)))
482 return -EFAULT;
483
484 if (config.cpu < 0 || !cpu_online(config.cpu)) {
485 printk(KERN_ERR "invalid polling reservation (%u): "
486 "CPU %d offline\n", config.id, config.cpu);
487 return -EINVAL;
488 }
489
490 switch (res_type) {
491 case PERIODIC_POLLING:
492 case SPORADIC_POLLING:
493 ret = create_polling_reservation(res_type, &config);
494 break;
495
496 case TABLE_DRIVEN:
497 ret = create_table_driven_reservation(&config);
498 break;
499
500 default:
501 return -EINVAL;
502 };
503
504 return ret;
505}
506
507static struct domain_proc_info pres_domain_proc_info;
508
509static long pres_get_domain_proc_info(struct domain_proc_info **ret)
510{
511 *ret = &pres_domain_proc_info;
512 return 0;
513}
514
515static void pres_setup_domain_proc(void)
516{
517 int i, cpu;
518 int num_rt_cpus = num_online_cpus();
519
520 struct cd_mapping *cpu_map, *domain_map;
521
522 memset(&pres_domain_proc_info, sizeof(pres_domain_proc_info), 0);
523 init_domain_proc_info(&pres_domain_proc_info, num_rt_cpus, num_rt_cpus);
524 pres_domain_proc_info.num_cpus = num_rt_cpus;
525 pres_domain_proc_info.num_domains = num_rt_cpus;
526
527 i = 0;
528 for_each_online_cpu(cpu) {
529 cpu_map = &pres_domain_proc_info.cpu_to_domains[i];
530 domain_map = &pres_domain_proc_info.domain_to_cpus[i];
531
532 cpu_map->id = cpu;
533 domain_map->id = i;
534 cpumask_set_cpu(i, cpu_map->mask);
535 cpumask_set_cpu(cpu, domain_map->mask);
536 ++i;
537 }
538}
539
540static long pres_activate_plugin(void)
541{
542 int cpu;
543 struct pres_cpu_state *state;
544
545 for_each_online_cpu(cpu) {
546 TRACE("Initializing CPU%d...\n", cpu);
547
548 state = cpu_state_for(cpu);
549
550 raw_spin_lock_init(&state->lock);
551 state->cpu = cpu;
552 state->scheduled = NULL;
553
554 sup_init(&state->sup_env);
555
556 hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
557 state->timer.function = on_scheduling_timer;
558 }
559
560 pres_setup_domain_proc();
561
562 return 0;
563}
564
565static long pres_deactivate_plugin(void)
566{
567 int cpu;
568 struct pres_cpu_state *state;
569 struct reservation *res;
570
571 for_each_online_cpu(cpu) {
572 state = cpu_state_for(cpu);
573 raw_spin_lock(&state->lock);
574
575 hrtimer_cancel(&state->timer);
576
577 /* Delete all reservations --- assumes struct reservation
578 * is prefix of containing struct. */
579
580 while (!list_empty(&state->sup_env.active_reservations)) {
581 res = list_first_entry(
582 &state->sup_env.active_reservations,
583 struct reservation, list);
584 list_del(&res->list);
585 kfree(res);
586 }
587
588 while (!list_empty(&state->sup_env.inactive_reservations)) {
589 res = list_first_entry(
590 &state->sup_env.inactive_reservations,
591 struct reservation, list);
592 list_del(&res->list);
593 kfree(res);
594 }
595
596 while (!list_empty(&state->sup_env.depleted_reservations)) {
597 res = list_first_entry(
598 &state->sup_env.depleted_reservations,
599 struct reservation, list);
600 list_del(&res->list);
601 kfree(res);
602 }
603
604 raw_spin_unlock(&state->lock);
605 }
606
607 destroy_domain_proc_info(&pres_domain_proc_info);
608 return 0;
609}
610
611static struct sched_plugin pres_plugin = {
612 .plugin_name = "P-RES",
613 .schedule = pres_schedule,
614 .task_wake_up = pres_task_resume,
615 .admit_task = pres_admit_task,
616 .task_new = pres_task_new,
617 .task_exit = pres_task_exit,
618 .complete_job = pres_complete_job,
619 .get_domain_proc_info = pres_get_domain_proc_info,
620 .activate_plugin = pres_activate_plugin,
621 .deactivate_plugin = pres_deactivate_plugin,
622 .reservation_create = pres_reservation_create,
623};
624
625static int __init init_pres(void)
626{
627 return register_sched_plugin(&pres_plugin);
628}
629
630module_init(init_pres);
631