aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrea Bastoni <bastoni@cs.unc.edu>2009-12-17 21:23:36 -0500
committerAndrea Bastoni <bastoni@cs.unc.edu>2010-05-29 17:05:45 -0400
commit4b38febbd59fd33542a343991262119eb9860f5e (patch)
tree1af88a0d354abe344c2c2869631f76a1806d75c3
parent22763c5cf3690a681551162c15d34d935308c8d7 (diff)
[ported from 2008.3] Core LITMUS^RT infrastructure
Port 2008.3 Core LITMUS^RT infrastructure to Linux 2.6.32 litmus_sched_class implements 4 new methods: - prio_changed: void - switched_to: void - get_rr_interval: return infinity (i.e., 0) - select_task_rq: return current cpu
-rw-r--r--Makefile4
-rw-r--r--include/linux/sched.h7
-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.c7
-rw-r--r--kernel/sched.c92
-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.c654
-rw-r--r--litmus/sched_litmus.c275
-rw-r--r--litmus/sched_plugin.c199
23 files changed, 2723 insertions, 12 deletions
diff --git a/Makefile b/Makefile
index f5cdb72ba2ce..2603066a012d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
1VERSION = 2 1VERSION = 2
2PATCHLEVEL = 6 2PATCHLEVEL = 6
3SUBLEVEL = 32 3SUBLEVEL = 32
4EXTRAVERSION = 4EXTRAVERSION =-litmus2010
5NAME = Man-Eating Seals of Antiquity 5NAME = Man-Eating Seals of Antiquity
6 6
7# *DOCUMENTATION* 7# *DOCUMENTATION*
@@ -644,7 +644,7 @@ export mod_strip_cmd
644 644
645 645
646ifeq ($(KBUILD_EXTMOD),) 646ifeq ($(KBUILD_EXTMOD),)
647core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ 647core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ litmus/
648 648
649vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ 649vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
650 $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ 650 $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 75e6e60bf583..bb046c0adf99 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -38,6 +38,7 @@
38#define SCHED_BATCH 3 38#define SCHED_BATCH 3
39/* SCHED_ISO: reserved but not implemented yet */ 39/* SCHED_ISO: reserved but not implemented yet */
40#define SCHED_IDLE 5 40#define SCHED_IDLE 5
41#define SCHED_LITMUS 6
41/* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */ 42/* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */
42#define SCHED_RESET_ON_FORK 0x40000000 43#define SCHED_RESET_ON_FORK 0x40000000
43 44
@@ -94,6 +95,8 @@ struct sched_param {
94 95
95#include <asm/processor.h> 96#include <asm/processor.h>
96 97
98#include <litmus/rt_param.h>
99
97struct exec_domain; 100struct exec_domain;
98struct futex_pi_state; 101struct futex_pi_state;
99struct robust_list_head; 102struct robust_list_head;
@@ -1505,6 +1508,10 @@ struct task_struct {
1505 int make_it_fail; 1508 int make_it_fail;
1506#endif 1509#endif
1507 struct prop_local_single dirties; 1510 struct prop_local_single dirties;
1511
1512 /* LITMUS RT parameters and state */
1513 struct rt_param rt_param;
1514
1508#ifdef CONFIG_LATENCYTOP 1515#ifdef CONFIG_LATENCYTOP
1509 int latency_record_count; 1516 int latency_record_count;
1510 struct latency_record latency_record[LT_SAVECOUNT]; 1517 struct latency_record latency_record[LT_SAVECOUNT];
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..380fcb8acb33
--- /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->rt.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 166b8c49257c..889730cce3ad 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -74,6 +74,9 @@
74 74
75#include <trace/events/sched.h> 75#include <trace/events/sched.h>
76 76
77#include <litmus/litmus.h>
78#include <litmus/sched_plugin.h>
79
77/* 80/*
78 * Protected counters by write_lock_irq(&tasklist_lock) 81 * Protected counters by write_lock_irq(&tasklist_lock)
79 */ 82 */
@@ -162,6 +165,7 @@ void __put_task_struct(struct task_struct *tsk)
162 WARN_ON(atomic_read(&tsk->usage)); 165 WARN_ON(atomic_read(&tsk->usage));
163 WARN_ON(tsk == current); 166 WARN_ON(tsk == current);
164 167
168 exit_litmus(tsk);
165 exit_creds(tsk); 169 exit_creds(tsk);
166 delayacct_tsk_free(tsk); 170 delayacct_tsk_free(tsk);
167 171
@@ -244,6 +248,9 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
244 248
245 tsk->stack = ti; 249 tsk->stack = ti;
246 250
251 /* Don't let the new task be a real-time task. */
252 memset(&tsk->rt_param, 0, sizeof(struct rt_task));
253
247 err = prop_local_init_single(&tsk->dirties); 254 err = prop_local_init_single(&tsk->dirties);
248 if (err) 255 if (err)
249 goto out; 256 goto out;
diff --git a/kernel/sched.c b/kernel/sched.c
index 3c11ae0a948d..fcaed6b96442 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -77,6 +77,9 @@
77 77
78#include "sched_cpupri.h" 78#include "sched_cpupri.h"
79 79
80#include <litmus/sched_trace.h>
81#include <litmus/trace.h>
82
80#define CREATE_TRACE_POINTS 83#define CREATE_TRACE_POINTS
81#include <trace/events/sched.h> 84#include <trace/events/sched.h>
82 85
@@ -571,6 +574,8 @@ struct rq {
571 574
572 atomic_t nr_iowait; 575 atomic_t nr_iowait;
573 576
577 struct task_struct *litmus_next;
578
574#ifdef CONFIG_SMP 579#ifdef CONFIG_SMP
575 struct root_domain *rd; 580 struct root_domain *rd;
576 struct sched_domain *sd; 581 struct sched_domain *sd;
@@ -1815,11 +1820,12 @@ static void calc_load_account_active(struct rq *this_rq);
1815#include "sched_idletask.c" 1820#include "sched_idletask.c"
1816#include "sched_fair.c" 1821#include "sched_fair.c"
1817#include "sched_rt.c" 1822#include "sched_rt.c"
1823#include "../litmus/sched_litmus.c"
1818#ifdef CONFIG_SCHED_DEBUG 1824#ifdef CONFIG_SCHED_DEBUG
1819# include "sched_debug.c" 1825# include "sched_debug.c"
1820#endif 1826#endif
1821 1827
1822#define sched_class_highest (&rt_sched_class) 1828#define sched_class_highest (&litmus_sched_class)
1823#define for_each_class(class) \ 1829#define for_each_class(class) \
1824 for (class = sched_class_highest; class; class = class->next) 1830 for (class = sched_class_highest; class; class = class->next)
1825 1831
@@ -2343,6 +2349,9 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state,
2343 unsigned long flags; 2349 unsigned long flags;
2344 struct rq *rq, *orig_rq; 2350 struct rq *rq, *orig_rq;
2345 2351
2352 if (is_realtime(p))
2353 TRACE_TASK(p, "try_to_wake_up() state:%d\n", p->state);
2354
2346 if (!sched_feat(SYNC_WAKEUPS)) 2355 if (!sched_feat(SYNC_WAKEUPS))
2347 wake_flags &= ~WF_SYNC; 2356 wake_flags &= ~WF_SYNC;
2348 2357
@@ -2361,7 +2370,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state,
2361 orig_cpu = cpu; 2370 orig_cpu = cpu;
2362 2371
2363#ifdef CONFIG_SMP 2372#ifdef CONFIG_SMP
2364 if (unlikely(task_running(rq, p))) 2373 if (unlikely(task_running(rq, p)) || is_realtime(p))
2365 goto out_activate; 2374 goto out_activate;
2366 2375
2367 /* 2376 /*
@@ -2442,6 +2451,8 @@ out_running:
2442 p->sched_class->task_wake_up(rq, p); 2451 p->sched_class->task_wake_up(rq, p);
2443#endif 2452#endif
2444out: 2453out:
2454 if (is_realtime(p))
2455 TRACE_TASK(p, "try_to_wake_up() done state:%d\n", p->state);
2445 task_rq_unlock(rq, &flags); 2456 task_rq_unlock(rq, &flags);
2446 put_cpu(); 2457 put_cpu();
2447 2458
@@ -2750,6 +2761,8 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
2750 */ 2761 */
2751 prev_state = prev->state; 2762 prev_state = prev->state;
2752 finish_arch_switch(prev); 2763 finish_arch_switch(prev);
2764 litmus->finish_switch(prev);
2765 prev->rt_param.stack_in_use = NO_CPU;
2753 perf_event_task_sched_in(current, cpu_of(rq)); 2766 perf_event_task_sched_in(current, cpu_of(rq));
2754 finish_lock_switch(rq, prev); 2767 finish_lock_switch(rq, prev);
2755 2768
@@ -5232,18 +5245,31 @@ void scheduler_tick(void)
5232 5245
5233 sched_clock_tick(); 5246 sched_clock_tick();
5234 5247
5248 TS_TICK_START(current);
5249
5235 spin_lock(&rq->lock); 5250 spin_lock(&rq->lock);
5236 update_rq_clock(rq); 5251 update_rq_clock(rq);
5237 update_cpu_load(rq); 5252 update_cpu_load(rq);
5238 curr->sched_class->task_tick(rq, curr, 0); 5253 curr->sched_class->task_tick(rq, curr, 0);
5254
5255 /*
5256 * LITMUS_TODO: can we move litmus_tick inside task_tick
5257 * or will deadlock ?
5258 */
5259 TS_PLUGIN_TICK_START;
5260 litmus_tick(rq, curr);
5261 TS_PLUGIN_TICK_END;
5262
5239 spin_unlock(&rq->lock); 5263 spin_unlock(&rq->lock);
5240 5264
5241 perf_event_task_tick(curr, cpu); 5265 perf_event_task_tick(curr, cpu);
5242 5266
5243#ifdef CONFIG_SMP 5267#ifdef CONFIG_SMP
5244 rq->idle_at_tick = idle_cpu(cpu); 5268 rq->idle_at_tick = idle_cpu(cpu);
5245 trigger_load_balance(rq, cpu); 5269 if (!is_realtime(current))
5270 trigger_load_balance(rq, cpu);
5246#endif 5271#endif
5272 TS_TICK_END(current);
5247} 5273}
5248 5274
5249notrace unsigned long get_parent_ip(unsigned long addr) 5275notrace unsigned long get_parent_ip(unsigned long addr)
@@ -5387,11 +5413,17 @@ pick_next_task(struct rq *rq)
5387 * Optimization: we know that if all tasks are in 5413 * Optimization: we know that if all tasks are in
5388 * the fair class we can call that function directly: 5414 * the fair class we can call that function directly:
5389 */ 5415 */
5416 /*
5417 * LITMUS_TODO: can we move processes out of fair class?
5418 * i.e., create a litmus_rq
5419 */
5420 /* Don't do this for LITMUS
5390 if (likely(rq->nr_running == rq->cfs.nr_running)) { 5421 if (likely(rq->nr_running == rq->cfs.nr_running)) {
5391 p = fair_sched_class.pick_next_task(rq); 5422 p = fair_sched_class.pick_next_task(rq);
5392 if (likely(p)) 5423 if (likely(p))
5393 return p; 5424 return p;
5394 } 5425 }
5426 */
5395 5427
5396 class = sched_class_highest; 5428 class = sched_class_highest;
5397 for ( ; ; ) { 5429 for ( ; ; ) {
@@ -5426,6 +5458,8 @@ need_resched:
5426 5458
5427 release_kernel_lock(prev); 5459 release_kernel_lock(prev);
5428need_resched_nonpreemptible: 5460need_resched_nonpreemptible:
5461 TS_SCHED_START;
5462 sched_trace_task_switch_away(prev);
5429 5463
5430 schedule_debug(prev); 5464 schedule_debug(prev);
5431 5465
@@ -5436,6 +5470,14 @@ need_resched_nonpreemptible:
5436 update_rq_clock(rq); 5470 update_rq_clock(rq);
5437 clear_tsk_need_resched(prev); 5471 clear_tsk_need_resched(prev);
5438 5472
5473 /*
5474 * LITMUS_TODO: can we integrate litmus_schedule in
5475 * pick_next_task?
5476 */
5477 TS_PLUGIN_SCHED_START;
5478 litmus_schedule(rq, prev);
5479 TS_PLUGIN_SCHED_END;
5480
5439 if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { 5481 if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
5440 if (unlikely(signal_pending_state(prev->state, prev))) 5482 if (unlikely(signal_pending_state(prev->state, prev)))
5441 prev->state = TASK_RUNNING; 5483 prev->state = TASK_RUNNING;
@@ -5460,22 +5502,35 @@ need_resched_nonpreemptible:
5460 rq->curr = next; 5502 rq->curr = next;
5461 ++*switch_count; 5503 ++*switch_count;
5462 5504
5505 TS_SCHED_END(next);
5506 TS_CXS_START(next);
5463 context_switch(rq, prev, next); /* unlocks the rq */ 5507 context_switch(rq, prev, next); /* unlocks the rq */
5508 TS_CXS_END(current);
5464 /* 5509 /*
5465 * the context switch might have flipped the stack from under 5510 * the context switch might have flipped the stack from under
5466 * us, hence refresh the local variables. 5511 * us, hence refresh the local variables.
5467 */ 5512 */
5468 cpu = smp_processor_id(); 5513 cpu = smp_processor_id();
5469 rq = cpu_rq(cpu); 5514 rq = cpu_rq(cpu);
5470 } else 5515 } else {
5516 TS_SCHED_END(prev);
5471 spin_unlock_irq(&rq->lock); 5517 spin_unlock_irq(&rq->lock);
5518 }
5519
5520 TS_SCHED2_START(current);
5521 sched_trace_task_switch_to(current);
5472 5522
5473 post_schedule(rq); 5523 post_schedule(rq);
5474 5524
5475 if (unlikely(reacquire_kernel_lock(current) < 0)) 5525 if (unlikely(reacquire_kernel_lock(current) < 0)) {
5526 TS_SCHED2_END(current);
5476 goto need_resched_nonpreemptible; 5527 goto need_resched_nonpreemptible;
5528 }
5477 5529
5478 preempt_enable_no_resched(); 5530 preempt_enable_no_resched();
5531
5532 TS_SCHED2_END(current);
5533
5479 if (need_resched()) 5534 if (need_resched())
5480 goto need_resched; 5535 goto need_resched;
5481} 5536}
@@ -6185,6 +6240,9 @@ __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
6185 case SCHED_RR: 6240 case SCHED_RR:
6186 p->sched_class = &rt_sched_class; 6241 p->sched_class = &rt_sched_class;
6187 break; 6242 break;
6243 case SCHED_LITMUS:
6244 p->sched_class = &litmus_sched_class;
6245 break;
6188 } 6246 }
6189 6247
6190 p->rt_priority = prio; 6248 p->rt_priority = prio;
@@ -6232,7 +6290,7 @@ recheck:
6232 6290
6233 if (policy != SCHED_FIFO && policy != SCHED_RR && 6291 if (policy != SCHED_FIFO && policy != SCHED_RR &&
6234 policy != SCHED_NORMAL && policy != SCHED_BATCH && 6292 policy != SCHED_NORMAL && policy != SCHED_BATCH &&
6235 policy != SCHED_IDLE) 6293 policy != SCHED_IDLE && policy != SCHED_LITMUS)
6236 return -EINVAL; 6294 return -EINVAL;
6237 } 6295 }
6238 6296
@@ -6247,6 +6305,8 @@ recheck:
6247 return -EINVAL; 6305 return -EINVAL;
6248 if (rt_policy(policy) != (param->sched_priority != 0)) 6306 if (rt_policy(policy) != (param->sched_priority != 0))
6249 return -EINVAL; 6307 return -EINVAL;
6308 if (policy == SCHED_LITMUS && policy == p->policy)
6309 return -EINVAL;
6250 6310
6251 /* 6311 /*
6252 * Allow unprivileged RT tasks to decrease priority: 6312 * Allow unprivileged RT tasks to decrease priority:
@@ -6301,6 +6361,12 @@ recheck:
6301 return retval; 6361 return retval;
6302 } 6362 }
6303 6363
6364 if (policy == SCHED_LITMUS) {
6365 retval = litmus_admit_task(p);
6366 if (retval)
6367 return retval;
6368 }
6369
6304 /* 6370 /*
6305 * make sure no PI-waiters arrive (or leave) while we are 6371 * make sure no PI-waiters arrive (or leave) while we are
6306 * changing the priority of the task: 6372 * changing the priority of the task:
@@ -6328,9 +6394,18 @@ recheck:
6328 6394
6329 p->sched_reset_on_fork = reset_on_fork; 6395 p->sched_reset_on_fork = reset_on_fork;
6330 6396
6397 if (p->policy == SCHED_LITMUS)
6398 litmus_exit_task(p);
6399
6331 oldprio = p->prio; 6400 oldprio = p->prio;
6332 __setscheduler(rq, p, policy, param->sched_priority); 6401 __setscheduler(rq, p, policy, param->sched_priority);
6333 6402
6403 if (policy == SCHED_LITMUS) {
6404 p->rt_param.stack_in_use = running ? rq->cpu : NO_CPU;
6405 p->rt_param.present = running;
6406 litmus->task_new(p, on_rq, running);
6407 }
6408
6334 if (running) 6409 if (running)
6335 p->sched_class->set_curr_task(rq); 6410 p->sched_class->set_curr_task(rq);
6336 if (on_rq) { 6411 if (on_rq) {
@@ -6500,10 +6575,11 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
6500 read_lock(&tasklist_lock); 6575 read_lock(&tasklist_lock);
6501 6576
6502 p = find_process_by_pid(pid); 6577 p = find_process_by_pid(pid);
6503 if (!p) { 6578 /* Don't set affinity if task not found and for LITMUS tasks */
6579 if (!p || is_realtime(p)) {
6504 read_unlock(&tasklist_lock); 6580 read_unlock(&tasklist_lock);
6505 put_online_cpus(); 6581 put_online_cpus();
6506 return -ESRCH; 6582 return p ? -EPERM : -ESRCH;
6507 } 6583 }
6508 6584
6509 /* 6585 /*
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c
index 37087a7fac22..ef43ff95999d 100644
--- a/kernel/sched_fair.c
+++ b/kernel/sched_fair.c
@@ -1598,7 +1598,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_
1598 1598
1599 update_curr(cfs_rq); 1599 update_curr(cfs_rq);
1600 1600
1601 if (unlikely(rt_prio(p->prio))) { 1601 if (unlikely(rt_prio(p->prio)) || p->policy == SCHED_LITMUS) {
1602 resched_task(curr); 1602 resched_task(curr);
1603 return; 1603 return;
1604 } 1604 }
diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c
index a4d790cddb19..f622880e918f 100644
--- a/kernel/sched_rt.c
+++ b/kernel/sched_rt.c
@@ -1004,7 +1004,7 @@ static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p)
1004 */ 1004 */
1005static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int flags) 1005static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int flags)
1006{ 1006{
1007 if (p->prio < rq->curr->prio) { 1007 if (p->prio < rq->curr->prio || p->policy == SCHED_LITMUS) {
1008 resched_task(rq->curr); 1008 resched_task(rq->curr);
1009 return; 1009 return;
1010 } 1010 }
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..36e314625d86
--- /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->rt.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..eb0d17e298d7
--- /dev/null
+++ b/litmus/litmus.c
@@ -0,0 +1,654 @@
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 *
59 * find_task_by_vpid() assumes that we are in the same namespace of the
60 * target.
61 */
62asmlinkage long sys_set_rt_task_param(pid_t pid, struct rt_task __user * param)
63{
64 struct rt_task tp;
65 struct task_struct *target;
66 int retval = -EINVAL;
67
68 printk("Setting up rt task parameters for process %d.\n", pid);
69
70 if (pid < 0 || param == 0) {
71 goto out;
72 }
73 if (copy_from_user(&tp, param, sizeof(tp))) {
74 retval = -EFAULT;
75 goto out;
76 }
77
78 /* Task search and manipulation must be protected */
79 read_lock_irq(&tasklist_lock);
80 if (!(target = find_task_by_vpid(pid))) {
81 retval = -ESRCH;
82 goto out_unlock;
83 }
84
85 if (is_realtime(target)) {
86 /* The task is already a real-time task.
87 * We cannot not allow parameter changes at this point.
88 */
89 retval = -EBUSY;
90 goto out_unlock;
91 }
92
93 if (tp.exec_cost <= 0)
94 goto out_unlock;
95 if (tp.period <= 0)
96 goto out_unlock;
97 if (!cpu_online(tp.cpu))
98 goto out_unlock;
99 if (tp.period < tp.exec_cost)
100 {
101 printk(KERN_INFO "litmus: real-time task %d rejected "
102 "because wcet > period\n", pid);
103 goto out_unlock;
104 }
105
106 target->rt_param.task_params = tp;
107
108 retval = 0;
109 out_unlock:
110 read_unlock_irq(&tasklist_lock);
111 out:
112 return retval;
113}
114
115/*
116 * Getter of task's RT params
117 * returns EINVAL if param or pid is NULL
118 * returns ESRCH if pid does not correspond to a valid task
119 * returns EFAULT if copying of parameters has failed.
120 *
121 * find_task_by_vpid() assumes that we are in the same namespace of the
122 * target.
123 */
124asmlinkage long sys_get_rt_task_param(pid_t pid, struct rt_task __user * param)
125{
126 int retval = -EINVAL;
127 struct task_struct *source;
128 struct rt_task lp;
129 if (param == 0 || pid < 0)
130 goto out;
131 read_lock(&tasklist_lock);
132 if (!(source = find_task_by_vpid(pid))) {
133 retval = -ESRCH;
134 goto out_unlock;
135 }
136 lp = source->rt_param.task_params;
137 read_unlock(&tasklist_lock);
138 /* Do copying outside the lock */
139 retval =
140 copy_to_user(param, &lp, sizeof(lp)) ? -EFAULT : 0;
141 return retval;
142 out_unlock:
143 read_unlock(&tasklist_lock);
144 out:
145 return retval;
146
147}
148
149/*
150 * This is the crucial function for periodic task implementation,
151 * It checks if a task is periodic, checks if such kind of sleep
152 * is permitted and calls plugin-specific sleep, which puts the
153 * task into a wait array.
154 * returns 0 on successful wakeup
155 * returns EPERM if current conditions do not permit such sleep
156 * returns EINVAL if current task is not able to go to sleep
157 */
158asmlinkage long sys_complete_job(void)
159{
160 int retval = -EPERM;
161 if (!is_realtime(current)) {
162 retval = -EINVAL;
163 goto out;
164 }
165 /* Task with negative or zero period cannot sleep */
166 if (get_rt_period(current) <= 0) {
167 retval = -EINVAL;
168 goto out;
169 }
170 /* The plugin has to put the task into an
171 * appropriate queue and call schedule
172 */
173 retval = litmus->complete_job();
174 out:
175 return retval;
176}
177
178/* This is an "improved" version of sys_complete_job that
179 * addresses the problem of unintentionally missing a job after
180 * an overrun.
181 *
182 * returns 0 on successful wakeup
183 * returns EPERM if current conditions do not permit such sleep
184 * returns EINVAL if current task is not able to go to sleep
185 */
186asmlinkage long sys_wait_for_job_release(unsigned int job)
187{
188 int retval = -EPERM;
189 if (!is_realtime(current)) {
190 retval = -EINVAL;
191 goto out;
192 }
193
194 /* Task with negative or zero period cannot sleep */
195 if (get_rt_period(current) <= 0) {
196 retval = -EINVAL;
197 goto out;
198 }
199
200 retval = 0;
201
202 /* first wait until we have "reached" the desired job
203 *
204 * This implementation has at least two problems:
205 *
206 * 1) It doesn't gracefully handle the wrap around of
207 * job_no. Since LITMUS is a prototype, this is not much
208 * of a problem right now.
209 *
210 * 2) It is theoretically racy if a job release occurs
211 * between checking job_no and calling sleep_next_period().
212 * A proper solution would requiring adding another callback
213 * in the plugin structure and testing the condition with
214 * interrupts disabled.
215 *
216 * FIXME: At least problem 2 should be taken care of eventually.
217 */
218 while (!retval && job > current->rt_param.job_params.job_no)
219 /* If the last job overran then job <= job_no and we
220 * don't send the task to sleep.
221 */
222 retval = litmus->complete_job();
223 out:
224 return retval;
225}
226
227/* This is a helper syscall to query the current job sequence number.
228 *
229 * returns 0 on successful query
230 * returns EPERM if task is not a real-time task.
231 * returns EFAULT if &job is not a valid pointer.
232 */
233asmlinkage long sys_query_job_no(unsigned int __user *job)
234{
235 int retval = -EPERM;
236 if (is_realtime(current))
237 retval = put_user(current->rt_param.job_params.job_no, job);
238
239 return retval;
240}
241
242/* sys_null_call() is only used for determining raw system call
243 * overheads (kernel entry, kernel exit). It has no useful side effects.
244 * If ts is non-NULL, then the current Feather-Trace time is recorded.
245 */
246asmlinkage long sys_null_call(cycles_t __user *ts)
247{
248 long ret = 0;
249 cycles_t now;
250
251 if (ts) {
252 now = get_cycles();
253 ret = put_user(now, ts);
254 }
255
256 return ret;
257}
258
259/* p is a real-time task. Re-init its state as a best-effort task. */
260static void reinit_litmus_state(struct task_struct* p, int restore)
261{
262 struct rt_task user_config = {};
263 __user short *np_flag = NULL;
264
265 if (restore) {
266 /* Safe user-space provided configuration data. */
267 user_config = p->rt_param.task_params;
268 np_flag = p->rt_param.np_flag;
269 }
270
271 /* We probably should not be inheriting any task's priority
272 * at this point in time.
273 */
274 WARN_ON(p->rt_param.inh_task);
275
276 /* We need to restore the priority of the task. */
277// __setscheduler(p, p->rt_param.old_policy, p->rt_param.old_prio);
278
279 /* Cleanup everything else. */
280 memset(&p->rt_param, 0, sizeof(user_config));
281
282 /* Restore preserved fields. */
283 if (restore) {
284 p->rt_param.task_params = user_config;
285 p->rt_param.np_flag = np_flag;
286 }
287}
288
289long litmus_admit_task(struct task_struct* tsk)
290{
291 long retval = 0;
292 unsigned long flags;
293
294 BUG_ON(is_realtime(tsk));
295
296 if (get_rt_period(tsk) == 0 ||
297 get_exec_cost(tsk) > get_rt_period(tsk)) {
298 TRACE_TASK(tsk, "litmus admit: invalid task parameters "
299 "(%lu, %lu)\n",
300 get_exec_cost(tsk), get_rt_period(tsk));
301 return -EINVAL;
302 }
303
304 if (!cpu_online(get_partition(tsk)))
305 {
306 TRACE_TASK(tsk, "litmus admit: cpu %d is not online\n",
307 get_partition(tsk));
308 return -EINVAL;
309 }
310
311 INIT_LIST_HEAD(&tsk_rt(tsk)->list);
312
313 /* avoid scheduler plugin changing underneath us */
314 spin_lock_irqsave(&task_transition_lock, flags);
315
316 /* allocate heap node for this task */
317 tsk_rt(tsk)->heap_node = heap_node_alloc(GFP_ATOMIC);
318 if (!tsk_rt(tsk)->heap_node ||
319 !tsk_rt(tsk)->rel_heap) {
320 printk(KERN_WARNING "litmus: no more heap node memory!?\n");
321 retval = -ENOMEM;
322 heap_node_free(tsk_rt(tsk)->heap_node);
323 } else
324 heap_node_init(&tsk_rt(tsk)->heap_node, tsk);
325
326 if (!retval)
327 retval = litmus->admit_task(tsk);
328
329 if (!retval) {
330 sched_trace_task_name(tsk);
331 sched_trace_task_param(tsk);
332 atomic_inc(&rt_task_count);
333 }
334
335 spin_unlock_irqrestore(&task_transition_lock, flags);
336
337 return retval;
338}
339
340void litmus_exit_task(struct task_struct* tsk)
341{
342 if (is_realtime(tsk)) {
343 sched_trace_task_completion(tsk, 1);
344 litmus->task_exit(tsk);
345 BUG_ON(heap_node_in_heap(tsk_rt(tsk)->heap_node));
346 heap_node_free(tsk_rt(tsk)->heap_node);
347 atomic_dec(&rt_task_count);
348 reinit_litmus_state(tsk, 1);
349 }
350}
351
352/* Switching a plugin in use is tricky.
353 * We must watch out that no real-time tasks exists
354 * (and that none is created in parallel) and that the plugin is not
355 * currently in use on any processor (in theory).
356 *
357 * For now, we don't enforce the second part since it is unlikely to cause
358 * any trouble by itself as long as we don't unload modules.
359 */
360int switch_sched_plugin(struct sched_plugin* plugin)
361{
362 unsigned long flags;
363 int ret = 0;
364
365 BUG_ON(!plugin);
366
367 /* stop task transitions */
368 spin_lock_irqsave(&task_transition_lock, flags);
369
370 /* don't switch if there are active real-time tasks */
371 if (atomic_read(&rt_task_count) == 0) {
372 ret = litmus->deactivate_plugin();
373 if (0 != ret)
374 goto out;
375 ret = plugin->activate_plugin();
376 if (0 != ret) {
377 printk(KERN_INFO "Can't activate %s (%d).\n",
378 plugin->plugin_name, ret);
379 plugin = &linux_sched_plugin;
380 }
381 printk(KERN_INFO "Switching to LITMUS^RT plugin %s.\n", plugin->plugin_name);
382 litmus = plugin;
383 } else
384 ret = -EBUSY;
385out:
386 spin_unlock_irqrestore(&task_transition_lock, flags);
387 return ret;
388}
389
390/* Called upon fork.
391 * p is the newly forked task.
392 */
393void litmus_fork(struct task_struct* p)
394{
395 if (is_realtime(p))
396 /* clean out any litmus related state, don't preserve anything*/
397 reinit_litmus_state(p, 0);
398}
399
400/* Called upon execve().
401 * current is doing the exec.
402 * Don't let address space specific stuff leak.
403 */
404void litmus_exec(void)
405{
406 struct task_struct* p = current;
407
408 if (is_realtime(p)) {
409 WARN_ON(p->rt_param.inh_task);
410 p->rt_param.np_flag = NULL;
411 }
412}
413
414void exit_litmus(struct task_struct *dead_tsk)
415{
416 if (is_realtime(dead_tsk))
417 litmus_exit_task(dead_tsk);
418}
419
420
421#ifdef CONFIG_MAGIC_SYSRQ
422int sys_kill(int pid, int sig);
423
424static void sysrq_handle_kill_rt_tasks(int key, struct tty_struct *tty)
425{
426 struct task_struct *t;
427 read_lock(&tasklist_lock);
428 for_each_process(t) {
429 if (is_realtime(t)) {
430 sys_kill(t->pid, SIGKILL);
431 }
432 }
433 read_unlock(&tasklist_lock);
434}
435
436static struct sysrq_key_op sysrq_kill_rt_tasks_op = {
437 .handler = sysrq_handle_kill_rt_tasks,
438 .help_msg = "quit-rt-tasks(X)",
439 .action_msg = "sent SIGKILL to all LITMUS^RT real-time tasks",
440};
441
442
443#endif
444
445
446static int proc_read_stats(char *page, char **start,
447 off_t off, int count,
448 int *eof, void *data)
449{
450 int len;
451
452 len = snprintf(page, PAGE_SIZE,
453 "real-time tasks = %d\n"
454 "ready for release = %d\n",
455 atomic_read(&rt_task_count),
456 0);
457 return len;
458}
459
460static int proc_read_plugins(char *page, char **start,
461 off_t off, int count,
462 int *eof, void *data)
463{
464 int len;
465
466 len = print_sched_plugins(page, PAGE_SIZE);
467 return len;
468}
469
470static int proc_read_curr(char *page, char **start,
471 off_t off, int count,
472 int *eof, void *data)
473{
474 int len;
475
476 len = snprintf(page, PAGE_SIZE, "%s\n", litmus->plugin_name);
477 return len;
478}
479
480static int proc_write_curr(struct file *file,
481 const char *buffer,
482 unsigned long count,
483 void *data)
484{
485 int len, ret;
486 char name[65];
487 struct sched_plugin* found;
488
489 if(count > 64)
490 len = 64;
491 else
492 len = count;
493
494 if(copy_from_user(name, buffer, len))
495 return -EFAULT;
496
497 name[len] = '\0';
498 /* chomp name */
499 if (len > 1 && name[len - 1] == '\n')
500 name[len - 1] = '\0';
501
502 found = find_sched_plugin(name);
503
504 if (found) {
505 ret = switch_sched_plugin(found);
506 if (ret != 0)
507 printk(KERN_INFO "Could not switch plugin: %d\n", ret);
508 } else
509 printk(KERN_INFO "Plugin '%s' is unknown.\n", name);
510
511 return len;
512}
513
514
515static int proc_read_release_master(char *page, char **start,
516 off_t off, int count,
517 int *eof, void *data)
518{
519 int len, master;
520 master = atomic_read(&release_master_cpu);
521 if (master == NO_CPU)
522 len = snprintf(page, PAGE_SIZE, "NO_CPU\n");
523 else
524 len = snprintf(page, PAGE_SIZE, "%d\n", master);
525 return len;
526}
527
528static int proc_write_release_master(struct file *file,
529 const char *buffer,
530 unsigned long count,
531 void *data)
532{
533 int cpu, err, online = 0;
534 char msg[64];
535
536 if (count > 63)
537 return -EINVAL;
538
539 if (copy_from_user(msg, buffer, count))
540 return -EFAULT;
541
542 /* terminate */
543 msg[count] = '\0';
544 /* chomp */
545 if (count > 1 && msg[count - 1] == '\n')
546 msg[count - 1] = '\0';
547
548 if (strcmp(msg, "NO_CPU") == 0) {
549 atomic_set(&release_master_cpu, NO_CPU);
550 return count;
551 } else {
552 err = sscanf(msg, "%d", &cpu);
553 if (err == 1 && cpu >= 0 && (online = cpu_online(cpu))) {
554 atomic_set(&release_master_cpu, cpu);
555 return count;
556 } else {
557 TRACE("invalid release master: '%s' "
558 "(err:%d cpu:%d online:%d)\n",
559 msg, err, cpu, online);
560 return -EINVAL;
561 }
562 }
563}
564
565static struct proc_dir_entry *litmus_dir = NULL,
566 *curr_file = NULL,
567 *stat_file = NULL,
568 *plugs_file = NULL,
569 *release_master_file = NULL;
570
571static int __init init_litmus_proc(void)
572{
573 litmus_dir = proc_mkdir("litmus", NULL);
574 if (!litmus_dir) {
575 printk(KERN_ERR "Could not allocate LITMUS^RT procfs entry.\n");
576 return -ENOMEM;
577 }
578
579 curr_file = create_proc_entry("active_plugin",
580 0644, litmus_dir);
581 if (!curr_file) {
582 printk(KERN_ERR "Could not allocate active_plugin "
583 "procfs entry.\n");
584 return -ENOMEM;
585 }
586 curr_file->read_proc = proc_read_curr;
587 curr_file->write_proc = proc_write_curr;
588
589 release_master_file = create_proc_entry("release_master",
590 0644, litmus_dir);
591 if (!release_master_file) {
592 printk(KERN_ERR "Could not allocate release_master "
593 "procfs entry.\n");
594 return -ENOMEM;
595 }
596 release_master_file->read_proc = proc_read_release_master;
597 release_master_file->write_proc = proc_write_release_master;
598
599 stat_file = create_proc_read_entry("stats", 0444, litmus_dir,
600 proc_read_stats, NULL);
601
602 plugs_file = create_proc_read_entry("plugins", 0444, litmus_dir,
603 proc_read_plugins, NULL);
604
605 return 0;
606}
607
608static void exit_litmus_proc(void)
609{
610 if (plugs_file)
611 remove_proc_entry("plugins", litmus_dir);
612 if (stat_file)
613 remove_proc_entry("stats", litmus_dir);
614 if (curr_file)
615 remove_proc_entry("active_plugin", litmus_dir);
616 if (litmus_dir)
617 remove_proc_entry("litmus", NULL);
618}
619
620extern struct sched_plugin linux_sched_plugin;
621
622static int __init _init_litmus(void)
623{
624 /* Common initializers,
625 * mode change lock is used to enforce single mode change
626 * operation.
627 */
628 printk("Starting LITMUS^RT kernel\n");
629
630 register_sched_plugin(&linux_sched_plugin);
631
632 heap_node_cache = KMEM_CACHE(heap_node, SLAB_PANIC);
633
634#ifdef CONFIG_MAGIC_SYSRQ
635 /* offer some debugging help */
636 if (!register_sysrq_key('x', &sysrq_kill_rt_tasks_op))
637 printk("Registered kill rt tasks magic sysrq.\n");
638 else
639 printk("Could not register kill rt tasks magic sysrq.\n");
640#endif
641
642 init_litmus_proc();
643
644 return 0;
645}
646
647static void _exit_litmus(void)
648{
649 exit_litmus_proc();
650 kmem_cache_destroy(heap_node_cache);
651}
652
653module_init(_init_litmus);
654module_exit(_exit_litmus);
diff --git a/litmus/sched_litmus.c b/litmus/sched_litmus.c
new file mode 100644
index 000000000000..ccedd3670ac5
--- /dev/null
+++ b/litmus/sched_litmus.c
@@ -0,0 +1,275 @@
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, int flags)
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, int queued)
192{
193}
194
195static void switched_to_litmus(struct rq *rq, struct task_struct *p, int running)
196{
197}
198
199static void prio_changed_litmus(struct rq *rq, struct task_struct *p,
200 int oldprio, int running)
201{
202}
203
204unsigned int get_rr_interval_litmus(struct task_struct *p)
205{
206 /* return infinity */
207 return 0;
208}
209
210/* This is called when a task became a real-time task, either due to a SCHED_*
211 * class transition or due to PI mutex inheritance. We don't handle Linux PI
212 * mutex inheritance yet (and probably never will). Use LITMUS provided
213 * synchronization primitives instead.
214 */
215static void set_curr_task_litmus(struct rq *rq)
216{
217 rq->curr->se.exec_start = rq->clock;
218}
219
220
221#ifdef CONFIG_SMP
222/* execve tries to rebalance task in this scheduling domain */
223static int select_task_rq_litmus(struct task_struct *p, int sd_flag, int flags)
224{
225 /* preemption is already disabled.
226 * We don't want to change cpu here
227 */
228 return smp_processor_id();
229}
230
231/* we don't repartition at runtime */
232
233static unsigned long
234load_balance_litmus(struct rq *this_rq, int this_cpu, struct rq *busiest,
235 unsigned long max_load_move,
236 struct sched_domain *sd, enum cpu_idle_type idle,
237 int *all_pinned, int *this_best_prio)
238{
239 return 0;
240}
241
242static int
243move_one_task_litmus(struct rq *this_rq, int this_cpu, struct rq *busiest,
244 struct sched_domain *sd, enum cpu_idle_type idle)
245{
246 return 0;
247}
248#endif
249
250const struct sched_class litmus_sched_class = {
251 .next = &rt_sched_class,
252 .enqueue_task = enqueue_task_litmus,
253 .dequeue_task = dequeue_task_litmus,
254 .yield_task = yield_task_litmus,
255
256 .check_preempt_curr = check_preempt_curr_litmus,
257
258 .pick_next_task = pick_next_task_litmus,
259 .put_prev_task = put_prev_task_litmus,
260
261#ifdef CONFIG_SMP
262 .select_task_rq = select_task_rq_litmus,
263
264 .load_balance = load_balance_litmus,
265 .move_one_task = move_one_task_litmus,
266#endif
267
268 .set_curr_task = set_curr_task_litmus,
269 .task_tick = task_tick_litmus,
270
271 .get_rr_interval = get_rr_interval_litmus,
272
273 .prio_changed = prio_changed_litmus,
274 .switched_to = switched_to_litmus,
275};
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}