diff options
author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2014-09-08 12:19:43 -0400 |
---|---|---|
committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2014-09-08 12:19:43 -0400 |
commit | 642d997fc6d6500e14f3c2fa72e481800d687cd1 (patch) | |
tree | 1b7a320dfb3640ca0f0e45f40c2fa5680cd0053b | |
parent | 85533ddcfa62fdb273d2f33e5e729ad1461f771a (diff) |
P-RES: ensure scheduler timer fires on _local_ CPU only
Accidentally setting up the timer on the wrong CPU when a thread
resumes is problematic can lead (potentially) to deadlock and to
missed scheduling events.
-rw-r--r-- | litmus/sched_pres.c | 55 |
1 files changed, 50 insertions, 5 deletions
diff --git a/litmus/sched_pres.c b/litmus/sched_pres.c index 6779ffd16dae..60afde6f8d1b 100644 --- a/litmus/sched_pres.c +++ b/litmus/sched_pres.c | |||
@@ -65,14 +65,22 @@ static void task_arrives(struct task_struct *tsk) | |||
65 | 65 | ||
66 | static void pres_update_timer(struct pres_cpu_state *state) | 66 | static void pres_update_timer(struct pres_cpu_state *state) |
67 | { | 67 | { |
68 | int local; | ||
68 | lt_t update, now; | 69 | lt_t update, now; |
69 | 70 | ||
70 | update = state->sup_env.next_scheduler_update; | 71 | update = state->sup_env.next_scheduler_update; |
71 | now = state->sup_env.env.current_time; | 72 | now = state->sup_env.env.current_time; |
73 | |||
74 | /* Be sure we're actually running on the right core, | ||
75 | * as pres_update_timer() is also called from pres_task_resume(), | ||
76 | * which might be called on any CPU when a thread resumes. | ||
77 | */ | ||
78 | local = local_cpu_state() == state; | ||
79 | |||
72 | if (update <= now) { | 80 | if (update <= now) { |
73 | litmus_reschedule(state->cpu); | 81 | litmus_reschedule(state->cpu); |
74 | } else if (update != SUP_NO_SCHEDULER_UPDATE) { | 82 | } else if (likely(local && update != SUP_NO_SCHEDULER_UPDATE)) { |
75 | /* reprogram only if not already set correctly */ | 83 | /* Reprogram only if not already set correctly. */ |
76 | if (!hrtimer_active(&state->timer) || | 84 | if (!hrtimer_active(&state->timer) || |
77 | ktime_to_ns(hrtimer_get_expires(&state->timer)) != update) { | 85 | ktime_to_ns(hrtimer_get_expires(&state->timer)) != update) { |
78 | TRACE("canceling timer...\n"); | 86 | TRACE("canceling timer...\n"); |
@@ -81,6 +89,25 @@ static void pres_update_timer(struct pres_cpu_state *state) | |||
81 | hrtimer_start(&state->timer, ns_to_ktime(update), | 89 | hrtimer_start(&state->timer, ns_to_ktime(update), |
82 | HRTIMER_MODE_ABS_PINNED); | 90 | HRTIMER_MODE_ABS_PINNED); |
83 | } | 91 | } |
92 | } else if (unlikely(!local && update != SUP_NO_SCHEDULER_UPDATE)) { | ||
93 | /* Poke remote core only if timer needs to be set earlier than | ||
94 | * it is currently set. | ||
95 | */ | ||
96 | TRACE("pres_update_timer for remote CPU %d (update=%llu, " | ||
97 | "active:%d, set:%llu)\n", | ||
98 | state->cpu, | ||
99 | update, | ||
100 | hrtimer_active(&state->timer), | ||
101 | ktime_to_ns(hrtimer_get_expires(&state->timer))); | ||
102 | if (!hrtimer_active(&state->timer) || | ||
103 | ktime_to_ns(hrtimer_get_expires(&state->timer)) > update) { | ||
104 | TRACE("poking CPU %d so that it can update its " | ||
105 | "scheduling timer (active:%d, set:%llu)\n", | ||
106 | state->cpu, | ||
107 | hrtimer_active(&state->timer), | ||
108 | ktime_to_ns(hrtimer_get_expires(&state->timer))); | ||
109 | litmus_reschedule(state->cpu); | ||
110 | } | ||
84 | } | 111 | } |
85 | } | 112 | } |
86 | 113 | ||
@@ -88,16 +115,27 @@ static enum hrtimer_restart on_scheduling_timer(struct hrtimer *timer) | |||
88 | { | 115 | { |
89 | unsigned long flags; | 116 | unsigned long flags; |
90 | enum hrtimer_restart restart = HRTIMER_NORESTART; | 117 | enum hrtimer_restart restart = HRTIMER_NORESTART; |
91 | struct pres_cpu_state *state = local_cpu_state(); | 118 | struct pres_cpu_state *state; |
92 | lt_t update, now; | 119 | lt_t update, now; |
93 | 120 | ||
121 | state = container_of(timer, struct pres_cpu_state, timer); | ||
122 | |||
123 | /* The scheduling timer should only fire on the local CPU, because | ||
124 | * otherwise deadlocks via timer_cancel() are possible. | ||
125 | * Note: this does not interfere with dedicated interrupt handling, as | ||
126 | * even under dedicated interrupt handling scheduling timers for | ||
127 | * budget enforcement must occur locally on each CPU. | ||
128 | */ | ||
129 | BUG_ON(state->cpu != raw_smp_processor_id()); | ||
130 | |||
94 | raw_spin_lock_irqsave(&state->lock, flags); | 131 | raw_spin_lock_irqsave(&state->lock, flags); |
95 | sup_update_time(&state->sup_env, litmus_clock()); | 132 | sup_update_time(&state->sup_env, litmus_clock()); |
96 | 133 | ||
97 | update = state->sup_env.next_scheduler_update; | 134 | update = state->sup_env.next_scheduler_update; |
98 | now = state->sup_env.env.current_time; | 135 | now = state->sup_env.env.current_time; |
99 | 136 | ||
100 | TRACE_CUR("on_scheduling_timer at %llu, upd:%llu\n", now, update); | 137 | TRACE_CUR("on_scheduling_timer at %llu, upd:%llu (for cpu=%d)\n", |
138 | now, update, state->cpu); | ||
101 | 139 | ||
102 | if (update <= now) { | 140 | if (update <= now) { |
103 | litmus_reschedule_local(); | 141 | litmus_reschedule_local(); |
@@ -173,12 +211,15 @@ static void pres_task_resume(struct task_struct *tsk) | |||
173 | struct pres_task_state* tinfo = get_pres_state(tsk); | 211 | struct pres_task_state* tinfo = get_pres_state(tsk); |
174 | struct pres_cpu_state *state = cpu_state_for(tinfo->cpu); | 212 | struct pres_cpu_state *state = cpu_state_for(tinfo->cpu); |
175 | 213 | ||
176 | TRACE_TASK(tsk, "wake_up at %llu\n", litmus_clock()); | 214 | TRACE_TASK(tsk, "thread wakes up at %llu\n", litmus_clock()); |
177 | 215 | ||
178 | raw_spin_lock_irqsave(&state->lock, flags); | 216 | raw_spin_lock_irqsave(&state->lock, flags); |
179 | /* Requeue if self-suspension was already processed. */ | 217 | /* Requeue if self-suspension was already processed. */ |
180 | if (state->scheduled != tsk) | 218 | if (state->scheduled != tsk) |
181 | { | 219 | { |
220 | /* Assumption: litmus_clock() is synchronized across cores, | ||
221 | * since we might not actually be executing on tinfo->cpu | ||
222 | * at the moment. */ | ||
182 | sup_update_time(&state->sup_env, litmus_clock()); | 223 | sup_update_time(&state->sup_env, litmus_clock()); |
183 | task_arrives(tsk); | 224 | task_arrives(tsk); |
184 | pres_update_timer(state); | 225 | pres_update_timer(state); |
@@ -270,6 +311,8 @@ static void pres_task_new(struct task_struct *tsk, int on_runqueue, | |||
270 | } | 311 | } |
271 | 312 | ||
272 | if (on_runqueue || is_running) { | 313 | if (on_runqueue || is_running) { |
314 | /* Assumption: litmus_clock() is synchronized across cores | ||
315 | * [see comment in pres_task_resume()] */ | ||
273 | sup_update_time(&state->sup_env, litmus_clock()); | 316 | sup_update_time(&state->sup_env, litmus_clock()); |
274 | task_arrives(tsk); | 317 | task_arrives(tsk); |
275 | pres_update_timer(state); | 318 | pres_update_timer(state); |
@@ -293,6 +336,8 @@ static void pres_task_exit(struct task_struct *tsk) | |||
293 | 336 | ||
294 | /* remove from queues */ | 337 | /* remove from queues */ |
295 | if (is_running(tsk)) { | 338 | if (is_running(tsk)) { |
339 | /* Assumption: litmus_clock() is synchronized across cores | ||
340 | * [see comment in pres_task_resume()] */ | ||
296 | sup_update_time(&state->sup_env, litmus_clock()); | 341 | sup_update_time(&state->sup_env, litmus_clock()); |
297 | task_departs(tsk, 0); | 342 | task_departs(tsk, 0); |
298 | pres_update_timer(state); | 343 | pres_update_timer(state); |