aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern B. Brandenburg <bbb@cs.unc.edu>2009-12-07 19:10:29 -0500
committerBjoern B. Brandenburg <bbb@cs.unc.edu>2009-12-08 10:59:40 -0500
commit940bd1a0dfe070e009060e2f304f1a337e6e29f0 (patch)
tree5621e3d3cfd1c04c0f1da89410a7b5938c48518c
parent49914084e797530d9baaf51df9eda77babc98fa8 (diff)
Core LITMUS^RT infrastructure.
remove SRP from sched.c (to be merged) fix generic feather-trace implementation (to be merged) Remove sync support from KConfig (to be merged)
-rw-r--r--Makefile2
-rw-r--r--include/linux/sched.h6
-rw-r--r--include/litmus/feather_buffer.h94
-rw-r--r--include/litmus/feather_trace.h36
-rw-r--r--include/litmus/heap.h77
-rw-r--r--include/litmus/jobs.h9
-rw-r--r--include/litmus/litmus.h177
-rw-r--r--include/litmus/rt_param.h175
-rw-r--r--include/litmus/sched_plugin.h159
-rw-r--r--include/litmus/sched_trace.h191
-rw-r--r--include/litmus/trace.h113
-rw-r--r--kernel/fork.c8
-rw-r--r--kernel/sched.c80
-rw-r--r--kernel/sched_fair.c2
-rw-r--r--kernel/sched_rt.c2
-rw-r--r--litmus/Kconfig50
-rw-r--r--litmus/Makefile12
-rw-r--r--litmus/ft_event.c43
-rw-r--r--litmus/heap.c314
-rw-r--r--litmus/jobs.c43
-rw-r--r--litmus/litmus.c650
-rw-r--r--litmus/sched_litmus.c245
-rw-r--r--litmus/sched_plugin.c199
23 files changed, 2674 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 189d8ef416e6..d9e449503812 100644
--- a/Makefile
+++ b/Makefile
@@ -597,7 +597,7 @@ export mod_strip_cmd
597 597
598 598
599ifeq ($(KBUILD_EXTMOD),) 599ifeq ($(KBUILD_EXTMOD),)
600core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ 600core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ litmus/
601 601
602vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ 602vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
603 $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ 603 $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
diff --git a/include/linux/sched.h b/include/linux/sched.h
index cc14656f8682..3bf424c433ce 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -37,6 +37,7 @@
37#define SCHED_BATCH 3 37#define SCHED_BATCH 3
38/* SCHED_ISO: reserved but not implemented yet */ 38/* SCHED_ISO: reserved but not implemented yet */
39#define SCHED_IDLE 5 39#define SCHED_IDLE 5
40#define SCHED_LITMUS 6
40 41
41#ifdef __KERNEL__ 42#ifdef __KERNEL__
42 43
@@ -91,6 +92,8 @@ struct sched_param {
91 92
92#include <asm/processor.h> 93#include <asm/processor.h>
93 94
95#include <litmus/rt_param.h>
96
94struct exec_domain; 97struct exec_domain;
95struct futex_pi_state; 98struct futex_pi_state;
96struct bio; 99struct bio;
@@ -1178,6 +1181,9 @@ struct task_struct {
1178 int make_it_fail; 1181 int make_it_fail;
1179#endif 1182#endif
1180 struct prop_local_single dirties; 1183 struct prop_local_single dirties;
1184
1185 /* litmus parameters and state */
1186 struct rt_param rt_param;
1181}; 1187};
1182 1188
1183/* 1189/*
diff --git a/include/litmus/feather_buffer.h b/include/litmus/feather_buffer.h
new file mode 100644
index 000000000000..6c18277fdfc9
--- /dev/null
+++ b/include/litmus/feather_buffer.h
@@ -0,0 +1,94 @@
1#ifndef _FEATHER_BUFFER_H_
2#define _FEATHER_BUFFER_H_
3
4/* requires UINT_MAX and memcpy */
5
6#define SLOT_FREE 0
7#define SLOT_BUSY 1
8#define SLOT_READY 2
9
10struct ft_buffer {
11 unsigned int slot_count;
12 unsigned int slot_size;
13
14 int free_count;
15 unsigned int write_idx;
16 unsigned int read_idx;
17
18 char* slots;
19 void* buffer_mem;
20 unsigned int failed_writes;
21};
22
23static inline int init_ft_buffer(struct ft_buffer* buf,
24 unsigned int slot_count,
25 unsigned int slot_size,
26 char* slots,
27 void* buffer_mem)
28{
29 int i = 0;
30 if (!slot_count || UINT_MAX % slot_count != slot_count - 1) {
31 /* The slot count must divide UNIT_MAX + 1 so that when it
32 * wraps around the index correctly points to 0.
33 */
34 return 0;
35 } else {
36 buf->slot_count = slot_count;
37 buf->slot_size = slot_size;
38 buf->slots = slots;
39 buf->buffer_mem = buffer_mem;
40 buf->free_count = slot_count;
41 buf->write_idx = 0;
42 buf->read_idx = 0;
43 buf->failed_writes = 0;
44 for (i = 0; i < slot_count; i++)
45 buf->slots[i] = SLOT_FREE;
46 return 1;
47 }
48}
49
50static inline int ft_buffer_start_write(struct ft_buffer* buf, void **ptr)
51{
52 int free = fetch_and_dec(&buf->free_count);
53 unsigned int idx;
54 if (free <= 0) {
55 fetch_and_inc(&buf->free_count);
56 *ptr = 0;
57 fetch_and_inc(&buf->failed_writes);
58 return 0;
59 } else {
60 idx = fetch_and_inc((int*) &buf->write_idx) % buf->slot_count;
61 buf->slots[idx] = SLOT_BUSY;
62 *ptr = ((char*) buf->buffer_mem) + idx * buf->slot_size;
63 return 1;
64 }
65}
66
67static inline void ft_buffer_finish_write(struct ft_buffer* buf, void *ptr)
68{
69 unsigned int idx = ((char*) ptr - (char*) buf->buffer_mem) / buf->slot_size;
70 buf->slots[idx] = SLOT_READY;
71}
72
73
74/* exclusive reader access is assumed */
75static inline int ft_buffer_read(struct ft_buffer* buf, void* dest)
76{
77 unsigned int idx;
78 if (buf->free_count == buf->slot_count)
79 /* nothing available */
80 return 0;
81 idx = buf->read_idx % buf->slot_count;
82 if (buf->slots[idx] == SLOT_READY) {
83 memcpy(dest, ((char*) buf->buffer_mem) + idx * buf->slot_size,
84 buf->slot_size);
85 buf->slots[idx] = SLOT_FREE;
86 buf->read_idx++;
87 fetch_and_inc(&buf->free_count);
88 return 1;
89 } else
90 return 0;
91}
92
93
94#endif
diff --git a/include/litmus/feather_trace.h b/include/litmus/feather_trace.h
new file mode 100644
index 000000000000..3ac1ee5e0277
--- /dev/null
+++ b/include/litmus/feather_trace.h
@@ -0,0 +1,36 @@
1#ifndef _FEATHER_TRACE_H_
2#define _FEATHER_TRACE_H_
3
4
5int ft_enable_event(unsigned long id);
6int ft_disable_event(unsigned long id);
7int ft_is_event_enabled(unsigned long id);
8int ft_disable_all_events(void);
9
10#ifndef __ARCH_HAS_FEATHER_TRACE
11/* provide default implementation */
12
13#define feather_callback
14
15#define MAX_EVENTS 1024
16
17extern int ft_events[MAX_EVENTS];
18
19#define ft_event(id, callback) \
20 if (ft_events[id]) callback();
21
22#define ft_event0(id, callback) \
23 if (ft_events[id]) callback(id);
24
25#define ft_event1(id, callback, param) \
26 if (ft_events[id]) callback(id, param);
27
28#define ft_event2(id, callback, param, param2) \
29 if (ft_events[id]) callback(id, param, param2);
30
31#define ft_event3(id, callback, p, p2, p3) \
32 if (ft_events[id]) callback(id, p, p2, p3);
33#endif
34
35
36#endif
diff --git a/include/litmus/heap.h b/include/litmus/heap.h
new file mode 100644
index 000000000000..da959b0bec9c
--- /dev/null
+++ b/include/litmus/heap.h
@@ -0,0 +1,77 @@
1/* heaps.h -- Binomial Heaps
2 *
3 * (c) 2008, 2009 Bjoern Brandenburg
4 */
5
6#ifndef HEAP_H
7#define HEAP_H
8
9#define NOT_IN_HEAP UINT_MAX
10
11struct heap_node {
12 struct heap_node* parent;
13 struct heap_node* next;
14 struct heap_node* child;
15
16 unsigned int degree;
17 void* value;
18 struct heap_node** ref;
19};
20
21struct heap {
22 struct heap_node* head;
23 /* We cache the minimum of the heap.
24 * This speeds up repeated peek operations.
25 */
26 struct heap_node* min;
27};
28
29typedef int (*heap_prio_t)(struct heap_node* a, struct heap_node* b);
30
31void heap_init(struct heap* heap);
32void heap_node_init(struct heap_node** ref_to_heap_node_ptr, void* value);
33
34static inline int heap_node_in_heap(struct heap_node* h)
35{
36 return h->degree != NOT_IN_HEAP;
37}
38
39static inline int heap_empty(struct heap* heap)
40{
41 return heap->head == NULL && heap->min == NULL;
42}
43
44/* insert (and reinitialize) a node into the heap */
45void heap_insert(heap_prio_t higher_prio,
46 struct heap* heap,
47 struct heap_node* node);
48
49/* merge addition into target */
50void heap_union(heap_prio_t higher_prio,
51 struct heap* target,
52 struct heap* addition);
53
54struct heap_node* heap_peek(heap_prio_t higher_prio,
55 struct heap* heap);
56
57struct heap_node* heap_take(heap_prio_t higher_prio,
58 struct heap* heap);
59
60void heap_uncache_min(heap_prio_t higher_prio, struct heap* heap);
61int heap_decrease(heap_prio_t higher_prio, struct heap_node* node);
62
63void heap_delete(heap_prio_t higher_prio,
64 struct heap* heap,
65 struct heap_node* node);
66
67/* allocate from memcache */
68struct heap_node* heap_node_alloc(int gfp_flags);
69void heap_node_free(struct heap_node* hn);
70
71/* allocate a heap node for value and insert into the heap */
72int heap_add(heap_prio_t higher_prio, struct heap* heap,
73 void* value, int gfp_flags);
74
75void* heap_take_del(heap_prio_t higher_prio,
76 struct heap* heap);
77#endif
diff --git a/include/litmus/jobs.h b/include/litmus/jobs.h
new file mode 100644
index 000000000000..9bd361ef3943
--- /dev/null
+++ b/include/litmus/jobs.h
@@ -0,0 +1,9 @@
1#ifndef __LITMUS_JOBS_H__
2#define __LITMUS_JOBS_H__
3
4void prepare_for_next_period(struct task_struct *t);
5void release_at(struct task_struct *t, lt_t start);
6long complete_job(void);
7
8#endif
9
diff --git a/include/litmus/litmus.h b/include/litmus/litmus.h
new file mode 100644
index 000000000000..96ac99b70ae4
--- /dev/null
+++ b/include/litmus/litmus.h
@@ -0,0 +1,177 @@
1/*
2 * Constant definitions related to
3 * scheduling policy.
4 */
5
6#ifndef _LINUX_LITMUS_H_
7#define _LINUX_LITMUS_H_
8
9#include <linux/jiffies.h>
10#include <litmus/sched_trace.h>
11
12extern atomic_t release_master_cpu;
13
14extern atomic_t __log_seq_no;
15
16#define TRACE(fmt, args...) \
17 sched_trace_log_message("%d P%d: " fmt, atomic_add_return(1, &__log_seq_no), \
18 raw_smp_processor_id(), ## args)
19
20#define TRACE_TASK(t, fmt, args...) \
21 TRACE("(%s/%d) " fmt, (t)->comm, (t)->pid, ##args)
22
23#define TRACE_CUR(fmt, args...) \
24 TRACE_TASK(current, fmt, ## args)
25
26#define TRACE_BUG_ON(cond) \
27 do { if (cond) TRACE("BUG_ON(%s) at %s:%d " \
28 "called from %p current=%s/%d state=%d " \
29 "flags=%x partition=%d cpu=%d rtflags=%d"\
30 " job=%u knp=%d timeslice=%u\n", \
31 #cond, __FILE__, __LINE__, __builtin_return_address(0), current->comm, \
32 current->pid, current->state, current->flags, \
33 get_partition(current), smp_processor_id(), get_rt_flags(current), \
34 current->rt_param.job_params.job_no, current->rt_param.kernel_np, \
35 current->time_slice\
36 ); } while(0);
37
38
39/* in_list - is a given list_head queued on some list?
40 */
41static inline int in_list(struct list_head* list)
42{
43 return !( /* case 1: deleted */
44 (list->next == LIST_POISON1 &&
45 list->prev == LIST_POISON2)
46 ||
47 /* case 2: initialized */
48 (list->next == list &&
49 list->prev == list)
50 );
51}
52
53#define NO_CPU 0xffffffff
54
55void litmus_fork(struct task_struct *tsk);
56void litmus_exec(void);
57/* clean up real-time state of a task */
58void exit_litmus(struct task_struct *dead_tsk);
59
60long litmus_admit_task(struct task_struct *tsk);
61void litmus_exit_task(struct task_struct *tsk);
62
63#define is_realtime(t) ((t)->policy == SCHED_LITMUS)
64#define rt_transition_pending(t) \
65 ((t)->rt_param.transition_pending)
66
67#define tsk_rt(t) (&(t)->rt_param)
68
69/* Realtime utility macros */
70#define get_rt_flags(t) (tsk_rt(t)->flags)
71#define set_rt_flags(t,f) (tsk_rt(t)->flags=(f))
72#define get_exec_cost(t) (tsk_rt(t)->task_params.exec_cost)
73#define get_exec_time(t) (tsk_rt(t)->job_params.exec_time)
74#define get_rt_period(t) (tsk_rt(t)->task_params.period)
75#define get_rt_phase(t) (tsk_rt(t)->task_params.phase)
76#define get_partition(t) (tsk_rt(t)->task_params.cpu)
77#define get_deadline(t) (tsk_rt(t)->job_params.deadline)
78#define get_release(t) (tsk_rt(t)->job_params.release)
79#define get_class(t) (tsk_rt(t)->task_params.cls)
80
81inline static int budget_exhausted(struct task_struct* t)
82{
83 return get_exec_time(t) >= get_exec_cost(t);
84}
85
86
87#define is_hrt(t) \
88 (tsk_rt(t)->task_params.class == RT_CLASS_HARD)
89#define is_srt(t) \
90 (tsk_rt(t)->task_params.class == RT_CLASS_SOFT)
91#define is_be(t) \
92 (tsk_rt(t)->task_params.class == RT_CLASS_BEST_EFFORT)
93
94/* Our notion of time within LITMUS: kernel monotonic time. */
95static inline lt_t litmus_clock(void)
96{
97 return ktime_to_ns(ktime_get());
98}
99
100/* A macro to convert from nanoseconds to ktime_t. */
101#define ns_to_ktime(t) ktime_add_ns(ktime_set(0, 0), t)
102
103#define get_domain(t) (tsk_rt(t)->domain)
104
105/* Honor the flag in the preempt_count variable that is set
106 * when scheduling is in progress.
107 */
108#define is_running(t) \
109 ((t)->state == TASK_RUNNING || \
110 task_thread_info(t)->preempt_count & PREEMPT_ACTIVE)
111
112#define is_blocked(t) \
113 (!is_running(t))
114#define is_released(t, now) \
115 (lt_before_eq(get_release(t), now))
116#define is_tardy(t, now) \
117 (lt_before_eq(tsk_rt(t)->job_params.deadline, now))
118
119/* real-time comparison macros */
120#define earlier_deadline(a, b) (lt_before(\
121 (a)->rt_param.job_params.deadline,\
122 (b)->rt_param.job_params.deadline))
123#define earlier_release(a, b) (lt_before(\
124 (a)->rt_param.job_params.release,\
125 (b)->rt_param.job_params.release))
126
127#define make_np(t) do {t->rt_param.kernel_np++;} while(0);
128#define take_np(t) do {t->rt_param.kernel_np--;} while(0);
129
130#ifdef CONFIG_SRP
131void srp_ceiling_block(void);
132#else
133#define srp_ceiling_block() /* nothing */
134#endif
135
136#define heap2task(hn) ((struct task_struct*) hn->value)
137
138static inline int is_np(struct task_struct *t)
139{
140 return tsk_rt(t)->kernel_np;
141}
142
143#define request_exit_np(t)
144
145static inline int is_present(struct task_struct* t)
146{
147 return t && tsk_rt(t)->present;
148}
149
150
151/* make the unit explicit */
152typedef unsigned long quanta_t;
153
154enum round {
155 FLOOR,
156 CEIL
157};
158
159
160/* Tick period is used to convert ns-specified execution
161 * costs and periods into tick-based equivalents.
162 */
163extern ktime_t tick_period;
164
165static inline quanta_t time2quanta(lt_t time, enum round round)
166{
167 s64 quantum_length = ktime_to_ns(tick_period);
168
169 if (do_div(time, quantum_length) && round == CEIL)
170 time++;
171 return (quanta_t) time;
172}
173
174/* By how much is cpu staggered behind CPU 0? */
175u64 cpu_stagger_offset(int cpu);
176
177#endif
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h
new file mode 100644
index 000000000000..c599f848d1ed
--- /dev/null
+++ b/include/litmus/rt_param.h
@@ -0,0 +1,175 @@
1/*
2 * Definition of the scheduler plugin interface.
3 *
4 */
5#ifndef _LINUX_RT_PARAM_H_
6#define _LINUX_RT_PARAM_H_
7
8/* Litmus time type. */
9typedef unsigned long long lt_t;
10
11static inline int lt_after(lt_t a, lt_t b)
12{
13 return ((long long) b) - ((long long) a) < 0;
14}
15#define lt_before(a, b) lt_after(b, a)
16
17static inline int lt_after_eq(lt_t a, lt_t b)
18{
19 return ((long long) a) - ((long long) b) >= 0;
20}
21#define lt_before_eq(a, b) lt_after_eq(b, a)
22
23/* different types of clients */
24typedef enum {
25 RT_CLASS_HARD,
26 RT_CLASS_SOFT,
27 RT_CLASS_BEST_EFFORT
28} task_class_t;
29
30struct rt_task {
31 lt_t exec_cost;
32 lt_t period;
33 lt_t phase;
34 unsigned int cpu;
35 task_class_t cls;
36};
37
38/* don't export internal data structures to user space (liblitmus) */
39#ifdef __KERNEL__
40
41struct _rt_domain;
42struct heap_node;
43struct release_heap;
44
45struct rt_job {
46 /* Time instant the the job was or will be released. */
47 lt_t release;
48 /* What is the current deadline? */
49 lt_t deadline;
50
51 /* How much service has this job received so far? */
52 lt_t exec_time;
53
54 /* Which job is this. This is used to let user space
55 * specify which job to wait for, which is important if jobs
56 * overrun. If we just call sys_sleep_next_period() then we
57 * will unintentionally miss jobs after an overrun.
58 *
59 * Increase this sequence number when a job is released.
60 */
61 unsigned int job_no;
62};
63
64
65struct pfair_param;
66
67/* RT task parameters for scheduling extensions
68 * These parameters are inherited during clone and therefore must
69 * be explicitly set up before the task set is launched.
70 */
71struct rt_param {
72 /* is the task sleeping? */
73 unsigned int flags:8;
74
75 /* do we need to check for srp blocking? */
76 unsigned int srp_non_recurse:1;
77
78 /* is the task present? (true if it can be scheduled) */
79 unsigned int present:1;
80
81 /* user controlled parameters */
82 struct rt_task task_params;
83
84 /* timing parameters */
85 struct rt_job job_params;
86
87 /* task representing the current "inherited" task
88 * priority, assigned by inherit_priority and
89 * return priority in the scheduler plugins.
90 * could point to self if PI does not result in
91 * an increased task priority.
92 */
93 struct task_struct* inh_task;
94
95 /* Don't just dereference this pointer in kernel space!
96 * It might very well point to junk or nothing at all.
97 * NULL indicates that the task has not requested any non-preemptable
98 * section support.
99 * Not inherited upon fork.
100 */
101 short* np_flag;
102
103 /* re-use unused counter in plugins that don't need it */
104 union {
105 /* For the FMLP under PSN-EDF, it is required to make the task
106 * non-preemptive from kernel space. In order not to interfere with
107 * user space, this counter indicates the kernel space np setting.
108 * kernel_np > 0 => task is non-preemptive
109 */
110 unsigned int kernel_np;
111
112 /* Used by GQ-EDF */
113 unsigned int last_cpu;
114 };
115
116 /* This field can be used by plugins to store where the task
117 * is currently scheduled. It is the responsibility of the
118 * plugin to avoid race conditions.
119 *
120 * This used by GSN-EDF and PFAIR.
121 */
122 volatile int scheduled_on;
123
124 /* Is the stack of the task currently in use? This is updated by
125 * the LITMUS core.
126 *
127 * Be careful to avoid deadlocks!
128 */
129 volatile int stack_in_use;
130
131 /* This field can be used by plugins to store where the task
132 * is currently linked. It is the responsibility of the plugin
133 * to avoid race conditions.
134 *
135 * Used by GSN-EDF.
136 */
137 volatile int linked_on;
138
139 /* PFAIR/PD^2 state. Allocated on demand. */
140 struct pfair_param* pfair;
141
142 /* Fields saved before BE->RT transition.
143 */
144 int old_policy;
145 int old_prio;
146
147 /* ready queue for this task */
148 struct _rt_domain* domain;
149
150 /* heap element for this task
151 *
152 * Warning: Don't statically allocate this node. The heap
153 * implementation swaps these between tasks, thus after
154 * dequeuing from a heap you may end up with a different node
155 * then the one you had when enqueuing the task. For the same
156 * reason, don't obtain and store references to this node
157 * other than this pointer (which is updated by the heap
158 * implementation).
159 */
160 struct heap_node* heap_node;
161 struct release_heap* rel_heap;
162
163 /* Used by rt_domain to queue task in release list.
164 */
165 struct list_head list;
166};
167
168/* Possible RT flags */
169#define RT_F_RUNNING 0x00000000
170#define RT_F_SLEEP 0x00000001
171#define RT_F_EXIT_SEM 0x00000008
172
173#endif
174
175#endif
diff --git a/include/litmus/sched_plugin.h b/include/litmus/sched_plugin.h
new file mode 100644
index 000000000000..94952f6ccbfa
--- /dev/null
+++ b/include/litmus/sched_plugin.h
@@ -0,0 +1,159 @@
1/*
2 * Definition of the scheduler plugin interface.
3 *
4 */
5#ifndef _LINUX_SCHED_PLUGIN_H_
6#define _LINUX_SCHED_PLUGIN_H_
7
8#include <linux/sched.h>
9
10/* struct for semaphore with priority inheritance */
11struct pi_semaphore {
12 atomic_t count;
13 int sleepers;
14 wait_queue_head_t wait;
15 union {
16 /* highest-prio holder/waiter */
17 struct task_struct *task;
18 struct task_struct* cpu_task[NR_CPUS];
19 } hp;
20 /* current lock holder */
21 struct task_struct *holder;
22};
23
24/************************ setup/tear down ********************/
25
26typedef long (*activate_plugin_t) (void);
27typedef long (*deactivate_plugin_t) (void);
28
29
30
31/********************* scheduler invocation ******************/
32
33/* Plugin-specific realtime tick handler */
34typedef void (*scheduler_tick_t) (struct task_struct *cur);
35/* Novell make sched decision function */
36typedef struct task_struct* (*schedule_t)(struct task_struct * prev);
37/* Clean up after the task switch has occured.
38 * This function is called after every (even non-rt) task switch.
39 */
40typedef void (*finish_switch_t)(struct task_struct *prev);
41
42
43/********************* task state changes ********************/
44
45/* Called to setup a new real-time task.
46 * Release the first job, enqueue, etc.
47 * Task may already be running.
48 */
49typedef void (*task_new_t) (struct task_struct *task,
50 int on_rq,
51 int running);
52
53/* Called to re-introduce a task after blocking.
54 * Can potentially be called multiple times.
55 */
56typedef void (*task_wake_up_t) (struct task_struct *task);
57/* called to notify the plugin of a blocking real-time task
58 * it will only be called for real-time tasks and before schedule is called */
59typedef void (*task_block_t) (struct task_struct *task);
60/* Called when a real-time task exits or changes to a different scheduling
61 * class.
62 * Free any allocated resources
63 */
64typedef void (*task_exit_t) (struct task_struct *);
65
66/* Called when the new_owner is released from the wait queue
67 * it should now inherit the priority from sem, _before_ it gets readded
68 * to any queue
69 */
70typedef long (*inherit_priority_t) (struct pi_semaphore *sem,
71 struct task_struct *new_owner);
72
73/* Called when the current task releases a semahpore where it might have
74 * inherited a piority from
75 */
76typedef long (*return_priority_t) (struct pi_semaphore *sem);
77
78/* Called when a task tries to acquire a semaphore and fails. Check if its
79 * priority is higher than that of the current holder.
80 */
81typedef long (*pi_block_t) (struct pi_semaphore *sem, struct task_struct *t);
82
83
84
85
86/********************* sys call backends ********************/
87/* This function causes the caller to sleep until the next release */
88typedef long (*complete_job_t) (void);
89
90typedef long (*admit_task_t)(struct task_struct* tsk);
91
92typedef void (*release_at_t)(struct task_struct *t, lt_t start);
93
94struct sched_plugin {
95 struct list_head list;
96 /* basic info */
97 char *plugin_name;
98
99 /* setup */
100 activate_plugin_t activate_plugin;
101 deactivate_plugin_t deactivate_plugin;
102
103#ifdef CONFIG_SRP
104 unsigned int srp_active;
105#endif
106
107 /* scheduler invocation */
108 scheduler_tick_t tick;
109 schedule_t schedule;
110 finish_switch_t finish_switch;
111
112 /* syscall backend */
113 complete_job_t complete_job;
114 release_at_t release_at;
115
116 /* task state changes */
117 admit_task_t admit_task;
118
119 task_new_t task_new;
120 task_wake_up_t task_wake_up;
121 task_block_t task_block;
122 task_exit_t task_exit;
123
124#ifdef CONFIG_FMLP
125 /* priority inheritance */
126 unsigned int fmlp_active;
127 inherit_priority_t inherit_priority;
128 return_priority_t return_priority;
129 pi_block_t pi_block;
130#endif
131} __attribute__ ((__aligned__(SMP_CACHE_BYTES)));
132
133
134extern struct sched_plugin *litmus;
135
136int register_sched_plugin(struct sched_plugin* plugin);
137struct sched_plugin* find_sched_plugin(const char* name);
138int print_sched_plugins(char* buf, int max);
139
140static inline int srp_active(void)
141{
142#ifdef CONFIG_SRP
143 return litmus->srp_active;
144#else
145 return 0;
146#endif
147}
148static inline int fmlp_active(void)
149{
150#ifdef CONFIG_FMLP
151 return litmus->fmlp_active;
152#else
153 return 0;
154#endif
155}
156
157extern struct sched_plugin linux_sched_plugin;
158
159#endif
diff --git a/include/litmus/sched_trace.h b/include/litmus/sched_trace.h
new file mode 100644
index 000000000000..afd0391d127b
--- /dev/null
+++ b/include/litmus/sched_trace.h
@@ -0,0 +1,191 @@
1/* sched_trace.h -- record scheduler events to a byte stream for offline analysis.
2 */
3#ifndef _LINUX_SCHED_TRACE_H_
4#define _LINUX_SCHED_TRACE_H_
5
6/* all times in nanoseconds */
7
8struct st_trace_header {
9 u8 type; /* Of what type is this record? */
10 u8 cpu; /* On which CPU was it recorded? */
11 u16 pid; /* PID of the task. */
12 u32 job; /* The job sequence number. */
13};
14
15#define ST_NAME_LEN 16
16struct st_name_data {
17 char cmd[ST_NAME_LEN];/* The name of the executable of this process. */
18};
19
20struct st_param_data { /* regular params */
21 u32 wcet;
22 u32 period;
23 u32 phase;
24 u8 partition;
25 u8 __unused[3];
26};
27
28struct st_release_data { /* A job is was/is going to be released. */
29 u64 release; /* What's the release time? */
30 u64 deadline; /* By when must it finish? */
31};
32
33struct st_assigned_data { /* A job was asigned to a CPU. */
34 u64 when;
35 u8 target; /* Where should it execute? */
36 u8 __unused[3];
37};
38
39struct st_switch_to_data { /* A process was switched to on a given CPU. */
40 u64 when; /* When did this occur? */
41 u32 exec_time; /* Time the current job has executed. */
42
43};
44
45struct st_switch_away_data { /* A process was switched away from on a given CPU. */
46 u64 when;
47 u64 exec_time;
48};
49
50struct st_completion_data { /* A job completed. */
51 u64 when;
52 u8 forced:1; /* Set to 1 if job overran and kernel advanced to the
53 * next task automatically; set to 0 otherwise.
54 */
55 u8 __uflags:7;
56 u8 __unused[3];
57};
58
59struct st_block_data { /* A task blocks. */
60 u64 when;
61 u64 __unused;
62};
63
64struct st_resume_data { /* A task resumes. */
65 u64 when;
66 u64 __unused;
67};
68
69struct st_sys_release_data {
70 u64 when;
71 u64 release;
72};
73
74#define DATA(x) struct st_ ## x ## _data x;
75
76typedef enum {
77 ST_NAME = 1, /* Start at one, so that we can spot
78 * uninitialized records. */
79 ST_PARAM,
80 ST_RELEASE,
81 ST_ASSIGNED,
82 ST_SWITCH_TO,
83 ST_SWITCH_AWAY,
84 ST_COMPLETION,
85 ST_BLOCK,
86 ST_RESUME,
87 ST_SYS_RELEASE,
88} st_event_record_type_t;
89
90struct st_event_record {
91 struct st_trace_header hdr;
92 union {
93 u64 raw[2];
94
95 DATA(name);
96 DATA(param);
97 DATA(release);
98 DATA(assigned);
99 DATA(switch_to);
100 DATA(switch_away);
101 DATA(completion);
102 DATA(block);
103 DATA(resume);
104 DATA(sys_release);
105
106 } data;
107};
108
109#undef DATA
110
111#ifdef __KERNEL__
112
113#include <linux/sched.h>
114#include <litmus/feather_trace.h>
115
116#ifdef CONFIG_SCHED_TASK_TRACE
117
118#define SCHED_TRACE(id, callback, task) \
119 ft_event1(id, callback, task)
120#define SCHED_TRACE2(id, callback, task, xtra) \
121 ft_event2(id, callback, task, xtra)
122
123/* provide prototypes; needed on sparc64 */
124#ifndef NO_TASK_TRACE_DECLS
125feather_callback void do_sched_trace_task_name(unsigned long id,
126 struct task_struct* task);
127feather_callback void do_sched_trace_task_param(unsigned long id,
128 struct task_struct* task);
129feather_callback void do_sched_trace_task_release(unsigned long id,
130 struct task_struct* task);
131feather_callback void do_sched_trace_task_switch_to(unsigned long id,
132 struct task_struct* task);
133feather_callback void do_sched_trace_task_switch_away(unsigned long id,
134 struct task_struct* task);
135feather_callback void do_sched_trace_task_completion(unsigned long id,
136 struct task_struct* task,
137 unsigned long forced);
138feather_callback void do_sched_trace_task_block(unsigned long id,
139 struct task_struct* task);
140feather_callback void do_sched_trace_task_resume(unsigned long id,
141 struct task_struct* task);
142feather_callback void do_sched_trace_sys_release(unsigned long id,
143 lt_t* start);
144#endif
145
146#else
147
148#define SCHED_TRACE(id, callback, task) /* no tracing */
149#define SCHED_TRACE2(id, callback, task, xtra) /* no tracing */
150
151#endif
152
153
154#define SCHED_TRACE_BASE_ID 500
155
156
157#define sched_trace_task_name(t) \
158 SCHED_TRACE(SCHED_TRACE_BASE_ID + 1, do_sched_trace_task_name, t)
159#define sched_trace_task_param(t) \
160 SCHED_TRACE(SCHED_TRACE_BASE_ID + 2, do_sched_trace_task_param, t)
161#define sched_trace_task_release(t) \
162 SCHED_TRACE(SCHED_TRACE_BASE_ID + 3, do_sched_trace_task_release, t)
163#define sched_trace_task_switch_to(t) \
164 SCHED_TRACE(SCHED_TRACE_BASE_ID + 4, do_sched_trace_task_switch_to, t)
165#define sched_trace_task_switch_away(t) \
166 SCHED_TRACE(SCHED_TRACE_BASE_ID + 5, do_sched_trace_task_switch_away, t)
167#define sched_trace_task_completion(t, forced) \
168 SCHED_TRACE2(SCHED_TRACE_BASE_ID + 6, do_sched_trace_task_completion, t, \
169 forced)
170#define sched_trace_task_block(t) \
171 SCHED_TRACE(SCHED_TRACE_BASE_ID + 7, do_sched_trace_task_block, t)
172#define sched_trace_task_resume(t) \
173 SCHED_TRACE(SCHED_TRACE_BASE_ID + 8, do_sched_trace_task_resume, t)
174
175#define sched_trace_sys_release(when) \
176 SCHED_TRACE(SCHED_TRACE_BASE_ID + 9, do_sched_trace_sys_release, when)
177
178#define sched_trace_quantum_boundary() /* NOT IMPLEMENTED */
179
180#ifdef CONFIG_SCHED_DEBUG_TRACE
181void sched_trace_log_message(const char* fmt, ...);
182void dump_trace_buffer(int max);
183#else
184
185#define sched_trace_log_message(fmt, ...)
186
187#endif
188
189#endif /* __KERNEL__ */
190
191#endif
diff --git a/include/litmus/trace.h b/include/litmus/trace.h
new file mode 100644
index 000000000000..e8e0c7b6cc6a
--- /dev/null
+++ b/include/litmus/trace.h
@@ -0,0 +1,113 @@
1#ifndef _SYS_TRACE_H_
2#define _SYS_TRACE_H_
3
4#ifdef CONFIG_SCHED_OVERHEAD_TRACE
5
6#include <litmus/feather_trace.h>
7#include <litmus/feather_buffer.h>
8
9
10/*********************** TIMESTAMPS ************************/
11
12enum task_type_marker {
13 TSK_BE,
14 TSK_RT,
15 TSK_UNKNOWN
16};
17
18struct timestamp {
19 uint64_t timestamp;
20 uint32_t seq_no;
21 uint8_t cpu;
22 uint8_t event;
23 uint8_t task_type;
24};
25
26/* tracing callbacks */
27feather_callback void save_timestamp(unsigned long event);
28feather_callback void save_timestamp_def(unsigned long event, unsigned long type);
29feather_callback void save_timestamp_task(unsigned long event, unsigned long t_ptr);
30feather_callback void save_timestamp_cpu(unsigned long event, unsigned long cpu);
31
32
33#define TIMESTAMP(id) ft_event0(id, save_timestamp)
34
35#define DTIMESTAMP(id, def) ft_event1(id, save_timestamp_def, def)
36
37#define TTIMESTAMP(id, task) \
38 ft_event1(id, save_timestamp_task, (unsigned long) task)
39
40#define CTIMESTAMP(id, cpu) \
41 ft_event1(id, save_timestamp_cpu, cpu)
42
43#else /* !CONFIG_SCHED_OVERHEAD_TRACE */
44
45#define TIMESTAMP(id) /* no tracing */
46
47#define DTIMESTAMP(id, def) /* no tracing */
48
49#define TTIMESTAMP(id, task) /* no tracing */
50
51#define CTIMESTAMP(id, cpu) /* no tracing */
52
53#endif
54
55
56/* Convention for timestamps
57 * =========================
58 *
59 * In order to process the trace files with a common tool, we use the following
60 * convention to measure execution times: The end time id of a code segment is
61 * always the next number after the start time event id.
62 */
63
64#define TS_SCHED_START DTIMESTAMP(100, TSK_UNKNOWN) /* we only
65 * care
66 * about
67 * next */
68#define TS_SCHED_END(t) TTIMESTAMP(101, t)
69#define TS_SCHED2_START(t) TTIMESTAMP(102, t)
70#define TS_SCHED2_END(t) TTIMESTAMP(103, t)
71
72#define TS_CXS_START(t) TTIMESTAMP(104, t)
73#define TS_CXS_END(t) TTIMESTAMP(105, t)
74
75#define TS_RELEASE_START DTIMESTAMP(106, TSK_RT)
76#define TS_RELEASE_END DTIMESTAMP(107, TSK_RT)
77
78#define TS_TICK_START(t) TTIMESTAMP(110, t)
79#define TS_TICK_END(t) TTIMESTAMP(111, t)
80
81
82#define TS_PLUGIN_SCHED_START /* TIMESTAMP(120) */ /* currently unused */
83#define TS_PLUGIN_SCHED_END /* TIMESTAMP(121) */
84
85#define TS_PLUGIN_TICK_START /* TIMESTAMP(130) */
86#define TS_PLUGIN_TICK_END /* TIMESTAMP(131) */
87
88#define TS_ENTER_NP_START TIMESTAMP(140)
89#define TS_ENTER_NP_END TIMESTAMP(141)
90
91#define TS_EXIT_NP_START TIMESTAMP(150)
92#define TS_EXIT_NP_END TIMESTAMP(151)
93
94#define TS_SRP_UP_START TIMESTAMP(160)
95#define TS_SRP_UP_END TIMESTAMP(161)
96#define TS_SRP_DOWN_START TIMESTAMP(162)
97#define TS_SRP_DOWN_END TIMESTAMP(163)
98
99#define TS_PI_UP_START TIMESTAMP(170)
100#define TS_PI_UP_END TIMESTAMP(171)
101#define TS_PI_DOWN_START TIMESTAMP(172)
102#define TS_PI_DOWN_END TIMESTAMP(173)
103
104#define TS_FIFO_UP_START TIMESTAMP(180)
105#define TS_FIFO_UP_END TIMESTAMP(181)
106#define TS_FIFO_DOWN_START TIMESTAMP(182)
107#define TS_FIFO_DOWN_END TIMESTAMP(183)
108
109#define TS_SEND_RESCHED_START(c) CTIMESTAMP(190, c)
110#define TS_SEND_RESCHED_END DTIMESTAMP(191, TSK_UNKNOWN)
111
112
113#endif /* !_SYS_TRACE_H_ */
diff --git a/kernel/fork.c b/kernel/fork.c
index 8dd8ff281009..4c322d4ee0d8 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -59,6 +59,9 @@
59#include <asm/cacheflush.h> 59#include <asm/cacheflush.h>
60#include <asm/tlbflush.h> 60#include <asm/tlbflush.h>
61 61
62#include <litmus/litmus.h>
63#include <litmus/sched_plugin.h>
64
62/* 65/*
63 * Protected counters by write_lock_irq(&tasklist_lock) 66 * Protected counters by write_lock_irq(&tasklist_lock)
64 */ 67 */
@@ -121,6 +124,8 @@ void __put_task_struct(struct task_struct *tsk)
121 WARN_ON(atomic_read(&tsk->usage)); 124 WARN_ON(atomic_read(&tsk->usage));
122 WARN_ON(tsk == current); 125 WARN_ON(tsk == current);
123 126
127 exit_litmus(tsk);
128
124 security_task_free(tsk); 129 security_task_free(tsk);
125 free_uid(tsk->user); 130 free_uid(tsk->user);
126 put_group_info(tsk->group_info); 131 put_group_info(tsk->group_info);
@@ -182,6 +187,9 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
182 *tsk = *orig; 187 *tsk = *orig;
183 tsk->stack = ti; 188 tsk->stack = ti;
184 189
190 /* Don't let the new task be a real-time task. */
191 memset(&tsk->rt_param, 0, sizeof(struct rt_task));
192
185 err = prop_local_init_single(&tsk->dirties); 193 err = prop_local_init_single(&tsk->dirties);
186 if (err) { 194 if (err) {
187 free_thread_info(ti); 195 free_thread_info(ti);
diff --git a/kernel/sched.c b/kernel/sched.c
index e76b11ca6df3..09b529cf2bd8 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -67,6 +67,9 @@
67#include <asm/tlb.h> 67#include <asm/tlb.h>
68#include <asm/irq_regs.h> 68#include <asm/irq_regs.h>
69 69
70#include <litmus/sched_trace.h>
71#include <litmus/trace.h>
72
70/* 73/*
71 * Scheduler clock - returns current time in nanosec units. 74 * Scheduler clock - returns current time in nanosec units.
72 * This is default implementation. 75 * This is default implementation.
@@ -324,6 +327,8 @@ struct rq {
324 327
325 atomic_t nr_iowait; 328 atomic_t nr_iowait;
326 329
330 struct task_struct* litmus_next;
331
327#ifdef CONFIG_SMP 332#ifdef CONFIG_SMP
328 struct sched_domain *sd; 333 struct sched_domain *sd;
329 334
@@ -875,11 +880,12 @@ static inline void cpuacct_charge(struct task_struct *tsk, u64 cputime) {}
875#include "sched_idletask.c" 880#include "sched_idletask.c"
876#include "sched_fair.c" 881#include "sched_fair.c"
877#include "sched_rt.c" 882#include "sched_rt.c"
883#include "../litmus/sched_litmus.c"
878#ifdef CONFIG_SCHED_DEBUG 884#ifdef CONFIG_SCHED_DEBUG
879# include "sched_debug.c" 885# include "sched_debug.c"
880#endif 886#endif
881 887
882#define sched_class_highest (&rt_sched_class) 888#define sched_class_highest (&litmus_sched_class)
883 889
884/* 890/*
885 * Update delta_exec, delta_fair fields for rq. 891 * Update delta_exec, delta_fair fields for rq.
@@ -1516,6 +1522,8 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)
1516 int new_cpu; 1522 int new_cpu;
1517#endif 1523#endif
1518 1524
1525 if (is_realtime(p))
1526 TRACE_TASK(p, "try_to_wake_up() state:%d\n", p->state);
1519 rq = task_rq_lock(p, &flags); 1527 rq = task_rq_lock(p, &flags);
1520 old_state = p->state; 1528 old_state = p->state;
1521 if (!(old_state & state)) 1529 if (!(old_state & state))
@@ -1529,7 +1537,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)
1529 this_cpu = smp_processor_id(); 1537 this_cpu = smp_processor_id();
1530 1538
1531#ifdef CONFIG_SMP 1539#ifdef CONFIG_SMP
1532 if (unlikely(task_running(rq, p))) 1540 if (unlikely(task_running(rq, p) || is_realtime(p)))
1533 goto out_activate; 1541 goto out_activate;
1534 1542
1535 new_cpu = cpu; 1543 new_cpu = cpu;
@@ -1650,8 +1658,9 @@ out_activate:
1650out_running: 1658out_running:
1651 p->state = TASK_RUNNING; 1659 p->state = TASK_RUNNING;
1652out: 1660out:
1661 if (is_realtime(p))
1662 TRACE_TASK(p, "try_to_wake_up() done state:%d\n", p->state);
1653 task_rq_unlock(rq, &flags); 1663 task_rq_unlock(rq, &flags);
1654
1655 return success; 1664 return success;
1656} 1665}
1657 1666
@@ -1890,6 +1899,8 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
1890 */ 1899 */
1891 prev_state = prev->state; 1900 prev_state = prev->state;
1892 finish_arch_switch(prev); 1901 finish_arch_switch(prev);
1902 litmus->finish_switch(prev);
1903 prev->rt_param.stack_in_use = NO_CPU;
1893 finish_lock_switch(rq, prev); 1904 finish_lock_switch(rq, prev);
1894 fire_sched_in_preempt_notifiers(current); 1905 fire_sched_in_preempt_notifiers(current);
1895 if (mm) 1906 if (mm)
@@ -3480,6 +3491,7 @@ void scheduler_tick(void)
3480 struct task_struct *curr = rq->curr; 3491 struct task_struct *curr = rq->curr;
3481 u64 next_tick = rq->tick_timestamp + TICK_NSEC; 3492 u64 next_tick = rq->tick_timestamp + TICK_NSEC;
3482 3493
3494 TS_TICK_START(current);
3483 spin_lock(&rq->lock); 3495 spin_lock(&rq->lock);
3484 __update_rq_clock(rq); 3496 __update_rq_clock(rq);
3485 /* 3497 /*
@@ -3491,12 +3503,17 @@ void scheduler_tick(void)
3491 update_cpu_load(rq); 3503 update_cpu_load(rq);
3492 if (curr != rq->idle) /* FIXME: needed? */ 3504 if (curr != rq->idle) /* FIXME: needed? */
3493 curr->sched_class->task_tick(rq, curr); 3505 curr->sched_class->task_tick(rq, curr);
3506 TS_PLUGIN_TICK_START;
3507 litmus_tick(rq, curr);
3508 TS_PLUGIN_TICK_END;
3494 spin_unlock(&rq->lock); 3509 spin_unlock(&rq->lock);
3495 3510
3496#ifdef CONFIG_SMP 3511#ifdef CONFIG_SMP
3497 rq->idle_at_tick = idle_cpu(cpu); 3512 rq->idle_at_tick = idle_cpu(cpu);
3498 trigger_load_balance(rq, cpu); 3513 if (!is_realtime(current))
3514 trigger_load_balance(rq, cpu);
3499#endif 3515#endif
3516 TS_TICK_END(current);
3500} 3517}
3501 3518
3502#if defined(CONFIG_PREEMPT) && defined(CONFIG_DEBUG_PREEMPT) 3519#if defined(CONFIG_PREEMPT) && defined(CONFIG_DEBUG_PREEMPT)
@@ -3594,11 +3611,13 @@ pick_next_task(struct rq *rq, struct task_struct *prev)
3594 * Optimization: we know that if all tasks are in 3611 * Optimization: we know that if all tasks are in
3595 * the fair class we can call that function directly: 3612 * the fair class we can call that function directly:
3596 */ 3613 */
3597 if (likely(rq->nr_running == rq->cfs.nr_running)) { 3614 /* Don't do that for LITMUS.
3615 if (likely(rq->nr_running == rq->cfs.nr_running)) {
3598 p = fair_sched_class.pick_next_task(rq); 3616 p = fair_sched_class.pick_next_task(rq);
3599 if (likely(p)) 3617 if (likely(p))
3600 return p; 3618 return p;
3601 } 3619 }
3620 */
3602 3621
3603 class = sched_class_highest; 3622 class = sched_class_highest;
3604 for ( ; ; ) { 3623 for ( ; ; ) {
@@ -3633,6 +3652,9 @@ need_resched:
3633 3652
3634 release_kernel_lock(prev); 3653 release_kernel_lock(prev);
3635need_resched_nonpreemptible: 3654need_resched_nonpreemptible:
3655 TS_SCHED_START;
3656
3657 sched_trace_task_switch_away(prev);
3636 3658
3637 schedule_debug(prev); 3659 schedule_debug(prev);
3638 3660
@@ -3643,6 +3665,9 @@ need_resched_nonpreemptible:
3643 __update_rq_clock(rq); 3665 __update_rq_clock(rq);
3644 spin_lock(&rq->lock); 3666 spin_lock(&rq->lock);
3645 clear_tsk_need_resched(prev); 3667 clear_tsk_need_resched(prev);
3668 TS_PLUGIN_SCHED_START;
3669 litmus_schedule(rq, prev);
3670 TS_PLUGIN_SCHED_END;
3646 3671
3647 if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { 3672 if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
3648 if (unlikely((prev->state & TASK_INTERRUPTIBLE) && 3673 if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
@@ -3667,18 +3692,30 @@ need_resched_nonpreemptible:
3667 rq->curr = next; 3692 rq->curr = next;
3668 ++*switch_count; 3693 ++*switch_count;
3669 3694
3695 TS_SCHED_END(next);
3696 TS_CXS_START(next);
3670 context_switch(rq, prev, next); /* unlocks the rq */ 3697 context_switch(rq, prev, next); /* unlocks the rq */
3671 } else 3698 TS_CXS_END(current);
3699 } else {
3700 TS_SCHED_END(prev);
3672 spin_unlock_irq(&rq->lock); 3701 spin_unlock_irq(&rq->lock);
3702 }
3703 TS_SCHED2_START(current);
3704
3705 sched_trace_task_switch_to(current);
3673 3706
3674 if (unlikely(reacquire_kernel_lock(current) < 0)) { 3707 if (unlikely(reacquire_kernel_lock(current) < 0)) {
3675 cpu = smp_processor_id(); 3708 cpu = smp_processor_id();
3676 rq = cpu_rq(cpu); 3709 rq = cpu_rq(cpu);
3710 TS_SCHED2_END(current);
3677 goto need_resched_nonpreemptible; 3711 goto need_resched_nonpreemptible;
3678 } 3712 }
3679 preempt_enable_no_resched(); 3713 preempt_enable_no_resched();
3680 if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) 3714 if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) {
3715 TS_SCHED2_END(current);
3681 goto need_resched; 3716 goto need_resched;
3717 }
3718 TS_SCHED2_END(current);
3682} 3719}
3683EXPORT_SYMBOL(schedule); 3720EXPORT_SYMBOL(schedule);
3684 3721
@@ -4236,6 +4273,9 @@ __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
4236 case SCHED_RR: 4273 case SCHED_RR:
4237 p->sched_class = &rt_sched_class; 4274 p->sched_class = &rt_sched_class;
4238 break; 4275 break;
4276 case SCHED_LITMUS:
4277 p->sched_class = &litmus_sched_class;
4278 break;
4239 } 4279 }
4240 4280
4241 p->rt_priority = prio; 4281 p->rt_priority = prio;
@@ -4268,7 +4308,7 @@ recheck:
4268 policy = oldpolicy = p->policy; 4308 policy = oldpolicy = p->policy;
4269 else if (policy != SCHED_FIFO && policy != SCHED_RR && 4309 else if (policy != SCHED_FIFO && policy != SCHED_RR &&
4270 policy != SCHED_NORMAL && policy != SCHED_BATCH && 4310 policy != SCHED_NORMAL && policy != SCHED_BATCH &&
4271 policy != SCHED_IDLE) 4311 policy != SCHED_IDLE && policy != SCHED_LITMUS)
4272 return -EINVAL; 4312 return -EINVAL;
4273 /* 4313 /*
4274 * Valid priorities for SCHED_FIFO and SCHED_RR are 4314 * Valid priorities for SCHED_FIFO and SCHED_RR are
@@ -4282,6 +4322,9 @@ recheck:
4282 if (rt_policy(policy) != (param->sched_priority != 0)) 4322 if (rt_policy(policy) != (param->sched_priority != 0))
4283 return -EINVAL; 4323 return -EINVAL;
4284 4324
4325 if (policy == SCHED_LITMUS && policy == p->policy)
4326 return -EINVAL;
4327
4285 /* 4328 /*
4286 * Allow unprivileged RT tasks to decrease priority: 4329 * Allow unprivileged RT tasks to decrease priority:
4287 */ 4330 */
@@ -4316,6 +4359,12 @@ recheck:
4316 return -EPERM; 4359 return -EPERM;
4317 } 4360 }
4318 4361
4362 if (policy == SCHED_LITMUS) {
4363 retval = litmus_admit_task(p);
4364 if (retval)
4365 return retval;
4366 }
4367
4319 retval = security_task_setscheduler(p, policy, param); 4368 retval = security_task_setscheduler(p, policy, param);
4320 if (retval) 4369 if (retval)
4321 return retval; 4370 return retval;
@@ -4345,9 +4394,18 @@ recheck:
4345 p->sched_class->put_prev_task(rq, p); 4394 p->sched_class->put_prev_task(rq, p);
4346 } 4395 }
4347 4396
4397 if (p->policy == SCHED_LITMUS)
4398 litmus_exit_task(p);
4399
4348 oldprio = p->prio; 4400 oldprio = p->prio;
4349 __setscheduler(rq, p, policy, param->sched_priority); 4401 __setscheduler(rq, p, policy, param->sched_priority);
4350 4402
4403 if (policy == SCHED_LITMUS) {
4404 p->rt_param.stack_in_use = running ? rq->cpu : NO_CPU;
4405 p->rt_param.present = running;
4406 litmus->task_new(p, on_rq, running);
4407 }
4408
4351 if (on_rq) { 4409 if (on_rq) {
4352 if (running) 4410 if (running)
4353 p->sched_class->set_curr_task(rq); 4411 p->sched_class->set_curr_task(rq);
@@ -4364,6 +4422,7 @@ recheck:
4364 check_preempt_curr(rq, p); 4422 check_preempt_curr(rq, p);
4365 } 4423 }
4366 } 4424 }
4425
4367 __task_rq_unlock(rq); 4426 __task_rq_unlock(rq);
4368 spin_unlock_irqrestore(&p->pi_lock, flags); 4427 spin_unlock_irqrestore(&p->pi_lock, flags);
4369 4428
@@ -4494,10 +4553,11 @@ long sched_setaffinity(pid_t pid, cpumask_t new_mask)
4494 read_lock(&tasklist_lock); 4553 read_lock(&tasklist_lock);
4495 4554
4496 p = find_process_by_pid(pid); 4555 p = find_process_by_pid(pid);
4497 if (!p) { 4556 if (!p || is_realtime(p)) {
4557 /* LITMUS tasks don't get to do this, transition to BE first */
4498 read_unlock(&tasklist_lock); 4558 read_unlock(&tasklist_lock);
4499 mutex_unlock(&sched_hotcpu_mutex); 4559 mutex_unlock(&sched_hotcpu_mutex);
4500 return -ESRCH; 4560 return p ? -EPERM : -ESRCH;
4501 } 4561 }
4502 4562
4503 /* 4563 /*
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c
index da7c061e7206..de304962633d 100644
--- a/kernel/sched_fair.c
+++ b/kernel/sched_fair.c
@@ -845,7 +845,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
845 struct sched_entity *se = &curr->se, *pse = &p->se; 845 struct sched_entity *se = &curr->se, *pse = &p->se;
846 unsigned long gran; 846 unsigned long gran;
847 847
848 if (unlikely(rt_prio(p->prio))) { 848 if (unlikely(rt_prio(p->prio) || p->policy == SCHED_LITMUS)) {
849 update_rq_clock(rq); 849 update_rq_clock(rq);
850 update_curr(cfs_rq); 850 update_curr(cfs_rq);
851 resched_task(curr); 851 resched_task(curr);
diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c
index 9ba3daa03475..c7c938cee243 100644
--- a/kernel/sched_rt.c
+++ b/kernel/sched_rt.c
@@ -70,7 +70,7 @@ yield_task_rt(struct rq *rq)
70 */ 70 */
71static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p) 71static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p)
72{ 72{
73 if (p->prio < rq->curr->prio) 73 if (p->prio < rq->curr->prio || p->policy == SCHED_LITMUS)
74 resched_task(rq->curr); 74 resched_task(rq->curr);
75} 75}
76 76
diff --git a/litmus/Kconfig b/litmus/Kconfig
new file mode 100644
index 000000000000..f8c642658a2f
--- /dev/null
+++ b/litmus/Kconfig
@@ -0,0 +1,50 @@
1menu "LITMUS^RT"
2
3menu "Tracing"
4
5config FEATHER_TRACE
6 bool "Feather-Trace Infrastructure"
7 default y
8 help
9 Feather-Trace basic tracing infrastructure. Includes device file
10 driver and instrumentation point support.
11
12
13config SCHED_TASK_TRACE
14 bool "Trace real-time tasks"
15 depends on FEATHER_TRACE
16 default y
17 help
18 Include support for the sched_trace_XXX() tracing functions. This
19 allows the collection of real-time task events such as job
20 completions, job releases, early completions, etc. This results in a
21 small overhead in the scheduling code. Disable if the overhead is not
22 acceptable (e.g., benchmarking).
23
24 Say Yes for debugging.
25 Say No for overhead tracing.
26
27config SCHED_OVERHEAD_TRACE
28 bool "Record timestamps for overhead measurements"
29 depends on FEATHER_TRACE
30 default n
31 help
32 Export event stream for overhead tracing.
33 Say Yes for overhead tracing.
34
35config SCHED_DEBUG_TRACE
36 bool "TRACE() debugging"
37 default y
38 help
39 Include support for sched_trace_log_messageg(), which is used to
40 implement TRACE(). If disabled, no TRACE() messages will be included
41 in the kernel, and no overheads due to debugging statements will be
42 incurred by the scheduler. Disable if the overhead is not acceptable
43 (e.g. benchmarking).
44
45 Say Yes for debugging.
46 Say No for overhead tracing.
47
48endmenu
49
50endmenu
diff --git a/litmus/Makefile b/litmus/Makefile
new file mode 100644
index 000000000000..f4c2d564cd0b
--- /dev/null
+++ b/litmus/Makefile
@@ -0,0 +1,12 @@
1#
2# Makefile for LITMUS^RT
3#
4
5obj-y = sched_plugin.o litmus.o \
6 jobs.o \
7 heap.o
8
9obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o
10obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o
11obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o
12obj-$(CONFIG_SCHED_OVERHEAD_TRACE) += trace.o
diff --git a/litmus/ft_event.c b/litmus/ft_event.c
new file mode 100644
index 000000000000..6084b6d6b364
--- /dev/null
+++ b/litmus/ft_event.c
@@ -0,0 +1,43 @@
1#include <linux/types.h>
2
3#include <litmus/feather_trace.h>
4
5#ifndef __ARCH_HAS_FEATHER_TRACE
6/* provide dummy implementation */
7
8int ft_events[MAX_EVENTS];
9
10int ft_enable_event(unsigned long id)
11{
12 if (id < MAX_EVENTS) {
13 ft_events[id]++;
14 return 1;
15 } else
16 return 0;
17}
18
19int ft_disable_event(unsigned long id)
20{
21 if (id < MAX_EVENTS && ft_events[id]) {
22 ft_events[id]--;
23 return 1;
24 } else
25 return 0;
26}
27
28int ft_disable_all_events(void)
29{
30 int i;
31
32 for (i = 0; i < MAX_EVENTS; i++)
33 ft_events[i] = 0;
34
35 return MAX_EVENTS;
36}
37
38int ft_is_event_enabled(unsigned long id)
39{
40 return id < MAX_EVENTS && ft_events[id];
41}
42
43#endif
diff --git a/litmus/heap.c b/litmus/heap.c
new file mode 100644
index 000000000000..112d14da46c3
--- /dev/null
+++ b/litmus/heap.c
@@ -0,0 +1,314 @@
1#include "linux/kernel.h"
2#include "litmus/heap.h"
3
4void heap_init(struct heap* heap)
5{
6 heap->head = NULL;
7 heap->min = NULL;
8}
9
10void heap_node_init(struct heap_node** _h, void* value)
11{
12 struct heap_node* h = *_h;
13 h->parent = NULL;
14 h->next = NULL;
15 h->child = NULL;
16 h->degree = NOT_IN_HEAP;
17 h->value = value;
18 h->ref = _h;
19}
20
21
22/* make child a subtree of root */
23static void __heap_link(struct heap_node* root,
24 struct heap_node* child)
25{
26 child->parent = root;
27 child->next = root->child;
28 root->child = child;
29 root->degree++;
30}
31
32/* merge root lists */
33static struct heap_node* __heap_merge(struct heap_node* a,
34 struct heap_node* b)
35{
36 struct heap_node* head = NULL;
37 struct heap_node** pos = &head;
38
39 while (a && b) {
40 if (a->degree < b->degree) {
41 *pos = a;
42 a = a->next;
43 } else {
44 *pos = b;
45 b = b->next;
46 }
47 pos = &(*pos)->next;
48 }
49 if (a)
50 *pos = a;
51 else
52 *pos = b;
53 return head;
54}
55
56/* reverse a linked list of nodes. also clears parent pointer */
57static struct heap_node* __heap_reverse(struct heap_node* h)
58{
59 struct heap_node* tail = NULL;
60 struct heap_node* next;
61
62 if (!h)
63 return h;
64
65 h->parent = NULL;
66 while (h->next) {
67 next = h->next;
68 h->next = tail;
69 tail = h;
70 h = next;
71 h->parent = NULL;
72 }
73 h->next = tail;
74 return h;
75}
76
77static void __heap_min(heap_prio_t higher_prio, struct heap* heap,
78 struct heap_node** prev, struct heap_node** node)
79{
80 struct heap_node *_prev, *cur;
81 *prev = NULL;
82
83 if (!heap->head) {
84 *node = NULL;
85 return;
86 }
87
88 *node = heap->head;
89 _prev = heap->head;
90 cur = heap->head->next;
91 while (cur) {
92 if (higher_prio(cur, *node)) {
93 *node = cur;
94 *prev = _prev;
95 }
96 _prev = cur;
97 cur = cur->next;
98 }
99}
100
101static void __heap_union(heap_prio_t higher_prio, struct heap* heap,
102 struct heap_node* h2)
103{
104 struct heap_node* h1;
105 struct heap_node *prev, *x, *next;
106 if (!h2)
107 return;
108 h1 = heap->head;
109 if (!h1) {
110 heap->head = h2;
111 return;
112 }
113 h1 = __heap_merge(h1, h2);
114 prev = NULL;
115 x = h1;
116 next = x->next;
117 while (next) {
118 if (x->degree != next->degree ||
119 (next->next && next->next->degree == x->degree)) {
120 /* nothing to do, advance */
121 prev = x;
122 x = next;
123 } else if (higher_prio(x, next)) {
124 /* x becomes the root of next */
125 x->next = next->next;
126 __heap_link(x, next);
127 } else {
128 /* next becomes the root of x */
129 if (prev)
130 prev->next = next;
131 else
132 h1 = next;
133 __heap_link(next, x);
134 x = next;
135 }
136 next = x->next;
137 }
138 heap->head = h1;
139}
140
141static struct heap_node* __heap_extract_min(heap_prio_t higher_prio,
142 struct heap* heap)
143{
144 struct heap_node *prev, *node;
145 __heap_min(higher_prio, heap, &prev, &node);
146 if (!node)
147 return NULL;
148 if (prev)
149 prev->next = node->next;
150 else
151 heap->head = node->next;
152 __heap_union(higher_prio, heap, __heap_reverse(node->child));
153 return node;
154}
155
156/* insert (and reinitialize) a node into the heap */
157void heap_insert(heap_prio_t higher_prio, struct heap* heap,
158 struct heap_node* node)
159{
160 struct heap_node *min;
161 node->child = NULL;
162 node->parent = NULL;
163 node->next = NULL;
164 node->degree = 0;
165 if (heap->min && higher_prio(node, heap->min)) {
166 /* swap min cache */
167 min = heap->min;
168 min->child = NULL;
169 min->parent = NULL;
170 min->next = NULL;
171 min->degree = 0;
172 __heap_union(higher_prio, heap, min);
173 heap->min = node;
174 } else
175 __heap_union(higher_prio, heap, node);
176}
177
178void heap_uncache_min(heap_prio_t higher_prio, struct heap* heap)
179{
180 struct heap_node* min;
181 if (heap->min) {
182 min = heap->min;
183 heap->min = NULL;
184 heap_insert(higher_prio, heap, min);
185 }
186}
187
188/* merge addition into target */
189void heap_union(heap_prio_t higher_prio,
190 struct heap* target, struct heap* addition)
191{
192 /* first insert any cached minima, if necessary */
193 heap_uncache_min(higher_prio, target);
194 heap_uncache_min(higher_prio, addition);
195 __heap_union(higher_prio, target, addition->head);
196 /* this is a destructive merge */
197 addition->head = NULL;
198}
199
200struct heap_node* heap_peek(heap_prio_t higher_prio,
201 struct heap* heap)
202{
203 if (!heap->min)
204 heap->min = __heap_extract_min(higher_prio, heap);
205 return heap->min;
206}
207
208struct heap_node* heap_take(heap_prio_t higher_prio,
209 struct heap* heap)
210{
211 struct heap_node *node;
212 if (!heap->min)
213 heap->min = __heap_extract_min(higher_prio, heap);
214 node = heap->min;
215 heap->min = NULL;
216 if (node)
217 node->degree = NOT_IN_HEAP;
218 return node;
219}
220
221int heap_decrease(heap_prio_t higher_prio, struct heap_node* node)
222{
223 struct heap_node *parent;
224 struct heap_node** tmp_ref;
225 void* tmp;
226
227 /* bubble up */
228 parent = node->parent;
229 while (parent && higher_prio(node, parent)) {
230 /* swap parent and node */
231 tmp = parent->value;
232 parent->value = node->value;
233 node->value = tmp;
234 /* swap references */
235 *(parent->ref) = node;
236 *(node->ref) = parent;
237 tmp_ref = parent->ref;
238 parent->ref = node->ref;
239 node->ref = tmp_ref;
240 /* step up */
241 node = parent;
242 parent = node->parent;
243 }
244
245 return parent != NULL;
246}
247
248void heap_delete(heap_prio_t higher_prio, struct heap* heap,
249 struct heap_node* node)
250{
251 struct heap_node *parent, *prev, *pos;
252 struct heap_node** tmp_ref;
253 void* tmp;
254
255 if (heap->min != node) {
256 /* bubble up */
257 parent = node->parent;
258 while (parent) {
259 /* swap parent and node */
260 tmp = parent->value;
261 parent->value = node->value;
262 node->value = tmp;
263 /* swap references */
264 *(parent->ref) = node;
265 *(node->ref) = parent;
266 tmp_ref = parent->ref;
267 parent->ref = node->ref;
268 node->ref = tmp_ref;
269 /* step up */
270 node = parent;
271 parent = node->parent;
272 }
273 /* now delete:
274 * first find prev */
275 prev = NULL;
276 pos = heap->head;
277 while (pos != node) {
278 prev = pos;
279 pos = pos->next;
280 }
281 /* we have prev, now remove node */
282 if (prev)
283 prev->next = node->next;
284 else
285 heap->head = node->next;
286 __heap_union(higher_prio, heap, __heap_reverse(node->child));
287 } else
288 heap->min = NULL;
289 node->degree = NOT_IN_HEAP;
290}
291
292/* allocate a heap node for value and insert into the heap */
293int heap_add(heap_prio_t higher_prio, struct heap* heap,
294 void* value, int gfp_flags)
295{
296 struct heap_node* hn = heap_node_alloc(gfp_flags);
297 if (likely(hn)) {
298 heap_node_init(&hn, value);
299 heap_insert(higher_prio, heap, hn);
300 }
301 return hn != NULL;
302}
303
304void* heap_take_del(heap_prio_t higher_prio,
305 struct heap* heap)
306{
307 struct heap_node* hn = heap_take(higher_prio, heap);
308 void* ret = NULL;
309 if (hn) {
310 ret = hn->value;
311 heap_node_free(hn);
312 }
313 return ret;
314}
diff --git a/litmus/jobs.c b/litmus/jobs.c
new file mode 100644
index 000000000000..e294bc5b1246
--- /dev/null
+++ b/litmus/jobs.c
@@ -0,0 +1,43 @@
1/* litmus/jobs.c - common job control code
2 */
3
4#include <linux/sched.h>
5
6#include <litmus/litmus.h>
7#include <litmus/jobs.h>
8
9void prepare_for_next_period(struct task_struct *t)
10{
11 BUG_ON(!t);
12 /* prepare next release */
13 t->rt_param.job_params.release = t->rt_param.job_params.deadline;
14 t->rt_param.job_params.deadline += get_rt_period(t);
15 t->rt_param.job_params.exec_time = 0;
16 /* update job sequence number */
17 t->rt_param.job_params.job_no++;
18
19 /* don't confuse Linux */
20 t->time_slice = 1;
21}
22
23void release_at(struct task_struct *t, lt_t start)
24{
25 t->rt_param.job_params.deadline = start;
26 prepare_for_next_period(t);
27 set_rt_flags(t, RT_F_RUNNING);
28}
29
30
31/*
32 * Deactivate current task until the beginning of the next period.
33 */
34long complete_job(void)
35{
36 /* Mark that we do not excute anymore */
37 set_rt_flags(current, RT_F_SLEEP);
38 /* call schedule, this will return when a new job arrives
39 * it also takes care of preparing for the next release
40 */
41 schedule();
42 return 0;
43}
diff --git a/litmus/litmus.c b/litmus/litmus.c
new file mode 100644
index 000000000000..dd3867715af6
--- /dev/null
+++ b/litmus/litmus.c
@@ -0,0 +1,650 @@
1/* litmus.c -- Implementation of the LITMUS syscalls, the LITMUS intialization code,
2 * and the procfs interface..
3 */
4#include <asm/uaccess.h>
5#include <linux/uaccess.h>
6#include <linux/sysrq.h>
7
8#include <linux/module.h>
9#include <linux/proc_fs.h>
10#include <linux/slab.h>
11
12#include <litmus/litmus.h>
13#include <linux/sched.h>
14#include <litmus/sched_plugin.h>
15
16#include <litmus/heap.h>
17
18#include <litmus/trace.h>
19
20/* Number of RT tasks that exist in the system */
21atomic_t rt_task_count = ATOMIC_INIT(0);
22static DEFINE_SPINLOCK(task_transition_lock);
23
24/* Give log messages sequential IDs. */
25atomic_t __log_seq_no = ATOMIC_INIT(0);
26
27/* current master CPU for handling timer IRQs */
28atomic_t release_master_cpu = ATOMIC_INIT(NO_CPU);
29
30static struct kmem_cache * heap_node_cache;
31
32struct heap_node* heap_node_alloc(int gfp_flags)
33{
34 return kmem_cache_alloc(heap_node_cache, gfp_flags);
35}
36
37void heap_node_free(struct heap_node* hn)
38{
39 kmem_cache_free(heap_node_cache, hn);
40}
41
42/*
43 * sys_set_task_rt_param
44 * @pid: Pid of the task which scheduling parameters must be changed
45 * @param: New real-time extension parameters such as the execution cost and
46 * period
47 * Syscall for manipulating with task rt extension params
48 * Returns EFAULT if param is NULL.
49 * ESRCH if pid is not corrsponding
50 * to a valid task.
51 * EINVAL if either period or execution cost is <=0
52 * EPERM if pid is a real-time task
53 * 0 if success
54 *
55 * Only non-real-time tasks may be configured with this system call
56 * to avoid races with the scheduler. In practice, this means that a
57 * task's parameters must be set _before_ calling sys_prepare_rt_task()
58 */
59asmlinkage long sys_set_rt_task_param(pid_t pid, struct rt_task __user * param)
60{
61 struct rt_task tp;
62 struct task_struct *target;
63 int retval = -EINVAL;
64
65 printk("Setting up rt task parameters for process %d.\n", pid);
66
67 if (pid < 0 || param == 0) {
68 goto out;
69 }
70 if (copy_from_user(&tp, param, sizeof(tp))) {
71 retval = -EFAULT;
72 goto out;
73 }
74
75 /* Task search and manipulation must be protected */
76 read_lock_irq(&tasklist_lock);
77 if (!(target = find_task_by_pid(pid))) {
78 retval = -ESRCH;
79 goto out_unlock;
80 }
81
82 if (is_realtime(target)) {
83 /* The task is already a real-time task.
84 * We cannot not allow parameter changes at this point.
85 */
86 retval = -EBUSY;
87 goto out_unlock;
88 }
89
90 if (tp.exec_cost <= 0)
91 goto out_unlock;
92 if (tp.period <= 0)
93 goto out_unlock;
94 if (!cpu_online(tp.cpu))
95 goto out_unlock;
96 if (tp.period < tp.exec_cost)
97 {
98 printk(KERN_INFO "litmus: real-time task %d rejected "
99 "because wcet > period\n", pid);
100 goto out_unlock;
101 }
102
103 target->rt_param.task_params = tp;
104
105 retval = 0;
106 out_unlock:
107 read_unlock_irq(&tasklist_lock);
108 out:
109 return retval;
110}
111
112/* Getter of task's RT params
113 * returns EINVAL if param or pid is NULL
114 * returns ESRCH if pid does not correspond to a valid task
115 * returns EFAULT if copying of parameters has failed.
116 */
117asmlinkage long sys_get_rt_task_param(pid_t pid, struct rt_task __user * param)
118{
119 int retval = -EINVAL;
120 struct task_struct *source;
121 struct rt_task lp;
122 if (param == 0 || pid < 0)
123 goto out;
124 read_lock(&tasklist_lock);
125 if (!(source = find_task_by_pid(pid))) {
126 retval = -ESRCH;
127 goto out_unlock;
128 }
129 lp = source->rt_param.task_params;
130 read_unlock(&tasklist_lock);
131 /* Do copying outside the lock */
132 retval =
133 copy_to_user(param, &lp, sizeof(lp)) ? -EFAULT : 0;
134 return retval;
135 out_unlock:
136 read_unlock(&tasklist_lock);
137 out:
138 return retval;
139
140}
141
142/*
143 * This is the crucial function for periodic task implementation,
144 * It checks if a task is periodic, checks if such kind of sleep
145 * is permitted and calls plugin-specific sleep, which puts the
146 * task into a wait array.
147 * returns 0 on successful wakeup
148 * returns EPERM if current conditions do not permit such sleep
149 * returns EINVAL if current task is not able to go to sleep
150 */
151asmlinkage long sys_complete_job(void)
152{
153 int retval = -EPERM;
154 if (!is_realtime(current)) {
155 retval = -EINVAL;
156 goto out;
157 }
158 /* Task with negative or zero period cannot sleep */
159 if (get_rt_period(current) <= 0) {
160 retval = -EINVAL;
161 goto out;
162 }
163 /* The plugin has to put the task into an
164 * appropriate queue and call schedule
165 */
166 retval = litmus->complete_job();
167 out:
168 return retval;
169}
170
171/* This is an "improved" version of sys_complete_job that
172 * addresses the problem of unintentionally missing a job after
173 * an overrun.
174 *
175 * returns 0 on successful wakeup
176 * returns EPERM if current conditions do not permit such sleep
177 * returns EINVAL if current task is not able to go to sleep
178 */
179asmlinkage long sys_wait_for_job_release(unsigned int job)
180{
181 int retval = -EPERM;
182 if (!is_realtime(current)) {
183 retval = -EINVAL;
184 goto out;
185 }
186
187 /* Task with negative or zero period cannot sleep */
188 if (get_rt_period(current) <= 0) {
189 retval = -EINVAL;
190 goto out;
191 }
192
193 retval = 0;
194
195 /* first wait until we have "reached" the desired job
196 *
197 * This implementation has at least two problems:
198 *
199 * 1) It doesn't gracefully handle the wrap around of
200 * job_no. Since LITMUS is a prototype, this is not much
201 * of a problem right now.
202 *
203 * 2) It is theoretically racy if a job release occurs
204 * between checking job_no and calling sleep_next_period().
205 * A proper solution would requiring adding another callback
206 * in the plugin structure and testing the condition with
207 * interrupts disabled.
208 *
209 * FIXME: At least problem 2 should be taken care of eventually.
210 */
211 while (!retval && job > current->rt_param.job_params.job_no)
212 /* If the last job overran then job <= job_no and we
213 * don't send the task to sleep.
214 */
215 retval = litmus->complete_job();
216 out:
217 return retval;
218}
219
220/* This is a helper syscall to query the current job sequence number.
221 *
222 * returns 0 on successful query
223 * returns EPERM if task is not a real-time task.
224 * returns EFAULT if &job is not a valid pointer.
225 */
226asmlinkage long sys_query_job_no(unsigned int __user *job)
227{
228 int retval = -EPERM;
229 if (is_realtime(current))
230 retval = put_user(current->rt_param.job_params.job_no, job);
231
232 return retval;
233}
234
235/* sys_null_call() is only used for determining raw system call
236 * overheads (kernel entry, kernel exit). It has no useful side effects.
237 * If ts is non-NULL, then the current Feather-Trace time is recorded.
238 */
239asmlinkage long sys_null_call(cycles_t __user *ts)
240{
241 long ret = 0;
242 cycles_t now;
243
244 if (ts) {
245 now = get_cycles();
246 ret = put_user(now, ts);
247 }
248
249 return ret;
250}
251
252/* p is a real-time task. Re-init its state as a best-effort task. */
253static void reinit_litmus_state(struct task_struct* p, int restore)
254{
255 struct rt_task user_config = {};
256 __user short *np_flag = NULL;
257
258 if (restore) {
259 /* Safe user-space provided configuration data. */
260 user_config = p->rt_param.task_params;
261 np_flag = p->rt_param.np_flag;
262 }
263
264 /* We probably should not be inheriting any task's priority
265 * at this point in time.
266 */
267 WARN_ON(p->rt_param.inh_task);
268
269 /* We need to restore the priority of the task. */
270// __setscheduler(p, p->rt_param.old_policy, p->rt_param.old_prio);
271
272 /* Cleanup everything else. */
273 memset(&p->rt_param, 0, sizeof(user_config));
274
275 /* Restore preserved fields. */
276 if (restore) {
277 p->rt_param.task_params = user_config;
278 p->rt_param.np_flag = np_flag;
279 }
280}
281
282long litmus_admit_task(struct task_struct* tsk)
283{
284 long retval = 0;
285 long flags;
286
287 BUG_ON(is_realtime(tsk));
288
289 if (get_rt_period(tsk) == 0 ||
290 get_exec_cost(tsk) > get_rt_period(tsk)) {
291 TRACE_TASK(tsk, "litmus admit: invalid task parameters "
292 "(%lu, %lu)\n",
293 get_exec_cost(tsk), get_rt_period(tsk));
294 return -EINVAL;
295 }
296
297 if (!cpu_online(get_partition(tsk)))
298 {
299 TRACE_TASK(tsk, "litmus admit: cpu %d is not online\n",
300 get_partition(tsk));
301 return -EINVAL;
302 }
303
304 INIT_LIST_HEAD(&tsk_rt(tsk)->list);
305
306 /* avoid scheduler plugin changing underneath us */
307 spin_lock_irqsave(&task_transition_lock, flags);
308
309 /* allocate heap node for this task */
310 tsk_rt(tsk)->heap_node = heap_node_alloc(GFP_ATOMIC);
311 if (!tsk_rt(tsk)->heap_node ||
312 !tsk_rt(tsk)->rel_heap) {
313 printk(KERN_WARNING "litmus: no more heap node memory!?\n");
314 retval = -ENOMEM;
315 heap_node_free(tsk_rt(tsk)->heap_node);
316 } else
317 heap_node_init(&tsk_rt(tsk)->heap_node, tsk);
318
319 if (!retval)
320 retval = litmus->admit_task(tsk);
321
322 if (!retval) {
323 sched_trace_task_name(tsk);
324 sched_trace_task_param(tsk);
325 atomic_inc(&rt_task_count);
326 }
327
328 spin_unlock_irqrestore(&task_transition_lock, flags);
329
330 return retval;
331}
332
333void litmus_exit_task(struct task_struct* tsk)
334{
335 if (is_realtime(tsk)) {
336 sched_trace_task_completion(tsk, 1);
337 litmus->task_exit(tsk);
338 BUG_ON(heap_node_in_heap(tsk_rt(tsk)->heap_node));
339 heap_node_free(tsk_rt(tsk)->heap_node);
340 atomic_dec(&rt_task_count);
341 reinit_litmus_state(tsk, 1);
342 }
343}
344
345/* Switching a plugin in use is tricky.
346 * We must watch out that no real-time tasks exists
347 * (and that none is created in parallel) and that the plugin is not
348 * currently in use on any processor (in theory).
349 *
350 * For now, we don't enforce the second part since it is unlikely to cause
351 * any trouble by itself as long as we don't unload modules.
352 */
353int switch_sched_plugin(struct sched_plugin* plugin)
354{
355 long flags;
356 int ret = 0;
357
358 BUG_ON(!plugin);
359
360 /* stop task transitions */
361 spin_lock_irqsave(&task_transition_lock, flags);
362
363 /* don't switch if there are active real-time tasks */
364 if (atomic_read(&rt_task_count) == 0) {
365 ret = litmus->deactivate_plugin();
366 if (0 != ret)
367 goto out;
368 ret = plugin->activate_plugin();
369 if (0 != ret) {
370 printk(KERN_INFO "Can't activate %s (%d).\n",
371 plugin->plugin_name, ret);
372 plugin = &linux_sched_plugin;
373 }
374 printk(KERN_INFO "Switching to LITMUS^RT plugin %s.\n", plugin->plugin_name);
375 litmus = plugin;
376 } else
377 ret = -EBUSY;
378out:
379 spin_unlock_irqrestore(&task_transition_lock, flags);
380 return ret;
381}
382
383/* Called upon fork.
384 * p is the newly forked task.
385 */
386void litmus_fork(struct task_struct* p)
387{
388 if (is_realtime(p))
389 /* clean out any litmus related state, don't preserve anything*/
390 reinit_litmus_state(p, 0);
391}
392
393/* Called upon execve().
394 * current is doing the exec.
395 * Don't let address space specific stuff leak.
396 */
397void litmus_exec(void)
398{
399 struct task_struct* p = current;
400
401 if (is_realtime(p)) {
402 WARN_ON(p->rt_param.inh_task);
403 p->rt_param.np_flag = NULL;
404 }
405}
406
407void exit_litmus(struct task_struct *dead_tsk)
408{
409 if (is_realtime(dead_tsk))
410 litmus_exit_task(dead_tsk);
411}
412
413
414#ifdef CONFIG_MAGIC_SYSRQ
415int sys_kill(int pid, int sig);
416
417static void sysrq_handle_kill_rt_tasks(int key, struct tty_struct *tty)
418{
419 struct task_struct *t;
420 read_lock(&tasklist_lock);
421 for_each_process(t) {
422 if (is_realtime(t)) {
423 sys_kill(t->pid, SIGKILL);
424 }
425 }
426 read_unlock(&tasklist_lock);
427}
428
429static struct sysrq_key_op sysrq_kill_rt_tasks_op = {
430 .handler = sysrq_handle_kill_rt_tasks,
431 .help_msg = "quit-rt-tasks(X)",
432 .action_msg = "sent SIGKILL to all LITMUS^RT real-time tasks",
433};
434
435
436#endif
437
438
439static int proc_read_stats(char *page, char **start,
440 off_t off, int count,
441 int *eof, void *data)
442{
443 int len;
444
445 len = snprintf(page, PAGE_SIZE,
446 "real-time tasks = %d\n"
447 "ready for release = %d\n",
448 atomic_read(&rt_task_count),
449 0);
450 return len;
451}
452
453static int proc_read_plugins(char *page, char **start,
454 off_t off, int count,
455 int *eof, void *data)
456{
457 int len;
458
459 len = print_sched_plugins(page, PAGE_SIZE);
460 return len;
461}
462
463static int proc_read_curr(char *page, char **start,
464 off_t off, int count,
465 int *eof, void *data)
466{
467 int len;
468
469 len = snprintf(page, PAGE_SIZE, "%s\n", litmus->plugin_name);
470 return len;
471}
472
473static int proc_write_curr(struct file *file,
474 const char *buffer,
475 unsigned long count,
476 void *data)
477{
478 int len, ret;
479 char name[65];
480 struct sched_plugin* found;
481
482 if(count > 64)
483 len = 64;
484 else
485 len = count;
486
487 if(copy_from_user(name, buffer, len))
488 return -EFAULT;
489
490 name[len] = '\0';
491 /* chomp name */
492 if (len > 1 && name[len - 1] == '\n')
493 name[len - 1] = '\0';
494
495 found = find_sched_plugin(name);
496
497 if (found) {
498 ret = switch_sched_plugin(found);
499 if (ret != 0)
500 printk(KERN_INFO "Could not switch plugin: %d\n", ret);
501 } else
502 printk(KERN_INFO "Plugin '%s' is unknown.\n", name);
503
504 return len;
505}
506
507
508static int proc_read_release_master(char *page, char **start,
509 off_t off, int count,
510 int *eof, void *data)
511{
512 int len, master;
513 master = atomic_read(&release_master_cpu);
514 if (master == NO_CPU)
515 len = snprintf(page, PAGE_SIZE, "NO_CPU\n");
516 else
517 len = snprintf(page, PAGE_SIZE, "%d\n", master);
518 return len;
519}
520
521static int proc_write_release_master(struct file *file,
522 const char *buffer,
523 unsigned long count,
524 void *data)
525{
526 int cpu, err, online = 0;
527 char msg[64];
528
529 if (count > 63)
530 return -EINVAL;
531
532 if (copy_from_user(msg, buffer, count))
533 return -EFAULT;
534
535 /* terminate */
536 msg[count] = '\0';
537 /* chomp */
538 if (count > 1 && msg[count - 1] == '\n')
539 msg[count - 1] = '\0';
540
541 if (strcmp(msg, "NO_CPU") == 0) {
542 atomic_set(&release_master_cpu, NO_CPU);
543 return count;
544 } else {
545 err = sscanf(msg, "%d", &cpu);
546 if (err == 1 && cpu >= 0 && (online = cpu_online(cpu))) {
547 atomic_set(&release_master_cpu, cpu);
548 return count;
549 } else {
550 TRACE("invalid release master: '%s' "
551 "(err:%d cpu:%d online:%d)\n",
552 msg, err, cpu, online);
553 return -EINVAL;
554 }
555 }
556}
557
558static struct proc_dir_entry *litmus_dir = NULL,
559 *curr_file = NULL,
560 *stat_file = NULL,
561 *plugs_file = NULL,
562 *release_master_file = NULL;
563
564static int __init init_litmus_proc(void)
565{
566 litmus_dir = proc_mkdir("litmus", NULL);
567 if (!litmus_dir) {
568 printk(KERN_ERR "Could not allocate LITMUS^RT procfs entry.\n");
569 return -ENOMEM;
570 }
571 litmus_dir->owner = THIS_MODULE;
572
573 curr_file = create_proc_entry("active_plugin",
574 0644, litmus_dir);
575 if (!curr_file) {
576 printk(KERN_ERR "Could not allocate active_plugin "
577 "procfs entry.\n");
578 return -ENOMEM;
579 }
580 curr_file->owner = THIS_MODULE;
581 curr_file->read_proc = proc_read_curr;
582 curr_file->write_proc = proc_write_curr;
583
584 release_master_file = create_proc_entry("release_master",
585 0644, litmus_dir);
586 if (!release_master_file) {
587 printk(KERN_ERR "Could not allocate release_master "
588 "procfs entry.\n");
589 return -ENOMEM;
590 }
591 release_master_file->owner = THIS_MODULE;
592 release_master_file->read_proc = proc_read_release_master;
593 release_master_file->write_proc = proc_write_release_master;
594
595 stat_file = create_proc_read_entry("stats", 0444, litmus_dir,
596 proc_read_stats, NULL);
597
598 plugs_file = create_proc_read_entry("plugins", 0444, litmus_dir,
599 proc_read_plugins, NULL);
600
601 return 0;
602}
603
604static void exit_litmus_proc(void)
605{
606 if (plugs_file)
607 remove_proc_entry("plugins", litmus_dir);
608 if (stat_file)
609 remove_proc_entry("stats", litmus_dir);
610 if (curr_file)
611 remove_proc_entry("active_plugin", litmus_dir);
612 if (litmus_dir)
613 remove_proc_entry("litmus", NULL);
614}
615
616extern struct sched_plugin linux_sched_plugin;
617
618static int __init _init_litmus(void)
619{
620 /* Common initializers,
621 * mode change lock is used to enforce single mode change
622 * operation.
623 */
624 printk("Starting LITMUS^RT kernel\n");
625
626 register_sched_plugin(&linux_sched_plugin);
627
628 heap_node_cache = KMEM_CACHE(heap_node, SLAB_PANIC);
629
630#ifdef CONFIG_MAGIC_SYSRQ
631 /* offer some debugging help */
632 if (!register_sysrq_key('x', &sysrq_kill_rt_tasks_op))
633 printk("Registered kill rt tasks magic sysrq.\n");
634 else
635 printk("Could not register kill rt tasks magic sysrq.\n");
636#endif
637
638 init_litmus_proc();
639
640 return 0;
641}
642
643static void _exit_litmus(void)
644{
645 exit_litmus_proc();
646 kmem_cache_destroy(heap_node_cache);
647}
648
649module_init(_init_litmus);
650module_exit(_exit_litmus);
diff --git a/litmus/sched_litmus.c b/litmus/sched_litmus.c
new file mode 100644
index 000000000000..3ebec1dfd234
--- /dev/null
+++ b/litmus/sched_litmus.c
@@ -0,0 +1,245 @@
1/* This file is included from kernel/sched.c */
2
3#include <litmus/litmus.h>
4#include <litmus/sched_plugin.h>
5
6static void update_time_litmus(struct rq *rq, struct task_struct *p)
7{
8 u64 delta = rq->clock - p->se.exec_start;
9 if (unlikely((s64)delta < 0))
10 delta = 0;
11 /* per job counter */
12 p->rt_param.job_params.exec_time += delta;
13 /* task counter */
14 p->se.sum_exec_runtime += delta;
15 /* sched_clock() */
16 p->se.exec_start = rq->clock;
17 cpuacct_charge(p, delta);
18}
19
20static void double_rq_lock(struct rq *rq1, struct rq *rq2);
21static void double_rq_unlock(struct rq *rq1, struct rq *rq2);
22
23static void litmus_tick(struct rq *rq, struct task_struct *p)
24{
25 if (is_realtime(p))
26 update_time_litmus(rq, p);
27 litmus->tick(p);
28}
29
30static void litmus_schedule(struct rq *rq, struct task_struct *prev)
31{
32 struct rq* other_rq;
33 long was_running;
34 lt_t _maybe_deadlock = 0;
35 /* WARNING: rq is _not_ locked! */
36 if (is_realtime(prev)) {
37 update_time_litmus(rq, prev);
38 if (!is_running(prev))
39 tsk_rt(prev)->present = 0;
40 }
41
42 /* let the plugin schedule */
43 rq->litmus_next = litmus->schedule(prev);
44
45 /* check if a global plugin pulled a task from a different RQ */
46 if (rq->litmus_next && task_rq(rq->litmus_next) != rq) {
47 /* we need to migrate the task */
48 other_rq = task_rq(rq->litmus_next);
49 TRACE_TASK(rq->litmus_next, "migrate from %d\n", other_rq->cpu);
50
51 /* while we drop the lock, the prev task could change its
52 * state
53 */
54 was_running = is_running(prev);
55 mb();
56 spin_unlock(&rq->lock);
57
58 /* Don't race with a concurrent switch. This could deadlock in
59 * the case of cross or circular migrations. It's the job of
60 * the plugin to make sure that doesn't happen.
61 */
62 TRACE_TASK(rq->litmus_next, "stack_in_use=%d\n",
63 rq->litmus_next->rt_param.stack_in_use);
64 if (rq->litmus_next->rt_param.stack_in_use != NO_CPU) {
65 TRACE_TASK(rq->litmus_next, "waiting to deschedule\n");
66 _maybe_deadlock = litmus_clock();
67 }
68 while (rq->litmus_next->rt_param.stack_in_use != NO_CPU) {
69 cpu_relax();
70 mb();
71 if (rq->litmus_next->rt_param.stack_in_use == NO_CPU)
72 TRACE_TASK(rq->litmus_next,
73 "descheduled. Proceeding.\n");
74 if (lt_before(_maybe_deadlock + 10000000,
75 litmus_clock())) {
76 /* We've been spinning for 10ms.
77 * Something can't be right!
78 * Let's abandon the task and bail out; at least
79 * we will have debug info instead of a hard
80 * deadlock.
81 */
82 TRACE_TASK(rq->litmus_next,
83 "stack too long in use. "
84 "Deadlock?\n");
85 rq->litmus_next = NULL;
86
87 /* bail out */
88 spin_lock(&rq->lock);
89 return;
90 }
91 }
92#ifdef __ARCH_WANT_UNLOCKED_CTXSW
93 if (rq->litmus_next->oncpu)
94 TRACE_TASK(rq->litmus_next, "waiting for !oncpu");
95 while (rq->litmus_next->oncpu) {
96 cpu_relax();
97 mb();
98 }
99#endif
100 double_rq_lock(rq, other_rq);
101 mb();
102 if (is_realtime(prev) && is_running(prev) != was_running) {
103 TRACE_TASK(prev,
104 "state changed while we dropped"
105 " the lock: is_running=%d, was_running=%d\n",
106 is_running(prev), was_running);
107 if (is_running(prev) && !was_running) {
108 /* prev task became unblocked
109 * we need to simulate normal sequence of events
110 * to scheduler plugins.
111 */
112 litmus->task_block(prev);
113 litmus->task_wake_up(prev);
114 }
115 }
116
117 set_task_cpu(rq->litmus_next, smp_processor_id());
118
119 /* DEBUG: now that we have the lock we need to make sure a
120 * couple of things still hold:
121 * - it is still a real-time task
122 * - it is still runnable (could have been stopped)
123 * If either is violated, then the active plugin is
124 * doing something wrong.
125 */
126 if (!is_realtime(rq->litmus_next) ||
127 !is_running(rq->litmus_next)) {
128 /* BAD BAD BAD */
129 TRACE_TASK(rq->litmus_next,
130 "BAD: migration invariant FAILED: "
131 "rt=%d running=%d\n",
132 is_realtime(rq->litmus_next),
133 is_running(rq->litmus_next));
134 /* drop the task */
135 rq->litmus_next = NULL;
136 }
137 /* release the other CPU's runqueue, but keep ours */
138 spin_unlock(&other_rq->lock);
139 }
140 if (rq->litmus_next)
141 rq->litmus_next->rt_param.stack_in_use = rq->cpu;
142}
143
144static void enqueue_task_litmus(struct rq *rq, struct task_struct *p,
145 int wakeup)
146{
147 if (wakeup) {
148 sched_trace_task_resume(p);
149 tsk_rt(p)->present = 1;
150 litmus->task_wake_up(p);
151 } else
152 TRACE_TASK(p, "ignoring an enqueue, not a wake up.\n");
153}
154
155static void dequeue_task_litmus(struct rq *rq, struct task_struct *p, int sleep)
156{
157 if (sleep) {
158 litmus->task_block(p);
159 tsk_rt(p)->present = 0;
160 sched_trace_task_block(p);
161 } else
162 TRACE_TASK(p, "ignoring a dequeue, not going to sleep.\n");
163}
164
165static void yield_task_litmus(struct rq *rq)
166{
167 BUG_ON(rq->curr != current);
168 litmus->complete_job();
169}
170
171/* Plugins are responsible for this.
172 */
173static void check_preempt_curr_litmus(struct rq *rq, struct task_struct *p)
174{
175}
176
177/* has already been taken care of */
178static void put_prev_task_litmus(struct rq *rq, struct task_struct *p)
179{
180}
181
182static struct task_struct *pick_next_task_litmus(struct rq *rq)
183{
184 struct task_struct* picked = rq->litmus_next;
185 rq->litmus_next = NULL;
186 if (picked)
187 picked->se.exec_start = rq->clock;
188 return picked;
189}
190
191static void task_tick_litmus(struct rq *rq, struct task_struct *p)
192{
193}
194
195/* This is called when a task became a real-time task, either due to a SCHED_*
196 * class transition or due to PI mutex inheritance. We don't handle Linux PI
197 * mutex inheritance yet (and probably never will). Use LITMUS provided
198 * synchronization primitives instead.
199 */
200static void set_curr_task_litmus(struct rq *rq)
201{
202 rq->curr->se.exec_start = rq->clock;
203}
204
205
206#ifdef CONFIG_SMP
207
208/* we don't repartition at runtime */
209
210static unsigned long
211load_balance_litmus(struct rq *this_rq, int this_cpu, struct rq *busiest,
212 unsigned long max_load_move,
213 struct sched_domain *sd, enum cpu_idle_type idle,
214 int *all_pinned, int *this_best_prio)
215{
216 return 0;
217}
218
219static int
220move_one_task_litmus(struct rq *this_rq, int this_cpu, struct rq *busiest,
221 struct sched_domain *sd, enum cpu_idle_type idle)
222{
223 return 0;
224}
225#endif
226
227const struct sched_class litmus_sched_class = {
228 .next = &rt_sched_class,
229 .enqueue_task = enqueue_task_litmus,
230 .dequeue_task = dequeue_task_litmus,
231 .yield_task = yield_task_litmus,
232
233 .check_preempt_curr = check_preempt_curr_litmus,
234
235 .pick_next_task = pick_next_task_litmus,
236 .put_prev_task = put_prev_task_litmus,
237
238#ifdef CONFIG_SMP
239 .load_balance = load_balance_litmus,
240 .move_one_task = move_one_task_litmus,
241#endif
242
243 .set_curr_task = set_curr_task_litmus,
244 .task_tick = task_tick_litmus,
245};
diff --git a/litmus/sched_plugin.c b/litmus/sched_plugin.c
new file mode 100644
index 000000000000..0be091ece569
--- /dev/null
+++ b/litmus/sched_plugin.c
@@ -0,0 +1,199 @@
1/* sched_plugin.c -- core infrastructure for the scheduler plugin system
2 *
3 * This file includes the initialization of the plugin system, the no-op Linux
4 * scheduler plugin and some dummy functions.
5 */
6
7#include <linux/list.h>
8#include <linux/spinlock.h>
9
10#include <litmus/litmus.h>
11#include <litmus/sched_plugin.h>
12
13#include <litmus/jobs.h>
14
15/*************************************************************
16 * Dummy plugin functions *
17 *************************************************************/
18
19static void litmus_dummy_finish_switch(struct task_struct * prev)
20{
21}
22
23static struct task_struct* litmus_dummy_schedule(struct task_struct * prev)
24{
25 return NULL;
26}
27
28static void litmus_dummy_tick(struct task_struct* tsk)
29{
30}
31
32static long litmus_dummy_admit_task(struct task_struct* tsk)
33{
34 printk(KERN_CRIT "LITMUS^RT: Linux plugin rejects %s/%d.\n",
35 tsk->comm, tsk->pid);
36 return -EINVAL;
37}
38
39static void litmus_dummy_task_new(struct task_struct *t, int on_rq, int running)
40{
41}
42
43static void litmus_dummy_task_wake_up(struct task_struct *task)
44{
45}
46
47static void litmus_dummy_task_block(struct task_struct *task)
48{
49}
50
51static void litmus_dummy_task_exit(struct task_struct *task)
52{
53}
54
55static long litmus_dummy_complete_job(void)
56{
57 return -ENOSYS;
58}
59
60static long litmus_dummy_activate_plugin(void)
61{
62 return 0;
63}
64
65static long litmus_dummy_deactivate_plugin(void)
66{
67 return 0;
68}
69
70#ifdef CONFIG_FMLP
71
72static long litmus_dummy_inherit_priority(struct pi_semaphore *sem,
73 struct task_struct *new_owner)
74{
75 return -ENOSYS;
76}
77
78static long litmus_dummy_return_priority(struct pi_semaphore *sem)
79{
80 return -ENOSYS;
81}
82
83static long litmus_dummy_pi_block(struct pi_semaphore *sem,
84 struct task_struct *new_waiter)
85{
86 return -ENOSYS;
87}
88
89#endif
90
91
92/* The default scheduler plugin. It doesn't do anything and lets Linux do its
93 * job.
94 */
95struct sched_plugin linux_sched_plugin = {
96 .plugin_name = "Linux",
97 .tick = litmus_dummy_tick,
98 .task_new = litmus_dummy_task_new,
99 .task_exit = litmus_dummy_task_exit,
100 .task_wake_up = litmus_dummy_task_wake_up,
101 .task_block = litmus_dummy_task_block,
102 .complete_job = litmus_dummy_complete_job,
103 .schedule = litmus_dummy_schedule,
104 .finish_switch = litmus_dummy_finish_switch,
105 .activate_plugin = litmus_dummy_activate_plugin,
106 .deactivate_plugin = litmus_dummy_deactivate_plugin,
107#ifdef CONFIG_FMLP
108 .inherit_priority = litmus_dummy_inherit_priority,
109 .return_priority = litmus_dummy_return_priority,
110 .pi_block = litmus_dummy_pi_block,
111#endif
112 .admit_task = litmus_dummy_admit_task
113};
114
115/*
116 * The reference to current plugin that is used to schedule tasks within
117 * the system. It stores references to actual function implementations
118 * Should be initialized by calling "init_***_plugin()"
119 */
120struct sched_plugin *litmus = &linux_sched_plugin;
121
122/* the list of registered scheduling plugins */
123static LIST_HEAD(sched_plugins);
124static DEFINE_SPINLOCK(sched_plugins_lock);
125
126#define CHECK(func) {\
127 if (!plugin->func) \
128 plugin->func = litmus_dummy_ ## func;}
129
130/* FIXME: get reference to module */
131int register_sched_plugin(struct sched_plugin* plugin)
132{
133 printk(KERN_INFO "Registering LITMUS^RT plugin %s.\n",
134 plugin->plugin_name);
135
136 /* make sure we don't trip over null pointers later */
137 CHECK(finish_switch);
138 CHECK(schedule);
139 CHECK(tick);
140 CHECK(task_wake_up);
141 CHECK(task_exit);
142 CHECK(task_block);
143 CHECK(task_new);
144 CHECK(complete_job);
145 CHECK(activate_plugin);
146 CHECK(deactivate_plugin);
147#ifdef CONFIG_FMLP
148 CHECK(inherit_priority);
149 CHECK(return_priority);
150 CHECK(pi_block);
151#endif
152 CHECK(admit_task);
153
154 if (!plugin->release_at)
155 plugin->release_at = release_at;
156
157 spin_lock(&sched_plugins_lock);
158 list_add(&plugin->list, &sched_plugins);
159 spin_unlock(&sched_plugins_lock);
160
161 return 0;
162}
163
164
165/* FIXME: reference counting, etc. */
166struct sched_plugin* find_sched_plugin(const char* name)
167{
168 struct list_head *pos;
169 struct sched_plugin *plugin;
170
171 spin_lock(&sched_plugins_lock);
172 list_for_each(pos, &sched_plugins) {
173 plugin = list_entry(pos, struct sched_plugin, list);
174 if (!strcmp(plugin->plugin_name, name))
175 goto out_unlock;
176 }
177 plugin = NULL;
178
179out_unlock:
180 spin_unlock(&sched_plugins_lock);
181 return plugin;
182}
183
184int print_sched_plugins(char* buf, int max)
185{
186 int count = 0;
187 struct list_head *pos;
188 struct sched_plugin *plugin;
189
190 spin_lock(&sched_plugins_lock);
191 list_for_each(pos, &sched_plugins) {
192 plugin = list_entry(pos, struct sched_plugin, list);
193 count += snprintf(buf + count, max - count, "%s\n", plugin->plugin_name);
194 if (max - count <= 0)
195 break;
196 }
197 spin_unlock(&sched_plugins_lock);
198 return count;
199}