Borislav Petkov (AMD)x86/alternative: Drop smp_locks glue4a73b934 months agoHistory
1528 lines · 38.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later /* *	x86 SMP booting functions * *	(c) 1995 Alan Cox, Building #3 <alan@lxorguk.ukuu.org.uk> *	(c) 1998, 1999, 2000, 2009 Ingo Molnar <mingo@redhat.com> *	Copyright 2001 Andi Kleen, SuSE Labs. * *	Much of the core SMP work is based on previous work by Thomas Radke, to *	whom a great many thanks are extended. * *	Thanks to Intel for making available several different Pentium, *	Pentium Pro and Pentium-II/Xeon MP machines. *	Original development of Linux SMP code supported by Caldera. * *	Fixes *		Felix Koop	:	NR_CPUS used properly *		Jose Renau	:	Handle single CPU case. *		Alan Cox	:	By repeated request 8) - Total BogoMIPS report. *		Greg Wright	:	Fix for kernel stacks panic. *		Erich Boleyn	:	MP v1.4 and additional changes. *	Matthias Sattler	:	Changes for 2.1 kernel map. *	Michel Lespinasse	:	Changes for 2.1 kernel map. *	Michael Chastain	:	Change trampoline.S to gnu as. *		Alan Cox	:	Dumb bug: 'B' step PPro's are fine *		Ingo Molnar	:	Added APIC timers, based on code *					from Jose Renau *		Ingo Molnar	:	various cleanups and rewrites *		Tigran Aivazian	:	fixed "0.00 in /proc/uptime on SMP" bug. *	Maciej W. Rozycki	:	Bits for genuine 82489DX APICs *	Andi Kleen		:	Changed for SMP boot into long mode. *		Martin J. Bligh	: 	Added support for multi-quad systems *		Dave Jones	:	Report invalid combinations of Athlon CPUs. *		Rusty Russell	:	Hacked into shape for new "hotplug" boot process. *      Andi Kleen              :       Converted to new state machine. *	Ashok Raj		: 	CPU hotplug support *	Glauber Costa		:	i386 and x86_64 integration */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/init.h>#include <linux/smp.h>#include <linux/export.h>#include <linux/sched.h>#include <linux/sched/topology.h>#include <linux/sched/hotplug.h>#include <linux/sched/task_stack.h>#include <linux/percpu.h>#include <linux/memblock.h>#include <linux/err.h>#include <linux/nmi.h>#include <linux/tboot.h>#include <linux/gfp.h>#include <linux/cpuidle.h>#include <linux/kexec.h>#include <linux/numa.h>#include <linux/pgtable.h>#include <linux/overflow.h>#include <linux/stackprotector.h>#include <linux/cpuhotplug.h>#include <linux/mc146818rtc.h>#include <linux/acpi.h> #include <asm/acpi.h>#include <asm/cacheinfo.h>#include <asm/cpuid/api.h>#include <asm/desc.h>#include <asm/nmi.h>#include <asm/irq.h>#include <asm/realmode.h>#include <asm/cpu.h>#include <asm/numa.h>#include <asm/tlbflush.h>#include <asm/mtrr.h>#include <asm/mwait.h>#include <asm/apic.h>#include <asm/io_apic.h>#include <asm/fpu/api.h>#include <asm/setup.h>#include <asm/uv/uv.h>#include <asm/microcode.h>#include <asm/i8259.h>#include <asm/misc.h>#include <asm/qspinlock.h>#include <asm/intel-family.h>#include <asm/cpu_device_id.h>#include <asm/spec-ctrl.h>#include <asm/hw_irq.h>#include <asm/stackprotector.h>#include <asm/sev.h>#include <asm/spec-ctrl.h> /* representing HT siblings of each logical CPU */DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map);EXPORT_PER_CPU_SYMBOL(cpu_sibling_map); /* representing HT and core siblings of each logical CPU */DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_core_map);EXPORT_PER_CPU_SYMBOL(cpu_core_map); /* representing HT, core, and die siblings of each logical CPU */DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_die_map);EXPORT_PER_CPU_SYMBOL(cpu_die_map); /* Representing CPUs for which sibling maps can be computed */static cpumask_var_t cpu_sibling_setup_mask; struct mwait_cpu_dead {	unsigned int	control;	unsigned int	status;}; #define CPUDEAD_MWAIT_WAIT	0xDEADBEEF#define CPUDEAD_MWAIT_KEXEC_HLT	0x4A17DEAD /* * Cache line aligned data for mwait_play_dead(). Separate on purpose so * that it's unlikely to be touched by other CPUs. */static DEFINE_PER_CPU_ALIGNED(struct mwait_cpu_dead, mwait_cpu_dead); /* Maximum number of SMT threads on any online core */int __read_mostly __max_smt_threads = 1; /* Flag to indicate if a complete sched domain rebuild is required */bool x86_topology_update; int arch_update_cpu_topology(void){	int retval = x86_topology_update; 	x86_topology_update = false;	return retval;} static unsigned int smpboot_warm_reset_vector_count; static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip){	unsigned long flags; 	spin_lock_irqsave(&rtc_lock, flags);	if (!smpboot_warm_reset_vector_count++) {		CMOS_WRITE(0xa, 0xf);		*((volatile unsigned short *)phys_to_virt(TRAMPOLINE_PHYS_HIGH)) = start_eip >> 4;		*((volatile unsigned short *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) = start_eip & 0xf;	}	spin_unlock_irqrestore(&rtc_lock, flags);} static inline void smpboot_restore_warm_reset_vector(void){	unsigned long flags; 	/*	 * Paranoid:  Set warm reset code and vector here back	 * to default values.	 */	spin_lock_irqsave(&rtc_lock, flags);	if (!--smpboot_warm_reset_vector_count) {		CMOS_WRITE(0, 0xf);		*((volatile u32 *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) = 0;	}	spin_unlock_irqrestore(&rtc_lock, flags); } /* Run the next set of setup steps for the upcoming CPU */static void ap_starting(void){	int cpuid = smp_processor_id(); 	/* Mop up eventual mwait_play_dead() wreckage */	this_cpu_write(mwait_cpu_dead.status, 0);	this_cpu_write(mwait_cpu_dead.control, 0); 	/*	 * If woken up by an INIT in an 82489DX configuration the alive	 * synchronization guarantees that the CPU does not reach this	 * point before an INIT_deassert IPI reaches the local APIC, so it	 * is now safe to touch the local APIC.	 *	 * Set up this CPU, first the APIC, which is probably redundant on	 * most boards.	 */	apic_ap_setup(); 	/* Save the processor parameters. */	identify_secondary_cpu(cpuid); 	/*	 * The topology information must be up to date before	 * notify_cpu_starting().	 */	set_cpu_sibling_map(cpuid); 	ap_init_aperfmperf(); 	pr_debug("Stack at about %p\n", &cpuid); 	wmb(); 	/*	 * This runs the AP through all the cpuhp states to its target	 * state CPUHP_ONLINE.	 */	notify_cpu_starting(cpuid);} static void ap_calibrate_delay(void){	/*	 * Calibrate the delay loop and update loops_per_jiffy in cpu_data.	 * identify_secondary_cpu() stored a value that is close but not as	 * accurate as the value just calculated.	 *	 * As this is invoked after the TSC synchronization check,	 * calibrate_delay_is_known() will skip the calibration routine	 * when TSC is synchronized across sockets.	 */	calibrate_delay();	cpu_data(smp_processor_id()).loops_per_jiffy = loops_per_jiffy;} /* * Activate a secondary processor. */static void notrace __noendbr start_secondary(void *unused){	/*	 * Don't put *anything* except direct CPU state initialization	 * before cpu_init(), SMP booting is too fragile that we want to	 * limit the things done here to the most necessary things.	 */	cr4_init(); 	/*	 * 32-bit specific. 64-bit reaches this code with the correct page	 * table established. Yet another historical divergence.	 */	if (IS_ENABLED(CONFIG_X86_32)) {		/* switch away from the initial page table */		load_cr3(swapper_pg_dir);		__flush_tlb_all();	} 	cpu_init_exception_handling(false); 	/*	 * Load the microcode before reaching the AP alive synchronization	 * point below so it is not part of the full per CPU serialized	 * bringup part when "parallel" bringup is enabled.	 *	 * That's even safe when hyperthreading is enabled in the CPU as	 * the core code starts the primary threads first and leaves the	 * secondary threads waiting for SIPI. Loading microcode on	 * physical cores concurrently is a safe operation.	 *	 * This covers both the Intel specific issue that concurrent	 * microcode loading on SMT siblings must be prohibited and the	 * vendor independent issue`that microcode loading which changes	 * CPUID, MSRs etc. must be strictly serialized to maintain	 * software state correctness.	 */	load_ucode_ap(); 	/*	 * Synchronization point with the hotplug core. Sets this CPUs	 * synchronization state to ALIVE and spin-waits for the control CPU to	 * release this CPU for further bringup.	 */	cpuhp_ap_sync_alive(); 	cpu_init();	fpu__init_cpu();	rcutree_report_cpu_starting(raw_smp_processor_id());	x86_cpuinit.early_percpu_clock_init(); 	ap_starting(); 	/* Check TSC synchronization with the control CPU. */	check_tsc_sync_target(); 	/*	 * Calibrate the delay loop after the TSC synchronization check.	 * This allows to skip the calibration when TSC is synchronized	 * across sockets.	 */	ap_calibrate_delay(); 	speculative_store_bypass_ht_init(); 	/*	 * Lock vector_lock, set CPU online and bring the vector	 * allocator online. Online must be set with vector_lock held	 * to prevent a concurrent irq setup/teardown from seeing a	 * half valid vector space.	 */	lock_vector_lock();	set_cpu_online(smp_processor_id(), true);	lapic_online();	unlock_vector_lock();	x86_platform.nmi_init(); 	/* enable local interrupts */	local_irq_enable(); 	x86_cpuinit.setup_percpu_clockev(); 	wmb();	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);}ANNOTATE_NOENDBR_SYM(start_secondary); static booltopology_same_node(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o){	int cpu1 = c->cpu_index, cpu2 = o->cpu_index; 	return (cpu_to_node(cpu1) == cpu_to_node(cpu2));} static booltopology_sane(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o, const char *name){	int cpu1 = c->cpu_index, cpu2 = o->cpu_index; 	return !WARN_ONCE(!topology_same_node(c, o),		"sched: CPU #%d's %s-sibling CPU #%d is not on the same node! "		"[node: %d != %d]. Ignoring dependency.\n",		cpu1, name, cpu2, cpu_to_node(cpu1), cpu_to_node(cpu2));} #define link_mask(mfunc, c1, c2)					\do {									\	cpumask_set_cpu((c1), mfunc(c2));				\	cpumask_set_cpu((c2), mfunc(c1));				\} while (0) static bool match_smt(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o){	if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {		int cpu1 = c->cpu_index, cpu2 = o->cpu_index; 		if (c->topo.pkg_id == o->topo.pkg_id &&		    c->topo.die_id == o->topo.die_id &&		    c->topo.amd_node_id == o->topo.amd_node_id &&		    per_cpu_llc_id(cpu1) == per_cpu_llc_id(cpu2)) {			if (c->topo.core_id == o->topo.core_id)				return topology_sane(c, o, "smt"); 			if ((c->topo.cu_id != 0xff) &&			    (o->topo.cu_id != 0xff) &&			    (c->topo.cu_id == o->topo.cu_id))				return topology_sane(c, o, "smt");		} 	} else if (c->topo.pkg_id == o->topo.pkg_id &&		   c->topo.die_id == o->topo.die_id &&		   c->topo.core_id == o->topo.core_id) {		return topology_sane(c, o, "smt");	} 	return false;} static bool match_die(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o){	if (c->topo.pkg_id != o->topo.pkg_id || c->topo.die_id != o->topo.die_id)		return false; 	if (cpu_feature_enabled(X86_FEATURE_TOPOEXT) && topology_amd_nodes_per_pkg() > 1)		return c->topo.amd_node_id == o->topo.amd_node_id; 	return true;} static bool match_l2c(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o){	int cpu1 = c->cpu_index, cpu2 = o->cpu_index; 	/* If the arch didn't set up l2c_id, fall back to SMT */	if (per_cpu_l2c_id(cpu1) == BAD_APICID)		return match_smt(c, o); 	/* Do not match if L2 cache id does not match: */	if (per_cpu_l2c_id(cpu1) != per_cpu_l2c_id(cpu2))		return false; 	return topology_sane(c, o, "l2c");} /* * Unlike the other levels, we do not enforce keeping a * multicore group inside a NUMA node.  If this happens, we will * discard the MC level of the topology later. */static bool match_pkg(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o){	if (c->topo.pkg_id == o->topo.pkg_id)		return true;	return false;} /* * Define intel_cod_cpu[] for Intel COD (Cluster-on-Die) CPUs. * * Any Intel CPU that has multiple nodes per package and does not * match intel_cod_cpu[] has the SNC (Sub-NUMA Cluster) topology. * * When in SNC mode, these CPUs enumerate an LLC that is shared * by multiple NUMA nodes. The LLC is shared for off-package data * access but private to the NUMA node (half of the package) for * on-package access. CPUID (the source of the information about * the LLC) can only enumerate the cache as shared or unshared, * but not this particular configuration. */ static const struct x86_cpu_id intel_cod_cpu[] = {	X86_MATCH_VFM(INTEL_HASWELL_X,	 0),	/* COD */	X86_MATCH_VFM(INTEL_BROADWELL_X, 0),	/* COD */	X86_MATCH_VFM(INTEL_ANY,	 1),	/* SNC */	{}}; /* * Allows splitting the LLC by matching 'core_id % split_llc'. * * This is mostly a debug hack to emulate systems with multiple LLCs per node * on systems that do not naturally have this. */static unsigned int split_llc = 0; static int __init split_llc_setup(char *str){	get_option(&str, &split_llc);	return 0;}early_param("split_llc", split_llc_setup); static bool match_llc(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o){	const struct x86_cpu_id *id = x86_match_cpu(intel_cod_cpu);	int cpu1 = c->cpu_index, cpu2 = o->cpu_index;	bool intel_snc = id && id->driver_data; 	/* Do not match if we do not have a valid APICID for cpu: */	if (per_cpu_llc_id(cpu1) == BAD_APICID)		return false; 	/* Do not match if LLC id does not match: */	if (per_cpu_llc_id(cpu1) != per_cpu_llc_id(cpu2))		return false; 	if (split_llc &&	    (per_cpu_core_id(cpu1) % split_llc) !=	    (per_cpu_core_id(cpu2) % split_llc))		return false; 	/*	 * Allow the SNC topology without warning. Return of false	 * means 'c' does not share the LLC of 'o'. This will be	 * reflected to userspace.	 */	if (match_pkg(c, o) && !topology_same_node(c, o) && intel_snc)		return false; 	return topology_sane(c, o, "llc");}  static inline int x86_sched_itmt_flags(void){	return sysctl_sched_itmt_enabled ? SD_ASYM_PACKING : 0;} #ifdef CONFIG_SCHED_MCstatic int x86_core_flags(void){	return cpu_core_flags() | x86_sched_itmt_flags();}#endif#ifdef CONFIG_SCHED_CLUSTERstatic int x86_cluster_flags(void){	return cpu_cluster_flags() | x86_sched_itmt_flags();}#endif static struct sched_domain_topology_level x86_topology[] = {	SDTL_INIT(tl_smt_mask, cpu_smt_flags, SMT),#ifdef CONFIG_SCHED_CLUSTER	SDTL_INIT(tl_cls_mask, x86_cluster_flags, CLS),#endif#ifdef CONFIG_SCHED_MC	SDTL_INIT(tl_mc_mask, x86_core_flags, MC),#endif	SDTL_INIT(tl_pkg_mask, x86_sched_itmt_flags, PKG),	{ NULL },

Lines 1–500 of 1528.