diff options
Diffstat (limited to 'arch/x86/kernel/apic/ipi.c')
| -rw-r--r-- | arch/x86/kernel/apic/ipi.c | 176 | 
1 files changed, 77 insertions, 99 deletions
diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c index 9bfd6e397384..a44ba7209ef3 100644 --- a/arch/x86/kernel/apic/ipi.c +++ b/arch/x86/kernel/apic/ipi.c @@ -1,7 +1,9 @@  // SPDX-License-Identifier: GPL-2.0  #include <linux/cpumask.h> +#include <linux/delay.h>  #include <linux/smp.h> +  #include <asm/io_apic.h>  #include "local.h" @@ -52,9 +54,9 @@ void apic_send_IPI_allbutself(unsigned int vector)  		return;  	if (static_branch_likely(&apic_use_ipi_shorthand)) -		apic->send_IPI_allbutself(vector); +		__apic_send_IPI_allbutself(vector);  	else -		apic->send_IPI_mask_allbutself(cpu_online_mask, vector); +		__apic_send_IPI_mask_allbutself(cpu_online_mask, vector);  }  /* @@ -68,12 +70,12 @@ void native_smp_send_reschedule(int cpu)  		WARN(1, "sched: Unexpected reschedule of offline CPU#%d!\n", cpu);  		return;  	} -	apic->send_IPI(cpu, RESCHEDULE_VECTOR); +	__apic_send_IPI(cpu, RESCHEDULE_VECTOR);  }  void native_send_call_func_single_ipi(int cpu)  { -	apic->send_IPI(cpu, CALL_FUNCTION_SINGLE_VECTOR); +	__apic_send_IPI(cpu, CALL_FUNCTION_SINGLE_VECTOR);  }  void native_send_call_func_ipi(const struct cpumask *mask) @@ -85,14 +87,14 @@ void native_send_call_func_ipi(const struct cpumask *mask)  			goto sendmask;  		if (cpumask_test_cpu(cpu, mask)) -			apic->send_IPI_all(CALL_FUNCTION_VECTOR); +			__apic_send_IPI_all(CALL_FUNCTION_VECTOR);  		else if (num_online_cpus() > 1) -			apic->send_IPI_allbutself(CALL_FUNCTION_VECTOR); +			__apic_send_IPI_allbutself(CALL_FUNCTION_VECTOR);  		return;  	}  sendmask: -	apic->send_IPI_mask(mask, CALL_FUNCTION_VECTOR); +	__apic_send_IPI_mask(mask, CALL_FUNCTION_VECTOR);  }  #endif /* CONFIG_SMP */ @@ -102,74 +104,77 @@ static inline int __prepare_ICR2(unsigned int mask)  	return SET_XAPIC_DEST_FIELD(mask);  } -static inline void __xapic_wait_icr_idle(void) +u32 apic_mem_wait_icr_idle_timeout(void) +{ +	int cnt; + +	for (cnt = 0; cnt < 1000; cnt++) { +		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY)) +			return 0; +		inc_irq_stat(icr_read_retry_count); +		udelay(100); +	} +	return APIC_ICR_BUSY; +} + +void apic_mem_wait_icr_idle(void)  {  	while (native_apic_mem_read(APIC_ICR) & APIC_ICR_BUSY)  		cpu_relax();  } -void __default_send_IPI_shortcut(unsigned int shortcut, int vector) +/* + * This is safe against interruption because it only writes the lower 32 + * bits of the APIC_ICR register. The destination field is ignored for + * short hand IPIs. + * + *  wait_icr_idle() + *  write(ICR2, dest) + *  NMI + *	wait_icr_idle() + *	write(ICR) + *	wait_icr_idle() + *  write(ICR) + * + * This function does not need to disable interrupts as there is no ICR2 + * interaction. The memory write is direct except when the machine is + * affected by the 11AP Pentium erratum, which turns the plain write into + * an XCHG operation. + */ +static void __default_send_IPI_shortcut(unsigned int shortcut, int vector)  {  	/* -	 * Subtle. In the case of the 'never do double writes' workaround -	 * we have to lock out interrupts to be safe.  As we don't care -	 * of the value read we use an atomic rmw access to avoid costly -	 * cli/sti.  Otherwise we use an even cheaper single atomic write -	 * to the APIC. -	 */ -	unsigned int cfg; - -	/* -	 * Wait for idle. +	 * Wait for the previous ICR command to complete.  Use +	 * safe_apic_wait_icr_idle() for the NMI vector as there have been +	 * issues where otherwise the system hangs when the panic CPU tries +	 * to stop the others before launching the kdump kernel.  	 */  	if (unlikely(vector == NMI_VECTOR)) -		safe_apic_wait_icr_idle(); +		apic_mem_wait_icr_idle_timeout();  	else -		__xapic_wait_icr_idle(); +		apic_mem_wait_icr_idle(); -	/* -	 * No need to touch the target chip field. Also the destination -	 * mode is ignored when a shorthand is used. -	 */ -	cfg = __prepare_ICR(shortcut, vector, 0); - -	/* -	 * Send the IPI. The write to APIC_ICR fires this off. -	 */ -	native_apic_mem_write(APIC_ICR, cfg); +	/* Destination field (ICR2) and the destination mode are ignored */ +	native_apic_mem_write(APIC_ICR, __prepare_ICR(shortcut, vector, 0));  }  /*   * This is used to send an IPI with no shorthand notation (the destination is   * specified in bits 56 to 63 of the ICR).   */ -void __default_send_IPI_dest_field(unsigned int mask, int vector, unsigned int dest) +void __default_send_IPI_dest_field(unsigned int dest_mask, int vector, +				   unsigned int dest_mode)  { -	unsigned long cfg; - -	/* -	 * Wait for idle. -	 */ +	/* See comment in __default_send_IPI_shortcut() */  	if (unlikely(vector == NMI_VECTOR)) -		safe_apic_wait_icr_idle(); +		apic_mem_wait_icr_idle_timeout();  	else -		__xapic_wait_icr_idle(); +		apic_mem_wait_icr_idle(); -	/* -	 * prepare target chip field -	 */ -	cfg = __prepare_ICR2(mask); -	native_apic_mem_write(APIC_ICR2, cfg); - -	/* -	 * program the ICR -	 */ -	cfg = __prepare_ICR(0, vector, dest); - -	/* -	 * Send the IPI. The write to APIC_ICR fires this off. -	 */ -	native_apic_mem_write(APIC_ICR, cfg); +	/* Set the IPI destination field in the ICR */ +	native_apic_mem_write(APIC_ICR2, __prepare_ICR2(dest_mask)); +	/* Send it with the proper destination mode */ +	native_apic_mem_write(APIC_ICR, __prepare_ICR(0, vector, dest_mode));  }  void default_send_IPI_single_phys(int cpu, int vector) @@ -184,18 +189,13 @@ void default_send_IPI_single_phys(int cpu, int vector)  void default_send_IPI_mask_sequence_phys(const struct cpumask *mask, int vector)  { -	unsigned long query_cpu;  	unsigned long flags; +	unsigned long cpu; -	/* -	 * Hack. The clustered APIC addressing mode doesn't allow us to send -	 * to an arbitrary mask, so I do a unicast to each CPU instead. -	 * - mbligh -	 */  	local_irq_save(flags); -	for_each_cpu(query_cpu, mask) { +	for_each_cpu(cpu, mask) {  		__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid, -				query_cpu), vector, APIC_DEST_PHYSICAL); +				cpu), vector, APIC_DEST_PHYSICAL);  	}  	local_irq_restore(flags);  } @@ -203,18 +203,15 @@ void default_send_IPI_mask_sequence_phys(const struct cpumask *mask, int vector)  void default_send_IPI_mask_allbutself_phys(const struct cpumask *mask,  						 int vector)  { -	unsigned int this_cpu = smp_processor_id(); -	unsigned int query_cpu; +	unsigned int cpu, this_cpu = smp_processor_id();  	unsigned long flags; -	/* See Hack comment above */ -  	local_irq_save(flags); -	for_each_cpu(query_cpu, mask) { -		if (query_cpu == this_cpu) +	for_each_cpu(cpu, mask) { +		if (cpu == this_cpu)  			continue;  		__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid, -				 query_cpu), vector, APIC_DEST_PHYSICAL); +				 cpu), vector, APIC_DEST_PHYSICAL);  	}  	local_irq_restore(flags);  } @@ -224,7 +221,7 @@ void default_send_IPI_mask_allbutself_phys(const struct cpumask *mask,   */  void default_send_IPI_single(int cpu, int vector)  { -	apic->send_IPI_mask(cpumask_of(cpu), vector); +	__apic_send_IPI_mask(cpumask_of(cpu), vector);  }  void default_send_IPI_allbutself(int vector) @@ -243,50 +240,32 @@ void default_send_IPI_self(int vector)  }  #ifdef CONFIG_X86_32 - -void default_send_IPI_mask_sequence_logical(const struct cpumask *mask, -						 int vector) +void default_send_IPI_mask_sequence_logical(const struct cpumask *mask, int vector)  {  	unsigned long flags; -	unsigned int query_cpu; - -	/* -	 * Hack. The clustered APIC addressing mode doesn't allow us to send -	 * to an arbitrary mask, so I do a unicasts to each CPU instead. This -	 * should be modified to do 1 message per cluster ID - mbligh -	 */ +	unsigned int cpu;  	local_irq_save(flags); -	for_each_cpu(query_cpu, mask) -		__default_send_IPI_dest_field( -			early_per_cpu(x86_cpu_to_logical_apicid, query_cpu), -			vector, APIC_DEST_LOGICAL); +	for_each_cpu(cpu, mask) +		__default_send_IPI_dest_field(1U << cpu, vector, APIC_DEST_LOGICAL);  	local_irq_restore(flags);  }  void default_send_IPI_mask_allbutself_logical(const struct cpumask *mask,  						 int vector)  { +	unsigned int cpu, this_cpu = smp_processor_id();  	unsigned long flags; -	unsigned int query_cpu; -	unsigned int this_cpu = smp_processor_id(); - -	/* See Hack comment above */  	local_irq_save(flags); -	for_each_cpu(query_cpu, mask) { -		if (query_cpu == this_cpu) +	for_each_cpu(cpu, mask) { +		if (cpu == this_cpu)  			continue; -		__default_send_IPI_dest_field( -			early_per_cpu(x86_cpu_to_logical_apicid, query_cpu), -			vector, APIC_DEST_LOGICAL); -		} +		__default_send_IPI_dest_field(1U << cpu, vector, APIC_DEST_LOGICAL); +	}  	local_irq_restore(flags);  } -/* - * This is only used on smaller machines. - */  void default_send_IPI_mask_logical(const struct cpumask *cpumask, int vector)  {  	unsigned long mask = cpumask_bits(cpumask)[0]; @@ -302,7 +281,6 @@ void default_send_IPI_mask_logical(const struct cpumask *cpumask, int vector)  }  #ifdef CONFIG_SMP -/* must come after the send_IPI functions above for inlining */  static int convert_apicid_to_cpu(int apic_id)  {  	int i; @@ -321,7 +299,7 @@ int safe_smp_processor_id(void)  	if (!boot_cpu_has(X86_FEATURE_APIC))  		return 0; -	apicid = hard_smp_processor_id(); +	apicid = read_apic_id();  	if (apicid == BAD_APICID)  		return 0;  |