--- linux-2.5.69.virgin/kernel/sched.c Tue May 6 11:36:53 2003 +++ linux-2.5.69X/kernel/sched.c Wed May 7 11:29:22 2003 @@ -74,6 +74,8 @@ #define MAX_SLEEP_AVG (10*HZ) #define STARVATION_LIMIT (10*HZ) #define NODE_THRESHOLD 125 +#define MIN_REQUEUE_TIME MAX_TIMESLICE +#define TIMESLICE_GRANULARITY (HZ/20 ?: 1) /* * If a task is 'interactive' then we reinsert it in the active @@ -1166,9 +1168,9 @@ * increasing number of running tasks: */ #define EXPIRED_STARVING(rq) \ - (STARVATION_LIMIT && ((rq)->expired_timestamp && \ - (jiffies - (rq)->expired_timestamp >= \ - STARVATION_LIMIT * ((rq)->nr_running) + 1))) + (STARVATION_LIMIT && (rq)->expired_timestamp && \ + time_after_eq(jiffies, (rq)->expired_timestamp + \ + (STARVATION_LIMIT * ((rq)->nr_running) + 1))) /* * This function gets called by the timer code, with HZ frequency. @@ -1248,12 +1250,91 @@ enqueue_task(p, rq->expired); } else enqueue_task(p, rq->active); + goto out; + } + if (time_after_eq(jiffies, p->last_run + TIMESLICE_GRANULARITY)) { + dequeue_task(p, rq->active); + set_tsk_need_resched(p); + p->prio = effective_prio(p); + enqueue_task(p, rq->active); } out: spin_unlock(&rq->lock); rebalance_tick(rq, 0); } +/* + * Search an array for the oldest runnable task. if searching the + * expired array, requeue it to the head of it's corresponding active + * queue and update rq->expired_timestamp. + * + * Return the location of the task, or MAX_PRIO if nothing found. + */ +static inline int select_oldest(runqueue_t *rq, prio_array_t *array) +{ + int idx = 0, old_idx = 0; + struct list_head *head, *curr; + task_t *next, *old = NULL, *skip = NULL; + int exp = array == rq->expired; + +next_queue: + if (!idx) + idx = sched_find_first_bit(array->bitmap); + else + idx = find_next_bit(array->bitmap, MAX_PRIO, idx); + if (idx >= MAX_PRIO) + goto out; + + head = array->queue + idx; + curr = head->next; +next_task: + next = list_entry(curr, task_t, run_list); + curr = curr->next; + + /* Find a runnable candidate. */ + if (next == rq->curr || next->state > TASK_INTERRUPTIBLE || + test_ti_thread_flag(next->thread_info, TIF_NEED_RESCHED) || + (next->state == TASK_INTERRUPTIBLE && !signal_pending(next)) || + time_before(jiffies, next->last_run + MIN_REQUEUE_TIME)) { + if (exp && (!skip || time_after(next->last_run, skip->last_run))) + skip = next; + if (curr != head) + goto next_task; + idx++; + goto next_queue; + } + /* Good, we found a candidate. Evaluate it.. */ + if (!old || time_after(next->last_run, old->last_run)) { + old = next; + old_idx = idx; + } + /* and record a skip if present. */ + if (exp && curr != head) { + next = list_entry(curr, task_t, run_list); + if (!skip || time_after_eq(next->last_run, skip->last_run)) + skip = next; + } + idx++; + goto next_queue; +out: + if (old) { + old->state = TASK_RUNNING; + if (exp) { + dequeue_task(old, array); + if (!array->nr_active) + rq->expired_timestamp = 0; + else + rq->expired_timestamp = skip->last_run; + list_add(&old->run_list, rq->active->queue + old->prio); + __set_bit(old->prio, rq->active->bitmap); + rq->active->nr_active++; + old->array = rq->active; + } + return old_idx; + } + return idx; +} + void scheduling_functions_start_here(void) { } /* @@ -1265,7 +1346,7 @@ runqueue_t *rq; prio_array_t *array; struct list_head *queue; - int idx; + int idx = 0, retry = 0; /* * Test if we are atomic. Since do_exit() needs to call into @@ -1290,8 +1371,8 @@ spin_lock_irq(&rq->lock); /* - * if entering off of a kernel preemption go straight - * to picking the next task. + * if entering off of a kernel preemption or reschedule, + * go straight to picking the next task. */ if (unlikely(preempt_count() & PREEMPT_ACTIVE)) goto pick_next_task; @@ -1330,7 +1411,23 @@ rq->expired_timestamp = 0; } - idx = sched_find_first_bit(array->bitmap); + if (!idx) + idx = sched_find_first_bit(array->bitmap); + else if (unlikely(retry)) { + prio_array_t *expired = rq->expired; + /* If we didn't switch, try the oldest active task. */ + idx = select_oldest(rq, array); + /* If we still won't switch, try the oldest expired task. */ + if (idx >= MAX_PRIO || expired->nr_active > array->nr_active) { + int tmp = select_oldest(rq, expired); + if (tmp < MAX_PRIO) + idx = tmp; + } + /* If we simply _can't_ switch, punt back to highest. */ + if (idx >= MAX_PRIO) + idx = sched_find_first_bit(array->bitmap); + } + queue = array->queue + idx; next = list_entry(queue->next, task_t, run_list); @@ -1342,19 +1439,27 @@ if (likely(prev != next)) { rq->nr_switches++; rq->curr = next; + next->last_run = jiffies; prepare_arch_switch(rq, next); prev = context_switch(rq, prev, next); barrier(); finish_task_switch(prev); - } else + } else { + if (!retry++) { + idx = MAX_PRIO; + goto pick_next_task; + } spin_unlock_irq(&rq->lock); + } reacquire_kernel_lock(current); preempt_enable_no_resched(); - if (test_thread_flag(TIF_NEED_RESCHED)) + if (test_thread_flag(TIF_NEED_RESCHED)) { + idx = 0; goto need_resched; + } } #ifdef CONFIG_PREEMPT