diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-02-10 19:08:50 -0500 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-02-10 19:08:50 -0500 |
commit | 851baec36d80e7ee279c69677b113428d20562c8 (patch) | |
tree | c54caa78e40fdcb7d446005e3f81fd9fd66e6159 | |
parent | 7a5b2e2906fdc575bf7fc93f954419c5d54bb0a5 (diff) |
Bugfix: PSN-EDF task_block flow-path may race with schedule()
The task block flow-path in PSN-EDF may race with schedule()
(e.g., fast task release, hrtimer, etc.). As psnedf_task_block does not
reset the pedf->scheduled field, the BUG_ON(pedf->scheduled != prev) condition
in psnedf_schedule() fires.
Setting pedf-> schedule to NULL in psnedf_task_block() is not enough as we
may loose a rescheduling point (as we skip the check for new real-time tasks)
We need therefore to trace the block event in the first rescheduling point.
The BUG was first reported by Glenn:
[ 46.986089] kernel BUG at litmus/sched_psn_edf.c:138!
[ 46.986089] invalid opcode: 0000 [#1] 0P
TtaEsEk_MmoPdTe( LISMP
MUS_RT_T[AS K) o k46.986089] last sysfs file: /sys/devices/pci0000:00/0000:00:01.1/ide0/0.0/block/hda/size
[ 46.986089] CPU 1 .
[
4] Wai[ ti ng 4f6.986089] Modules linked in:
6r T[S re lea se4.
.986089] Pid: 1488, comm: longtest Not tainted 2.6.32-litmus2010 #3
[ 46.986089] RIP: 0010:[<ffffffff811f3f40>] [<ffffffff811f3f40>] psnedf_schedule+0x360/0x370
[ 46.986089] RSP: 0018:ffff88007bf7fd18 EFLAGS: 00010087
[ 46.986089] RAX: 000000000000f5f5 RBX: ffffffff814412c0 RCX: ffff88007befef90
[ 46.986089] RDX: ffff88007befc830 RSI: ffff88007befef90 RDI: ffff880001850a40
[ 46.986089] RBP: ffff88007bf7fd58 R08: 0000000000000000 R09: ffff88000180dc50
[ 46.986089] R10: 0000000000000000 R11: 0000000000000000 R12: ffff880001850a40
[ 46.986089] R13: ffff88007befef90 R14: ffff88000186cec0 R15: ffff88007befef90
[ 46.986089] FS: 00007fc0694e4910(0000) GS:ffff880001840000(0000) knlGS:0000000000000000
[ 46.986089] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 46.986089] CR2: 00000000006dc2d8 CR3: 000000007b2ed000 CR4: 00000000000006a0
[ 46.986089] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 46.986089] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 46.986089] Process longtest (pid: 1488, threadinfo ffff88007bf7e000, task ffff88007befef90)
[ 46.986089] Stack:
[ 46.986089] 0000000000000000 0000000000000000 0000000000000000 ffffffff814412c0
[ 46.986089] <0> 0000000000000000 ffff88007befef90 ffff88000186cec0 ffff88000184db08
[ 46.986089] <0> ffff88007bf7fdb8 ffffffff81035f64 0000000000000000 0000000000000000
[ 46.986089] Call Trace:
[ 46.986089] [<ffffffff81035f64>] pick_next_task_litmus+0x44/0x400
[ 46.986089] [<ffffffff814381e9>] schedule+0x239/0x356
[ 46.986089] [<ffffffff810cf1b7>] ? nameidata_to_filp+0x57/0x70
[ 46.986089] [<ffffffff8143a515>] __down_write_nested+0x85/0xd0
[ 46.986089] [<ffffffff8143a56b>] __down_write+0xb/0x10
[ 46.986089] [<ffffffff81439c2e>] down_write+0xe/0x10
[ 46.986089] [<ffffffff810103cd>] sys_mmap+0xdd/0x120
[ 46.986089] [<ffffffff8100b2ab>] system_call_fastpath+0x16/0x1b
[ 46.986089] Code: c0 83 c6 01 48 c7 c7 80 07 55 81 e8 eb 44 00 00 48 8b 83 50 06 00 00 c7 40 04 01 00 00 00 e9 d5 fd ff ff 0f 0b eb fe 0f 1f 40 00 <0
[ 46.986089] RIP [<ffffffff811f3f40>] psnedf_schedule+0x360/0x370
[ 46.986089] RSP <ffff88007bf7fd18>
[ 46.986089] ---[ end trace 6205a69dc6b27ca5 ]---
-rw-r--r-- | litmus/sched_psn_edf.c | 52 |
1 files changed, 41 insertions, 11 deletions
diff --git a/litmus/sched_psn_edf.c b/litmus/sched_psn_edf.c index 3a93124e24f6..4829653b8f76 100644 --- a/litmus/sched_psn_edf.c +++ b/litmus/sched_psn_edf.c | |||
@@ -26,6 +26,13 @@ typedef struct { | |||
26 | int cpu; | 26 | int cpu; |
27 | struct task_struct* scheduled; /* only RT tasks */ | 27 | struct task_struct* scheduled; /* only RT tasks */ |
28 | /* | 28 | /* |
29 | * A block event (mainly during synchronous release) may race | ||
30 | * with other releases or with triggering of the schedule() function. | ||
31 | * We need to trace a previous blocking event in the first execution of | ||
32 | * schedule() | ||
33 | */ | ||
34 | int block; | ||
35 | /* | ||
29 | * scheduling lock slock | 36 | * scheduling lock slock |
30 | * protects the domain and serializes scheduling decisions | 37 | * protects the domain and serializes scheduling decisions |
31 | */ | 38 | */ |
@@ -51,6 +58,7 @@ static void psnedf_domain_init(psnedf_domain_t* pedf, | |||
51 | edf_domain_init(&pedf->domain, check, release); | 58 | edf_domain_init(&pedf->domain, check, release); |
52 | pedf->cpu = cpu; | 59 | pedf->cpu = cpu; |
53 | pedf->scheduled = NULL; | 60 | pedf->scheduled = NULL; |
61 | pedf->block = 0; | ||
54 | } | 62 | } |
55 | 63 | ||
56 | static void requeue(struct task_struct* t, rt_domain_t *edf) | 64 | static void requeue(struct task_struct* t, rt_domain_t *edf) |
@@ -140,22 +148,36 @@ static struct task_struct* psnedf_schedule(struct task_struct * prev) | |||
140 | 148 | ||
141 | /* (0) Determine state */ | 149 | /* (0) Determine state */ |
142 | exists = pedf->scheduled != NULL; | 150 | exists = pedf->scheduled != NULL; |
143 | blocks = exists && !is_running(pedf->scheduled); | 151 | /* a task may block if 1) the task exists but is no more running |
152 | * 2) the task blocked and this schedule is racing with it while | ||
153 | * it is going to sleep. In this case pedf->schedule != prev (and we | ||
154 | * already reset pedf->schedule to NULL), but nonetheless we want to | ||
155 | * pick a real-time task as next (if such a task exists in the ready | ||
156 | * queue). | ||
157 | */ | ||
158 | blocks = pedf->block || (exists && !is_running(pedf->scheduled)); | ||
144 | out_of_time = exists && budget_exhausted(pedf->scheduled); | 159 | out_of_time = exists && budget_exhausted(pedf->scheduled); |
145 | np = exists && is_np(pedf->scheduled); | 160 | np = exists && is_np(pedf->scheduled); |
146 | sleep = exists && get_rt_flags(pedf->scheduled) == RT_F_SLEEP; | 161 | sleep = exists && get_rt_flags(pedf->scheduled) == RT_F_SLEEP; |
147 | preempt = edf_preemption_needed(edf, prev); | 162 | preempt = edf_preemption_needed(edf, prev); |
148 | 163 | ||
149 | /* If we need to preempt do so. | ||
150 | * The following checks set resched to 1 in case of special | ||
151 | * circumstances. | ||
152 | */ | ||
153 | resched = preempt; | ||
154 | 164 | ||
155 | /* If a task blocks we have no choice but to reschedule. | 165 | /* If a task blocks we have no choice but to reschedule. |
156 | */ | 166 | */ |
157 | if (blocks) | 167 | if (blocks) { |
168 | |||
158 | resched = 1; | 169 | resched = 1; |
170 | /* reset the block flag, we are about to reschedule */ | ||
171 | pedf->block = 0; | ||
172 | } else { | ||
173 | |||
174 | /* If we need to preempt do so. | ||
175 | * The following checks set resched to 1 in case of special | ||
176 | * circumstances. | ||
177 | */ | ||
178 | resched = preempt; | ||
179 | } | ||
180 | |||
159 | 181 | ||
160 | /* Request a sys_exit_np() call if we would like to preempt but cannot. | 182 | /* Request a sys_exit_np() call if we would like to preempt but cannot. |
161 | * Multiple calls to request_exit_np() don't hurt. | 183 | * Multiple calls to request_exit_np() don't hurt. |
@@ -266,6 +288,7 @@ static void psnedf_task_wake_up(struct task_struct *task) | |||
266 | static void psnedf_task_block(struct task_struct *t) | 288 | static void psnedf_task_block(struct task_struct *t) |
267 | { | 289 | { |
268 | psnedf_domain_t *pedf = task_pedf(t); | 290 | psnedf_domain_t *pedf = task_pedf(t); |
291 | unsigned long flags; | ||
269 | 292 | ||
270 | /* only running tasks can block, thus t is in no queue */ | 293 | /* only running tasks can block, thus t is in no queue */ |
271 | TRACE_TASK(t, "block at %llu, state=%d\n", litmus_clock(), t->state); | 294 | TRACE_TASK(t, "block at %llu, state=%d\n", litmus_clock(), t->state); |
@@ -273,12 +296,18 @@ static void psnedf_task_block(struct task_struct *t) | |||
273 | BUG_ON(!is_realtime(t)); | 296 | BUG_ON(!is_realtime(t)); |
274 | BUG_ON(is_queued(t)); | 297 | BUG_ON(is_queued(t)); |
275 | 298 | ||
276 | /* if this task is dead, then we need to reset pedf->schedule now | 299 | /* if this task is no more runnable, then we need to reset pedf->schedule |
277 | * as we might get rescheduled before task_exit executes | 300 | * and set the block flag as another schedule() may race wotj is while |
301 | * we are going to sleep | ||
278 | */ | 302 | */ |
279 | if(unlikely(t->state == TASK_DEAD)) { | 303 | if(likely(t->state != TASK_RUNNING)) { |
280 | TRACE_TASK(t, "Dead, setting scheduled = NULL\n"); | 304 | |
305 | TRACE_TASK(t, "psnedf_task_block, setting block flag\n"); | ||
306 | |||
307 | spin_lock_irqsave(&pedf->slock, flags); | ||
308 | pedf->block = 1; | ||
281 | pedf->scheduled = NULL; | 309 | pedf->scheduled = NULL; |
310 | spin_unlock_irqrestore(&pedf->slock, flags); | ||
282 | } | 311 | } |
283 | } | 312 | } |
284 | 313 | ||
@@ -347,6 +376,7 @@ static long psnedf_pi_block(struct pi_semaphore *sem, | |||
347 | 376 | ||
348 | spin_unlock(&pedf->slock); | 377 | spin_unlock(&pedf->slock); |
349 | } | 378 | } |
379 | TRACE_TASK(sem->holder, "psnedf_pi_block\n"); | ||
350 | 380 | ||
351 | return 0; | 381 | return 0; |
352 | } | 382 | } |