diff options
author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2008-05-02 19:00:27 -0400 |
---|---|---|
committer | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2008-05-02 19:00:27 -0400 |
commit | 2f030cd48bbdfc6f4155c38684d0e8b98195f4f5 (patch) | |
tree | 2d67fe40b43f2a05940b8c37375e83bfd42ef602 | |
parent | c55e411400c13bcfc774d541fc1c9f0f62b644c4 (diff) |
LITMUS: rework job migration code
The old version had a significant window for races with
interrupt handlers executing on other CPUs.
-rw-r--r-- | kernel/sched.c | 3 | ||||
-rw-r--r-- | litmus/sched_litmus.c | 77 |
2 files changed, 55 insertions, 25 deletions
diff --git a/kernel/sched.c b/kernel/sched.c index 00097dea89..170237e3a4 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -3649,9 +3649,10 @@ need_resched_nonpreemptible: | |||
3649 | /* do litmus scheduling outside of rq lock, so that we | 3649 | /* do litmus scheduling outside of rq lock, so that we |
3650 | * can do proper migrations for global schedulers | 3650 | * can do proper migrations for global schedulers |
3651 | */ | 3651 | */ |
3652 | litmus_schedule(rq, prev); | 3652 | |
3653 | spin_lock(&rq->lock); | 3653 | spin_lock(&rq->lock); |
3654 | clear_tsk_need_resched(prev); | 3654 | clear_tsk_need_resched(prev); |
3655 | litmus_schedule(rq, prev); | ||
3655 | 3656 | ||
3656 | if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { | 3657 | if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { |
3657 | if (unlikely((prev->state & TASK_INTERRUPTIBLE) && | 3658 | if (unlikely((prev->state & TASK_INTERRUPTIBLE) && |
diff --git a/litmus/sched_litmus.c b/litmus/sched_litmus.c index 16cdf2db59..feb0159033 100644 --- a/litmus/sched_litmus.c +++ b/litmus/sched_litmus.c | |||
@@ -24,46 +24,75 @@ static void litmus_tick(struct rq *rq, struct task_struct *p) | |||
24 | static void litmus_schedule(struct rq *rq, struct task_struct *prev) | 24 | static void litmus_schedule(struct rq *rq, struct task_struct *prev) |
25 | { | 25 | { |
26 | struct rq* other_rq; | 26 | struct rq* other_rq; |
27 | int success = 0; | 27 | long prev_state; |
28 | /* WARNING: rq is _not_ locked! */ | 28 | /* WARNING: rq is _not_ locked! */ |
29 | if (is_realtime(prev)) | 29 | if (is_realtime(prev)) |
30 | update_time_litmus(rq, prev); | 30 | update_time_litmus(rq, prev); |
31 | 31 | ||
32 | while (!success) { | 32 | /* let the plugin schedule */ |
33 | /* let the plugin schedule */ | 33 | rq->litmus_next = litmus->schedule(prev); |
34 | rq->litmus_next = litmus->schedule(prev); | 34 | |
35 | 35 | /* check if a global plugin pulled a task from a different RQ */ | |
36 | /* check if a global plugin pulled a task from a different RQ */ | 36 | if (rq->litmus_next && task_rq(rq->litmus_next) != rq) { |
37 | if (rq->litmus_next && task_rq(rq->litmus_next) != rq) { | 37 | /* we need to migrate the task */ |
38 | /* we need to migrate the task */ | 38 | other_rq = task_rq(rq->litmus_next); |
39 | other_rq = task_rq(rq->litmus_next); | 39 | TRACE_TASK(rq->litmus_next, "migrate from %d\n", other_rq->cpu); |
40 | double_rq_lock(rq, other_rq); | 40 | |
41 | /* now that we have the lock we need to make sure a | 41 | /* while we drop the lock, the prev task could change its |
42 | * couple of things still hold: | 42 | * state |
43 | * - it is still a real-time task | 43 | */ |
44 | * - it is still runnable (could have been stopped) | 44 | prev_state = prev->state; |
45 | */ | 45 | spin_unlock(&rq->lock); |
46 | if (is_realtime(rq->litmus_next) && | 46 | double_rq_lock(rq, other_rq); |
47 | is_running(rq->litmus_next)) { | 47 | if (prev->state != prev_state) { |
48 | set_task_cpu(rq->litmus_next, smp_processor_id()); | 48 | TRACE_TASK(prev, |
49 | success = 1; | 49 | "state changed while we dropped" |
50 | } /* else something raced, retry */ | 50 | " the lock: now=%d, old=%d", |
51 | double_rq_unlock(rq, other_rq); | 51 | prev->state, prev_state); |
52 | } else | 52 | if (prev_state && !prev->state) { |
53 | success = 1; | 53 | /* prev task became unblocked |
54 | } | 54 | * we need to simulate normal sequence of events |
55 | * to scheduler plugins. | ||
56 | */ | ||
57 | litmus->task_block(prev); | ||
58 | litmus->task_wake_up(prev); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | set_task_cpu(rq->litmus_next, smp_processor_id()); | ||
63 | |||
64 | /* now that we have the lock we need to make sure a | ||
65 | * couple of things still hold: | ||
66 | * - it is still a real-time task | ||
67 | * - it is still runnable (could have been stopped) | ||
68 | */ | ||
69 | if (!is_realtime(rq->litmus_next) || | ||
70 | !is_running(rq->litmus_next)) { | ||
71 | /* BAD BAD BAD */ | ||
72 | TRACE_TASK(rq->litmus_next, | ||
73 | "migration invariant FAILED: rt=%d running=%d\n", | ||
74 | is_realtime(rq->litmus_next), | ||
75 | is_running(rq->litmus_next)); | ||
76 | } | ||
77 | /* release the other CPU's runqueue, but keep ours */ | ||
78 | spin_unlock(&other_rq->lock); | ||
79 | } | ||
55 | } | 80 | } |
56 | 81 | ||
57 | static void enqueue_task_litmus(struct rq *rq, struct task_struct *p, int wakeup) | 82 | static void enqueue_task_litmus(struct rq *rq, struct task_struct *p, int wakeup) |
58 | { | 83 | { |
59 | if (wakeup) | 84 | if (wakeup) |
60 | litmus->task_wake_up(p); | 85 | litmus->task_wake_up(p); |
86 | else | ||
87 | TRACE_TASK(p, "ignoring an enqueue, not a wake up.\n"); | ||
61 | } | 88 | } |
62 | 89 | ||
63 | static void dequeue_task_litmus(struct rq *rq, struct task_struct *p, int sleep) | 90 | static void dequeue_task_litmus(struct rq *rq, struct task_struct *p, int sleep) |
64 | { | 91 | { |
65 | if (sleep) | 92 | if (sleep) |
66 | litmus->task_block(p); | 93 | litmus->task_block(p); |
94 | else | ||
95 | TRACE_TASK(p, "ignoring a dequeue, not going to sleep.\n"); | ||
67 | } | 96 | } |
68 | 97 | ||
69 | static void yield_task_litmus(struct rq *rq) | 98 | static void yield_task_litmus(struct rq *rq) |