mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer fixes from Thomas Gleixner:
 "A pile of fixes for long standing issues with the timer wheel and the
  NOHZ code:
   - Prevent timer base confusion accross the nohz switch, which can
     cause unlocked access and data corruption
   - Reinitialize the stale base clock on cpu hotplug to prevent subtle
     side effects including rollovers on 32bit
   - Prevent an interrupt storm when the timer softirq is already
     pending caused by tick_nohz_stop_sched_tick()
   - Move the timer start tracepoint to a place where it actually makes
     sense
   - Add documentation to timerqueue functions as they caused confusion
     several times now"
* 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  timerqueue: Document return values of timerqueue_add/del()
  timers: Invoke timer_start_debug() where it makes sense
  nohz: Prevent a timer interrupt storm in tick_nohz_stop_sched_tick()
  timers: Reinitialize per cpu bases on hotplug
  timers: Use deferrable base independent of base::nohz_active
			
			
This commit is contained in:
		
						commit
						cea92e843e
					
				
					 6 changed files with 52 additions and 20 deletions
				
			
		|  | @ -86,7 +86,7 @@ enum cpuhp_state { | |||
| 	CPUHP_MM_ZSWP_POOL_PREPARE, | ||||
| 	CPUHP_KVM_PPC_BOOK3S_PREPARE, | ||||
| 	CPUHP_ZCOMP_PREPARE, | ||||
| 	CPUHP_TIMERS_DEAD, | ||||
| 	CPUHP_TIMERS_PREPARE, | ||||
| 	CPUHP_MIPS_SOC_PREPARE, | ||||
| 	CPUHP_BP_PREPARE_DYN, | ||||
| 	CPUHP_BP_PREPARE_DYN_END		= CPUHP_BP_PREPARE_DYN + 20, | ||||
|  |  | |||
|  | @ -207,9 +207,11 @@ unsigned long round_jiffies_up(unsigned long j); | |||
| unsigned long round_jiffies_up_relative(unsigned long j); | ||||
| 
 | ||||
| #ifdef CONFIG_HOTPLUG_CPU | ||||
| int timers_prepare_cpu(unsigned int cpu); | ||||
| int timers_dead_cpu(unsigned int cpu); | ||||
| #else | ||||
| #define timers_dead_cpu NULL | ||||
| #define timers_prepare_cpu	NULL | ||||
| #define timers_dead_cpu		NULL | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -1277,9 +1277,9 @@ static struct cpuhp_step cpuhp_bp_states[] = { | |||
| 	 * before blk_mq_queue_reinit_notify() from notify_dead(), | ||||
| 	 * otherwise a RCU stall occurs. | ||||
| 	 */ | ||||
| 	[CPUHP_TIMERS_DEAD] = { | ||||
| 	[CPUHP_TIMERS_PREPARE] = { | ||||
| 		.name			= "timers:dead", | ||||
| 		.startup.single		= NULL, | ||||
| 		.startup.single		= timers_prepare_cpu, | ||||
| 		.teardown.single	= timers_dead_cpu, | ||||
| 	}, | ||||
| 	/* Kicks the plugged cpu into life */ | ||||
|  |  | |||
|  | @ -650,6 +650,11 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now) | |||
| 	ts->next_tick = 0; | ||||
| } | ||||
| 
 | ||||
| static inline bool local_timer_softirq_pending(void) | ||||
| { | ||||
| 	return local_softirq_pending() & TIMER_SOFTIRQ; | ||||
| } | ||||
| 
 | ||||
| static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts, | ||||
| 					 ktime_t now, int cpu) | ||||
| { | ||||
|  | @ -666,8 +671,18 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts, | |||
| 	} while (read_seqretry(&jiffies_lock, seq)); | ||||
| 	ts->last_jiffies = basejiff; | ||||
| 
 | ||||
