Skip to content

Commit

Permalink
Merge branch 'timers-fixes-for-linus' of git://git.kernel.org/pub/scm…
Browse files Browse the repository at this point in the history
…/linux/kernel/git/tip/linux-2.6-tip

* 'timers-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  hrtimer: prevent negative expiry value after clock_was_set()
  hrtimers: allow the hot-unplugging of all cpus
  hrtimers: increase clock min delta threshold while interrupt hanging
  • Loading branch information
Linus Torvalds committed Jan 31, 2009
2 parents f649043 + b0a9b51 commit 5b2d3e6
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 8 deletions.
1 change: 1 addition & 0 deletions include/linux/clockchips.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum clock_event_nofitiers {
CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
CLOCK_EVT_NOTIFY_SUSPEND,
CLOCK_EVT_NOTIFY_RESUME,
CLOCK_EVT_NOTIFY_CPU_DYING,
CLOCK_EVT_NOTIFY_CPU_DEAD,
};

Expand Down
41 changes: 40 additions & 1 deletion kernel/hrtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,13 @@ static void hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base)
continue;
timer = rb_entry(base->first, struct hrtimer, node);
expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
/*
* clock_was_set() has changed base->offset so the
* result might be negative. Fix it up to prevent a
* false positive in clockevents_program_event()
*/
if (expires.tv64 < 0)
expires.tv64 = 0;
if (expires.tv64 < cpu_base->expires_next.tv64)
cpu_base->expires_next = expires;
}
Expand Down Expand Up @@ -1158,6 +1165,29 @@ static void __run_hrtimer(struct hrtimer *timer)

#ifdef CONFIG_HIGH_RES_TIMERS

static int force_clock_reprogram;

/*
* After 5 iteration's attempts, we consider that hrtimer_interrupt()
* is hanging, which could happen with something that slows the interrupt
* such as the tracing. Then we force the clock reprogramming for each future
* hrtimer interrupts to avoid infinite loops and use the min_delta_ns
* threshold that we will overwrite.
* The next tick event will be scheduled to 3 times we currently spend on
* hrtimer_interrupt(). This gives a good compromise, the cpus will spend
* 1/4 of their time to process the hrtimer interrupts. This is enough to
* let it running without serious starvation.
*/

static inline void
hrtimer_interrupt_hanging(struct clock_event_device *dev,
ktime_t try_time)
{
force_clock_reprogram = 1;
dev->min_delta_ns = (unsigned long)try_time.tv64 * 3;
printk(KERN_WARNING "hrtimer: interrupt too slow, "
"forcing clock min delta to %lu ns\n", dev->min_delta_ns);
}
/*
* High resolution timer interrupt
* Called with interrupts disabled
Expand All @@ -1167,13 +1197,18 @@ void hrtimer_interrupt(struct clock_event_device *dev)
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
struct hrtimer_clock_base *base;
ktime_t expires_next, now;
int nr_retries = 0;
int i;

BUG_ON(!cpu_base->hres_active);
cpu_base->nr_events++;
dev->next_event.tv64 = KTIME_MAX;

retry:
/* 5 retries is enough to notice a hang */
if (!(++nr_retries % 5))
hrtimer_interrupt_hanging(dev, ktime_sub(ktime_get(), now));

now = ktime_get();

expires_next.tv64 = KTIME_MAX;
Expand Down Expand Up @@ -1226,7 +1261,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)

/* Reprogramming necessary ? */
if (expires_next.tv64 != KTIME_MAX) {
if (tick_program_event(expires_next, 0))
if (tick_program_event(expires_next, force_clock_reprogram))
goto retry;
}
}
Expand Down Expand Up @@ -1580,6 +1615,10 @@ static int __cpuinit hrtimer_cpu_notify(struct notifier_block *self,
break;

#ifdef CONFIG_HOTPLUG_CPU
case CPU_DYING:
case CPU_DYING_FROZEN:
clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DYING, &scpu);
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
{
Expand Down
26 changes: 19 additions & 7 deletions kernel/time/tick-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,21 @@ static int tick_check_new_device(struct clock_event_device *newdev)
return ret;
}

/*
* Transfer the do_timer job away from a dying cpu.
*
* Called with interrupts disabled.
*/
static void tick_handover_do_timer(int *cpup)
{
if (*cpup == tick_do_timer_cpu) {
int cpu = cpumask_first(cpu_online_mask);

tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
TICK_DO_TIMER_NONE;
}
}

/*
* Shutdown an event device on a given cpu:
*
Expand All @@ -297,13 +312,6 @@ static void tick_shutdown(unsigned int *cpup)
clockevents_exchange_device(dev, NULL);
td->evtdev = NULL;
}
/* Transfer the do_timer job away from this cpu */
if (*cpup == tick_do_timer_cpu) {
int cpu = cpumask_first(cpu_online_mask);

tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
TICK_DO_TIMER_NONE;
}
spin_unlock_irqrestore(&tick_device_lock, flags);
}

Expand Down Expand Up @@ -357,6 +365,10 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason,
tick_broadcast_oneshot_control(reason);
break;

case CLOCK_EVT_NOTIFY_CPU_DYING:
tick_handover_do_timer(dev);
break;

case CLOCK_EVT_NOTIFY_CPU_DEAD:
tick_shutdown_broadcast_oneshot(dev);
tick_shutdown_broadcast(dev);
Expand Down

0 comments on commit 5b2d3e6

Please sign in to comment.