aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorBjoern Brandenburg <bbb@mpi-sws.org>2013-06-25 01:27:07 -0400
committerBjoern Brandenburg <bbb@mpi-sws.org>2014-06-07 05:30:42 -0400
commit2edfb17682026b6a1efcd67d4ec9bb3f75b02d3b (patch)
treeb49af42cb00ace1be3c3ab1920688db9c08933c6 /kernel
parent493f1cfd648d8b50276b61532bea8b862308a4a1 (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.c340
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
13static 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
27static void double_rq_lock(struct rq *rq1, struct rq *rq2);
28static void double_rq_unlock(struct rq *rq1, struct rq *rq2);
29
30static struct task_struct *
31litmus_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
159static 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
182static 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
195static 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 */
214static void check_preempt_curr_litmus(struct rq *rq, struct task_struct *p, int flags)
215{
216}
217
218static void put_prev_task_litmus(struct rq *rq, struct task_struct *p)
219{
220}
221
222#ifdef CONFIG_SMP
223static 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 */
238static 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
259static 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
270static void switched_to_litmus(struct rq *rq, struct task_struct *p)
271{
272}
273
274static void prio_changed_litmus(struct rq *rq, struct task_struct *p,
275 int oldprio)
276{
277}
278
279unsigned 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 */
290static 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 */
301static int
302select_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
311const 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};