diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2009-12-17 21:23:36 -0500 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-29 17:05:45 -0400 |
commit | 4b38febbd59fd33542a343991262119eb9860f5e (patch) | |
tree | 1af88a0d354abe344c2c2869631f76a1806d75c3 | |
parent | 22763c5cf3690a681551162c15d34d935308c8d7 (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-- | Makefile | 4 | ||||
-rw-r--r-- | include/linux/sched.h | 7 | ||||
-rw-r--r-- | include/litmus/feather_buffer.h | 94 | ||||
-rw-r--r-- | include/litmus/feather_trace.h | 36 | ||||
-rw-r--r-- | include/litmus/heap.h | 77 | ||||
-rw-r--r-- | include/litmus/jobs.h | 9 | ||||
-rw-r--r-- | include/litmus/litmus.h | 177 | ||||
-rw-r--r-- | include/litmus/rt_param.h | 175 | ||||
-rw-r--r-- | include/litmus/sched_plugin.h | 159 | ||||
-rw-r--r-- | include/litmus/sched_trace.h | 191 | ||||
-rw-r--r-- | include/litmus/trace.h | 113 | ||||
-rw-r--r-- | kernel/fork.c | 7 | ||||
-rw-r--r-- | kernel/sched.c | 92 | ||||
-rw-r--r-- | kernel/sched_fair.c | 2 | ||||
-rw-r--r-- | kernel/sched_rt.c | 2 | ||||
-rw-r--r-- | litmus/Kconfig | 50 | ||||
-rw-r--r-- | litmus/Makefile | 12 | ||||
-rw-r--r-- | litmus/ft_event.c | 43 | ||||
-rw-r--r-- | litmus/heap.c | 314 | ||||
-rw-r--r-- | litmus/jobs.c | 43 | ||||
-rw-r--r-- | litmus/litmus.c | 654 | ||||
-rw-r--r-- | litmus/sched_litmus.c | 275 | ||||
-rw-r--r-- | litmus/sched_plugin.c | 199 |
23 files changed, 2723 insertions, 12 deletions
@@ -1,7 +1,7 @@ | |||
1 | VERSION = 2 | 1 | VERSION = 2 |
2 | PATCHLEVEL = 6 | 2 | PATCHLEVEL = 6 |
3 | SUBLEVEL = 32 | 3 | SUBLEVEL = 32 |
4 | EXTRAVERSION = | 4 | EXTRAVERSION =-litmus2010 |
5 | NAME = Man-Eating Seals of Antiquity | 5 | NAME = Man-Eating Seals of Antiquity |
6 | 6 | ||
7 | # *DOCUMENTATION* | 7 | # *DOCUMENTATION* |
@@ -644,7 +644,7 @@ export mod_strip_cmd | |||
644 | 644 | ||
645 | 645 | ||
646 | ifeq ($(KBUILD_EXTMOD),) | 646 | ifeq ($(KBUILD_EXTMOD),) |
647 | core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ | 647 | core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ litmus/ |
648 | 648 | ||
649 | vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ | 649 | vmlinux-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 | |||
97 | struct exec_domain; | 100 | struct exec_domain; |
98 | struct futex_pi_state; | 101 | struct futex_pi_state; |
99 | struct robust_list_head; | 102 | struct 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 | |||
10 | struct 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 | |||
23 | static 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 | |||
50 | static 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 | |||
67 | static 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 */ | ||
75 | static 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 | |||
5 | int ft_enable_event(unsigned long id); | ||
6 | int ft_disable_event(unsigned long id); | ||
7 | int ft_is_event_enabled(unsigned long id); | ||
8 | int 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 | |||
17 | extern 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 | |||
11 | struct 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 | |||
21 | struct 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 | |||
29 | typedef int (*heap_prio_t)(struct heap_node* a, struct heap_node* b); | ||
30 | |||
31 | void heap_init(struct heap* heap); | ||
32 | void heap_node_init(struct heap_node** ref_to_heap_node_ptr, void* value); | ||
33 | |||
34 | static inline int heap_node_in_heap(struct heap_node* h) | ||
35 | { | ||
36 | return h->degree != NOT_IN_HEAP; | ||
37 | } | ||
38 | |||
39 | static 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 */ | ||
45 | void heap_insert(heap_prio_t higher_prio, | ||
46 | struct heap* heap, | ||
47 | struct heap_node* node); | ||
48 | |||
49 | /* merge addition into target */ | ||
50 | void heap_union(heap_prio_t higher_prio, | ||
51 | struct heap* target, | ||
52 | struct heap* addition); | ||
53 | |||
54 | struct heap_node* heap_peek(heap_prio_t higher_prio, | ||
55 | struct heap* heap); | ||
56 | |||
57 | struct heap_node* heap_take(heap_prio_t higher_prio, | ||
58 | struct heap* heap); | ||
59 | |||
60 | void heap_uncache_min(heap_prio_t higher_prio, struct heap* heap); | ||
61 | int heap_decrease(heap_prio_t higher_prio, struct heap_node* node); | ||
62 | |||
63 | void heap_delete(heap_prio_t higher_prio, | ||
64 | struct heap* heap, | ||
65 | struct heap_node* node); | ||
66 | |||
67 | /* allocate from memcache */ | ||
68 | struct heap_node* heap_node_alloc(int gfp_flags); | ||
69 | void heap_node_free(struct heap_node* hn); | ||
70 | |||
71 | /* allocate a heap node for value and insert into the heap */ | ||
72 | int heap_add(heap_prio_t higher_prio, struct heap* heap, | ||
73 | void* value, int gfp_flags); | ||
74 | |||
75 | void* 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 | |||
4 | void prepare_for_next_period(struct task_struct *t); | ||
5 | void release_at(struct task_struct *t, lt_t start); | ||
6 | long 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 | |||
12 | extern atomic_t release_master_cpu; | ||
13 | |||
14 | extern 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 | */ | ||
41 | static 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 | |||
55 | void litmus_fork(struct task_struct *tsk); | ||
56 | void litmus_exec(void); | ||
57 | /* clean up real-time state of a task */ | ||
58 | void exit_litmus(struct task_struct *dead_tsk); | ||
59 | |||
60 | long litmus_admit_task(struct task_struct *tsk); | ||
61 | void 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 | |||
81 | inline 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. */ | ||
95 | static 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 | ||
131 | void 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 | |||
138 | static 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 | |||
145 | static inline int is_present(struct task_struct* t) | ||
146 | { | ||
147 | return t && tsk_rt(t)->present; | ||
148 | } | ||
149 | |||
150 | |||
151 | /* make the unit explicit */ | ||
152 | typedef unsigned long quanta_t; | ||
153 | |||
154 | enum 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 | */ | ||
163 | extern ktime_t tick_period; | ||
164 | |||
165 | static 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? */ | ||
175 | u64 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. */ | ||
9 | typedef unsigned long long lt_t; | ||
10 | |||
11 | static 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 | |||
17 | static 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 */ | ||
24 | typedef enum { | ||
25 | RT_CLASS_HARD, | ||
26 | RT_CLASS_SOFT, | ||
27 | RT_CLASS_BEST_EFFORT | ||
28 | } task_class_t; | ||
29 | |||
30 | struct 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 | |||
41 | struct _rt_domain; | ||
42 | struct heap_node; | ||
43 | struct release_heap; | ||
44 | |||
45 | struct 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 | |||
65 | struct 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 | */ | ||
71 | struct 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 */ | ||
11 | struct 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 | |||
26 | typedef long (*activate_plugin_t) (void); | ||
27 | typedef long (*deactivate_plugin_t) (void); | ||
28 | |||
29 | |||
30 | |||
31 | /********************* scheduler invocation ******************/ | ||
32 | |||
33 | /* Plugin-specific realtime tick handler */ | ||
34 | typedef void (*scheduler_tick_t) (struct task_struct *cur); | ||
35 | /* Novell make sched decision function */ | ||
36 | typedef 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 | */ | ||
40 | typedef 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 | */ | ||
49 | typedef 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 | */ | ||
56 | typedef 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 */ | ||
59 | typedef 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 | */ | ||
64 | typedef 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 | */ | ||
70 | typedef 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 | */ | ||
76 | typedef 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 | */ | ||
81 | typedef 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 */ | ||
88 | typedef long (*complete_job_t) (void); | ||
89 | |||
90 | typedef long (*admit_task_t)(struct task_struct* tsk); | ||
91 | |||
92 | typedef void (*release_at_t)(struct task_struct *t, lt_t start); | ||
93 | |||
94 | struct 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 | |||
134 | extern struct sched_plugin *litmus; | ||
135 | |||
136 | int register_sched_plugin(struct sched_plugin* plugin); | ||
137 | struct sched_plugin* find_sched_plugin(const char* name); | ||
138 | int print_sched_plugins(char* buf, int max); | ||
139 | |||
140 | static inline int srp_active(void) | ||
141 | { | ||
142 | #ifdef CONFIG_SRP | ||
143 | return litmus->srp_active; | ||
144 | #else | ||
145 | return 0; | ||
146 | #endif | ||
147 | } | ||
148 | static inline int fmlp_active(void) | ||
149 | { | ||
150 | #ifdef CONFIG_FMLP | ||
151 | return litmus->fmlp_active; | ||
152 | #else | ||
153 | return 0; | ||
154 | #endif | ||
155 | } | ||
156 | |||
157 | extern 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 | |||
8 | struct 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 | ||
16 | struct st_name_data { | ||
17 | char cmd[ST_NAME_LEN];/* The name of the executable of this process. */ | ||
18 | }; | ||
19 | |||
20 | struct st_param_data { /* regular params */ | ||
21 | u32 wcet; | ||
22 | u32 period; | ||
23 | u32 phase; | ||
24 | u8 partition; | ||
25 | u8 __unused[3]; | ||
26 | }; | ||
27 | |||
28 | struct 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 | |||
33 | struct 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 | |||
39 | struct 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 | |||
45 | struct st_switch_away_data { /* A process was switched away from on a given CPU. */ | ||
46 | u64 when; | ||
47 | u64 exec_time; | ||
48 | }; | ||
49 | |||
50 | struct 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 | |||
59 | struct st_block_data { /* A task blocks. */ | ||
60 | u64 when; | ||
61 | u64 __unused; | ||
62 | }; | ||
63 | |||
64 | struct st_resume_data { /* A task resumes. */ | ||
65 | u64 when; | ||
66 | u64 __unused; | ||
67 | }; | ||
68 | |||
69 | struct st_sys_release_data { | ||
70 | u64 when; | ||
71 | u64 release; | ||
72 | }; | ||
73 | |||
74 | #define DATA(x) struct st_ ## x ## _data x; | ||
75 | |||
76 | typedef 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 | |||
90 | struct 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 | ||
125 | feather_callback void do_sched_trace_task_name(unsigned long id, | ||
126 | struct task_struct* task); | ||
127 | feather_callback void do_sched_trace_task_param(unsigned long id, | ||
128 | struct task_struct* task); | ||
129 | feather_callback void do_sched_trace_task_release(unsigned long id, | ||
130 | struct task_struct* task); | ||
131 | feather_callback void do_sched_trace_task_switch_to(unsigned long id, | ||
132 | struct task_struct* task); | ||
133 | feather_callback void do_sched_trace_task_switch_away(unsigned long id, | ||
134 | struct task_struct* task); | ||
135 | feather_callback void do_sched_trace_task_completion(unsigned long id, | ||
136 | struct task_struct* task, | ||
137 | unsigned long forced); | ||
138 | feather_callback void do_sched_trace_task_block(unsigned long id, | ||
139 | struct task_struct* task); | ||
140 | feather_callback void do_sched_trace_task_resume(unsigned long id, | ||
141 | struct task_struct* task); | ||
142 | feather_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 | ||
181 | void sched_trace_log_message(const char* fmt, ...); | ||
182 | void 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 | |||
12 | enum task_type_marker { | ||
13 | TSK_BE, | ||
14 | TSK_RT, | ||
15 | TSK_UNKNOWN | ||
16 | }; | ||
17 | |||
18 | struct 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 */ | ||
27 | feather_callback void save_timestamp(unsigned long event); | ||
28 | feather_callback void save_timestamp_def(unsigned long event, unsigned long type); | ||
29 | feather_callback void save_timestamp_task(unsigned long event, unsigned long t_ptr); | ||
30 | feather_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 |
2444 | out: | 2453 | out: |
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 | ||
5249 | notrace unsigned long get_parent_ip(unsigned long addr) | 5275 | notrace 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); |
5428 | need_resched_nonpreemptible: | 5460 | need_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 | */ |
1005 | static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int flags) | 1005 | static 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 @@ | |||
1 | menu "LITMUS^RT" | ||
2 | |||
3 | menu "Tracing" | ||
4 | |||
5 | config 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 | |||
13 | config 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 | |||
27 | config 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 | |||
35 | config 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 | |||
48 | endmenu | ||
49 | |||
50 | endmenu | ||
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 | |||
5 | obj-y = sched_plugin.o litmus.o \ | ||
6 | jobs.o \ | ||
7 | heap.o | ||
8 | |||
9 | obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o | ||
10 | obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o | ||
11 | obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o | ||
12 | obj-$(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 | |||
8 | int ft_events[MAX_EVENTS]; | ||
9 | |||
10 | int 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 | |||
19 | int 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 | |||
28 | int 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 | |||
38 | int 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 | |||
4 | void heap_init(struct heap* heap) | ||
5 | { | ||
6 | heap->head = NULL; | ||
7 | heap->min = NULL; | ||
8 | } | ||
9 | |||
10 | void 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 */ | ||
23 | static 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 */ | ||
33 | static 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 */ | ||
57 | static 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 | |||
77 | static 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 | |||
101 | static 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 | |||
141 | static 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 */ | ||
157 | void 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 | |||
178 | void 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 */ | ||
189 | void 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 | |||
200 | struct 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 | |||
208 | struct 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 | |||
221 | int 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 | |||
248 | void 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 */ | ||
293 | int 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 | |||
304 | void* 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 | |||
9 | void 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 | |||
23 | void 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 | */ | ||
34 | long 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 */ | ||
21 | atomic_t rt_task_count = ATOMIC_INIT(0); | ||
22 | static DEFINE_SPINLOCK(task_transition_lock); | ||
23 | |||
24 | /* Give log messages sequential IDs. */ | ||
25 | atomic_t __log_seq_no = ATOMIC_INIT(0); | ||
26 | |||
27 | /* current master CPU for handling timer IRQs */ | ||
28 | atomic_t release_master_cpu = ATOMIC_INIT(NO_CPU); | ||
29 | |||
30 | static struct kmem_cache * heap_node_cache; | ||
31 | |||
32 | struct heap_node* heap_node_alloc(int gfp_flags) | ||
33 | { | ||
34 | return kmem_cache_alloc(heap_node_cache, gfp_flags); | ||
35 | } | ||
36 | |||
37 | void 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 | */ | ||
62 | asmlinkage 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 | */ | ||
124 | asmlinkage 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 | */ | ||
158 | asmlinkage 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 | */ | ||
186 | asmlinkage 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 | */ | ||
233 | asmlinkage 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 | */ | ||
246 | asmlinkage 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. */ | ||
260 | static 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 | |||
289 | long 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 | |||
340 | void 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 | */ | ||
360 | int 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; | ||
385 | out: | ||
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 | */ | ||
393 | void 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 | */ | ||
404 | void 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 | |||
414 | void 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 | ||
422 | int sys_kill(int pid, int sig); | ||
423 | |||
424 | static 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 | |||
436 | static 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 | |||
446 | static 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 | |||
460 | static 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 | |||
470 | static 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 | |||
480 | static 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 | |||
515 | static 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 | |||
528 | static 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 | |||
565 | static 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 | |||
571 | static 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 | |||
608 | static 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 | |||
620 | extern struct sched_plugin linux_sched_plugin; | ||
621 | |||
622 | static 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 | |||
647 | static void _exit_litmus(void) | ||
648 | { | ||
649 | exit_litmus_proc(); | ||
650 | kmem_cache_destroy(heap_node_cache); | ||
651 | } | ||
652 | |||
653 | module_init(_init_litmus); | ||
654 | module_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 | |||
6 | static 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 | |||
20 | static void double_rq_lock(struct rq *rq1, struct rq *rq2); | ||
21 | static void double_rq_unlock(struct rq *rq1, struct rq *rq2); | ||
22 | |||
23 | static 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 | |||
30 | static 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 | |||
144 | static 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 | |||
155 | static 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 | |||
165 | static 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 | */ | ||
173 | static void check_preempt_curr_litmus(struct rq *rq, struct task_struct *p, int flags) | ||
174 | { | ||
175 | } | ||
176 | |||
177 | /* has already been taken care of */ | ||
178 | static void put_prev_task_litmus(struct rq *rq, struct task_struct *p) | ||
179 | { | ||
180 | } | ||
181 | |||
182 | static 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 | |||
191 | static void task_tick_litmus(struct rq *rq, struct task_struct *p, int queued) | ||
192 | { | ||
193 | } | ||
194 | |||
195 | static void switched_to_litmus(struct rq *rq, struct task_struct *p, int running) | ||
196 | { | ||
197 | } | ||
198 | |||
199 | static void prio_changed_litmus(struct rq *rq, struct task_struct *p, | ||
200 | int oldprio, int running) | ||
201 | { | ||
202 | } | ||
203 | |||
204 | unsigned 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 | */ | ||
215 | static 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 */ | ||
223 | static 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 | |||
233 | static unsigned long | ||
234 | load_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 | |||
242 | static int | ||
243 | move_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 | |||
250 | const 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 | |||
19 | static void litmus_dummy_finish_switch(struct task_struct * prev) | ||
20 | { | ||
21 | } | ||
22 | |||
23 | static struct task_struct* litmus_dummy_schedule(struct task_struct * prev) | ||
24 | { | ||
25 | return NULL; | ||
26 | } | ||
27 | |||
28 | static void litmus_dummy_tick(struct task_struct* tsk) | ||
29 | { | ||
30 | } | ||
31 | |||
32 | static 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 | |||
39 | static void litmus_dummy_task_new(struct task_struct *t, int on_rq, int running) | ||
40 | { | ||
41 | } | ||
42 | |||
43 | static void litmus_dummy_task_wake_up(struct task_struct *task) | ||
44 | { | ||
45 | } | ||
46 | |||
47 | static void litmus_dummy_task_block(struct task_struct *task) | ||
48 | { | ||
49 | } | ||
50 | |||
51 | static void litmus_dummy_task_exit(struct task_struct *task) | ||
52 | { | ||
53 | } | ||
54 | |||
55 | static long litmus_dummy_complete_job(void) | ||
56 | { | ||
57 | return -ENOSYS; | ||
58 | } | ||
59 | |||
60 | static long litmus_dummy_activate_plugin(void) | ||
61 | { | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static long litmus_dummy_deactivate_plugin(void) | ||
66 | { | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | #ifdef CONFIG_FMLP | ||
71 | |||
72 | static long litmus_dummy_inherit_priority(struct pi_semaphore *sem, | ||
73 | struct task_struct *new_owner) | ||
74 | { | ||
75 | return -ENOSYS; | ||
76 | } | ||
77 | |||
78 | static long litmus_dummy_return_priority(struct pi_semaphore *sem) | ||
79 | { | ||
80 | return -ENOSYS; | ||
81 | } | ||
82 | |||
83 | static 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 | */ | ||
95 | struct 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 | */ | ||
120 | struct sched_plugin *litmus = &linux_sched_plugin; | ||
121 | |||
122 | /* the list of registered scheduling plugins */ | ||
123 | static LIST_HEAD(sched_plugins); | ||
124 | static 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 */ | ||
131 | int 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. */ | ||
166 | struct 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 | |||
179 | out_unlock: | ||
180 | spin_unlock(&sched_plugins_lock); | ||
181 | return plugin; | ||
182 | } | ||
183 | |||
184 | int 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 | } | ||