| 	if (rcu_needs_cpu(basemono, &next_rcu) || | ||||
| 	    arch_needs_cpu() || irq_work_needs_cpu()) { | ||||
| 	/*
 | ||||
| 	 * Keep the periodic tick, when RCU, architecture or irq_work | ||||
| 	 * requests it. | ||||
| 	 * Aside of that check whether the local timer softirq is | ||||
| 	 * pending. If so its a bad idea to call get_next_timer_interrupt() | ||||
| 	 * because there is an already expired timer, so it will request | ||||
| 	 * immeditate expiry, which rearms the hardware timer with a | ||||
| 	 * minimal delta which brings us back to this place | ||||
| 	 * immediately. Lather, rinse and repeat... | ||||
| 	 */ | ||||
| 	if (rcu_needs_cpu(basemono, &next_rcu) || arch_needs_cpu() || | ||||
| 	    irq_work_needs_cpu() || local_timer_softirq_pending()) { | ||||
| 		next_tick = basemono + TICK_NSEC; | ||||
| 	} else { | ||||
| 		/*
 | ||||
|  |  | |||
|  | @ -823,11 +823,10 @@ static inline struct timer_base *get_timer_cpu_base(u32 tflags, u32 cpu) | |||
| 	struct timer_base *base = per_cpu_ptr(&timer_bases[BASE_STD], cpu); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If the timer is deferrable and nohz is active then we need to use | ||||
| 	 * the deferrable base. | ||||
| 	 * If the timer is deferrable and NO_HZ_COMMON is set then we need | ||||
| 	 * to use the deferrable base. | ||||
| 	 */ | ||||
| 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active && | ||||
| 	    (tflags & TIMER_DEFERRABLE)) | ||||
| 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && (tflags & TIMER_DEFERRABLE)) | ||||
| 		base = per_cpu_ptr(&timer_bases[BASE_DEF], cpu); | ||||
| 	return base; | ||||
| } | ||||
|  | @ -837,11 +836,10 @@ static inline struct timer_base *get_timer_this_cpu_base(u32 tflags) | |||
| 	struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If the timer is deferrable and nohz is active then we need to use | ||||
| 	 * the deferrable base. | ||||
| 	 * If the timer is deferrable and NO_HZ_COMMON is set then we need | ||||
| 	 * to use the deferrable base. | ||||
| 	 */ | ||||
| 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active && | ||||
| 	    (tflags & TIMER_DEFERRABLE)) | ||||
| 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && (tflags & TIMER_DEFERRABLE)) | ||||
| 		base = this_cpu_ptr(&timer_bases[BASE_DEF]); | ||||
| 	return base; | ||||
| } | ||||
|  | @ -1009,8 +1007,6 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option | |||
| 	if (!ret && (options & MOD_TIMER_PENDING_ONLY)) | ||||
| 		goto out_unlock; | ||||
| 
 | ||||
| 	debug_activate(timer, expires); | ||||
| 
 | ||||
| 	new_base = get_target_base(base, timer->flags); | ||||
| 
 | ||||
| 	if (base != new_base) { | ||||
|  | @ -1034,6 +1030,8 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	debug_activate(timer, expires); | ||||
| 
 | ||||
| 	timer->expires = expires; | ||||
| 	/*
 | ||||
| 	 * If 'idx' was calculated above and the base time did not advance | ||||
|  | @ -1684,7 +1682,7 @@ static __latent_entropy void run_timer_softirq(struct softirq_action *h) | |||
| 	base->must_forward_clk = false; | ||||
| 
 | ||||
| 	__run_timers(base); | ||||
| 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active) | ||||
| 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) | ||||
| 		__run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); | ||||
| } | ||||
| 
 | ||||
|  | @ -1855,6 +1853,21 @@ static void migrate_timer_list(struct timer_base *new_base, struct hlist_head *h | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| int timers_prepare_cpu(unsigned int cpu) | ||||
| { | ||||
| 	struct timer_base *base; | ||||
| 	int b; | ||||
| 
 | ||||
| 	for (b = 0; b < NR_BASES; b++) { | ||||
| 		base = per_cpu_ptr(&timer_bases[b], cpu); | ||||
| 		base->clk = jiffies; | ||||
| 		base->next_expiry = base->clk + NEXT_TIMER_MAX_DELTA; | ||||
| 		base->is_idle = false; | ||||
| 		base->must_forward_clk = true; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int timers_dead_cpu(unsigned int cpu) | ||||
| { | ||||
| 	struct timer_base *old_base; | ||||
|  |  | |||
|  | @ -33,8 +33,9 @@ | |||
|  * @head: head of timerqueue | ||||
|  * @node: timer node to be added | ||||
|  * | ||||
|  * Adds the timer node to the timerqueue, sorted by the | ||||
|  * node's expires value. | ||||
|  * Adds the timer node to the timerqueue, sorted by the node's expires | ||||
|  * value. Returns true if the newly added timer is the first expiring timer in | ||||
|  * the queue. | ||||
|  */ | ||||
| bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node) | ||||
| { | ||||
|  | @ -70,7 +71,8 @@ EXPORT_SYMBOL_GPL(timerqueue_add); | |||
|  * @head: head of timerqueue | ||||
|  * @node: timer node to be removed | ||||
|  * | ||||
|  * Removes the timer node from the timerqueue. | ||||
|  * Removes the timer node from the timerqueue. Returns true if the queue is | ||||
|  * not empty after the remove. | ||||
|  */ | ||||
| bool timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node) | ||||
| { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds