xref: /kernel/linux/linux-6.6/arch/x86/kernel/apic/ipi.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/cpumask.h>
462306a36Sopenharmony_ci#include <linux/delay.h>
562306a36Sopenharmony_ci#include <linux/smp.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <asm/io_apic.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "local.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(apic_use_ipi_shorthand);
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#ifdef CONFIG_SMP
1462306a36Sopenharmony_cistatic int apic_ipi_shorthand_off __ro_after_init;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic __init int apic_ipi_shorthand(char *str)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	get_option(&str, &apic_ipi_shorthand_off);
1962306a36Sopenharmony_ci	return 1;
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci__setup("no_ipi_broadcast=", apic_ipi_shorthand);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int __init print_ipi_mode(void)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	pr_info("IPI shorthand broadcast: %s\n",
2662306a36Sopenharmony_ci		apic_ipi_shorthand_off ? "disabled" : "enabled");
2762306a36Sopenharmony_ci	return 0;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_cilate_initcall(print_ipi_mode);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_civoid apic_smt_update(void)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	/*
3462306a36Sopenharmony_ci	 * Do not switch to broadcast mode if:
3562306a36Sopenharmony_ci	 * - Disabled on the command line
3662306a36Sopenharmony_ci	 * - Only a single CPU is online
3762306a36Sopenharmony_ci	 * - Not all present CPUs have been at least booted once
3862306a36Sopenharmony_ci	 *
3962306a36Sopenharmony_ci	 * The latter is important as the local APIC might be in some
4062306a36Sopenharmony_ci	 * random state and a broadcast might cause havoc. That's
4162306a36Sopenharmony_ci	 * especially true for NMI broadcasting.
4262306a36Sopenharmony_ci	 */
4362306a36Sopenharmony_ci	if (apic_ipi_shorthand_off || num_online_cpus() == 1 ||
4462306a36Sopenharmony_ci	    !cpumask_equal(cpu_present_mask, &cpus_booted_once_mask)) {
4562306a36Sopenharmony_ci		static_branch_disable(&apic_use_ipi_shorthand);
4662306a36Sopenharmony_ci	} else {
4762306a36Sopenharmony_ci		static_branch_enable(&apic_use_ipi_shorthand);
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_civoid apic_send_IPI_allbutself(unsigned int vector)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	if (num_online_cpus() < 2)
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (static_branch_likely(&apic_use_ipi_shorthand))
5762306a36Sopenharmony_ci		__apic_send_IPI_allbutself(vector);
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		__apic_send_IPI_mask_allbutself(cpu_online_mask, vector);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * Send a 'reschedule' IPI to another CPU. It goes straight through and
6462306a36Sopenharmony_ci * wastes no time serializing anything. Worst case is that we lose a
6562306a36Sopenharmony_ci * reschedule ...
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_civoid native_smp_send_reschedule(int cpu)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	if (unlikely(cpu_is_offline(cpu))) {
7062306a36Sopenharmony_ci		WARN(1, "sched: Unexpected reschedule of offline CPU#%d!\n", cpu);
7162306a36Sopenharmony_ci		return;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci	__apic_send_IPI(cpu, RESCHEDULE_VECTOR);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid native_send_call_func_single_ipi(int cpu)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	__apic_send_IPI(cpu, CALL_FUNCTION_SINGLE_VECTOR);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_civoid native_send_call_func_ipi(const struct cpumask *mask)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	if (static_branch_likely(&apic_use_ipi_shorthand)) {
8462306a36Sopenharmony_ci		unsigned int cpu = smp_processor_id();
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		if (!cpumask_or_equal(mask, cpumask_of(cpu), cpu_online_mask))
8762306a36Sopenharmony_ci			goto sendmask;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		if (cpumask_test_cpu(cpu, mask))
9062306a36Sopenharmony_ci			__apic_send_IPI_all(CALL_FUNCTION_VECTOR);
9162306a36Sopenharmony_ci		else if (num_online_cpus() > 1)
9262306a36Sopenharmony_ci			__apic_send_IPI_allbutself(CALL_FUNCTION_VECTOR);
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cisendmask:
9762306a36Sopenharmony_ci	__apic_send_IPI_mask(mask, CALL_FUNCTION_VECTOR);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#endif /* CONFIG_SMP */
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic inline int __prepare_ICR2(unsigned int mask)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	return SET_XAPIC_DEST_FIELD(mask);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciu32 apic_mem_wait_icr_idle_timeout(void)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int cnt;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	for (cnt = 0; cnt < 1000; cnt++) {
11262306a36Sopenharmony_ci		if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
11362306a36Sopenharmony_ci			return 0;
11462306a36Sopenharmony_ci		inc_irq_stat(icr_read_retry_count);
11562306a36Sopenharmony_ci		udelay(100);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	return APIC_ICR_BUSY;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_civoid apic_mem_wait_icr_idle(void)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	while (native_apic_mem_read(APIC_ICR) & APIC_ICR_BUSY)
12362306a36Sopenharmony_ci		cpu_relax();
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * This is safe against interruption because it only writes the lower 32
12862306a36Sopenharmony_ci * bits of the APIC_ICR register. The destination field is ignored for
12962306a36Sopenharmony_ci * short hand IPIs.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci *  wait_icr_idle()
13262306a36Sopenharmony_ci *  write(ICR2, dest)
13362306a36Sopenharmony_ci *  NMI
13462306a36Sopenharmony_ci *	wait_icr_idle()
13562306a36Sopenharmony_ci *	write(ICR)
13662306a36Sopenharmony_ci *	wait_icr_idle()
13762306a36Sopenharmony_ci *  write(ICR)
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * This function does not need to disable interrupts as there is no ICR2
14062306a36Sopenharmony_ci * interaction. The memory write is direct except when the machine is
14162306a36Sopenharmony_ci * affected by the 11AP Pentium erratum, which turns the plain write into
14262306a36Sopenharmony_ci * an XCHG operation.
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_cistatic void __default_send_IPI_shortcut(unsigned int shortcut, int vector)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	/*
14762306a36Sopenharmony_ci	 * Wait for the previous ICR command to complete.  Use
14862306a36Sopenharmony_ci	 * safe_apic_wait_icr_idle() for the NMI vector as there have been
14962306a36Sopenharmony_ci	 * issues where otherwise the system hangs when the panic CPU tries
15062306a36Sopenharmony_ci	 * to stop the others before launching the kdump kernel.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	if (unlikely(vector == NMI_VECTOR))
15362306a36Sopenharmony_ci		apic_mem_wait_icr_idle_timeout();
15462306a36Sopenharmony_ci	else
15562306a36Sopenharmony_ci		apic_mem_wait_icr_idle();
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* Destination field (ICR2) and the destination mode are ignored */
15862306a36Sopenharmony_ci	native_apic_mem_write(APIC_ICR, __prepare_ICR(shortcut, vector, 0));
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * This is used to send an IPI with no shorthand notation (the destination is
16362306a36Sopenharmony_ci * specified in bits 56 to 63 of the ICR).
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_civoid __default_send_IPI_dest_field(unsigned int dest_mask, int vector,
16662306a36Sopenharmony_ci				   unsigned int dest_mode)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	/* See comment in __default_send_IPI_shortcut() */
16962306a36Sopenharmony_ci	if (unlikely(vector == NMI_VECTOR))
17062306a36Sopenharmony_ci		apic_mem_wait_icr_idle_timeout();
17162306a36Sopenharmony_ci	else
17262306a36Sopenharmony_ci		apic_mem_wait_icr_idle();
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Set the IPI destination field in the ICR */
17562306a36Sopenharmony_ci	native_apic_mem_write(APIC_ICR2, __prepare_ICR2(dest_mask));
17662306a36Sopenharmony_ci	/* Send it with the proper destination mode */
17762306a36Sopenharmony_ci	native_apic_mem_write(APIC_ICR, __prepare_ICR(0, vector, dest_mode));
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_civoid default_send_IPI_single_phys(int cpu, int vector)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	unsigned long flags;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	local_irq_save(flags);
18562306a36Sopenharmony_ci	__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid, cpu),
18662306a36Sopenharmony_ci				      vector, APIC_DEST_PHYSICAL);
18762306a36Sopenharmony_ci	local_irq_restore(flags);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_civoid default_send_IPI_mask_sequence_phys(const struct cpumask *mask, int vector)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	unsigned long flags;
19362306a36Sopenharmony_ci	unsigned long cpu;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	local_irq_save(flags);
19662306a36Sopenharmony_ci	for_each_cpu(cpu, mask) {
19762306a36Sopenharmony_ci		__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid,
19862306a36Sopenharmony_ci				cpu), vector, APIC_DEST_PHYSICAL);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	local_irq_restore(flags);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_civoid default_send_IPI_mask_allbutself_phys(const struct cpumask *mask,
20462306a36Sopenharmony_ci						 int vector)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	unsigned int cpu, this_cpu = smp_processor_id();
20762306a36Sopenharmony_ci	unsigned long flags;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	local_irq_save(flags);
21062306a36Sopenharmony_ci	for_each_cpu(cpu, mask) {
21162306a36Sopenharmony_ci		if (cpu == this_cpu)
21262306a36Sopenharmony_ci			continue;
21362306a36Sopenharmony_ci		__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid,
21462306a36Sopenharmony_ci				 cpu), vector, APIC_DEST_PHYSICAL);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci	local_irq_restore(flags);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/*
22062306a36Sopenharmony_ci * Helper function for APICs which insist on cpumasks
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_civoid default_send_IPI_single(int cpu, int vector)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	__apic_send_IPI_mask(cpumask_of(cpu), vector);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_civoid default_send_IPI_allbutself(int vector)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	__default_send_IPI_shortcut(APIC_DEST_ALLBUT, vector);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_civoid default_send_IPI_all(int vector)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	__default_send_IPI_shortcut(APIC_DEST_ALLINC, vector);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_civoid default_send_IPI_self(int vector)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	__default_send_IPI_shortcut(APIC_DEST_SELF, vector);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci#ifdef CONFIG_X86_32
24362306a36Sopenharmony_civoid default_send_IPI_mask_sequence_logical(const struct cpumask *mask, int vector)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	unsigned long flags;
24662306a36Sopenharmony_ci	unsigned int cpu;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	local_irq_save(flags);
24962306a36Sopenharmony_ci	for_each_cpu(cpu, mask)
25062306a36Sopenharmony_ci		__default_send_IPI_dest_field(1U << cpu, vector, APIC_DEST_LOGICAL);
25162306a36Sopenharmony_ci	local_irq_restore(flags);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_civoid default_send_IPI_mask_allbutself_logical(const struct cpumask *mask,
25562306a36Sopenharmony_ci						 int vector)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	unsigned int cpu, this_cpu = smp_processor_id();
25862306a36Sopenharmony_ci	unsigned long flags;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	local_irq_save(flags);
26162306a36Sopenharmony_ci	for_each_cpu(cpu, mask) {
26262306a36Sopenharmony_ci		if (cpu == this_cpu)
26362306a36Sopenharmony_ci			continue;
26462306a36Sopenharmony_ci		__default_send_IPI_dest_field(1U << cpu, vector, APIC_DEST_LOGICAL);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	local_irq_restore(flags);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_civoid default_send_IPI_mask_logical(const struct cpumask *cpumask, int vector)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	unsigned long mask = cpumask_bits(cpumask)[0];
27262306a36Sopenharmony_ci	unsigned long flags;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!mask)
27562306a36Sopenharmony_ci		return;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	local_irq_save(flags);
27862306a36Sopenharmony_ci	WARN_ON(mask & ~cpumask_bits(cpu_online_mask)[0]);
27962306a36Sopenharmony_ci	__default_send_IPI_dest_field(mask, vector, APIC_DEST_LOGICAL);
28062306a36Sopenharmony_ci	local_irq_restore(flags);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci#ifdef CONFIG_SMP
28462306a36Sopenharmony_cistatic int convert_apicid_to_cpu(int apic_id)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	int i;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	for_each_possible_cpu(i) {
28962306a36Sopenharmony_ci		if (per_cpu(x86_cpu_to_apicid, i) == apic_id)
29062306a36Sopenharmony_ci			return i;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci	return -1;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ciint safe_smp_processor_id(void)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int apicid, cpuid;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (!boot_cpu_has(X86_FEATURE_APIC))
30062306a36Sopenharmony_ci		return 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	apicid = read_apic_id();
30362306a36Sopenharmony_ci	if (apicid == BAD_APICID)
30462306a36Sopenharmony_ci		return 0;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	cpuid = convert_apicid_to_cpu(apicid);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return cpuid >= 0 ? cpuid : 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci#endif
31162306a36Sopenharmony_ci#endif
312