aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2007-06-21 16:45:15 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-06-21 18:57:04 -0400
commit58229a18994215bbfe0bcd1c99d2e039f30b076b (patch)
treec910efcef828d1bc5dd3e43a9575a8164504779c
parentb9bae3402572dc50a1e084c5b1ae5117918ef0f0 (diff)
posix-timers: Prevent softirq starvation by small intervals and SIG_IGN
posix-timers which deliver an ignored signal are currently rearmed in the timer softirq: This is necessary because the timer needs to be delivered again when SIG_IGN is removed. This is not a problem, when the interval is reasonable. With high resolution timers enabled one might arm a posix timer with a very small interval and ignore the signal. This might lead to a softirq starvation when the interval is so small that the timer is requeued onto the softirq pending list right away. This problem was pointed out by Jan Kiszka. Thanks Jan ! The correct solution would be to stop the timer, when the signal is ignored and rearm it when SIG_IGN is removed. Unfortunately this requires modification in sigaction and involves non trivial sighand locking. It's too late in the release cycle for such a change. For now we just keep the timer running and enforce that the timer only fires every jiffie. This does not break anything as we keep the overrun counter correct. It adds a little inaccuracy to the timer_gettime() interface, but... The more complex change is necessary anyway to fix another short coming of the current implementation, which I discovered while looking at this problem: A pending signal is discarded when SIG_IGN is set. In case that a posixtimer signal is pending then it is discarded as well, but when SIG_IGN is removed later nothing rearms the timer. This is not new, it's that way since posix timers have been merged. So nothing to worry about right now. I have a working solution to fix all of this, but the impact is too large for both stable and 2.6.22. I'm going to send it out for review in the next days. This should go into 2.6.21.stable as well. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Ingo Molnar <mingo@elte.hu> Cc: Jan Kiszka <jan.kiszka@web.de> Cc: Ulrich Drepper <drepper@redhat.com> Cc: Stable Team <stable@kernel.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/posix-timers.c35
1 files changed, 33 insertions, 2 deletions
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 588c99da0307..329ce0172074 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -353,9 +353,40 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
353 * it should be restarted. 353 * it should be restarted.
354 */ 354 */
355 if (timr->it.real.interval.tv64 != 0) { 355 if (timr->it.real.interval.tv64 != 0) {
356 ktime_t now = hrtimer_cb_get_time(timer);
357
358 /*
359 * FIXME: What we really want, is to stop this
360 * timer completely and restart it in case the
361 * SIG_IGN is removed. This is a non trivial
362 * change which involves sighand locking
363 * (sigh !), which we don't want to do late in
364 * the release cycle.
365 *
366 * For now we just let timers with an interval
367 * less than a jiffie expire every jiffie to
368 * avoid softirq starvation in case of SIG_IGN
369 * and a very small interval, which would put
370 * the timer right back on the softirq pending
371 * list. By moving now ahead of time we trick
372 * hrtimer_forward() to expire the timer
373 * later, while we still maintain the overrun
374 * accuracy, but have some inconsistency in
375 * the timer_gettime() case. This is at least
376 * better than a starved softirq. A more
377 * complex fix which solves also another related
378 * inconsistency is already in the pipeline.
379 */
380#ifdef CONFIG_HIGH_RES_TIMERS
381 {
382 ktime_t kj = ktime_set(0, NSEC_PER_SEC / HZ);
383
384 if (timr->it.real.interval.tv64 < kj.tv64)
385 now = ktime_add(now, kj);
386 }
387#endif
356 timr->it_overrun += 388 timr->it_overrun +=
357 hrtimer_forward(timer, 389 hrtimer_forward(timer, now,
358 hrtimer_cb_get_time(timer),
359 timr->it.real.interval); 390 timr->it.real.interval);
360 ret = HRTIMER_RESTART; 391 ret = HRTIMER_RESTART;
361 ++timr->it_requeue_pending; 392 ++timr->it_requeue_pending;