diff options
author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2013-06-25 01:27:07 -0400 |
---|---|---|
committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2014-06-07 05:30:42 -0400 |
commit | 2edfb17682026b6a1efcd67d4ec9bb3f75b02d3b (patch) | |
tree | b49af42cb00ace1be3c3ab1920688db9c08933c6 /kernel | |
parent | 493f1cfd648d8b50276b61532bea8b862308a4a1 (diff) |
Add LITMUS^RT core implementation
This patch adds the core of LITMUS^RT:
- library functionality (heaps, rt_domain, prioritization, etc.)
- budget enforcement logic
- job management
- system call backends
- virtual devices (control page, etc.)
- scheduler plugin API (and dummy plugin)
This code compiles, but is not yet integrated with the rest of Linux.
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/sched/litmus.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/kernel/sched/litmus.c b/kernel/sched/litmus.c new file mode 100644 index 000000000000..ad88a14ac170 --- /dev/null +++ b/kernel/sched/litmus.c | |||
@@ -0,0 +1,340 @@ | |||
1 | /* This file is included from kernel/sched.c */ | ||
2 | |||
3 | #include "sched.h" | ||
4 | |||
5 | #include <litmus/trace.h> | ||
6 | #include <litmus/sched_trace.h> | ||
7 | |||
8 | #include <litmus/litmus.h> | ||
9 | #include <litmus/budget.h> | ||
10 | #include <litmus/sched_plugin.h> | ||
11 | #include <litmus/preempt.h> | ||
12 | |||
13 | static void update_time_litmus(struct rq *rq, struct task_struct *p) | ||
14 | { | ||
15 | u64 delta = rq->clock - p->se.exec_start; | ||
16 | if (unlikely((s64)delta < 0)) | ||
17 | delta = 0; | ||
18 | /* per job counter */ | ||
19 | p->rt_param.job_params.exec_time += delta; | ||
20 | /* task counter */ | ||
21 | p->se.sum_exec_runtime += delta; | ||
22 | /* sched_clock() */ | ||
23 | p->se.exec_start = rq->clock; | ||
24 | cpuacct_charge(p, delta); | ||
25 | } | ||
26 | |||
27 | static void double_rq_lock(struct rq *rq1, struct rq *rq2); | ||
28 | static void double_rq_unlock(struct rq *rq1, struct rq *rq2); | ||
29 | |||
30 | static struct task_struct * | ||
31 | litmus_schedule(struct rq *rq, struct task_struct *prev) | ||
32 | { | ||
33 | struct task_struct *next; | ||
34 | |||
35 | #ifdef CONFIG_SMP | ||
36 | struct rq* other_rq; | ||
37 | long was_running; | ||
38 | lt_t _maybe_deadlock = 0; | ||
39 | #endif | ||
40 | |||
41 | /* let the plugin schedule */ | ||
42 | next = litmus->schedule(prev); | ||
43 | |||
44 | sched_state_plugin_check(); | ||
45 | |||
46 | #ifdef CONFIG_SMP | ||
47 | /* check if a global plugin pulled a task from a different RQ */ | ||
48 | if (next && task_rq(next) != rq) { | ||
49 | /* we need to migrate the task */ | ||
50 | other_rq = task_rq(next); | ||
51 | TRACE_TASK(next, "migrate from %d\n", other_rq->cpu); | ||
52 | |||
53 | /* while we drop the lock, the prev task could change its | ||
54 | * state | ||
55 | */ | ||
56 | was_running = is_running(prev); | ||
57 | mb(); | ||
58 | raw_spin_unlock(&rq->lock); | ||
59 | |||
60 | /* Don't race with a concurrent switch. This could deadlock in | ||
61 | * the case of cross or circular migrations. It's the job of | ||
62 | * the plugin to make sure that doesn't happen. | ||
63 | */ | ||
64 | TRACE_TASK(next, "stack_in_use=%d\n", | ||
65 | next->rt_param.stack_in_use); | ||
66 | if (next->rt_param.stack_in_use != NO_CPU) { | ||
67 | TRACE_TASK(next, "waiting to deschedule\n"); | ||
68 | _maybe_deadlock = litmus_clock(); | ||
69 | } | ||
70 | while (next->rt_param.stack_in_use != NO_CPU) { | ||
71 | cpu_relax(); | ||
72 | mb(); | ||
73 | if (next->rt_param.stack_in_use == NO_CPU) | ||
74 | TRACE_TASK(next,"descheduled. Proceeding.\n"); | ||
75 | |||
76 | if (lt_before(_maybe_deadlock + 1000000000L, | ||
77 | litmus_clock())) { | ||
78 | /* We've been spinning for 1s. | ||
79 | * Something can't be right! | ||
80 | * Let's abandon the task and bail out; at least | ||
81 | * we will have debug info instead of a hard | ||
82 | * deadlock. | ||
83 | */ | ||
84 | #ifdef CONFIG_BUG_ON_MIGRATION_DEADLOCK | ||
85 | BUG(); | ||
86 | #else | ||
87 | TRACE_TASK(next,"stack too long in use. " | ||
88 | "Deadlock?\n"); | ||
89 | next = NULL; | ||
90 | |||
91 | /* bail out */ | ||
92 | raw_spin_lock(&rq->lock); | ||
93 | return next; | ||
94 | #endif | ||
95 | } | ||
96 | } | ||
97 | #ifdef __ARCH_WANT_UNLOCKED_CTXSW | ||
98 | if (next->on_cpu) | ||
99 | TRACE_TASK(next, "waiting for !oncpu"); | ||
100 | while (next->on_cpu) { | ||
101 | cpu_relax(); | ||
102 | mb(); | ||
103 | } | ||
104 | #endif | ||
105 | double_rq_lock(rq, other_rq); | ||
106 | mb(); | ||
107 | if (is_realtime(prev) && is_running(prev) != was_running) { | ||
108 | TRACE_TASK(prev, | ||
109 | "state changed while we dropped" | ||
110 | " the lock: is_running=%d, was_running=%d\n", | ||
111 | is_running(prev), was_running); | ||
112 | if (is_running(prev) && !was_running) { | ||
113 | /* prev task became unblocked | ||
114 | * we need to simulate normal sequence of events | ||
115 | * to scheduler plugins. | ||
116 | */ | ||
117 | litmus->task_block(prev); | ||
118 | litmus->task_wake_up(prev); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | set_task_cpu(next, smp_processor_id()); | ||
123 | |||
124 | /* DEBUG: now that we have the lock we need to make sure a | ||
125 | * couple of things still hold: | ||
126 | * - it is still a real-time task | ||
127 | * - it is still runnable (could have been stopped) | ||
128 | * If either is violated, then the active plugin is | ||
129 | * doing something wrong. | ||
130 | */ | ||
131 | if (!is_realtime(next) || !is_running(next)) { | ||
132 | /* BAD BAD BAD */ | ||
133 | TRACE_TASK(next,"BAD: migration invariant FAILED: " | ||
134 | "rt=%d running=%d\n", | ||
135 | is_realtime(next), | ||
136 | is_running(next)); | ||
137 | /* drop the task */ | ||
138 | next = NULL; | ||
139 | } | ||
140 | /* release the other CPU's runqueue, but keep ours */ | ||
141 | raw_spin_unlock(&other_rq->lock); | ||
142 | } | ||
143 | #endif | ||
144 | |||
145 | if (next) { | ||
146 | #ifdef CONFIG_SMP | ||
147 | next->rt_param.stack_in_use = rq->cpu; | ||
148 | #else | ||
149 | next->rt_param.stack_in_use = 0; | ||
150 | #endif | ||
151 | update_rq_clock(rq); | ||
152 | next->se.exec_start = rq->clock; | ||
153 | } | ||
154 | |||
155 | update_enforcement_timer(next); | ||
156 | return next; | ||
157 | } | ||
158 | |||
159 | static void enqueue_task_litmus(struct rq *rq, struct task_struct *p, | ||
160 | int flags) | ||
161 | { | ||
162 | if (flags & ENQUEUE_WAKEUP) { | ||
163 | sched_trace_task_resume(p); | ||
164 | tsk_rt(p)->present = 1; | ||
165 | /* LITMUS^RT plugins need to update the state | ||
166 | * _before_ making it available in global structures. | ||
167 | * Linux gets away with being lazy about the task state | ||
168 | * update. We can't do that, hence we update the task | ||
169 | * state already here. | ||
170 | * | ||
171 | * WARNING: this needs to be re-evaluated when porting | ||
172 | * to newer kernel versions. | ||
173 | */ | ||
174 | p->state = TASK_RUNNING; | ||
175 | litmus->task_wake_up(p); | ||
176 | |||
177 | rq->litmus.nr_running++; | ||
178 | } else | ||
179 | TRACE_TASK(p, "ignoring an enqueue, not a wake up.\n"); | ||
180 | } | ||
181 | |||
182 | static void dequeue_task_litmus(struct rq *rq, struct task_struct *p, | ||
183 | int flags) | ||
184 | { | ||
185 | if (flags & DEQUEUE_SLEEP) { | ||
186 | litmus->task_block(p); | ||
187 | tsk_rt(p)->present = 0; | ||
188 | sched_trace_task_block(p); | ||
189 | |||
190 | rq->litmus.nr_running--; | ||
191 | } else | ||
192 | TRACE_TASK(p, "ignoring a dequeue, not going to sleep.\n"); | ||
193 | } | ||
194 | |||
195 | static void yield_task_litmus(struct rq *rq) | ||
196 | { | ||
197 | TS_SYSCALL_IN_START; | ||
198 | TS_SYSCALL_IN_END; | ||
199 | |||
200 | BUG_ON(rq->curr != current); | ||
201 | /* sched_yield() is called to trigger delayed preemptions. | ||
202 | * Thus, mark the current task as needing to be rescheduled. | ||
203 | * This will cause the scheduler plugin to be invoked, which can | ||
204 | * then determine if a preemption is still required. | ||
205 | */ | ||
206 | clear_exit_np(current); | ||
207 | litmus_reschedule_local(); | ||
208 | |||
209 | TS_SYSCALL_OUT_START; | ||
210 | } | ||
211 | |||
212 | /* Plugins are responsible for this. | ||
213 | */ | ||
214 | static void check_preempt_curr_litmus(struct rq *rq, struct task_struct *p, int flags) | ||
215 | { | ||
216 | } | ||
217 | |||
218 | static void put_prev_task_litmus(struct rq *rq, struct task_struct *p) | ||
219 | { | ||
220 | } | ||
221 | |||
222 | #ifdef CONFIG_SMP | ||
223 | static void pre_schedule_litmus(struct rq *rq, struct task_struct *prev) | ||
224 | { | ||
225 | update_rq_clock(rq); | ||
226 | /* tell update_rq_clock() that we just did that */ | ||
227 | rq->skip_clock_update = 1; | ||
228 | update_time_litmus(rq, prev); | ||
229 | if (!is_running(prev)) | ||
230 | tsk_rt(prev)->present = 0; | ||
231 | } | ||
232 | #endif | ||
233 | |||
234 | /* pick_next_task_litmus() - litmus_schedule() function | ||
235 | * | ||
236 | * return the next task to be scheduled | ||
237 | */ | ||
238 | static struct task_struct *pick_next_task_litmus(struct rq *rq) | ||
239 | { | ||
240 | /* get the to-be-switched-out task (prev) */ | ||
241 | struct task_struct *prev = rq->litmus.prev; | ||
242 | struct task_struct *next; | ||
243 | |||
244 | /* if not called from schedule() but from somewhere | ||
245 | * else (e.g., migration), return now! | ||
246 | */ | ||
247 | if(!rq->litmus.prev) | ||
248 | return NULL; | ||
249 | |||
250 | rq->litmus.prev = NULL; | ||
251 | |||
252 | TS_PLUGIN_SCHED_START; | ||
253 | next = litmus_schedule(rq, prev); | ||
254 | TS_PLUGIN_SCHED_END; | ||
255 | |||
256 | return next; | ||
257 | } | ||
258 | |||
259 | static void task_tick_litmus(struct rq *rq, struct task_struct *p, int queued) | ||
260 | { | ||
261 | if (is_realtime(p) && !queued) { | ||
262 | update_time_litmus(rq, p); | ||
263 | /* budget check for QUANTUM_ENFORCEMENT tasks */ | ||
264 | if (budget_enforced(p) && budget_exhausted(p)) { | ||
265 | litmus_reschedule_local(); | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static void switched_to_litmus(struct rq *rq, struct task_struct *p) | ||
271 | { | ||
272 | } | ||
273 | |||
274 | static void prio_changed_litmus(struct rq *rq, struct task_struct *p, | ||
275 | int oldprio) | ||
276 | { | ||
277 | } | ||
278 | |||
279 | unsigned int get_rr_interval_litmus(struct rq *rq, struct task_struct *p) | ||
280 | { | ||
281 | /* return infinity */ | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | /* This is called when a task became a real-time task, either due to a SCHED_* | ||
286 | * class transition or due to PI mutex inheritance. We don't handle Linux PI | ||
287 | * mutex inheritance yet (and probably never will). Use LITMUS provided | ||
288 | * synchronization primitives instead. | ||
289 | */ | ||
290 | static void set_curr_task_litmus(struct rq *rq) | ||
291 | { | ||
292 | rq->curr->se.exec_start = rq->clock; | ||
293 | } | ||
294 | |||
295 | |||
296 | #ifdef CONFIG_SMP | ||
297 | /* execve tries to rebalance task in this scheduling domain. | ||
298 | * We don't care about the scheduling domain; can gets called from | ||
299 | * exec, fork, wakeup. | ||
300 | */ | ||
301 | static int | ||
302 | select_task_rq_litmus(struct task_struct *p, int sd_flag, int flags) | ||
303 | { | ||
304 | /* preemption is already disabled. | ||
305 | * We don't want to change cpu here | ||
306 | */ | ||
307 | return task_cpu(p); | ||
308 | } | ||
309 | #endif | ||
310 | |||
311 | const struct sched_class litmus_sched_class = { | ||
312 | /* From 34f971f6 the stop/migrate worker threads have a class on | ||
313 | * their own, which is the highest prio class. We don't support | ||
314 | * cpu-hotplug or cpu throttling. Allows Litmus to use up to 1.0 | ||
315 | * CPU capacity. | ||
316 | */ | ||
317 | .next = &rt_sched_class, | ||
318 | .enqueue_task = enqueue_task_litmus, | ||
319 | .dequeue_task = dequeue_task_litmus, | ||
320 | .yield_task = yield_task_litmus, | ||
321 | |||
322 | .check_preempt_curr = check_preempt_curr_litmus, | ||
323 | |||
324 | .pick_next_task = pick_next_task_litmus, | ||
325 | .put_prev_task = put_prev_task_litmus, | ||
326 | |||
327 | #ifdef CONFIG_SMP | ||
328 | .select_task_rq = select_task_rq_litmus, | ||
329 | |||
330 | .pre_schedule = pre_schedule_litmus, | ||
331 | #endif | ||
332 | |||
333 | .set_curr_task = set_curr_task_litmus, | ||
334 | .task_tick = task_tick_litmus, | ||
335 | |||
336 | .get_rr_interval = get_rr_interval_litmus, | ||
337 | |||
338 | .prio_changed = prio_changed_litmus, | ||
339 | .switched_to = switched_to_litmus, | ||
340 | }; | ||