mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-27 00:51:35 +00:00
Merge branch 'timers-fixes-for-linus' of git://git.kernel.org/pub/scm/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: itimer: Fix the itimer trace print format hrtimer: move timer stats helper functions to hrtimer.c hrtimer: Tune hrtimer_interrupt hang logic
This commit is contained in:
commit
3070f27d6e
4 changed files with 95 additions and 97 deletions
123
kernel/hrtimer.c
123
kernel/hrtimer.c
|
@ -557,7 +557,7 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
|||
static int hrtimer_reprogram(struct hrtimer *timer,
|
||||
struct hrtimer_clock_base *base)
|
||||
{
|
||||
ktime_t *expires_next = &__get_cpu_var(hrtimer_bases).expires_next;
|
||||
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
|
||||
ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
int res;
|
||||
|
||||
|
@ -582,7 +582,16 @@ static int hrtimer_reprogram(struct hrtimer *timer,
|
|||
if (expires.tv64 < 0)
|
||||
return -ETIME;
|
||||
|
||||
if (expires.tv64 >= expires_next->tv64)
|
||||
if (expires.tv64 >= cpu_base->expires_next.tv64)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If a hang was detected in the last timer interrupt then we
|
||||
* do not schedule a timer which is earlier than the expiry
|
||||
* which we enforced in the hang detection. We want the system
|
||||
* to make progress.
|
||||
*/
|
||||
if (cpu_base->hang_detected)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -590,7 +599,7 @@ static int hrtimer_reprogram(struct hrtimer *timer,
|
|||
*/
|
||||
res = tick_program_event(expires, 0);
|
||||
if (!IS_ERR_VALUE(res))
|
||||
*expires_next = expires;
|
||||
cpu_base->expires_next = expires;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -747,17 +756,33 @@ static inline void hrtimer_init_timer_hres(struct hrtimer *timer) { }
|
|||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
|
||||
#ifdef CONFIG_TIMER_STATS
|
||||
void __timer_stats_hrtimer_set_start_info(struct hrtimer *timer, void *addr)
|
||||
static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer)
|
||||
{
|
||||
#ifdef CONFIG_TIMER_STATS
|
||||
if (timer->start_site)
|
||||
return;
|
||||
|
||||
timer->start_site = addr;
|
||||
timer->start_site = __builtin_return_address(0);
|
||||
memcpy(timer->start_comm, current->comm, TASK_COMM_LEN);
|
||||
timer->start_pid = current->pid;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void timer_stats_hrtimer_clear_start_info(struct hrtimer *timer)
|
||||
{
|
||||
#ifdef CONFIG_TIMER_STATS
|
||||
timer->start_site = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void timer_stats_account_hrtimer(struct hrtimer *timer)
|
||||
{
|
||||
#ifdef CONFIG_TIMER_STATS
|
||||
if (likely(!timer_stats_active))
|
||||
return;
|
||||
timer_stats_update_stats(timer, timer->start_pid, timer->start_site,
|
||||
timer->function, timer->start_comm, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Counterpart to lock_hrtimer_base above:
|
||||
|
@ -1217,30 +1242,6 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
|
|||
|
||||
#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 %llu ns\n",
|
||||
(unsigned long long) dev->min_delta_ns);
|
||||
}
|
||||
/*
|
||||
* High resolution timer interrupt
|
||||
* Called with interrupts disabled
|
||||
|
@ -1249,21 +1250,15 @@ 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;
|
||||
ktime_t expires_next, now, entry_time, delta;
|
||||
int i, retries = 0;
|
||||
|
||||
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();
|
||||
|
||||
entry_time = now = ktime_get();
|
||||
retry:
|
||||
expires_next.tv64 = KTIME_MAX;
|
||||
|
||||
spin_lock(&cpu_base->lock);
|
||||
|
@ -1325,10 +1320,48 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|||
spin_unlock(&cpu_base->lock);
|
||||
|
||||
/* Reprogramming necessary ? */
|
||||
if (expires_next.tv64 != KTIME_MAX) {
|
||||
if (tick_program_event(expires_next, force_clock_reprogram))
|
||||
goto retry;
|
||||
if (expires_next.tv64 == KTIME_MAX ||
|
||||
!tick_program_event(expires_next, 0)) {
|
||||
cpu_base->hang_detected = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The next timer was already expired due to:
|
||||
* - tracing
|
||||
* - long lasting callbacks
|
||||
* - being scheduled away when running in a VM
|
||||
*
|
||||
* We need to prevent that we loop forever in the hrtimer
|
||||
* interrupt routine. We give it 3 attempts to avoid
|
||||
* overreacting on some spurious event.
|
||||
*/
|
||||
now = ktime_get();
|
||||
cpu_base->nr_retries++;
|
||||
if (++retries < 3)
|
||||
goto retry;
|
||||
/*
|
||||
* Give the system a chance to do something else than looping
|
||||
* here. We stored the entry time, so we know exactly how long
|
||||
* we spent here. We schedule the next event this amount of
|
||||
* time away.
|
||||
*/
|
||||
cpu_base->nr_hangs++;
|
||||
cpu_base->hang_detected = 1;
|
||||
delta = ktime_sub(now, entry_time);
|
||||
if (delta.tv64 > cpu_base->max_hang_time.tv64)
|
||||
cpu_base->max_hang_time = delta;
|
||||
/*
|
||||
* Limit it to a sensible value as we enforce a longer
|
||||
* delay. Give the CPU at least 100ms to catch up.
|
||||
*/
|
||||
if (delta.tv64 > 100 * NSEC_PER_MSEC)
|
||||
expires_next = ktime_add_ns(now, 100 * NSEC_PER_MSEC);
|
||||
else
|
||||
expires_next = ktime_add(now, delta);
|
||||
tick_program_event(expires_next, 1);
|
||||
printk_once(KERN_WARNING "hrtimer: interrupt took %llu ns\n",
|
||||
ktime_to_ns(delta));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -150,6 +150,9 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now)
|
|||
P_ns(expires_next);
|
||||
P(hres_active);
|
||||
P(nr_events);
|
||||
P(nr_retries);
|
||||
P(nr_hangs);
|
||||
P_ns(max_hang_time);
|
||||
#endif
|
||||
#undef P
|
||||
#undef P_ns
|
||||
|
@ -254,7 +257,7 @@ static int timer_list_show(struct seq_file *m, void *v)
|
|||
u64 now = ktime_to_ns(ktime_get());
|
||||
int cpu;
|
||||
|
||||
SEQ_printf(m, "Timer List Version: v0.4\n");
|
||||
SEQ_printf(m, "Timer List Version: v0.5\n");
|
||||
SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
|
||||
SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue