diff options
Diffstat (limited to 'kernel/timer.c')
| -rw-r--r-- | kernel/timer.c | 110 | 
1 files changed, 61 insertions, 49 deletions
| diff --git a/kernel/timer.c b/kernel/timer.c index 6ec7e7e0db4..a61c09374eb 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -77,6 +77,7 @@ struct tvec_base {  	struct timer_list *running_timer;  	unsigned long timer_jiffies;  	unsigned long next_timer; +	unsigned long active_timers;  	struct tvec_root tv1;  	struct tvec tv2;  	struct tvec tv3; @@ -330,7 +331,8 @@ void set_timer_slack(struct timer_list *timer, int slack_hz)  }  EXPORT_SYMBOL_GPL(set_timer_slack); -static void internal_add_timer(struct tvec_base *base, struct timer_list *timer) +static void +__internal_add_timer(struct tvec_base *base, struct timer_list *timer)  {  	unsigned long expires = timer->expires;  	unsigned long idx = expires - base->timer_jiffies; @@ -372,6 +374,19 @@ static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)  	list_add_tail(&timer->entry, vec);  } +static void internal_add_timer(struct tvec_base *base, struct timer_list *timer) +{ +	__internal_add_timer(base, timer); +	/* +	 * Update base->active_timers and base->next_timer +	 */ +	if (!tbase_get_deferrable(timer->base)) { +		if (time_before(timer->expires, base->next_timer)) +			base->next_timer = timer->expires; +		base->active_timers++; +	} +} +  #ifdef CONFIG_TIMER_STATS  void __timer_stats_timer_set_start_info(struct timer_list *timer, void *addr)  { @@ -654,8 +669,7 @@ void init_timer_deferrable_key(struct timer_list *timer,  }  EXPORT_SYMBOL(init_timer_deferrable_key); -static inline void detach_timer(struct timer_list *timer, -				int clear_pending) +static inline void detach_timer(struct timer_list *timer, bool clear_pending)  {  	struct list_head *entry = &timer->entry; @@ -667,6 +681,29 @@ static inline void detach_timer(struct timer_list *timer,  	entry->prev = LIST_POISON2;  } +static inline void +detach_expired_timer(struct timer_list *timer, struct tvec_base *base) +{ +	detach_timer(timer, true); +	if (!tbase_get_deferrable(timer->base)) +		timer->base->active_timers--; +} + +static int detach_if_pending(struct timer_list *timer, struct tvec_base *base, +			     bool clear_pending) +{ +	if (!timer_pending(timer)) +		return 0; + +	detach_timer(timer, clear_pending); +	if (!tbase_get_deferrable(timer->base)) { +		timer->base->active_timers--; +		if (timer->expires == base->next_timer) +			base->next_timer = base->timer_jiffies; +	} +	return 1; +} +  /*   * We are using hashed locking: holding per_cpu(tvec_bases).lock   * means that all timers which are tied to this base via timer->base are @@ -712,16 +749,9 @@ __mod_timer(struct timer_list *timer, unsigned long expires,  	base = lock_timer_base(timer, &flags); -	if (timer_pending(timer)) { -		detach_timer(timer, 0); -		if (timer->expires == base->next_timer && -		    !tbase_get_deferrable(timer->base)) -			base->next_timer = base->timer_jiffies; -		ret = 1; -	} else { -		if (pending_only) -			goto out_unlock; -	} +	ret = detach_if_pending(timer, base, false); +	if (!ret && pending_only) +		goto out_unlock;  	debug_activate(timer, expires); @@ -752,9 +782,6 @@ __mod_timer(struct timer_list *timer, unsigned long expires,  	}  	timer->expires = expires; -	if (time_before(timer->expires, base->next_timer) && -	    !tbase_get_deferrable(timer->base)) -		base->next_timer = timer->expires;  	internal_add_timer(base, timer);  out_unlock: @@ -920,9 +947,6 @@ void add_timer_on(struct timer_list *timer, int cpu)  	spin_lock_irqsave(&base->lock, flags);  	timer_set_base(timer, base);  	debug_activate(timer, timer->expires); -	if (time_before(timer->expires, base->next_timer) && -	    !tbase_get_deferrable(timer->base)) -		base->next_timer = timer->expires;  	internal_add_timer(base, timer);  	/*  	 * Check whether the other CPU is idle and needs to be @@ -959,13 +983,7 @@ int del_timer(struct timer_list *timer)  	timer_stats_timer_clear_start_info(timer);  	if (timer_pending(timer)) {  		base = lock_timer_base(timer, &flags); -		if (timer_pending(timer)) { -			detach_timer(timer, 1); -			if (timer->expires == base->next_timer && -			    !tbase_get_deferrable(timer->base)) -				base->next_timer = base->timer_jiffies; -			ret = 1; -		} +		ret = detach_if_pending(timer, base, true);  		spin_unlock_irqrestore(&base->lock, flags);  	} @@ -990,19 +1008,10 @@ int try_to_del_timer_sync(struct timer_list *timer)  	base = lock_timer_base(timer, &flags); -	if (base->running_timer == timer) -		goto out; - -	timer_stats_timer_clear_start_info(timer); -	ret = 0; -	if (timer_pending(timer)) { -		detach_timer(timer, 1); -		if (timer->expires == base->next_timer && -		    !tbase_get_deferrable(timer->base)) -			base->next_timer = base->timer_jiffies; -		ret = 1; +	if (base->running_timer != timer) { +		timer_stats_timer_clear_start_info(timer); +		ret = detach_if_pending(timer, base, true);  	} -out:  	spin_unlock_irqrestore(&base->lock, flags);  	return ret; @@ -1089,7 +1098,8 @@ static int cascade(struct tvec_base *base, struct tvec *tv, int index)  	 */  	list_for_each_entry_safe(timer, tmp, &tv_list, entry) {  		BUG_ON(tbase_get_base(timer->base) != base); -		internal_add_timer(base, timer); +		/* No accounting, while moving them */ +		__internal_add_timer(base, timer);  	}  	return index; @@ -1178,7 +1188,7 @@ static inline void __run_timers(struct tvec_base *base)  			timer_stats_account_timer(timer);  			base->running_timer = timer; -			detach_timer(timer, 1); +			detach_expired_timer(timer, base);  			spin_unlock_irq(&base->lock);  			call_timer_fn(timer, fn, data); @@ -1316,18 +1326,21 @@ static unsigned long cmp_next_hrtimer_event(unsigned long now,  unsigned long get_next_timer_interrupt(unsigned long now)  {  	struct tvec_base *base = __this_cpu_read(tvec_bases); -	unsigned long expires; +	unsigned long expires = now + NEXT_TIMER_MAX_DELTA;  	/*  	 * Pretend that there is no timer pending if the cpu is offline.  	 * Possible pending timers will be migrated later to an active cpu.  	 */  	if (cpu_is_offline(smp_processor_id())) -		return now + NEXT_TIMER_MAX_DELTA; +		return expires; +  	spin_lock(&base->lock); -	if (time_before_eq(base->next_timer, base->timer_jiffies)) -		base->next_timer = __next_timer_interrupt(base); -	expires = base->next_timer; +	if (base->active_timers) { +		if (time_before_eq(base->next_timer, base->timer_jiffies)) +			base->next_timer = __next_timer_interrupt(base); +		expires = base->next_timer; +	}  	spin_unlock(&base->lock);  	if (time_before_eq(expires, now)) @@ -1704,6 +1717,7 @@ static int __cpuinit init_timers_cpu(int cpu)  	base->timer_jiffies = jiffies;  	base->next_timer = base->timer_jiffies; +	base->active_timers = 0;  	return 0;  } @@ -1714,11 +1728,9 @@ static void migrate_timer_list(struct tvec_base *new_base, struct list_head *hea  	while (!list_empty(head)) {  		timer = list_first_entry(head, struct timer_list, entry); -		detach_timer(timer, 0); +		/* We ignore the accounting on the dying cpu */ +		detach_timer(timer, false);  		timer_set_base(timer, new_base); -		if (time_before(timer->expires, new_base->next_timer) && -		    !tbase_get_deferrable(timer->base)) -			new_base->next_timer = timer->expires;  		internal_add_timer(new_base, timer);  	}  } | 
