]> git.neil.brown.name Git - history.git/commitdiff
Fix scheduler deadlock on some platforms.
authorDavid S. Miller <davem@nuts.ninka.net>
Sun, 10 Mar 2002 23:04:58 +0000 (15:04 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Sun, 10 Mar 2002 23:04:58 +0000 (15:04 -0800)
Some platforms need to grab mm->page_table_lock during switch_mm().
On the other hand code like swap_out() in mm/vmscan.c needs to hold
mm->page_table_lock during wakeups which needs to grab the runqueue
lock.  This creates a conflict and the resolution chosen here is to
not hold the runqueue lock during context_switch().

The implementation is specifically a "frozen" state implemented as a
spinlock, which is held around the context_switch() call.  This allows
the runqueue lock to be dropped during this time yet prevent another cpu
from running the "not switched away from yet" task.

kernel/sched.c

index 45e3b61033605356bc09cb5984c9a1f78170dec5..6492f3c6050eb5f6f0ff0963aff671fc461d2f81 100644 (file)
@@ -140,6 +140,7 @@ struct prio_array {
  */
 struct runqueue {
        spinlock_t lock;
+       spinlock_t frozen;
        unsigned long nr_running, nr_switches, expired_timestamp;
        task_t *curr, *idle;
        prio_array_t *active, *expired, arrays[2];
@@ -400,7 +401,7 @@ void sched_exit(task_t * p)
 #if CONFIG_SMP || CONFIG_PREEMPT
 asmlinkage void schedule_tail(void)
 {
-       spin_unlock_irq(&this_rq()->lock);
+       spin_unlock_irq(&this_rq()->frozen);
 }
 #endif
 
@@ -808,16 +809,22 @@ switch_tasks:
        if (likely(prev != next)) {
                rq->nr_switches++;
                rq->curr = next;
+               spin_lock(&rq->frozen);
+               spin_unlock(&rq->lock);
+
                context_switch(prev, next);
+
                /*
                 * The runqueue pointer might be from another CPU
                 * if the new task was last running on a different
                 * CPU - thus re-load it.
                 */
-               barrier();
+               mb();
                rq = this_rq();
+               spin_unlock_irq(&rq->frozen);
+       } else {
+               spin_unlock_irq(&rq->lock);
        }
-       spin_unlock_irq(&rq->lock);
 
        reacquire_kernel_lock(current);
        preempt_enable_no_resched();
@@ -1463,6 +1470,7 @@ void __init sched_init(void)
                rq->active = rq->arrays;
                rq->expired = rq->arrays + 1;
                spin_lock_init(&rq->lock);
+               spin_lock_init(&rq->frozen);
                INIT_LIST_HEAD(&rq->migration_queue);
 
                for (j = 0; j < 2; j++) {