]> git.hungrycats.org Git - linux/commitdiff
[PATCH] kernel preemption bits (2/2)
authorRobert Love <rml@tech9.net>
Fri, 14 Jun 2002 03:51:48 +0000 (20:51 -0700)
committerDave Kleikamp <shaggy@kleikamp.austin.ibm.com>
Fri, 14 Jun 2002 03:51:48 +0000 (20:51 -0700)
In both preempt_schedule in sched.c and resume_kernel in entry.S, it is
possible to return with need_resched set and thus a pending preemption
but not service that preemption for some time.

Consider:

- return from schedule() to preempt_schedule
- interrupt occurs, sets need_resched
- we cannot preempt since preempt_count = PREEMPT_ACTIVE
- back in preempt_schedule, set preempt_count = 0

Now we again can preempt, but we will not.  Instead we return and
continue executing.  On the next interrupt, we will redo the whole
fiasco which is a waste since we could of reentered schedule while we
were there.  Worse, if we acquire a lock before the next interrupt we
can potentially delay the pending reschedule a very long time.  This is
not acceptable.

The solution is to check for and loop on need_resched on resume_kernel
and preempt_schedule like schedule itself does.

arch/i386/kernel/entry.S
kernel/sched.c

index 6a42ae06ccbe8de465ef3d8245e714e51ed56319..09d3034543783cf1cee1d2c80a04342e98c44aba 100644 (file)
@@ -211,6 +211,7 @@ ENTRY(resume_userspace)
 ENTRY(resume_kernel)
        cmpl $0,TI_PRE_COUNT(%ebx)      # non-zero preempt_count ?
        jnz restore_all
+need_resched:
        movl TI_FLAGS(%ebx), %ecx       # need_resched set ?
        testb $_TIF_NEED_RESCHED, %cl
        jz restore_all
@@ -220,7 +221,8 @@ ENTRY(resume_kernel)
        sti
        call schedule
        movl $0,TI_PRE_COUNT(%ebx) 
-       jmp restore_all
+       cli
+       jmp need_resched
 #endif
 
        # system call handler stub
index 1254ad6df71ffc8686d5a206b6439fc7402550dd..7dc3cd10f3b81e93254727ac1c87e36cfc5302f7 100644 (file)
@@ -893,9 +893,15 @@ asmlinkage void preempt_schedule(void)
        if (unlikely(ti->preempt_count))
                return;
 
+need_resched:
        ti->preempt_count = PREEMPT_ACTIVE;
        schedule();
        ti->preempt_count = 0;
+
+       /* we can miss a preemption opportunity between schedule and now */
+       barrier();
+       if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
+               goto need_resched;
 }
 #endif /* CONFIG_PREEMPT */