1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2f62bae50SIngo Molnar 
3521b82feSThomas Gleixner #include <linux/cpuhotplug.h>
4521b82feSThomas Gleixner #include <linux/cpumask.h>
5521b82feSThomas Gleixner #include <linux/slab.h>
6521b82feSThomas Gleixner #include <linux/mm.h>
7521b82feSThomas Gleixner 
8521b82feSThomas Gleixner #include <asm/apic.h>
9521b82feSThomas Gleixner 
10c94f0718SThomas Gleixner #include "local.h"
11f62bae50SIngo Molnar 
12cefad862SDavid Woodhouse #define apic_cluster(apicid) ((apicid) >> 4)
13023a6117SThomas Gleixner 
14cc95a07fSEric Dumazet /*
15cc95a07fSEric Dumazet  * __x2apic_send_IPI_mask() possibly needs to read
16cc95a07fSEric Dumazet  * x86_cpu_to_logical_apicid for all online cpus in a sequential way.
17cc95a07fSEric Dumazet  * Using per cpu variable would cost one cache line per cpu.
18cc95a07fSEric Dumazet  */
19cc95a07fSEric Dumazet static u32 *x86_cpu_to_logical_apicid __read_mostly;
20cc95a07fSEric Dumazet 
219d0fa6c5SCyrill Gorcunov static DEFINE_PER_CPU(cpumask_var_t, ipi_mask);
22cefad862SDavid Woodhouse static DEFINE_PER_CPU_READ_MOSTLY(struct cpumask *, cluster_masks);
23f62bae50SIngo Molnar 
x2apic_acpi_madt_oem_check(char * oem_id,char * oem_table_id)24f62bae50SIngo Molnar static int x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
25f62bae50SIngo Molnar {
26ef1f87aaSSuresh Siddha 	return x2apic_enabled();
27f62bae50SIngo Molnar }
28f62bae50SIngo Molnar 
x2apic_send_IPI(int cpu,int vector)297b6ce46cSLinus Torvalds static void x2apic_send_IPI(int cpu, int vector)
307b6ce46cSLinus Torvalds {
31cc95a07fSEric Dumazet 	u32 dest = x86_cpu_to_logical_apicid[cpu];
327b6ce46cSLinus Torvalds 
3325a068b8SDave Hansen 	/* x2apic MSRs are special and need a special fence: */
3425a068b8SDave Hansen 	weak_wrmsr_fence();
357b6ce46cSLinus Torvalds 	__x2apic_send_IPI_dest(dest, vector, APIC_DEST_LOGICAL);
367b6ce46cSLinus Torvalds }
377b6ce46cSLinus Torvalds 
38a27d0b5eSSuresh Siddha static void
__x2apic_send_IPI_mask(const struct cpumask * mask,int vector,int apic_dest)39a27d0b5eSSuresh Siddha __x2apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest)
40f62bae50SIngo Molnar {
41023a6117SThomas Gleixner 	unsigned int cpu, clustercpu;
42023a6117SThomas Gleixner 	struct cpumask *tmpmsk;
43f62bae50SIngo Molnar 	unsigned long flags;
449d0fa6c5SCyrill Gorcunov 	u32 dest;
45f62bae50SIngo Molnar 
4625a068b8SDave Hansen 	/* x2apic MSRs are special and need a special fence: */
4725a068b8SDave Hansen 	weak_wrmsr_fence();
48f62bae50SIngo Molnar 	local_irq_save(flags);
49a27d0b5eSSuresh Siddha 
50023a6117SThomas Gleixner 	tmpmsk = this_cpu_cpumask_var_ptr(ipi_mask);
51023a6117SThomas Gleixner 	cpumask_copy(tmpmsk, mask);
52023a6117SThomas Gleixner 	/* If IPI should not be sent to self, clear current CPU */
53023a6117SThomas Gleixner 	if (apic_dest != APIC_DEST_ALLINC)
54dde3626fSNadav Amit 		__cpumask_clear_cpu(smp_processor_id(), tmpmsk);
559d0fa6c5SCyrill Gorcunov 
56023a6117SThomas Gleixner 	/* Collapse cpus in a cluster so a single IPI per cluster is sent */
57023a6117SThomas Gleixner 	for_each_cpu(cpu, tmpmsk) {
58cefad862SDavid Woodhouse 		struct cpumask *cmsk = per_cpu(cluster_masks, cpu);
599d0fa6c5SCyrill Gorcunov 
609d0fa6c5SCyrill Gorcunov 		dest = 0;
61cefad862SDavid Woodhouse 		for_each_cpu_and(clustercpu, tmpmsk, cmsk)
62cc95a07fSEric Dumazet 			dest |= x86_cpu_to_logical_apicid[clustercpu];
639d0fa6c5SCyrill Gorcunov 
649d0fa6c5SCyrill Gorcunov 		if (!dest)
65a27d0b5eSSuresh Siddha 			continue;
669d0fa6c5SCyrill Gorcunov 
6722e0db42SThomas Gleixner 		__x2apic_send_IPI_dest(dest, vector, APIC_DEST_LOGICAL);
68023a6117SThomas Gleixner 		/* Remove cluster CPUs from tmpmask */
69cefad862SDavid Woodhouse 		cpumask_andnot(tmpmsk, tmpmsk, cmsk);
70f62bae50SIngo Molnar 	}
71a27d0b5eSSuresh Siddha 
72f62bae50SIngo Molnar 	local_irq_restore(flags);
73f62bae50SIngo Molnar }
74f62bae50SIngo Molnar 
x2apic_send_IPI_mask(const struct cpumask * mask,int vector)75a27d0b5eSSuresh Siddha static void x2apic_send_IPI_mask(const struct cpumask *mask, int vector)
76a27d0b5eSSuresh Siddha {
77a27d0b5eSSuresh Siddha 	__x2apic_send_IPI_mask(mask, vector, APIC_DEST_ALLINC);
78a27d0b5eSSuresh Siddha }
79a27d0b5eSSuresh Siddha 
80f62bae50SIngo Molnar static void
x2apic_send_IPI_mask_allbutself(const struct cpumask * mask,int vector)81f62bae50SIngo Molnar x2apic_send_IPI_mask_allbutself(const struct cpumask *mask, int vector)
82f62bae50SIngo Molnar {
83a27d0b5eSSuresh Siddha 	__x2apic_send_IPI_mask(mask, vector, APIC_DEST_ALLBUT);
84f62bae50SIngo Molnar }
85f62bae50SIngo Molnar 
x2apic_calc_apicid(unsigned int cpu)869f9e3bb1SThomas Gleixner static u32 x2apic_calc_apicid(unsigned int cpu)
879f9e3bb1SThomas Gleixner {
88cc95a07fSEric Dumazet 	return x86_cpu_to_logical_apicid[cpu];
899f9e3bb1SThomas Gleixner }
909f9e3bb1SThomas Gleixner 
init_x2apic_ldr(void)91f62bae50SIngo Molnar static void init_x2apic_ldr(void)
92f62bae50SIngo Molnar {
93cefad862SDavid Woodhouse 	struct cpumask *cmsk = this_cpu_read(cluster_masks);
94a39d1f3fSCyrill Gorcunov 
95cefad862SDavid Woodhouse 	BUG_ON(!cmsk);
96a39d1f3fSCyrill Gorcunov 
97cefad862SDavid Woodhouse 	cpumask_set_cpu(smp_processor_id(), cmsk);
98a39d1f3fSCyrill Gorcunov }
99a39d1f3fSCyrill Gorcunov 
100cefad862SDavid Woodhouse /*
101cefad862SDavid Woodhouse  * As an optimisation during boot, set the cluster_mask for all present
102cefad862SDavid Woodhouse  * CPUs at once, to prevent each of them having to iterate over the others
103cefad862SDavid Woodhouse  * to find the existing cluster_mask.
104cefad862SDavid Woodhouse  */
prefill_clustermask(struct cpumask * cmsk,unsigned int cpu,u32 cluster)105cefad862SDavid Woodhouse static void prefill_clustermask(struct cpumask *cmsk, unsigned int cpu, u32 cluster)
106a39d1f3fSCyrill Gorcunov {
107cefad862SDavid Woodhouse 	int cpu_i;
108cefad862SDavid Woodhouse 
109cefad862SDavid Woodhouse 	for_each_present_cpu(cpu_i) {
110cefad862SDavid Woodhouse 		struct cpumask **cpu_cmsk = &per_cpu(cluster_masks, cpu_i);
111cefad862SDavid Woodhouse 		u32 apicid = apic->cpu_present_to_apicid(cpu_i);
112cefad862SDavid Woodhouse 
113cefad862SDavid Woodhouse 		if (apicid == BAD_APICID || cpu_i == cpu || apic_cluster(apicid) != cluster)
114cefad862SDavid Woodhouse 			continue;
115cefad862SDavid Woodhouse 
116cefad862SDavid Woodhouse 		if (WARN_ON_ONCE(*cpu_cmsk == cmsk))
117cefad862SDavid Woodhouse 			continue;
118cefad862SDavid Woodhouse 
119cefad862SDavid Woodhouse 		BUG_ON(*cpu_cmsk);
120cefad862SDavid Woodhouse 		*cpu_cmsk = cmsk;
121cefad862SDavid Woodhouse 	}
122cefad862SDavid Woodhouse }
123cefad862SDavid Woodhouse 
alloc_clustermask(unsigned int cpu,u32 cluster,int node)124cefad862SDavid Woodhouse static int alloc_clustermask(unsigned int cpu, u32 cluster, int node)
125cefad862SDavid Woodhouse {
126cefad862SDavid Woodhouse 	struct cpumask *cmsk = NULL;
127cefad862SDavid Woodhouse 	unsigned int cpu_i;
128cefad862SDavid Woodhouse 
129cefad862SDavid Woodhouse 	/*
130cefad862SDavid Woodhouse 	 * At boot time, the CPU present mask is stable. The cluster mask is
131cefad862SDavid Woodhouse 	 * allocated for the first CPU in the cluster and propagated to all
132cefad862SDavid Woodhouse 	 * present siblings in the cluster. If the cluster mask is already set
133cefad862SDavid Woodhouse 	 * on entry to this function for a given CPU, there is nothing to do.
134cefad862SDavid Woodhouse 	 */
135023a6117SThomas Gleixner 	if (per_cpu(cluster_masks, cpu))
136023a6117SThomas Gleixner 		return 0;
1376b2c2847SSebastian Andrzej Siewior 
138cefad862SDavid Woodhouse 	if (system_state < SYSTEM_RUNNING)
139cefad862SDavid Woodhouse 		goto alloc;
140cefad862SDavid Woodhouse 
141cefad862SDavid Woodhouse 	/*
142cefad862SDavid Woodhouse 	 * On post boot hotplug for a CPU which was not present at boot time,
143cefad862SDavid Woodhouse 	 * iterate over all possible CPUs (even those which are not present
144cefad862SDavid Woodhouse 	 * any more) to find any existing cluster mask.
145cefad862SDavid Woodhouse 	 */
146cefad862SDavid Woodhouse 	for_each_possible_cpu(cpu_i) {
147cefad862SDavid Woodhouse 		u32 apicid = apic->cpu_present_to_apicid(cpu_i);
148cefad862SDavid Woodhouse 
149cefad862SDavid Woodhouse 		if (apicid != BAD_APICID && apic_cluster(apicid) == cluster) {
150cefad862SDavid Woodhouse 			cmsk = per_cpu(cluster_masks, cpu_i);
151cefad862SDavid Woodhouse 			/*
152cefad862SDavid Woodhouse 			 * If the cluster is already initialized, just store
153cefad862SDavid Woodhouse 			 * the mask and return. There's no need to propagate.
154cefad862SDavid Woodhouse 			 */
155cefad862SDavid Woodhouse 			if (cmsk) {
156cefad862SDavid Woodhouse 				per_cpu(cluster_masks, cpu) = cmsk;
157cefad862SDavid Woodhouse 				return 0;
158cefad862SDavid Woodhouse 			}
159cefad862SDavid Woodhouse 		}
160cefad862SDavid Woodhouse 	}
161cefad862SDavid Woodhouse 	/*
162cefad862SDavid Woodhouse 	 * No CPU in the cluster has ever been initialized, so fall through to
163cefad862SDavid Woodhouse 	 * the boot time code which will also populate the cluster mask for any
164cefad862SDavid Woodhouse 	 * other CPU in the cluster which is (now) present.
165cefad862SDavid Woodhouse 	 */
166cefad862SDavid Woodhouse alloc:
167cefad862SDavid Woodhouse 	cmsk = kzalloc_node(sizeof(*cmsk), GFP_KERNEL, node);
168cefad862SDavid Woodhouse 	if (!cmsk)
169023a6117SThomas Gleixner 		return -ENOMEM;
170cefad862SDavid Woodhouse 	per_cpu(cluster_masks, cpu) = cmsk;
171cefad862SDavid Woodhouse 	prefill_clustermask(cmsk, cpu, cluster);
172cefad862SDavid Woodhouse 
1736b2c2847SSebastian Andrzej Siewior 	return 0;
1746b2c2847SSebastian Andrzej Siewior }
1756b2c2847SSebastian Andrzej Siewior 
x2apic_prepare_cpu(unsigned int cpu)176023a6117SThomas Gleixner static int x2apic_prepare_cpu(unsigned int cpu)
1776b2c2847SSebastian Andrzej Siewior {
178cefad862SDavid Woodhouse 	u32 phys_apicid = apic->cpu_present_to_apicid(cpu);
179cefad862SDavid Woodhouse 	u32 cluster = apic_cluster(phys_apicid);
180cefad862SDavid Woodhouse 	u32 logical_apicid = (cluster << 16) | (1 << (phys_apicid & 0xf));
181cefad862SDavid Woodhouse 
182cefad862SDavid Woodhouse 	x86_cpu_to_logical_apicid[cpu] = logical_apicid;
183cefad862SDavid Woodhouse 
184cefad862SDavid Woodhouse 	if (alloc_clustermask(cpu, cluster, cpu_to_node(cpu)) < 0)
185023a6117SThomas Gleixner 		return -ENOMEM;
186023a6117SThomas Gleixner 	if (!zalloc_cpumask_var(&per_cpu(ipi_mask, cpu), GFP_KERNEL))
187023a6117SThomas Gleixner 		return -ENOMEM;
188023a6117SThomas Gleixner 	return 0;
189a39d1f3fSCyrill Gorcunov }
190023a6117SThomas Gleixner 
x2apic_dead_cpu(unsigned int dead_cpu)191023a6117SThomas Gleixner static int x2apic_dead_cpu(unsigned int dead_cpu)
192023a6117SThomas Gleixner {
193cefad862SDavid Woodhouse 	struct cpumask *cmsk = per_cpu(cluster_masks, dead_cpu);
194023a6117SThomas Gleixner 
1957a22e03bSSean Christopherson 	if (cmsk)
196cefad862SDavid Woodhouse 		cpumask_clear_cpu(dead_cpu, cmsk);
197023a6117SThomas Gleixner 	free_cpumask_var(per_cpu(ipi_mask, dead_cpu));
1986b2c2847SSebastian Andrzej Siewior 	return 0;
199f62bae50SIngo Molnar }
200f62bae50SIngo Molnar 
x2apic_cluster_probe(void)2019ebd680bSSuresh Siddha static int x2apic_cluster_probe(void)
2029ebd680bSSuresh Siddha {
203cc95a07fSEric Dumazet 	u32 slots;
204cc95a07fSEric Dumazet 
2056b2c2847SSebastian Andrzej Siewior 	if (!x2apic_mode)
206a39d1f3fSCyrill Gorcunov 		return 0;
2076b2c2847SSebastian Andrzej Siewior 
208cc95a07fSEric Dumazet 	slots = max_t(u32, L1_CACHE_BYTES/sizeof(u32), nr_cpu_ids);
209cc95a07fSEric Dumazet 	x86_cpu_to_logical_apicid = kcalloc(slots, sizeof(u32), GFP_KERNEL);
210cc95a07fSEric Dumazet 	if (!x86_cpu_to_logical_apicid)
211cc95a07fSEric Dumazet 		return 0;
212cc95a07fSEric Dumazet 
213023a6117SThomas Gleixner 	if (cpuhp_setup_state(CPUHP_X2APIC_PREPARE, "x86/x2apic:prepare",
214023a6117SThomas Gleixner 			      x2apic_prepare_cpu, x2apic_dead_cpu) < 0) {
215d52c0569SSebastian Andrzej Siewior 		pr_err("Failed to register X2APIC_PREPARE\n");
216cc95a07fSEric Dumazet 		kfree(x86_cpu_to_logical_apicid);
217cc95a07fSEric Dumazet 		x86_cpu_to_logical_apicid = NULL;
218d52c0569SSebastian Andrzej Siewior 		return 0;
219d52c0569SSebastian Andrzej Siewior 	}
220023a6117SThomas Gleixner 	init_x2apic_ldr();
2216b2c2847SSebastian Andrzej Siewior 	return 1;
2229ebd680bSSuresh Siddha }
2239ebd680bSSuresh Siddha 
224404f6aacSKees Cook static struct apic apic_x2apic_cluster __ro_after_init = {
225f62bae50SIngo Molnar 
226f62bae50SIngo Molnar 	.name				= "cluster x2apic",
2279ebd680bSSuresh Siddha 	.probe				= x2apic_cluster_probe,
228f62bae50SIngo Molnar 	.acpi_madt_oem_check		= x2apic_acpi_madt_oem_check,
229f62bae50SIngo Molnar 
23072161299SThomas Gleixner 	.delivery_mode			= APIC_DELIVERY_MODE_FIXED,
2318c44963bSThomas Gleixner 	.dest_mode_logical		= true,
232f62bae50SIngo Molnar 
233f62bae50SIngo Molnar 	.disable_esr			= 0,
234e57d04e5SThomas Gleixner 
235f62bae50SIngo Molnar 	.check_apicid_used		= NULL,
236f62bae50SIngo Molnar 	.init_apic_ldr			= init_x2apic_ldr,
237f62bae50SIngo Molnar 	.ioapic_phys_id_map		= NULL,
238f62bae50SIngo Molnar 	.cpu_present_to_apicid		= default_cpu_present_to_apicid,
23979deb8e5SCyrill Gorcunov 	.phys_pkg_id			= x2apic_phys_pkg_id,
240f62bae50SIngo Molnar 
241d92e5e7cSThomas Gleixner 	.max_apic_id			= UINT_MAX,
242b5a5ce58SThomas Gleixner 	.x2apic_set_max_apicid		= true,
24379deb8e5SCyrill Gorcunov 	.get_apic_id			= x2apic_get_apic_id,
24479deb8e5SCyrill Gorcunov 	.set_apic_id			= x2apic_set_apic_id,
245f62bae50SIngo Molnar 
2469f9e3bb1SThomas Gleixner 	.calc_dest_apicid		= x2apic_calc_apicid,
247f62bae50SIngo Molnar 
2487b6ce46cSLinus Torvalds 	.send_IPI			= x2apic_send_IPI,
249f62bae50SIngo Molnar 	.send_IPI_mask			= x2apic_send_IPI_mask,
250f62bae50SIngo Molnar 	.send_IPI_mask_allbutself	= x2apic_send_IPI_mask_allbutself,
251f62bae50SIngo Molnar 	.send_IPI_allbutself		= x2apic_send_IPI_allbutself,
252f62bae50SIngo Molnar 	.send_IPI_all			= x2apic_send_IPI_all,
253f62bae50SIngo Molnar 	.send_IPI_self			= x2apic_send_IPI_self,
254f62bae50SIngo Molnar 
255f62bae50SIngo Molnar 	.read				= native_apic_msr_read,
256f62bae50SIngo Molnar 	.write				= native_apic_msr_write,
257*185c8f33SThomas Gleixner 	.eoi				= native_apic_msr_eoi,
258f62bae50SIngo Molnar 	.icr_read			= native_x2apic_icr_read,
259f62bae50SIngo Molnar 	.icr_write			= native_x2apic_icr_write,
260f62bae50SIngo Molnar };
261107e0e0cSSuresh Siddha 
262107e0e0cSSuresh Siddha apic_driver(apic_x2apic_cluster);
263