diff options
Diffstat (limited to 'kernel/smp.c')
| -rw-r--r-- | kernel/smp.c | 70 | 
1 files changed, 31 insertions, 39 deletions
| diff --git a/kernel/smp.c b/kernel/smp.c index 974f3a3962e8..4649fa4872ff 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -86,13 +86,15 @@ int smpcfd_dead_cpu(unsigned int cpu)  int smpcfd_dying_cpu(unsigned int cpu)  {  	/* -	 * The IPIs for the smp-call-function callbacks queued by other -	 * CPUs might arrive late, either due to hardware latencies or -	 * because this CPU disabled interrupts (inside stop-machine) -	 * before the IPIs were sent. So flush out any pending callbacks -	 * explicitly (without waiting for the IPIs to arrive), to -	 * ensure that the outgoing CPU doesn't go offline with work -	 * still pending. +	 * The IPIs for the smp-call-function callbacks queued by other CPUs +	 * might arrive late, either due to hardware latencies or because this +	 * CPU disabled interrupts (inside stop-machine) before the IPIs were +	 * sent. So flush out any pending callbacks explicitly (without waiting +	 * for the IPIs to arrive), to ensure that the outgoing CPU doesn't go +	 * offline with work still pending. +	 * +	 * This runs with interrupts disabled inside the stopper task invoked by +	 * stop_machine(), ensuring mutually exclusive CPU offlining and IPI flush.  	 */  	__flush_smp_call_function_queue(false);  	irq_work_run(); @@ -418,6 +420,10 @@ void __smp_call_single_queue(int cpu, struct llist_node *node)   */  static int generic_exec_single(int cpu, call_single_data_t *csd)  { +	/* +	 * Preemption already disabled here so stopper cannot run on this CPU, +	 * ensuring mutually exclusive CPU offlining and last IPI flush. +	 */  	if (cpu == smp_processor_id()) {  		smp_call_func_t func = csd->func;  		void *info = csd->info; @@ -638,8 +644,10 @@ int smp_call_function_single(int cpu, smp_call_func_t func, void *info,  	int err;  	/* -	 * prevent preemption and reschedule on another processor, -	 * as well as CPU removal +	 * Prevent preemption and reschedule on another CPU, as well as CPU +	 * removal. This prevents stopper from running on this CPU, thus +	 * providing mutual exclusion of the below cpu_online() check and +	 * IPI sending ensuring IPI are not missed by CPU going offline.  	 */  	this_cpu = get_cpu(); @@ -741,32 +749,19 @@ EXPORT_SYMBOL_GPL(smp_call_function_single_async);   *   * Selection preference:   *	1) current cpu if in @mask - *	2) any cpu of current node if in @mask - *	3) any other online cpu in @mask + *	2) nearest cpu in @mask, based on NUMA topology   */  int smp_call_function_any(const struct cpumask *mask,  			  smp_call_func_t func, void *info, int wait)  {  	unsigned int cpu; -	const struct cpumask *nodemask;  	int ret;  	/* Try for same CPU (cheapest) */  	cpu = get_cpu(); -	if (cpumask_test_cpu(cpu, mask)) -		goto call; - -	/* Try for same node. */ -	nodemask = cpumask_of_node(cpu_to_node(cpu)); -	for (cpu = cpumask_first_and(nodemask, mask); cpu < nr_cpu_ids; -	     cpu = cpumask_next_and(cpu, nodemask, mask)) { -		if (cpu_online(cpu)) -			goto call; -	} +	if (!cpumask_test_cpu(cpu, mask)) +		cpu = sched_numa_find_nth_cpu(mask, 0, cpu_to_node(cpu)); -	/* Any online will do: smp_call_function_single handles nr_cpu_ids. */ -	cpu = cpumask_any_and(mask, cpu_online_mask); -call:  	ret = smp_call_function_single(cpu, func, info, wait);  	put_cpu();  	return ret; @@ -792,7 +787,6 @@ static void smp_call_function_many_cond(const struct cpumask *mask,  	bool wait = scf_flags & SCF_WAIT;  	int nr_cpus = 0;  	bool run_remote = false; -	bool run_local = false;  	lockdep_assert_preemption_disabled(); @@ -814,19 +808,8 @@ static void smp_call_function_many_cond(const struct cpumask *mask,  	 */  	WARN_ON_ONCE(!in_task()); -	/* Check if we need local execution. */ -	if ((scf_flags & SCF_RUN_LOCAL) && cpumask_test_cpu(this_cpu, mask) && -	    (!cond_func || cond_func(this_cpu, info))) -		run_local = true; -  	/* Check if we need remote execution, i.e., any CPU excluding this one. */ -	cpu = cpumask_first_and(mask, cpu_online_mask); -	if (cpu == this_cpu) -		cpu = cpumask_next_and(cpu, mask, cpu_online_mask); -	if (cpu < nr_cpu_ids) -		run_remote = true; - -	if (run_remote) { +	if (cpumask_any_and_but(mask, cpu_online_mask, this_cpu) < nr_cpu_ids) {  		cfd = this_cpu_ptr(&cfd_data);  		cpumask_and(cfd->cpumask, mask, cpu_online_mask);  		__cpumask_clear_cpu(this_cpu, cfd->cpumask); @@ -840,6 +823,9 @@ static void smp_call_function_many_cond(const struct cpumask *mask,  				continue;  			} +			/* Work is enqueued on a remote CPU. */ +			run_remote = true; +  			csd_lock(csd);  			if (wait)  				csd->node.u_flags |= CSD_TYPE_SYNC; @@ -851,6 +837,10 @@ static void smp_call_function_many_cond(const struct cpumask *mask,  #endif  			trace_csd_queue_cpu(cpu, _RET_IP_, func, csd); +			/* +			 * Kick the remote CPU if this is the first work +			 * item enqueued. +			 */  			if (llist_add(&csd->node.llist, &per_cpu(call_single_queue, cpu))) {  				__cpumask_set_cpu(cpu, cfd->cpumask_ipi);  				nr_cpus++; @@ -869,7 +859,9 @@ static void smp_call_function_many_cond(const struct cpumask *mask,  			send_call_function_ipi_mask(cfd->cpumask_ipi);  	} -	if (run_local) { +	/* Check if we need local execution. */ +	if ((scf_flags & SCF_RUN_LOCAL) && cpumask_test_cpu(this_cpu, mask) && +	    (!cond_func || cond_func(this_cpu, info))) {  		unsigned long flags;  		local_irq_save(flags); | 
