diff options
Diffstat (limited to 'litmus/sched_global_plugin.c')
-rw-r--r-- | litmus/sched_global_plugin.c | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/litmus/sched_global_plugin.c b/litmus/sched_global_plugin.c new file mode 100644 index 000000000000..22dffa7d62fc --- /dev/null +++ b/litmus/sched_global_plugin.c | |||
@@ -0,0 +1,675 @@ | |||
1 | /* | ||
2 | * litmus/sched_global_plugin.c | ||
3 | * | ||
4 | * Implementation of the basic operations and architecture needed by | ||
5 | * G-EDF/G-FIFO/EDZL/AEDZL global schedulers. | ||
6 | * | ||
7 | * This version uses the simple approach and serializes all scheduling | ||
8 | * decisions by the use of a queue lock. This is probably not the | ||
9 | * best way to do it, but it should suffice for now. | ||
10 | */ | ||
11 | |||
12 | #include <linux/spinlock.h> | ||
13 | #include <linux/percpu.h> | ||
14 | #include <linux/sched.h> | ||
15 | |||
16 | #include <litmus/litmus.h> | ||
17 | #include <litmus/jobs.h> | ||
18 | #include <litmus/sched_global_plugin.h> | ||
19 | #include <litmus/sched_trace.h> | ||
20 | |||
21 | #include <litmus/preempt.h> | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | |||
25 | |||
26 | /* Overview of Global operations. | ||
27 | * | ||
28 | * gbl_link_task_to_cpu(T, cpu) - Low-level operation to update the linkage | ||
29 | * structure (NOT the actually scheduled | ||
30 | * task). If there is another linked task To | ||
31 | * already it will set To->linked_on = NO_CPU | ||
32 | * (thereby removing its association with this | ||
33 | * CPU). However, it will not requeue the | ||
34 | * previously linked task (if any). It will set | ||
35 | * T's state to RT_F_RUNNING and check whether | ||
36 | * it is already running somewhere else. If T | ||
37 | * is scheduled somewhere else it will link | ||
38 | * it to that CPU instead (and pull the linked | ||
39 | * task to cpu). T may be NULL. | ||
40 | * | ||
41 | * gbl_unlink(T) - Unlink removes T from all scheduler data | ||
42 | * structures. If it is linked to some CPU it | ||
43 | * will link NULL to that CPU. If it is | ||
44 | * currently queued in the gsnedf queue it will | ||
45 | * be removed from the rt_domain. It is safe to | ||
46 | * call gbl_unlink(T) if T is not linked. T may not | ||
47 | * be NULL. | ||
48 | * | ||
49 | * gbl_requeue(T) - Requeue will insert T into the appropriate | ||
50 | * queue. If the system is in real-time mode and | ||
51 | * the T is released already, it will go into the | ||
52 | * ready queue. If the system is not in | ||
53 | * real-time mode is T, then T will go into the | ||
54 | * release queue. If T's release time is in the | ||
55 | * future, it will go into the release | ||
56 | * queue. That means that T's release time/job | ||
57 | * no/etc. has to be updated before requeu(T) is | ||
58 | * called. It is not safe to call gbl_requeue(T) | ||
59 | * when T is already queued. T may not be NULL. | ||
60 | * | ||
61 | * job_arrival(T) - This is the catch all function when T enters | ||
62 | * the system after either a suspension or at a | ||
63 | * job release. It will queue T (which means it | ||
64 | * is not safe to call job_arrival(T) if | ||
65 | * T is already queued) and then check whether a | ||
66 | * preemption is necessary. If a preemption is | ||
67 | * necessary it will update the linkage | ||
68 | * accordingly and cause scheduled to be called | ||
69 | * (either with an IPI or need_resched). It is | ||
70 | * safe to call job_arrival(T) if T's | ||
71 | * next job has not been actually released yet | ||
72 | * (releast time in the future). T will be put | ||
73 | * on the release queue in that case. | ||
74 | * | ||
75 | * job_completion(T) - Take care of everything that needs to be done | ||
76 | * to prepare T for its next release and place | ||
77 | * it in the right queue with | ||
78 | * job_arrival(). | ||
79 | * | ||
80 | * | ||
81 | * When we now that T is linked to CPU then gbl_link_task_to_cpu(NULL, CPU) is | ||
82 | * equivalent to gbl_unlink(T). Note that if you unlink a task from a CPU none of | ||
83 | * the functions will automatically propagate pending task from the ready queue | ||
84 | * to a linked task. This is the job of the calling function (by means of | ||
85 | * __take_ready). | ||
86 | */ | ||
87 | |||
88 | /* Uncomment this if you want to see all scheduling decisions in the | ||
89 | * TRACE() log. | ||
90 | #define WANT_ALL_SCHED_EVENTS | ||
91 | */ | ||
92 | |||
93 | |||
94 | /* Macros to access the current active global plugin. These are | ||
95 | * a lot like C++'s 'this' pointer. | ||
96 | */ | ||
97 | struct sched_global_plugin* active_gbl_plugin; | ||
98 | #define active_gbl_domain (active_gbl_plugin->domain) | ||
99 | #define active_gbl_domain_lock (active_gbl_domain.ready_lock) | ||
100 | |||
101 | |||
102 | /*********************************************************************/ | ||
103 | /* "Member" functions for both sched_plugin and sched_global_plugin. */ | ||
104 | /* NOTE: These will automatically call down into "virtual" functions.*/ | ||
105 | /*********************************************************************/ | ||
106 | |||
107 | /* Priority-related functions */ | ||
108 | int gbl_preemption_needed(struct task_struct *t) | ||
109 | { | ||
110 | /* we need the read lock for active_gbl_domain's ready_queue */ | ||
111 | /* no need to preempt if there is nothing pending */ | ||
112 | if (!__jobs_pending(&active_gbl_domain)) | ||
113 | return 0; | ||
114 | /* we need to reschedule if t doesn't exist */ | ||
115 | if (!t) | ||
116 | return 1; | ||
117 | |||
118 | /* NOTE: We cannot check for non-preemptibility since we | ||
119 | * don't know what address space we're currently in. | ||
120 | */ | ||
121 | |||
122 | /* make sure to get non-rt stuff out of the way */ | ||
123 | return !is_realtime(t) || active_gbl_plugin->prio_order(__next_ready(&active_gbl_domain), t); | ||
124 | } | ||
125 | |||
126 | int gbl_ready_order(struct bheap_node* a, struct bheap_node* b) | ||
127 | { | ||
128 | return active_gbl_plugin->prio_order(bheap2task(a), bheap2task(b)); | ||
129 | } | ||
130 | |||
131 | |||
132 | |||
133 | int gbl_cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b) | ||
134 | { | ||
135 | cpu_entry_t *a, *b; | ||
136 | a = _a->value; | ||
137 | b = _b->value; | ||
138 | |||
139 | /* Note that a and b are inverted: we want the lowest-priority CPU at | ||
140 | * the top of the heap. | ||
141 | */ | ||
142 | return active_gbl_plugin->prio_order(b->linked, a->linked); | ||
143 | } | ||
144 | |||
145 | /* gbl_update_cpu_position - Move the cpu entry to the correct place to maintain | ||
146 | * order in the cpu queue. Caller must hold gbl_domain_lock. | ||
147 | */ | ||
148 | void gbl_update_cpu_position(cpu_entry_t *entry) | ||
149 | { | ||
150 | if (likely(bheap_node_in_heap(entry->hn))) | ||
151 | bheap_delete(gbl_cpu_lower_prio, &active_gbl_plugin->cpu_heap, entry->hn); | ||
152 | bheap_insert(gbl_cpu_lower_prio, &active_gbl_plugin->cpu_heap, entry->hn); | ||
153 | } | ||
154 | |||
155 | /* caller must hold gsnedf lock */ | ||
156 | cpu_entry_t* lowest_prio_cpu(void) | ||
157 | { | ||
158 | struct bheap_node* hn; | ||
159 | hn = bheap_peek(gbl_cpu_lower_prio, &active_gbl_plugin->cpu_heap); | ||
160 | return hn->value; | ||
161 | } | ||
162 | |||
163 | |||
164 | /* link_task_to_cpu - Update the link of a CPU. | ||
165 | * Handles the case where the to-be-linked task is already | ||
166 | * scheduled on a different CPU. | ||
167 | */ | ||
168 | void gbl_link_task_to_cpu(struct task_struct* linked, | ||
169 | cpu_entry_t *entry) | ||
170 | { | ||
171 | cpu_entry_t *sched; | ||
172 | struct task_struct* tmp; | ||
173 | int on_cpu; | ||
174 | |||
175 | BUG_ON(linked && !is_realtime(linked)); | ||
176 | |||
177 | /* Currently linked task is set to be unlinked. */ | ||
178 | if (entry->linked) { | ||
179 | entry->linked->rt_param.linked_on = NO_CPU; | ||
180 | } | ||
181 | |||
182 | /* Link new task to CPU. */ | ||
183 | if (linked) { | ||
184 | set_rt_flags(linked, RT_F_RUNNING); | ||
185 | /* handle task is already scheduled somewhere! */ | ||
186 | on_cpu = linked->rt_param.scheduled_on; | ||
187 | if (on_cpu != NO_CPU) { | ||
188 | sched = active_gbl_plugin->cpus[on_cpu]; | ||
189 | /* this should only happen if not linked already */ | ||
190 | BUG_ON(sched->linked == linked); | ||
191 | |||
192 | /* If we are already scheduled on the CPU to which we | ||
193 | * wanted to link, we don't need to do the swap -- | ||
194 | * we just link ourselves to the CPU and depend on | ||
195 | * the caller to get things right. | ||
196 | */ | ||
197 | if (entry != sched) { | ||
198 | TRACE_TASK(linked, | ||
199 | "already scheduled on %d, updating link.\n", | ||
200 | sched->cpu); | ||
201 | tmp = sched->linked; | ||
202 | linked->rt_param.linked_on = sched->cpu; | ||
203 | sched->linked = linked; | ||
204 | gbl_update_cpu_position(sched); | ||
205 | linked = tmp; | ||
206 | } | ||
207 | } | ||
208 | if (linked) /* might be NULL due to swap */ | ||
209 | linked->rt_param.linked_on = entry->cpu; | ||
210 | } | ||
211 | entry->linked = linked; | ||
212 | #ifdef WANT_ALL_SCHED_EVENTS | ||
213 | if (linked) | ||
214 | TRACE_TASK(linked, "linked to %d.\n", entry->cpu); | ||
215 | else | ||
216 | TRACE("NULL linked to %d.\n", entry->cpu); | ||
217 | #endif | ||
218 | gbl_update_cpu_position(entry); | ||
219 | } | ||
220 | |||
221 | /* unlink - Make sure a task is not linked any longer to an entry | ||
222 | * where it was linked before. Must hold | ||
223 | * active_gbl_domain_lock. | ||
224 | */ | ||
225 | void gbl_unlink(struct task_struct* t) | ||
226 | { | ||
227 | cpu_entry_t *entry; | ||
228 | |||
229 | if (t->rt_param.linked_on != NO_CPU) { | ||
230 | /* unlink */ | ||
231 | entry = active_gbl_plugin->cpus[t->rt_param.linked_on]; | ||
232 | t->rt_param.linked_on = NO_CPU; | ||
233 | gbl_link_task_to_cpu(NULL, entry); | ||
234 | } else if (is_queued(t)) { | ||
235 | /* This is an interesting situation: t is scheduled, | ||
236 | * but was just recently unlinked. It cannot be | ||
237 | * linked anywhere else (because then it would have | ||
238 | * been relinked to this CPU), thus it must be in some | ||
239 | * queue. We must remove it from the list in this | ||
240 | * case. | ||
241 | */ | ||
242 | remove(&active_gbl_domain, t); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | |||
247 | /* preempt - force a CPU to reschedule | ||
248 | */ | ||
249 | void gbl_preempt(cpu_entry_t *entry) | ||
250 | { | ||
251 | preempt_if_preemptable(entry->scheduled, entry->cpu); | ||
252 | } | ||
253 | |||
254 | /* requeue - Put an unlinked task into global domain. | ||
255 | * Caller must hold active_gbl_domain. | ||
256 | */ | ||
257 | void gbl_requeue(struct task_struct* task) | ||
258 | { | ||
259 | BUG_ON(!task); | ||
260 | /* sanity check before insertion */ | ||
261 | BUG_ON(is_queued(task)); | ||
262 | |||
263 | if (is_released(task, litmus_clock())) | ||
264 | active_gbl_plugin->add_ready(&active_gbl_domain, task); | ||
265 | else { | ||
266 | /* it has got to wait */ | ||
267 | add_release(&active_gbl_domain, task); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | |||
272 | /* check for any necessary preemptions */ | ||
273 | void gbl_check_for_preemptions(void) | ||
274 | { | ||
275 | struct task_struct *task; | ||
276 | cpu_entry_t* last; | ||
277 | |||
278 | for(last = lowest_prio_cpu(); | ||
279 | gbl_preemption_needed(last->linked); | ||
280 | last = lowest_prio_cpu()) | ||
281 | { | ||
282 | /* preemption necessary */ | ||
283 | task = active_gbl_plugin->take_ready(&active_gbl_domain); | ||
284 | TRACE("check_for_preemptions: attempting to link task %d to %d\n", | ||
285 | task->pid, last->cpu); | ||
286 | if (last->linked) | ||
287 | gbl_requeue(last->linked); | ||
288 | gbl_link_task_to_cpu(task, last); | ||
289 | gbl_preempt(last); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | |||
294 | void gbl_release_jobs(rt_domain_t* rt, struct bheap* tasks) | ||
295 | { | ||
296 | unsigned long flags; | ||
297 | |||
298 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
299 | |||
300 | __merge_ready(rt, tasks); | ||
301 | gbl_check_for_preemptions(); | ||
302 | |||
303 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
304 | } | ||
305 | |||
306 | /* caller holds active_gbl_domain_lock */ | ||
307 | void gbl_job_completion(struct task_struct *t, int forced) | ||
308 | { | ||
309 | BUG_ON(!t); | ||
310 | |||
311 | sched_trace_task_completion(t, forced); | ||
312 | |||
313 | TRACE_TASK(t, "job_completion().\n"); | ||
314 | |||
315 | /* set flags */ | ||
316 | set_rt_flags(t, RT_F_SLEEP); | ||
317 | /* prepare for next period */ | ||
318 | prepare_for_next_period(t); | ||
319 | if (is_released(t, litmus_clock())) | ||
320 | sched_trace_task_release(t); | ||
321 | /* unlink */ | ||
322 | gbl_unlink(t); | ||
323 | /* requeue | ||
324 | * But don't requeue a blocking task. */ | ||
325 | if (is_running(t)) | ||
326 | active_gbl_plugin->job_arrival(t); | ||
327 | } | ||
328 | |||
329 | |||
330 | /*********************************************************************/ | ||
331 | /* These two functions can't use active_* defines since the 'litmus' */ | ||
332 | /* pointer is undefined/invalid when these are called. Think of them */ | ||
333 | /* as static member functions. */ | ||
334 | /*********************************************************************/ | ||
335 | |||
336 | void gbl_domain_init(struct sched_global_plugin* gbl_plugin, | ||
337 | check_resched_needed_t resched, | ||
338 | release_jobs_t release) | ||
339 | { | ||
340 | rt_domain_init(&gbl_plugin->domain, gbl_ready_order, resched, release); | ||
341 | } | ||
342 | |||
343 | |||
344 | long gbl_activate_plugin(void* plg) | ||
345 | { | ||
346 | struct sched_plugin* plugin = (struct sched_plugin*)plg; | ||
347 | int cpu; | ||
348 | cpu_entry_t *entry; | ||
349 | |||
350 | /* set the active global plugin */ | ||
351 | active_gbl_plugin = | ||
352 | container_of(plugin, | ||
353 | struct sched_global_plugin, | ||
354 | plugin); | ||
355 | |||
356 | bheap_init(&active_gbl_plugin->cpu_heap); | ||
357 | #ifdef CONFIG_RELEASE_MASTER | ||
358 | active_gbl_domain.release_master = atomic_read(&release_master_cpu); | ||
359 | #endif | ||
360 | |||
361 | for_each_online_cpu(cpu) { | ||
362 | entry = active_gbl_plugin->cpus[cpu]; | ||
363 | bheap_node_init(&entry->hn, entry); | ||
364 | entry->linked = NULL; | ||
365 | entry->scheduled = NULL; | ||
366 | #ifdef CONFIG_RELEASE_MASTER | ||
367 | if (cpu != active_gbl_domain.release_master) { | ||
368 | #endif | ||
369 | TRACE("Global Plugin: Initializing CPU #%d.\n", cpu); | ||
370 | gbl_update_cpu_position(entry); | ||
371 | #ifdef CONFIG_RELEASE_MASTER | ||
372 | } else { | ||
373 | TRACE("Global Plugin: CPU %d is release master.\n", cpu); | ||
374 | } | ||
375 | #endif | ||
376 | } | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | |||
381 | /********************************************************************/ | ||
382 | /* "Virtual" functions in both sched_plugin and sched_global_plugin */ | ||
383 | /********************************************************************/ | ||
384 | |||
385 | |||
386 | /* gbl_job_arrival: task is either resumed or released */ | ||
387 | void gblv_job_arrival(struct task_struct* task) | ||
388 | { | ||
389 | BUG_ON(!task); | ||
390 | |||
391 | gbl_requeue(task); | ||
392 | gbl_check_for_preemptions(); | ||
393 | } | ||
394 | |||
395 | /* gbl_tick - this function is called for every local timer interrupt. | ||
396 | * | ||
397 | * checks whether the current task has expired and checks | ||
398 | * whether we need to preempt it if it has not expired | ||
399 | */ | ||
400 | void gblv_tick(struct task_struct* t) | ||
401 | { | ||
402 | if (is_realtime(t) && budget_enforced(t) && budget_exhausted(t)) { | ||
403 | if (!is_np(t)) { | ||
404 | /* np tasks will be preempted when they become | ||
405 | * preemptable again | ||
406 | */ | ||
407 | litmus_reschedule_local(); | ||
408 | TRACE("gbl_scheduler_tick: " | ||
409 | "%d is preemptable " | ||
410 | " => FORCE_RESCHED\n", t->pid); | ||
411 | } else if (is_user_np(t)) { | ||
412 | TRACE("gbl_scheduler_tick: " | ||
413 | "%d is non-preemptable, " | ||
414 | "preemption delayed.\n", t->pid); | ||
415 | request_exit_np(t); | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | /* Getting schedule() right is a bit tricky. schedule() may not make any | ||
421 | * assumptions on the state of the current task since it may be called for a | ||
422 | * number of reasons. The reasons include a scheduler_tick() determined that it | ||
423 | * was necessary, because sys_exit_np() was called, because some Linux | ||
424 | * subsystem determined so, or even (in the worst case) because there is a bug | ||
425 | * hidden somewhere. Thus, we must take extreme care to determine what the | ||
426 | * current state is. | ||
427 | * | ||
428 | * The CPU could currently be scheduling a task (or not), be linked (or not). | ||
429 | * | ||
430 | * The following assertions for the scheduled task could hold: | ||
431 | * | ||
432 | * - !is_running(scheduled) // the job blocks | ||
433 | * - scheduled->timeslice == 0 // the job completed (forcefully) | ||
434 | * - get_rt_flag() == RT_F_SLEEP // the job completed (by syscall) | ||
435 | * - linked != scheduled // we need to reschedule (for any reason) | ||
436 | * - is_np(scheduled) // rescheduling must be delayed, | ||
437 | * sys_exit_np must be requested | ||
438 | * | ||
439 | * Any of these can occur together. | ||
440 | */ | ||
441 | struct task_struct* gblv_schedule(struct task_struct * prev) | ||
442 | { | ||
443 | cpu_entry_t* entry = active_gbl_plugin->cpus[smp_processor_id()]; | ||
444 | int out_of_time, sleep, preempt, np, exists, blocks; | ||
445 | struct task_struct* next = NULL; | ||
446 | |||
447 | #ifdef CONFIG_RELEASE_MASTER | ||
448 | /* Bail out early if we are the release master. | ||
449 | * The release master never schedules any real-time tasks. | ||
450 | */ | ||
451 | if (active_gbl_domain.release_master == entry->cpu) | ||
452 | return NULL; | ||
453 | #endif | ||
454 | |||
455 | raw_spin_lock(&active_gbl_domain_lock); | ||
456 | |||
457 | /* sanity checking */ | ||
458 | BUG_ON(entry->scheduled && entry->scheduled != prev); | ||
459 | BUG_ON(entry->scheduled && !is_realtime(prev)); | ||
460 | BUG_ON(is_realtime(prev) && !entry->scheduled); | ||
461 | |||
462 | /* (0) Determine state */ | ||
463 | exists = entry->scheduled != NULL; | ||
464 | blocks = exists && !is_running(entry->scheduled); | ||
465 | out_of_time = exists && | ||
466 | budget_enforced(entry->scheduled) && | ||
467 | budget_exhausted(entry->scheduled); | ||
468 | np = exists && is_np(entry->scheduled); | ||
469 | sleep = exists && get_rt_flags(entry->scheduled) == RT_F_SLEEP; | ||
470 | preempt = entry->scheduled != entry->linked; | ||
471 | |||
472 | #ifdef WANT_ALL_SCHED_EVENTS | ||
473 | TRACE_TASK(prev, "invoked gsnedf_schedule.\n"); | ||
474 | #endif | ||
475 | |||
476 | if (exists) | ||
477 | TRACE_TASK(prev, | ||
478 | "blocks:%d out_of_time:%d np:%d sleep:%d preempt:%d " | ||
479 | "state:%d sig:%d\n", | ||
480 | blocks, out_of_time, np, sleep, preempt, | ||
481 | prev->state, signal_pending(prev)); | ||
482 | if (entry->linked && preempt) | ||
483 | TRACE_TASK(prev, "will be preempted by %s/%d\n", | ||
484 | entry->linked->comm, entry->linked->pid); | ||
485 | |||
486 | |||
487 | /* If a task blocks we have no choice but to reschedule. | ||
488 | */ | ||
489 | if (blocks) | ||
490 | gbl_unlink(entry->scheduled); | ||
491 | |||
492 | /* Request a sys_exit_np() call if we would like to preempt but cannot. | ||
493 | * We need to make sure to update the link structure anyway in case | ||
494 | * that we are still linked. Multiple calls to request_exit_np() don't | ||
495 | * hurt. | ||
496 | */ | ||
497 | if (np && (out_of_time || preempt || sleep)) { | ||
498 | gbl_unlink(entry->scheduled); | ||
499 | request_exit_np(entry->scheduled); | ||
500 | } | ||
501 | |||
502 | /* Any task that is preemptable and either exhausts its execution | ||
503 | * budget or wants to sleep completes. We may have to reschedule after | ||
504 | * this. Don't do a job completion if we block (can't have timers running | ||
505 | * for blocked jobs). Preemption go first for the same reason. | ||
506 | */ | ||
507 | if (!np && (out_of_time || sleep) && !blocks && !preempt) | ||
508 | active_gbl_plugin->job_completion(entry->scheduled, !sleep); | ||
509 | |||
510 | /* Link pending task if we became unlinked. | ||
511 | */ | ||
512 | if (!entry->linked) | ||
513 | gbl_link_task_to_cpu(active_gbl_plugin->take_ready(&active_gbl_domain), entry); | ||
514 | |||
515 | /* The final scheduling decision. Do we need to switch for some reason? | ||
516 | * If linked is different from scheduled, then select linked as next. | ||
517 | */ | ||
518 | if ((!np || blocks) && | ||
519 | entry->linked != entry->scheduled) { | ||
520 | /* Schedule a linked job? */ | ||
521 | if (entry->linked) { | ||
522 | entry->linked->rt_param.scheduled_on = entry->cpu; | ||
523 | next = entry->linked; | ||
524 | } | ||
525 | if (entry->scheduled) { | ||
526 | /* not gonna be scheduled soon */ | ||
527 | entry->scheduled->rt_param.scheduled_on = NO_CPU; | ||
528 | TRACE_TASK(entry->scheduled, "scheduled_on = NO_CPU\n"); | ||
529 | } | ||
530 | } else | ||
531 | /* Only override Linux scheduler if we have a real-time task | ||
532 | * scheduled that needs to continue. | ||
533 | */ | ||
534 | if (exists) | ||
535 | next = prev; | ||
536 | |||
537 | sched_state_task_picked(); | ||
538 | |||
539 | raw_spin_unlock(&active_gbl_domain_lock); | ||
540 | |||
541 | #ifdef WANT_ALL_SCHED_EVENTS | ||
542 | TRACE("active_gbl_domain_lock released, next=0x%p\n", next); | ||
543 | |||
544 | if (next) | ||
545 | TRACE_TASK(next, "scheduled at %llu\n", litmus_clock()); | ||
546 | else if (exists && !next) | ||
547 | TRACE("becomes idle at %llu.\n", litmus_clock()); | ||
548 | #endif | ||
549 | |||
550 | |||
551 | return next; | ||
552 | } | ||
553 | |||
554 | |||
555 | /* _finish_switch - we just finished the switch away from prev | ||
556 | */ | ||
557 | void gblv_finish_switch(struct task_struct *prev) | ||
558 | { | ||
559 | cpu_entry_t* entry = active_gbl_plugin->cpus[smp_processor_id()]; | ||
560 | |||
561 | entry->scheduled = is_realtime(current) ? current : NULL; | ||
562 | #ifdef WANT_ALL_SCHED_EVENTS | ||
563 | TRACE_TASK(prev, "switched away from\n"); | ||
564 | #endif | ||
565 | } | ||
566 | |||
567 | |||
568 | /* Prepare a task for running in RT mode | ||
569 | */ | ||
570 | void gblv_task_new(struct task_struct * t, int on_rq, int running) | ||
571 | { | ||
572 | unsigned long flags; | ||
573 | cpu_entry_t* entry; | ||
574 | |||
575 | TRACE("global plugin: task new %d\n", t->pid); | ||
576 | |||
577 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
578 | |||
579 | /* setup job params */ | ||
580 | release_at(t, litmus_clock()); | ||
581 | |||
582 | if (running) { | ||
583 | entry = active_gbl_plugin->cpus[task_cpu(t)]; | ||
584 | BUG_ON(entry->scheduled); | ||
585 | |||
586 | #ifdef CONFIG_RELEASE_MASTER | ||
587 | if (entry->cpu != active_gbl_domain.release_master) { | ||
588 | #endif | ||
589 | entry->scheduled = t; | ||
590 | tsk_rt(t)->scheduled_on = task_cpu(t); | ||
591 | #ifdef CONFIG_RELEASE_MASTER | ||
592 | } else { | ||
593 | /* do not schedule on release master */ | ||
594 | gbl_preempt(entry); /* force resched */ | ||
595 | tsk_rt(t)->scheduled_on = NO_CPU; | ||
596 | } | ||
597 | #endif | ||
598 | } else { | ||
599 | t->rt_param.scheduled_on = NO_CPU; | ||
600 | } | ||
601 | t->rt_param.linked_on = NO_CPU; | ||
602 | |||
603 | active_gbl_plugin->job_arrival(t); | ||
604 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
605 | } | ||
606 | |||
607 | void gblv_task_wake_up(struct task_struct *task) | ||
608 | { | ||
609 | unsigned long flags; | ||
610 | lt_t now; | ||
611 | |||
612 | TRACE_TASK(task, "wake_up at %llu\n", litmus_clock()); | ||
613 | |||
614 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
615 | /* We need to take suspensions because of semaphores into | ||
616 | * account! If a job resumes after being suspended due to acquiring | ||
617 | * a semaphore, it should never be treated as a new job release. | ||
618 | */ | ||
619 | if (get_rt_flags(task) == RT_F_EXIT_SEM) { | ||
620 | set_rt_flags(task, RT_F_RUNNING); | ||
621 | } else { | ||
622 | now = litmus_clock(); | ||
623 | if (is_tardy(task, now)) { | ||
624 | /* new sporadic release */ | ||
625 | release_at(task, now); | ||
626 | sched_trace_task_release(task); | ||
627 | } | ||
628 | else { | ||
629 | if (task->rt.time_slice) { | ||
630 | /* came back in time before deadline | ||
631 | */ | ||
632 | set_rt_flags(task, RT_F_RUNNING); | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | active_gbl_plugin->job_arrival(task); | ||
637 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
638 | } | ||
639 | |||
640 | void gblv_task_block(struct task_struct *t) | ||
641 | { | ||
642 | unsigned long flags; | ||
643 | |||
644 | TRACE_TASK(t, "block at %llu\n", litmus_clock()); | ||
645 | |||
646 | /* unlink if necessary */ | ||
647 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
648 | gbl_unlink(t); | ||
649 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
650 | |||
651 | BUG_ON(!is_realtime(t)); | ||
652 | } | ||
653 | |||
654 | |||
655 | void gblv_task_exit(struct task_struct * t) | ||
656 | { | ||
657 | unsigned long flags; | ||
658 | |||
659 | /* unlink if necessary */ | ||
660 | raw_spin_lock_irqsave(&active_gbl_domain_lock, flags); | ||
661 | gbl_unlink(t); | ||
662 | if (tsk_rt(t)->scheduled_on != NO_CPU) { | ||
663 | active_gbl_plugin->cpus[tsk_rt(t)->scheduled_on]->scheduled = NULL; | ||
664 | tsk_rt(t)->scheduled_on = NO_CPU; | ||
665 | } | ||
666 | raw_spin_unlock_irqrestore(&active_gbl_domain_lock, flags); | ||
667 | |||
668 | BUG_ON(!is_realtime(t)); | ||
669 | TRACE_TASK(t, "RIP\n"); | ||
670 | } | ||
671 | |||
672 | long gblv_admit_task(struct task_struct* tsk) | ||
673 | { | ||
674 | return 0; | ||
675 | } | ||