diff options
Diffstat (limited to 'kernel/sched/litmus.c')
-rw-r--r-- | kernel/sched/litmus.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/kernel/sched/litmus.c b/kernel/sched/litmus.c new file mode 100644 index 000000000000..d9c59998155b --- /dev/null +++ b/kernel/sched/litmus.c | |||
@@ -0,0 +1,386 @@ | |||
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/debug_trace.h> | ||
9 | #include <litmus/litmus.h> | ||
10 | #include <litmus/budget.h> | ||
11 | #include <litmus/sched_plugin.h> | ||
12 | #include <litmus/preempt.h> | ||
13 | #include <litmus/np.h> | ||
14 | |||
15 | static void update_time_litmus(struct rq *rq, struct task_struct *p) | ||
16 | { | ||
17 | u64 delta = rq->clock - p->se.exec_start; | ||
18 | if (unlikely((s64)delta < 0)) | ||
19 | delta = 0; | ||
20 | /* per job counter */ | ||
21 | p->rt_param.job_params.exec_time += delta; | ||
22 | /* task counter */ | ||
23 | p->se.sum_exec_runtime += delta; | ||
24 | if (delta) { | ||
25 | TRACE_TASK(p, "charged %llu exec time (total:%llu, rem:%llu)\n", | ||
26 | delta, p->rt_param.job_params.exec_time, budget_remaining(p)); | ||
27 | } | ||
28 | /* sched_clock() */ | ||
29 | p->se.exec_start = rq->clock; | ||
30 | cpuacct_charge(p, delta); | ||
31 | } | ||
32 | |||
33 | static void double_rq_lock(struct rq *rq1, struct rq *rq2); | ||
34 | static void double_rq_unlock(struct rq *rq1, struct rq *rq2); | ||
35 | |||
36 | static struct task_struct * | ||
37 | litmus_schedule(struct rq *rq, struct task_struct *prev) | ||
38 | { | ||
39 | struct task_struct *next; | ||
40 | |||
41 | #ifdef CONFIG_SMP | ||
42 | struct rq* other_rq; | ||
43 | long was_running; | ||
44 | int from_where; | ||
45 | lt_t _maybe_deadlock = 0; | ||
46 | #endif | ||
47 | |||
48 | /* let the plugin schedule */ | ||
49 | next = litmus->schedule(prev); | ||
50 | |||
51 | sched_state_plugin_check(); | ||
52 | |||
53 | #ifdef CONFIG_SMP | ||
54 | /* check if a global plugin pulled a task from a different RQ */ | ||
55 | if (next && task_rq(next) != rq) { | ||
56 | /* we need to migrate the task */ | ||
57 | other_rq = task_rq(next); | ||
58 | from_where = other_rq->cpu; | ||
59 | TRACE_TASK(next, "migrate from %d\n", from_where); | ||
60 | |||
61 | /* while we drop the lock, the prev task could change its | ||
62 | * state | ||
63 | */ | ||
64 | BUG_ON(prev != current); | ||
65 | was_running = is_current_running(); | ||
66 | |||
67 | /* Don't race with a concurrent switch. This could deadlock in | ||
68 | * the case of cross or circular migrations. It's the job of | ||
69 | * the plugin to make sure that doesn't happen. | ||
70 | */ | ||
71 | TRACE_TASK(next, "stack_in_use=%d\n", | ||
72 | next->rt_param.stack_in_use); | ||
73 | if (next->rt_param.stack_in_use != NO_CPU) { | ||
74 | TRACE_TASK(next, "waiting to deschedule\n"); | ||
75 | _maybe_deadlock = litmus_clock(); | ||
76 | } | ||
77 | |||
78 | raw_spin_unlock(&rq->lock); | ||
79 | |||
80 | while (next->rt_param.stack_in_use != NO_CPU) { | ||
81 | cpu_relax(); | ||
82 | mb(); | ||
83 | if (next->rt_param.stack_in_use == NO_CPU) | ||
84 | TRACE_TASK(next,"descheduled. Proceeding.\n"); | ||
85 | |||
86 | if (!litmus->should_wait_for_stack(next)) { | ||
87 | /* plugin aborted the wait */ | ||
88 | TRACE_TASK(next, | ||
89 | "plugin gave up waiting for stack\n"); | ||
90 | next = NULL; | ||
91 | /* Make sure plugin is given a chance to | ||
92 | * reconsider. */ | ||
93 | litmus_reschedule_local(); | ||
94 | /* give up */ | ||
95 | raw_spin_lock(&rq->lock); | ||
96 | goto out; | ||
97 | } | ||
98 | |||
99 | if (from_where != task_rq(next)->cpu) { | ||
100 | /* The plugin should not give us something | ||
101 | * that other cores are trying to pull, too */ | ||
102 | TRACE_TASK(next, "next invalid: task keeps " | ||
103 | "shifting around!? " | ||
104 | "(%d->%d)\n", | ||
105 | from_where, | ||
106 | task_rq(next)->cpu); | ||
107 | |||
108 | /* bail out */ | ||
109 | raw_spin_lock(&rq->lock); | ||
110 | litmus->next_became_invalid(next); | ||
111 | litmus_reschedule_local(); | ||
112 | next = NULL; | ||
113 | goto out; | ||
114 | } | ||
115 | |||
116 | if (lt_before(_maybe_deadlock + 1000000000L, | ||
117 | litmus_clock())) { | ||
118 | /* We've been spinning for 1s. | ||
119 | * Something can't be right! | ||
120 | * Let's abandon the task and bail out; at least | ||
121 | * we will have debug info instead of a hard | ||
122 | * deadlock. | ||
123 | */ | ||
124 | #ifdef CONFIG_BUG_ON_MIGRATION_DEADLOCK | ||
125 | BUG(); | ||
126 | #else | ||
127 | TRACE_TASK(next,"stack too long in use. " | ||
128 | "Deadlock?\n"); | ||
129 | next = NULL; | ||
130 | |||
131 | /* bail out */ | ||
132 | raw_spin_lock(&rq->lock); | ||
133 | goto out; | ||
134 | #endif | ||
135 | } | ||
136 | } | ||
137 | #ifdef __ARCH_WANT_UNLOCKED_CTXSW | ||
138 | if (next->on_cpu) | ||
139 | TRACE_TASK(next, "waiting for !oncpu"); | ||
140 | while (next->on_cpu) { | ||
141 | cpu_relax(); | ||
142 | mb(); | ||
143 | } | ||
144 | #endif | ||
145 | double_rq_lock(rq, other_rq); | ||
146 | if (other_rq == task_rq(next) && | ||
147 | next->rt_param.stack_in_use == NO_CPU) { | ||
148 | /* ok, we can grab it */ | ||
149 | set_task_cpu(next, rq->cpu); | ||
150 | /* release the other CPU's runqueue, but keep ours */ | ||
151 | raw_spin_unlock(&other_rq->lock); | ||
152 | } else { | ||
153 | /* Either it moved or the stack was claimed; both is | ||
154 | * bad and forces us to abort the migration. */ | ||
155 | TRACE_TASK(next, "next invalid: no longer available\n"); | ||
156 | raw_spin_unlock(&other_rq->lock); | ||
157 | litmus->next_became_invalid(next); | ||
158 | next = NULL; | ||
159 | goto out; | ||
160 | } | ||
161 | |||
162 | if (!litmus->post_migration_validate(next)) { | ||
163 | TRACE_TASK(next, "plugin deems task now invalid\n"); | ||
164 | litmus_reschedule_local(); | ||
165 | next = NULL; | ||
166 | } | ||
167 | } | ||
168 | #endif | ||
169 | |||
170 | /* check if the task became invalid while we dropped the lock */ | ||
171 | if (next && (!is_realtime(next) || !tsk_rt(next)->present)) { | ||
172 | TRACE_TASK(next, | ||
173 | "BAD: next (no longer?) valid\n"); | ||
174 | litmus->next_became_invalid(next); | ||
175 | litmus_reschedule_local(); | ||
176 | next = NULL; | ||
177 | } | ||
178 | |||
179 | if (next) { | ||
180 | #ifdef CONFIG_SMP | ||
181 | next->rt_param.stack_in_use = rq->cpu; | ||
182 | #else | ||
183 | next->rt_param.stack_in_use = 0; | ||
184 | #endif | ||
185 | update_rq_clock(rq); | ||
186 | next->se.exec_start = rq->clock; | ||
187 | } | ||
188 | |||
189 | out: | ||
190 | update_enforcement_timer(next); | ||
191 | return next; | ||
192 | } | ||
193 | |||
194 | static void enqueue_task_litmus(struct rq *rq, struct task_struct *p, | ||
195 | int flags) | ||
196 | { | ||
197 | tsk_rt(p)->present = 1; | ||
198 | if (flags & ENQUEUE_WAKEUP) { | ||
199 | sched_trace_task_resume(p); | ||
200 | /* LITMUS^RT plugins need to update the state | ||
201 | * _before_ making it available in global structures. | ||
202 | * Linux gets away with being lazy about the task state | ||
203 | * update. We can't do that, hence we update the task | ||
204 | * state already here. | ||
205 | * | ||
206 | * WARNING: this needs to be re-evaluated when porting | ||
207 | * to newer kernel versions. | ||
208 | */ | ||
209 | p->state = TASK_RUNNING; | ||
210 | litmus->task_wake_up(p); | ||
211 | |||
212 | rq->litmus.nr_running++; | ||
213 | } else { | ||
214 | TRACE_TASK(p, "ignoring an enqueue, not a wake up.\n"); | ||
215 | p->se.exec_start = rq->clock; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static void dequeue_task_litmus(struct rq *rq, struct task_struct *p, | ||
220 | int flags) | ||
221 | { | ||
222 | if (flags & DEQUEUE_SLEEP) { | ||
223 | #ifdef CONFIG_SCHED_TASK_TRACE | ||
224 | tsk_rt(p)->job_params.last_suspension = litmus_clock(); | ||
225 | #endif | ||
226 | litmus->task_block(p); | ||
227 | tsk_rt(p)->present = 0; | ||
228 | sched_trace_task_block(p); | ||
229 | |||
230 | rq->litmus.nr_running--; | ||
231 | } else | ||
232 | TRACE_TASK(p, "ignoring a dequeue, not going to sleep.\n"); | ||
233 | } | ||
234 | |||
235 | static void yield_task_litmus(struct rq *rq) | ||
236 | { | ||
237 | TS_SYSCALL_IN_START; | ||
238 | TS_SYSCALL_IN_END; | ||
239 | |||
240 | BUG_ON(rq->curr != current); | ||
241 | /* sched_yield() is called to trigger delayed preemptions. | ||
242 | * Thus, mark the current task as needing to be rescheduled. | ||
243 | * This will cause the scheduler plugin to be invoked, which can | ||
244 | * then determine if a preemption is still required. | ||
245 | */ | ||
246 | clear_exit_np(current); | ||
247 | litmus_reschedule_local(); | ||
248 | |||
249 | TS_SYSCALL_OUT_START; | ||
250 | } | ||
251 | |||
252 | /* Plugins are responsible for this. | ||
253 | */ | ||
254 | static void check_preempt_curr_litmus(struct rq *rq, struct task_struct *p, int flags) | ||
255 | { | ||
256 | } | ||
257 | |||
258 | static void put_prev_task_litmus(struct rq *rq, struct task_struct *p) | ||
259 | { | ||
260 | } | ||
261 | |||
262 | /* pick_next_task_litmus() - litmus_schedule() function | ||
263 | * | ||
264 | * return the next task to be scheduled | ||
265 | */ | ||
266 | static struct task_struct *pick_next_task_litmus(struct rq *rq, | ||
267 | struct task_struct *prev, struct pin_cookie cookie) | ||
268 | { | ||
269 | struct task_struct *next; | ||
270 | |||
271 | if (is_realtime(prev)) | ||
272 | update_time_litmus(rq, prev); | ||
273 | |||
274 | lockdep_unpin_lock(&rq->lock, cookie); | ||
275 | TS_PLUGIN_SCHED_START; | ||
276 | next = litmus_schedule(rq, prev); | ||
277 | TS_PLUGIN_SCHED_END; | ||
278 | lockdep_repin_lock(&rq->lock, cookie); | ||
279 | |||
280 | /* This is a bit backwards: the other classes call put_prev_task() | ||
281 | * _after_ they've determined that the class has some queued tasks. | ||
282 | * We can't determine this easily because each plugin manages its own | ||
283 | * ready queues, and because in the case of globally shared queues, | ||
284 | * we really don't know whether we'll have something ready even if | ||
285 | * we test here. So we do it in reverse: first ask the plugin to | ||
286 | * provide a task, and if we find one, call put_prev_task() on the | ||
287 | * previously scheduled task. | ||
288 | */ | ||
289 | if (next) | ||
290 | put_prev_task(rq, prev); | ||
291 | |||
292 | return next; | ||
293 | } | ||
294 | |||
295 | static void task_tick_litmus(struct rq *rq, struct task_struct *p, int queued) | ||
296 | { | ||
297 | if (is_realtime(p) && !queued) { | ||
298 | update_time_litmus(rq, p); | ||
299 | /* budget check for QUANTUM_ENFORCEMENT tasks */ | ||
300 | if (budget_enforced(p) && budget_exhausted(p)) { | ||
301 | litmus_reschedule_local(); | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | static void switched_to_litmus(struct rq *rq, struct task_struct *p) | ||
307 | { | ||
308 | } | ||
309 | |||
310 | static void prio_changed_litmus(struct rq *rq, struct task_struct *p, | ||
311 | int oldprio) | ||
312 | { | ||
313 | } | ||
314 | |||
315 | unsigned int get_rr_interval_litmus(struct rq *rq, struct task_struct *p) | ||
316 | { | ||
317 | /* return infinity */ | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | /* This is called when a task became a real-time task, either due to a SCHED_* | ||
322 | * class transition or due to PI mutex inheritance. We don't handle Linux PI | ||
323 | * mutex inheritance yet (and probably never will). Use LITMUS provided | ||
324 | * synchronization primitives instead. | ||
325 | */ | ||
326 | static void set_curr_task_litmus(struct rq *rq) | ||
327 | { | ||
328 | rq->curr->se.exec_start = rq->clock; | ||
329 | } | ||
330 | |||
331 | |||
332 | #ifdef CONFIG_SMP | ||
333 | /* execve tries to rebalance task in this scheduling domain. | ||
334 | * We don't care about the scheduling domain; can gets called from | ||
335 | * exec, fork, wakeup. | ||
336 | */ | ||
337 | static int | ||
338 | select_task_rq_litmus(struct task_struct *p, int cpu, int sd_flag, int flags) | ||
339 | { | ||
340 | /* preemption is already disabled. | ||
341 | * We don't want to change cpu here | ||
342 | */ | ||
343 | return task_cpu(p); | ||
344 | } | ||
345 | #endif | ||
346 | |||
347 | static void update_curr_litmus(struct rq *rq) | ||
348 | { | ||
349 | struct task_struct *p = rq->curr; | ||
350 | |||
351 | if (!is_realtime(p)) | ||
352 | return; | ||
353 | |||
354 | update_time_litmus(rq, p); | ||
355 | } | ||
356 | |||
357 | const struct sched_class litmus_sched_class = { | ||
358 | /* From 34f971f6 the stop/migrate worker threads have a class on | ||
359 | * their own, which is the highest prio class. We don't support | ||
360 | * cpu-hotplug or cpu throttling. Allows Litmus to use up to 1.0 | ||
361 | * CPU capacity. | ||
362 | */ | ||
363 | .next = &stop_sched_class, | ||
364 | .enqueue_task = enqueue_task_litmus, | ||
365 | .dequeue_task = dequeue_task_litmus, | ||
366 | .yield_task = yield_task_litmus, | ||
367 | |||
368 | .check_preempt_curr = check_preempt_curr_litmus, | ||
369 | |||
370 | .pick_next_task = pick_next_task_litmus, | ||
371 | .put_prev_task = put_prev_task_litmus, | ||
372 | |||
373 | #ifdef CONFIG_SMP | ||
374 | .select_task_rq = select_task_rq_litmus, | ||
375 | #endif | ||
376 | |||
377 | .set_curr_task = set_curr_task_litmus, | ||
378 | .task_tick = task_tick_litmus, | ||
379 | |||
380 | .get_rr_interval = get_rr_interval_litmus, | ||
381 | |||
382 | .prio_changed = prio_changed_litmus, | ||
383 | .switched_to = switched_to_litmus, | ||
384 | |||
385 | .update_curr = update_curr_litmus, | ||
386 | }; | ||