aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorBjoern B. Brandenburg <bbb@jupiter-cs.cs.unc.edu>2007-02-01 17:35:46 -0500
committerBjoern B. Brandenburg <bbb@jupiter-cs.cs.unc.edu>2007-02-01 17:35:46 -0500
commitc72efb7abd520758e86317c8eb451ed607042ad9 (patch)
treed14e61b14ed2b01f683829eefd20d66fd3d549eb /kernel
parentad1e19d0fafe82cc5f8180c0f9221458d439d9fa (diff)
Added litmus.c, made it compile.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/litmus.c588
2 files changed, 589 insertions, 1 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index f67634070a..103c1b1050 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
9 rcupdate.o extable.o params.o posix-timers.o \ 9 rcupdate.o extable.o params.o posix-timers.o \
10 kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ 10 kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
11 hrtimer.o rwsem.o latency.o nsproxy.o srcu.o \ 11 hrtimer.o rwsem.o latency.o nsproxy.o srcu.o \
12 sched_plugin.o 12 sched_plugin.o litmus.o
13 13
14obj-$(CONFIG_STACKTRACE) += stacktrace.o 14obj-$(CONFIG_STACKTRACE) += stacktrace.o
15obj-y += time/ 15obj-y += time/
diff --git a/kernel/litmus.c b/kernel/litmus.c
new file mode 100644
index 0000000000..7e97df948a
--- /dev/null
+++ b/kernel/litmus.c
@@ -0,0 +1,588 @@
1/*
2 * Variable and function definitions
3 * that help alter the underlying scheduler
4 * on the fly.
5 *
6 */
7
8
9#include <asm/uaccess.h>
10#include <linux/sysrq.h>
11
12#include <linux/queuelock.h>
13#include <linux/litmus.h>
14#include <linux/sched.h>
15#include <linux/sched_plugin.h>
16
17/*CLEANUP: Include when ported.
18 #include <linux/plugin_trace.h>
19*/
20
21
22/* External variables declared in plugin driver */
23extern sched_plugin_t *curr_sched_plugin;
24extern sched_plugin_t linux_sched_plugin;
25
26/* Variables that govern the scheduling process */
27spolicy sched_policy = SCHED_DEFAULT;
28int sched_options = 0;
29unsigned long slot_size = DEFAULT_SLOT_SIZE;
30unsigned long stagger_offset = DEFAULT_STAGGER; /* TODO: get rid of software stagger */
31
32spinlock_t litmus_task_set_lock = SPIN_LOCK_UNLOCKED;
33
34/* Use linux runqueues to store currently running CPU task by default */
35int sched_type = INDIRECT_SWITCH;
36
37
38/* This is a flag for switching the system into RT mode when it is booted up
39* In RT-mode non-realtime tasks are shut down and scheduled as spare time available
40* Even though the system may switch scheduling plugin on the fly
41* it must continue to mimic linux scheduler until rt_mode is explicitly
42* enabled.
43*/
44
45/* The system is booting in non-realtime mode */
46atomic_t rt_mode = ATOMIC_INIT(MODE_NON_RT);
47/* Here we specify a mode change to be made */
48atomic_t new_mode = ATOMIC_INIT(MODE_NON_RT);
49/* Number of RT tasks that exist in the system */
50atomic_t n_rt_tasks = ATOMIC_INIT(0);
51atomic_t *n_rt_tasks_ptr = &n_rt_tasks;
52/* Only one process can perform mode change */
53static queuelock_t mode_change_lock;
54/* A time instant when we switched to RT mode */
55volatile unsigned long rt_start_time = 0;
56/**
57* sys_set_rt_mode
58* @newmode: new mode the scheduler must be switched to
59* External syscall for setting the RT mode flag
60* Returns EINVAL if mode is not recognized or mode transition is not permitted
61* On success 0 is returned
62* TODO: implement transition table
63*/
64asmlinkage long sys_set_rt_mode(int newmode)
65{
66
67 if ((newmode == MODE_NON_RT) || (newmode == MODE_RT_RUN)) {
68 unsigned long flags;
69 local_irq_save(flags);
70 printk(KERN_INFO "real-time mode switch to %s\n",
71 (newmode == MODE_RT_RUN ? "rt" : "non-rt"));
72 local_irq_restore(flags);
73 atomic_set(&new_mode, newmode);
74 return 0;
75 }
76 return -EINVAL;
77}
78
79/**
80* sys_set_task_rt_param
81* @pid: Pid of the task which scheduling parameters must be changed
82* @param: New real-time extension parameters such as the execution cost and period
83* Syscall for manipulating with task rt extension params
84* Returns EFAULT if param is NULL.
85* ESRCH if pid is not corrsponding
86* to a valid task.
87* EINVAL if either period or execution cost is <=0
88* 0 if success
89*
90*
91* FIXME: This code is racy during real-time mode.
92*/
93asmlinkage long sys_set_rt_task_param(pid_t pid, rt_param_t __user * param)
94{
95 rt_param_t tp;
96 struct task_struct *target;
97 int retval = -EINVAL;
98
99 if (pid < 0 || param == 0) {
100 goto out;
101 }
102 if (copy_from_user(&tp, param, sizeof(tp))) {
103 retval = -EFAULT;
104 goto out;
105 }
106 /* Task search and manipulation must be protected */
107
108
109 read_lock_irq(&tasklist_lock);
110 if (!(target = find_task_by_pid(pid))) {
111 retval = -ESRCH;
112 goto out_unlock;
113 }
114 if (tp.exec_cost <= 0)
115 goto out_unlock;
116 if (tp.period <= 0)
117 goto out_unlock;
118 if (!cpu_online(tp.cpu))
119 goto out_unlock;
120 if (tp.period < tp.exec_cost)
121 {
122 printk(KERN_INFO "litmus: real-time task %d rejected "
123 "because wcet > period\n", pid);
124 goto out_unlock;
125 }
126
127 /* Assign params */
128 target->rt_param.basic_params = tp;
129
130 retval = 0;
131 out_unlock:
132 read_unlock_irq(&tasklist_lock);
133 out:
134 return retval;
135}
136
137/** Getter of task's RT params
138* returns EINVAL if param or pid is NULL
139* returns ESRCH if pid does not correspond to a valid task
140* returns EFAULT if copying of parameters has failed.
141*/
142asmlinkage long sys_get_rt_task_param(pid_t pid, rt_param_t __user * param)
143{
144 int retval = -EINVAL;
145 struct task_struct *source;
146 rt_param_t lp;
147 if (param == 0 || pid < 0)
148 goto out;
149 read_lock(&tasklist_lock);
150 if (!(source = find_task_by_pid(pid))) {
151 retval = -ESRCH;
152 goto out_unlock;
153 }
154 lp = source->rt_param.basic_params;
155 read_unlock(&tasklist_lock);
156 /* Do copying outside the lock */
157 retval =
158 copy_to_user(param, &lp, sizeof(lp)) ? -EFAULT : 0;
159 return retval;
160 out_unlock:
161 read_unlock(&tasklist_lock);
162 out:
163 return retval;
164
165}
166
167/*static inline int get_ok_sched(int cpu)
168{
169 return atomic_read(cpu_okay_to_sched(cpu));
170}
171static inline void set_ok_sched(int cpu, int val)
172{
173 atomic_set(cpu_okay_to_sched(cpu), val);
174}
175*/
176
177/* Returns true if okay_to_sched for current CPU is set to 1 */
178
179/*inline int get_this_ok_sched(void)
180{
181 return atomic_dec_and_test(thisoksched());
182}
183inline void set_this_ok_sched(int val)
184{
185 atomic_set(thisoksched(), val);
186}
187*/
188/**
189* sys_prepare_rt_task
190* @pid: Pid of the task we want to prepare for RT mode
191* Syscall for adding a task to RT queue, plugin dependent.
192* Must be called before RT tasks are going to start up.
193* Returns EPERM if current plugin does not define prepare operation
194* or scheduling policy does not allow the operation.
195* ESRCH if pid does not correspond to a valid task.
196* EINVAL if a task is non-realtime or in invalid state
197* from underlying plugin function
198* EAGAIN if a task is not in the right state
199* ENOMEM if there is no memory space to handle this task
200* 0 if success
201*
202*
203* FIXME:The cpu parameter is currently ignored. It s hould be removed
204* altogether
205*/
206asmlinkage long sys_prepare_rt_task(pid_t pid, int dummy)
207{
208 int retval = -EINVAL;
209 struct task_struct *target = 0;
210 /* If a plugin does not define preparation mode then nothing to do */
211 if (curr_sched_plugin->prepare_task == 0
212 || sched_policy == SCHED_DEFAULT) {
213 retval = -EPERM;
214 goto out_prepare;
215 }
216 read_lock_irq(&tasklist_lock);
217 if (!(target = find_task_by_pid(pid))) {
218 retval = -ESRCH;
219 goto out_prepare_unlock;
220 }
221 if (!cpu_online(get_partition(target)))
222 {
223 printk(KERN_WARNING "litmus prepare: cpu %d is not online\n",
224 get_partition(target));
225 goto out_prepare_unlock;
226 }
227 retval = curr_sched_plugin->prepare_task(target);
228 if (!retval) {
229 atomic_inc(&n_rt_tasks);
230 target->rt_param.is_realtime = 1;
231 }
232 out_prepare_unlock:
233 read_unlock_irq(&tasklist_lock);
234 out_prepare:
235 return retval;
236}
237
238/* External reference to kill, probably need to move at the header */
239extern long sys_kill(pid_t, int);
240/**
241* Task tear down, removes a task from plugin structures, sets its
242* properties to non-realtime and sends a signal (SIGKILL suggested, so that the task shuts down immediately)
243* returns EINVAL if the task is not a realtime task
244* returns EPERM if the task is current, or current kernel policy does not permit
245* realtime operations
246* returns ESRCH if the pid does not correspond to a valid task
247* Otherwise the result of sys_kill is returned
248*/
249asmlinkage long sys_tear_down_task(pid_t pid, int sig)
250{
251 int retval = -EINVAL;
252 struct task_struct *target = 0;
253 if (curr_sched_plugin->tear_down == 0
254 || sched_policy == SCHED_DEFAULT) {
255 retval = -EPERM;
256 goto out;
257 }
258 read_lock_irq(&tasklist_lock);
259 if (!(target = find_task_by_pid(pid))) {
260 retval = -ESRCH;
261 goto out_unlock;
262 }
263 if (target == current) {
264 retval = -EPERM;
265 goto out_unlock;
266 }
267 printk(KERN_WARNING "task %d: use of sys_tear_down_task is deprecated! "
268 "Just use kill instead.\n", current->pid);
269/* if (is_realtime(target)) {
270 // the task is not realtime now, there is no harm
271 // because it is going to be killed
272 retval = curr_sched_plugin->tear_down(target);
273 // drop all the RT params of the task
274 clear_task(target);
275 target->state = TASK_STOPPED;
276 }
277*/
278 read_unlock_irq(&tasklist_lock);
279
280 // call syskill
281 return sys_kill(pid, sig);
282 out_unlock:
283 read_unlock_irq(&tasklist_lock);
284 out:
285 return retval;
286}
287
288/**
289* This is the crucial function for periodic task implementation,
290* It checks if a task is periodic, checks if such kind of sleep is permitted
291* and calls plugin-specific sleep, which puts the task into a wait array.
292* returns 0 on successful wakeup
293* returns EPERM if current conditions do not permit such sleep
294* returns EINVAL if current task is not able to go to sleep
295*/
296asmlinkage long sys_sleep_next_period(void)
297{
298 int retval = -EPERM;
299 // Periodic sleep is not permitted for linux scheduler
300 // because quantum runqueues are not merged
301 if (curr_sched_plugin->sleep_next_period == 0
302 || sched_policy == SCHED_DEFAULT) {
303 goto out;
304 }
305 if (!is_realtime(current)) {
306 retval = -EINVAL;
307 goto out;
308 }
309 // Task with negative or zero period cannot sleep
310 if (get_rt_period(current) <= 0) {
311 retval = -EINVAL;
312 goto out;
313 }
314 /* Basically the plugin have to put the task into
315 * appropriate quantum queue and call schedule
316 */
317 retval = curr_sched_plugin->sleep_next_period();
318 out:
319 return retval;
320}
321
322/* Set scheduling options for all cpus. */
323void set_sched_options(int options)
324{
325 sched_options = options;
326}
327
328
329/* Wrapper code for tick function - called by all schedulers before
330 entering their own code.
331*/
332
333int rt_scheduler_tick(void)
334{
335 /* Check for mode change */
336 if ((get_rt_mode() != atomic_read(&new_mode))) {
337 queue_lock(&mode_change_lock);
338 // If the mode is already changed, proceed
339 if (get_rt_mode() == atomic_read(&new_mode)) {
340 queue_unlock(&mode_change_lock);
341 goto proceed;
342 }
343 // change the mode
344 if ((atomic_read(&new_mode) == MODE_RT_RUN)) {
345 /* The deferral of entering real-time mode should be
346 * handled by deferring task releases in the plugin.
347 * The plugin interface does not really need to know
348 * about quanta, that is the plugin's job.
349 */
350
351 /* update rt start time */
352 rt_start_time = jiffies;
353 TRACE("Real-Time mode enabled\n");
354 if (curr_sched_plugin->mode_change)
355 curr_sched_plugin->
356 mode_change(atomic_read(&new_mode));
357 }
358 set_rt_mode(atomic_read(&new_mode));
359 queue_unlock(&mode_change_lock);
360 }
361
362 proceed:
363 /* Call plugin-defined tick handler
364 *
365 * It is the plugin's tick handler' job to detect quantum
366 * boundaries in pfair.
367 */
368 return curr_sched_plugin->algo_scheduler_tick();
369}
370
371inline int get_sched_options(void)
372{
373 return sched_options;
374}
375
376/* This code must be non-preemptable */
377asmlinkage spolicy sys_sched_setpolicy(spolicy newpolicy)
378{
379 /* Dynamic policy change is disabled at the moment */
380 /*
381 spolicy cpolicy = sched_policy;
382
383 if (newpolicy >= SCHED_BEG && newpolicy <= SCHED_END) {
384 sched_policy = newpolicy;
385 switch (sched_policy) {
386 case SCHED_GLOBAL_EDF:
387 initialize_global_edf();
388 break;
389 case SCHED_PART_EDF:
390 initialize_part_edf();
391 break;
392 case SCHED_PFAIR:
393 initialize_scheduler_pfair();
394 break;
395 case SCHED_PFAIR_STAGGER:
396 initialize_scheduler_pfair_stagger();
397 break;
398 default:
399 initialize_scheduler_linux();
400 break;
401 }
402 return cpolicy;
403 } else
404 */
405 return SCHED_INVALID;
406}
407
408asmlinkage spolicy sys_sched_getpolicy(void)
409{
410 return sched_policy;
411}
412
413
414asmlinkage int sys_scheduler_setup(int cmd, void __user *parameter)
415{
416 return curr_sched_plugin->scheduler_setup(cmd, parameter);
417}
418
419
420#ifdef CONFIG_MAGIC_SYSRQ
421/* We offer the possibility to change the real-time mode of the system
422 * with a magic sys request. This helps in debugging in case the system fails
423 * to perform its planned switch back to normal mode. This may happen if we have
424 * total system utilization and the task that is supposed to do the switch is
425 * always preempted (if it is not a real-time task).
426 */
427
428static void sysrq_handle_toGgle_rt_mode(int key, struct tty_struct *tty)
429{
430 sys_set_rt_mode(get_rt_mode() == MODE_NON_RT);
431}
432
433static struct sysrq_key_op sysrq_toGgle_rt_mode_op = {
434 .handler = sysrq_handle_toGgle_rt_mode,
435 .help_msg = "toGgle-rt-mode",
436 .action_msg = "real-time mode changed",
437};
438
439static void sysrq_handle_kill_rt_tasks(int key, struct tty_struct *tty)
440{
441 struct task_struct *t;
442 read_lock(&tasklist_lock);
443 for_each_process(t) {
444 if (is_realtime(t)) {
445 sys_kill(t->pid, SIGKILL);
446 }
447 }
448 read_unlock(&tasklist_lock);
449}
450
451static struct sysrq_key_op sysrq_kill_rt_tasks_op = {
452 .handler = sysrq_handle_kill_rt_tasks,
453 .help_msg = "f:kill-rt-tasks",
454 .action_msg = "sent SIGKILL to all real-time tasks",
455};
456#endif
457
458/*
459 * Scheduler initialization so that customized scheduler is
460 * enabled at boot time
461 * by setting boot option "rtsched=plugin_name", e.g. "rtsched=pfair"
462 */
463
464/* All we need to know about other plugins is their initialization
465 * functions. These functions initialize internal data structures of a
466 * scheduler and return a pointer to initialized sched_plugin data
467 * structure with pointers to scheduling function implementations.
468 * If called repeatedly these init functions just return an existing
469 * plugin pointer.
470 */
471/*
472CLEANUP: Add init function when the plugin has been ported.
473sched_plugin_t *init_pfair_plugin(void);
474sched_plugin_t *init_global_edf_plugin(void);
475sched_plugin_t *init_part_edf_plugin(void);
476sched_plugin_t *init_desync_pfair_plugin(void);
477sched_plugin_t *init_global_edf_np_plugin(void);
478sched_plugin_t *init_edf_hsb_plugin(void);
479*/
480
481
482
483
484/* keep everything needed to setup plugins in one place */
485
486/* we are lazy, so we use a convention for function naming to fill
487 * a table
488 */
489#define PLUGIN(caps, small) \
490 {PLUGIN_ ## caps, SCHED_ ## caps, init_ ## small ## _plugin}
491
492#define init_nosetup_plugin 0
493
494static struct {
495 const char *name;
496 const spolicy policy_id;
497 sched_plugin_t *(*init) (void);
498} available_plugins[] = {
499 PLUGIN(LINUX, nosetup),
500/* CLEANUP: Add when ported.
501 PLUGIN(PFAIR, pfair),
502 PLUGIN(PART_EDF, part_edf),
503 PLUGIN(GLOBAL_EDF, global_edf),
504 PLUGIN(PFAIR_DESYNC, desync_pfair),
505 PLUGIN(GLOBAL_EDF_NP, global_edf_np),
506 PLUGIN(GLOBAL_EDF, global_edf),
507 PLUGIN(EDF_HSB, edf_hsb)
508*/
509
510 /*********************************************
511 * Add your custom plugin here
512 **********************************************/
513};
514
515/* Some plugins may leave important functions unused. We define dummies
516 * so that we don't have to check for null pointers all over the place.
517 */
518void litmus_dummy_finish_switch(struct task_struct * prev);
519int litmus_dummy_schedule(struct task_struct * prev, struct task_struct** next,
520 runqueue_t* q);
521reschedule_check_t litmus_dummy_scheduler_tick(void);
522long litmus_dummy_prepare_task(struct task_struct *t);
523void litmus_dummy_wake_up_task(struct task_struct *task);
524void litmus_dummy_task_blocks(struct task_struct *task);
525long litmus_dummy_tear_down(struct task_struct *task);
526int litmus_dummy_scheduler_setup(int cmd, void __user *parameter);
527
528#define CHECK(func) {\
529 if (!curr_sched_plugin->func) \
530 curr_sched_plugin->func = litmus_dummy_ ## func;}
531
532static int boot_sched_setup(char *plugin_name)
533{
534 int i = 0;
535
536 /* Common initializers,
537 * mode change lock is used to enforce single mode change
538 * operation.
539 */
540 queue_lock_init(&mode_change_lock, NR_CPUS);
541
542 printk("Starting LITMUS kernel\n");
543
544 /* Look for a matching plugin.
545 */
546 for (i = 0; i < ARRAY_SIZE(available_plugins); i++) {
547 if (!strcmp(plugin_name, available_plugins[i].name)) {
548 printk("Using %s scheduler plugin\n", plugin_name);
549 sched_policy = available_plugins[i].policy_id;
550 if (available_plugins[i].init)
551 curr_sched_plugin = available_plugins[i].init();
552 goto out;
553 }
554 }
555
556
557 /* Otherwise we have default linux scheduler */
558 printk("Plugin name %s is unknown, using default %s\n", plugin_name,
559 curr_sched_plugin->plugin_name);
560
561out:
562 /* make sure we don't trip over null pointers later */
563 CHECK(finish_switch);
564 CHECK(schedule);
565 CHECK(scheduler_tick);
566 CHECK(wake_up_task);
567 CHECK(tear_down);
568 CHECK(task_blocks);
569 CHECK(prepare_task);
570 CHECK(scheduler_setup);
571
572#ifdef CONFIG_MAGIC_SYSRQ
573 /* offer some debugging help */
574 if (!register_sysrq_key('g', &sysrq_toGgle_rt_mode_op))
575 printk("Registered eXit real-time mode magic sysrq.\n");
576 else
577 printk("Could not register eXit real-time mode magic sysrq.\n");
578 if (!register_sysrq_key('f', &sysrq_kill_rt_tasks_op))
579 printk("Registered kill rt tasks magic sysrq.\n");
580 else
581 printk("Could not register kill rt tasks magic sysrq.\n");
582#endif
583 printk("Litmus setup complete.");
584 return 1;
585}
586
587/* Register for boot option */
588__setup("rtsched=", boot_sched_setup);