diff options
| author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2014-09-08 12:19:43 -0400 |
|---|---|---|
| committer | Namhoon Kim <namhoonk@cs.unc.edu> | 2014-11-03 21:58:51 -0500 |
| commit | fcd5e594a26012c7457197fd111e58820bc34741 (patch) | |
| tree | 2e335a659461a3a0e0a363db188a36cf7472e4e1 | |
| parent | e55ab67a060ded6f2c47e5ede00a39176bacbab3 (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); |
