Skip to content

Commit 5ce74ab

Browse files
Mike GalbraithLinus Torvalds
authored andcommitted
[PATCH] sched: fix interactive task starvation
Fix a starvation problem that occurs when a stream of highly interactive tasks delay an array switch for extended periods despite EXPIRED_STARVING(rq) being true. AFAIKT, the only choice is to enqueue awakening tasks on the expired array in this case. Without this patch, it can be nearly impossible to remotely login to a busy server, and interactive shell commands can starve for minutes. Also, convert the EXPIRED_STARVING macro into an inline function which humans can understand. Signed-off-by: Mike Galbraith <[email protected]> Acked-by: Ingo Molnar <[email protected]> Cc: Nick Piggin <[email protected]> Acked-by: Con Kolivas <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 019ff2d commit 5ce74ab

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

kernel/sched.c

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -664,14 +664,56 @@ static int effective_prio(task_t *p)
664664
return prio;
665665
}
666666

667+
/*
668+
* We place interactive tasks back into the active array, if possible.
669+
*
670+
* To guarantee that this does not starve expired tasks we ignore the
671+
* interactivity of a task if the first expired task had to wait more
672+
* than a 'reasonable' amount of time. This deadline timeout is
673+
* load-dependent, as the frequency of array switched decreases with
674+
* increasing number of running tasks. We also ignore the interactivity
675+
* if a better static_prio task has expired, and switch periodically
676+
* regardless, to ensure that highly interactive tasks do not starve
677+
* the less fortunate for unreasonably long periods.
678+
*/
679+
static inline int expired_starving(runqueue_t *rq)
680+
{
681+
int limit;
682+
683+
/*
684+
* Arrays were recently switched, all is well
685+
*/
686+
if (!rq->expired_timestamp)
687+
return 0;
688+
689+
limit = STARVATION_LIMIT * rq->nr_running;
690+
691+
/*
692+
* It's time to switch arrays
693+
*/
694+
if (jiffies - rq->expired_timestamp >= limit)
695+
return 1;
696+
697+
/*
698+
* There's a better selection in the expired array
699+
*/
700+
if (rq->curr->static_prio > rq->best_expired_prio)
701+
return 1;
702+
703+
/*
704+
* All is well
705+
*/
706+
return 0;
707+
}
708+
667709
/*
668710
* __activate_task - move a task to the runqueue.
669711
*/
670712
static void __activate_task(task_t *p, runqueue_t *rq)
671713
{
672714
prio_array_t *target = rq->active;
673715

674-
if (batch_task(p))
716+
if (unlikely(batch_task(p) || expired_starving(rq)))
675717
target = rq->expired;
676718
enqueue_task(p, target);
677719
rq->nr_running++;
@@ -2489,22 +2531,6 @@ unsigned long long current_sched_time(const task_t *tsk)
24892531
return ns;
24902532
}
24912533

2492-
/*
2493-
* We place interactive tasks back into the active array, if possible.
2494-
*
2495-
* To guarantee that this does not starve expired tasks we ignore the
2496-
* interactivity of a task if the first expired task had to wait more
2497-
* than a 'reasonable' amount of time. This deadline timeout is
2498-
* load-dependent, as the frequency of array switched decreases with
2499-
* increasing number of running tasks. We also ignore the interactivity
2500-
* if a better static_prio task has expired:
2501-
*/
2502-
#define EXPIRED_STARVING(rq) \
2503-
((STARVATION_LIMIT && ((rq)->expired_timestamp && \
2504-
(jiffies - (rq)->expired_timestamp >= \
2505-
STARVATION_LIMIT * ((rq)->nr_running) + 1))) || \
2506-
((rq)->curr->static_prio > (rq)->best_expired_prio))
2507-
25082534
/*
25092535
* Account user cpu time to a process.
25102536
* @p: the process that the cpu time gets accounted to
@@ -2640,7 +2666,7 @@ void scheduler_tick(void)
26402666

26412667
if (!rq->expired_timestamp)
26422668
rq->expired_timestamp = jiffies;
2643-
if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) {
2669+
if (!TASK_INTERACTIVE(p) || expired_starving(rq)) {
26442670
enqueue_task(p, rq->expired);
26452671
if (p->static_prio < rq->best_expired_prio)
26462672
rq->best_expired_prio = p->static_prio;

0 commit comments

Comments
 (0)