From 851baec36d80e7ee279c69677b113428d20562c8 Mon Sep 17 00:00:00 2001 From: Andrea Bastoni Date: Wed, 10 Feb 2010 19:08:50 -0500 Subject: 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:[] [] 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] [] pick_next_task_litmus+0x44/0x400 [ 46.986089] [] schedule+0x239/0x356 [ 46.986089] [] ? nameidata_to_filp+0x57/0x70 [ 46.986089] [] __down_write_nested+0x85/0xd0 [ 46.986089] [] __down_write+0xb/0x10 [ 46.986089] [] down_write+0xe/0x10 [ 46.986089] [] sys_mmap+0xdd/0x120 [ 46.986089] [] 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 [] psnedf_schedule+0x360/0x370 [ 46.986089] RSP [ 46.986089] ---[ end trace 6205a69dc6b27ca5 ]--- --- litmus/sched_psn_edf.c | 52 +++++++++++++++++++++++++++++++++++++++----------- 1 file 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 @@ -25,6 +25,13 @@ typedef struct { rt_domain_t domain; int cpu; struct task_struct* scheduled; /* only RT tasks */ +/* + * A block event (mainly during synchronous release) may race + * with other releases or with triggering of the schedule() function. + * We need to trace a previous blocking event in the first execution of + * schedule() + */ + int block; /* * scheduling lock slock * protects the domain and serializes scheduling decisions @@ -51,6 +58,7 @@ static void psnedf_domain_init(psnedf_domain_t* pedf, edf_domain_init(&pedf->domain, check, release); pedf->cpu = cpu; pedf->scheduled = NULL; + pedf->block = 0; } 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) /* (0) Determine state */ exists = pedf->scheduled != NULL; - blocks = exists && !is_running(pedf->scheduled); + /* a task may block if 1) the task exists but is no more running + * 2) the task blocked and this schedule is racing with it while + * it is going to sleep. In this case pedf->schedule != prev (and we + * already reset pedf->schedule to NULL), but nonetheless we want to + * pick a real-time task as next (if such a task exists in the ready + * queue). + */ + blocks = pedf->block || (exists && !is_running(pedf->scheduled)); out_of_time = exists && budget_exhausted(pedf->scheduled); np = exists && is_np(pedf->scheduled); sleep = exists && get_rt_flags(pedf->scheduled) == RT_F_SLEEP; preempt = edf_preemption_needed(edf, prev); - /* If we need to preempt do so. - * The following checks set resched to 1 in case of special - * circumstances. - */ - resched = preempt; /* If a task blocks we have no choice but to reschedule. */ - if (blocks) + if (blocks) { + resched = 1; + /* reset the block flag, we are about to reschedule */ + pedf->block = 0; + } else { + + /* If we need to preempt do so. + * The following checks set resched to 1 in case of special + * circumstances. + */ + resched = preempt; + } + /* Request a sys_exit_np() call if we would like to preempt but cannot. * Multiple calls to request_exit_np() don't hurt. @@ -266,6 +288,7 @@ static void psnedf_task_wake_up(struct task_struct *task) static void psnedf_task_block(struct task_struct *t) { psnedf_domain_t *pedf = task_pedf(t); + unsigned long flags; /* only running tasks can block, thus t is in no queue */ 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) BUG_ON(!is_realtime(t)); BUG_ON(is_queued(t)); - /* if this task is dead, then we need to reset pedf->schedule now - * as we might get rescheduled before task_exit executes + /* if this task is no more runnable, then we need to reset pedf->schedule + * and set the block flag as another schedule() may race wotj is while + * we are going to sleep */ - if(unlikely(t->state == TASK_DEAD)) { - TRACE_TASK(t, "Dead, setting scheduled = NULL\n"); + if(likely(t->state != TASK_RUNNING)) { + + TRACE_TASK(t, "psnedf_task_block, setting block flag\n"); + + spin_lock_irqsave(&pedf->slock, flags); + pedf->block = 1; pedf->scheduled = NULL; + spin_unlock_irqrestore(&pedf->slock, flags); } } @@ -347,6 +376,7 @@ static long psnedf_pi_block(struct pi_semaphore *sem, spin_unlock(&pedf->slock); } + TRACE_TASK(sem->holder, "psnedf_pi_block\n"); return 0; } -- cgit v1.2.2