diff options
author | Mike Galbraith <efault@gmx.de> | 2006-04-11 01:52:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-11 09:18:30 -0400 |
commit | 5ce74abe788a26698876e66b9c9ce7e7acc25413 (patch) | |
tree | 2e0c2cfc1aad32a9e2903f5e01256f1ed43982e4 | |
parent | 019ff2d57b0bbe77d1eca19f5b634e5e7ff2a0b8 (diff) |
[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 <efault@gmx.de>
Acked-by: Ingo Molnar <mingo@elte.hu>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Acked-by: Con Kolivas <kernel@kolivas.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | kernel/sched.c | 62 |
1 files changed, 44 insertions, 18 deletions
diff --git a/kernel/sched.c b/kernel/sched.c index dd153d6f8a04..2e8a146dd066 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -665,13 +665,55 @@ static int effective_prio(task_t *p) | |||
665 | } | 665 | } |
666 | 666 | ||
667 | /* | 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 | |||
709 | /* | ||
668 | * __activate_task - move a task to the runqueue. | 710 | * __activate_task - move a task to the runqueue. |
669 | */ | 711 | */ |
670 | static void __activate_task(task_t *p, runqueue_t *rq) | 712 | static void __activate_task(task_t *p, runqueue_t *rq) |
671 | { | 713 | { |
672 | prio_array_t *target = rq->active; | 714 | prio_array_t *target = rq->active; |
673 | 715 | ||
674 | if (batch_task(p)) | 716 | if (unlikely(batch_task(p) || expired_starving(rq))) |
675 | target = rq->expired; | 717 | target = rq->expired; |
676 | enqueue_task(p, target); | 718 | enqueue_task(p, target); |
677 | rq->nr_running++; | 719 | rq->nr_running++; |
@@ -2490,22 +2532,6 @@ unsigned long long current_sched_time(const task_t *tsk) | |||
2490 | } | 2532 | } |
2491 | 2533 | ||
2492 | /* | 2534 | /* |
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 | |||
2508 | /* | ||
2509 | * Account user cpu time to a process. | 2535 | * Account user cpu time to a process. |
2510 | * @p: the process that the cpu time gets accounted to | 2536 | * @p: the process that the cpu time gets accounted to |
2511 | * @hardirq_offset: the offset to subtract from hardirq_count() | 2537 | * @hardirq_offset: the offset to subtract from hardirq_count() |
@@ -2640,7 +2666,7 @@ void scheduler_tick(void) | |||
2640 | 2666 | ||
2641 | if (!rq->expired_timestamp) | 2667 | if (!rq->expired_timestamp) |
2642 | rq->expired_timestamp = jiffies; | 2668 | rq->expired_timestamp = jiffies; |
2643 | if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) { | 2669 | if (!TASK_INTERACTIVE(p) || expired_starving(rq)) { |
2644 | enqueue_task(p, rq->expired); | 2670 | enqueue_task(p, rq->expired); |
2645 | if (p->static_prio < rq->best_expired_prio) | 2671 | if (p->static_prio < rq->best_expired_prio) |
2646 | rq->best_expired_prio = p->static_prio; | 2672 | rq->best_expired_prio = p->static_prio; |