PROBLEM: The round robin scheduling policy doesn't work

From: lucho (lucho@haemimont.bg)
Date: Tue Aug 21 2001 - 16:22:12 EST


The round robin scheduling policy does not work.

The kernel in question is 2.4.8 (i still haven't downloaded the 2.4.9).

Description:
Suppose there are several processes in RR mode, all with equal
priorities
(and all the remaining processes in the system are either SCHED_OTHER or
realtime but with
lower priorities) then in theory the scheduler should spin these
processes
(in round-robin manner), but in reality this does not hapen. Only one of
the processes
(in fact the first created) in question is given the entire CPU time,
though the others are not sleeping and have the same real-time priority.

The only ways for the process-monopolist to relinquish the CPU is to do
any
blocking syscall (and thus remove itself from the runqueue). The
sched_yield()
syscall does not work because it does not remove the current task from
the runqueue.

The following simple example code should demonstrate this odd behavior
of the scheduler:

int main()
{
    struct sched_param sp;

    sp.sched_priority = 1;
    sched_setscheduler(0, SCHED_RR, &sp);

    if(fork() == 0) {
        while(1) {
            printf("child\n");
            sched_yield();
        }
    }
    else {
        while(1) {
            printf("parent\n");
            sched_yield();
        }
    }

    return 0;
}

I investigated the problem by inserting printk()s at some points in the
schedule() function
in kernel/scheduler.c and managet to find out what's wrong. The reason
for this
wrong behavior seems to be the following piece of code in schedule():

    if (prev->state == TASK_RUNNING)
        goto still_running;

still_running_back:

    ........
    ........

still_running:
    if (!(prev->cpus_allowed & (1UL << this_cpu)))
        goto still_running_back;
    c = goodness(prev, this_cpu, prev->active_mm);
    next = prev;
    goto still_running_back;

which initializes the `c' variable with the current process goodness
(1001 in the example)
and the `next' - with `prev' regardless whether the timeslice of the
prev has expired
(current->counter == 0) or not. So the `list_for_each' loop over the
runqueue never chooses
any different task (because their goodnesses are never bigger than the
so initialized `c').

I didn't get why is that `stil_running' code necessary at all (at least
in uniprocessor system)
since the `list_for_all' loop will anyway go thru the prev task if it's
in running state
because it is certainly in the runqueue, but it rather makes the
scheduler a bit slower
because of the unnecessary two calculations of the prev's goodness.

I removed this code and rebuilt the kernel, and actually got the round
robin working
(i heavily tested it) i.e. then the scheduler really cycles through all
the SCHED_RR processes.

But still the yield() wasn't working (it was just like a NOP).

I found out that the erason for this is that the sys_sched_yield()
function just sets
current->need_resched which causes the schedule() to be called, but
schedule() looks at
the current->counter and when it's nonzero (when current is SCHED_RR
process),
it doesn't switch to other process.

So i added the code:

    if (current->policy == SCHED_RR)
        current->counter = 0;

in the sys_sched_yield() and now it works fine too.

I hope this bug report will be useful.
My name is Luchezar Belev.
Best regards.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Thu Aug 23 2001 - 21:00:45 EST