forked from mirrors/linux
		
	x86/topology: Create logical package id
For per package oriented services we must be able to rely on the number of CPU packages to be within bounds. Create a tracking facility, which - calculates the number of possible packages depending on nr_cpu_ids after boot - makes sure that the package id is within the number of possible packages. If the apic id is outside we map it to a logical package id if there is enough space available. Provide interfaces for drivers to query the mapping and do translations from physcial to logical ids. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Andi Kleen <andi.kleen@intel.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Harish Chegondi <harish.chegondi@intel.com> Cc: Jacob Pan <jacob.jun.pan@linux.intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Kan Liang <kan.liang@intel.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Luis R. Rodriguez <mcgrof@suse.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Toshi Kani <toshi.kani@hp.com> Cc: Vince Weaver <vincent.weaver@maine.edu> Cc: linux-kernel@vger.kernel.org Link: http://lkml.kernel.org/r/20160222221011.541071755@linutronix.de Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
		
							parent
							
								
									1f2569fac6
								
							
						
					
					
						commit
						1f12e32f4c
					
				
					 6 changed files with 142 additions and 0 deletions
				
			
		|  | @ -129,6 +129,8 @@ struct cpuinfo_x86 { | ||||||
| 	u16			booted_cores; | 	u16			booted_cores; | ||||||
| 	/* Physical processor id: */ | 	/* Physical processor id: */ | ||||||
| 	u16			phys_proc_id; | 	u16			phys_proc_id; | ||||||
|  | 	/* Logical processor id: */ | ||||||
|  | 	u16			logical_proc_id; | ||||||
| 	/* Core id: */ | 	/* Core id: */ | ||||||
| 	u16			cpu_core_id; | 	u16			cpu_core_id; | ||||||
| 	/* Compute unit id */ | 	/* Compute unit id */ | ||||||
|  |  | ||||||
|  | @ -119,12 +119,23 @@ static inline void setup_node_to_cpumask_map(void) { } | ||||||
| 
 | 
 | ||||||
| extern const struct cpumask *cpu_coregroup_mask(int cpu); | extern const struct cpumask *cpu_coregroup_mask(int cpu); | ||||||
| 
 | 
 | ||||||
|  | #define topology_logical_package_id(cpu)	(cpu_data(cpu).logical_proc_id) | ||||||
| #define topology_physical_package_id(cpu)	(cpu_data(cpu).phys_proc_id) | #define topology_physical_package_id(cpu)	(cpu_data(cpu).phys_proc_id) | ||||||
| #define topology_core_id(cpu)			(cpu_data(cpu).cpu_core_id) | #define topology_core_id(cpu)			(cpu_data(cpu).cpu_core_id) | ||||||
| 
 | 
 | ||||||
| #ifdef ENABLE_TOPO_DEFINES | #ifdef ENABLE_TOPO_DEFINES | ||||||
| #define topology_core_cpumask(cpu)		(per_cpu(cpu_core_map, cpu)) | #define topology_core_cpumask(cpu)		(per_cpu(cpu_core_map, cpu)) | ||||||
| #define topology_sibling_cpumask(cpu)		(per_cpu(cpu_sibling_map, cpu)) | #define topology_sibling_cpumask(cpu)		(per_cpu(cpu_sibling_map, cpu)) | ||||||
|  | 
 | ||||||
|  | extern unsigned int __max_logical_packages; | ||||||
|  | #define topology_max_packages()			(__max_logical_packages) | ||||||
|  | int topology_update_package_map(unsigned int apicid, unsigned int cpu); | ||||||
|  | extern int topology_phys_to_logical_pkg(unsigned int pkg); | ||||||
|  | #else | ||||||
|  | #define topology_max_packages()			(1) | ||||||
|  | static inline int | ||||||
|  | topology_update_package_map(unsigned int apicid, unsigned int cpu) { return 0; } | ||||||
|  | static inline int topology_phys_to_logical_pkg(unsigned int pkg) { return 0; } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| static inline void arch_fix_phys_package_id(int num, u32 slot) | static inline void arch_fix_phys_package_id(int num, u32 slot) | ||||||
|  |  | ||||||
|  | @ -2077,6 +2077,20 @@ int generic_processor_info(int apicid, int version) | ||||||
| 	} else | 	} else | ||||||
| 		cpu = cpumask_next_zero(-1, cpu_present_mask); | 		cpu = cpumask_next_zero(-1, cpu_present_mask); | ||||||
| 
 | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * This can happen on physical hotplug. The sanity check at boot time | ||||||
|  | 	 * is done from native_smp_prepare_cpus() after num_possible_cpus() is | ||||||
|  | 	 * established. | ||||||
|  | 	 */ | ||||||
|  | 	if (topology_update_package_map(apicid, cpu) < 0) { | ||||||
|  | 		int thiscpu = max + disabled_cpus; | ||||||
|  | 
 | ||||||
|  | 		pr_warning("ACPI: Package limit reached. Processor %d/0x%x ignored.\n", | ||||||
|  | 			   thiscpu, apicid); | ||||||
|  | 		disabled_cpus++; | ||||||
|  | 		return -ENOSPC; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Validate version | 	 * Validate version | ||||||
| 	 */ | 	 */ | ||||||
|  |  | ||||||
|  | @ -975,6 +975,8 @@ static void identify_cpu(struct cpuinfo_x86 *c) | ||||||
| #ifdef CONFIG_NUMA | #ifdef CONFIG_NUMA | ||||||
| 	numa_add_cpu(smp_processor_id()); | 	numa_add_cpu(smp_processor_id()); | ||||||
| #endif | #endif | ||||||
|  | 	/* The boot/hotplug time assigment got cleared, restore it */ | ||||||
|  | 	c->logical_proc_id = topology_phys_to_logical_pkg(c->phys_proc_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
|  | @ -160,6 +160,19 @@ static void early_init_intel(struct cpuinfo_x86 *c) | ||||||
| 		pr_info("Disabling PGE capability bit\n"); | 		pr_info("Disabling PGE capability bit\n"); | ||||||
| 		setup_clear_cpu_cap(X86_FEATURE_PGE); | 		setup_clear_cpu_cap(X86_FEATURE_PGE); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if (c->cpuid_level >= 0x00000001) { | ||||||
|  | 		u32 eax, ebx, ecx, edx; | ||||||
|  | 
 | ||||||
|  | 		cpuid(0x00000001, &eax, &ebx, &ecx, &edx); | ||||||
|  | 		/*
 | ||||||
|  | 		 * If HTT (EDX[28]) is set EBX[16:23] contain the number of | ||||||
|  | 		 * apicids which are reserved per package. Store the resulting | ||||||
|  | 		 * shift value for the package management code. | ||||||
|  | 		 */ | ||||||
|  | 		if (edx & (1U << 28)) | ||||||
|  | 			c->x86_coreid_bits = get_count_order((ebx >> 16) & 0xff); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_X86_32 | #ifdef CONFIG_X86_32 | ||||||
|  |  | ||||||
|  | @ -97,6 +97,14 @@ DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_llc_shared_map); | ||||||
| DEFINE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info); | DEFINE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info); | ||||||
| EXPORT_PER_CPU_SYMBOL(cpu_info); | EXPORT_PER_CPU_SYMBOL(cpu_info); | ||||||
| 
 | 
 | ||||||
|  | /* Logical package management. We might want to allocate that dynamically */ | ||||||
|  | static int *physical_to_logical_pkg __read_mostly; | ||||||
|  | static unsigned long *physical_package_map __read_mostly;; | ||||||
|  | static unsigned long *logical_package_map  __read_mostly; | ||||||
|  | static unsigned int max_physical_pkg_id __read_mostly; | ||||||
|  | unsigned int __max_logical_packages __read_mostly; | ||||||
|  | EXPORT_SYMBOL(__max_logical_packages); | ||||||
|  | 
 | ||||||
| static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip) | static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip) | ||||||
| { | { | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
|  | @ -251,6 +259,97 @@ static void notrace start_secondary(void *unused) | ||||||
| 	cpu_startup_entry(CPUHP_ONLINE); | 	cpu_startup_entry(CPUHP_ONLINE); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int topology_update_package_map(unsigned int apicid, unsigned int cpu) | ||||||
|  | { | ||||||
|  | 	unsigned int new, pkg = apicid >> boot_cpu_data.x86_coreid_bits; | ||||||
|  | 
 | ||||||
|  | 	/* Called from early boot ? */ | ||||||
|  | 	if (!physical_package_map) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	if (pkg >= max_physical_pkg_id) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	/* Set the logical package id */ | ||||||
|  | 	if (test_and_set_bit(pkg, physical_package_map)) | ||||||
|  | 		goto found; | ||||||
|  | 
 | ||||||
|  | 	if (pkg < __max_logical_packages) { | ||||||
|  | 		set_bit(pkg, logical_package_map); | ||||||
|  | 		physical_to_logical_pkg[pkg] = pkg; | ||||||
|  | 		goto found; | ||||||
|  | 	} | ||||||
|  | 	new = find_first_zero_bit(logical_package_map, __max_logical_packages); | ||||||
|  | 	if (new >= __max_logical_packages) { | ||||||
|  | 		physical_to_logical_pkg[pkg] = -1; | ||||||
|  | 		pr_warn("APIC(%x) Package %u exceeds logical package map\n", | ||||||
|  | 			apicid, pkg); | ||||||
|  | 		return -ENOSPC; | ||||||
|  | 	} | ||||||
|  | 	set_bit(new, logical_package_map); | ||||||
|  | 	pr_info("APIC(%x) Converting physical %u to logical package %u\n", | ||||||
|  | 		apicid, pkg, new); | ||||||
|  | 	physical_to_logical_pkg[pkg] = new; | ||||||
|  | 
 | ||||||
|  | found: | ||||||
|  | 	cpu_data(cpu).logical_proc_id = physical_to_logical_pkg[pkg]; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * topology_phys_to_logical_pkg - Map a physical package id to a logical | ||||||
|  |  * | ||||||
|  |  * Returns logical package id or -1 if not found | ||||||
|  |  */ | ||||||
|  | int topology_phys_to_logical_pkg(unsigned int phys_pkg) | ||||||
|  | { | ||||||
|  | 	if (phys_pkg >= max_physical_pkg_id) | ||||||
|  | 		return -1; | ||||||
|  | 	return physical_to_logical_pkg[phys_pkg]; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(topology_phys_to_logical_pkg); | ||||||
|  | 
 | ||||||
|  | static void __init smp_init_package_map(void) | ||||||
|  | { | ||||||
|  | 	unsigned int ncpus, cpu; | ||||||
|  | 	size_t size; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Today neither Intel nor AMD support heterogenous systems. That | ||||||
|  | 	 * might change in the future.... | ||||||
|  | 	 */ | ||||||
|  | 	ncpus = boot_cpu_data.x86_max_cores * smp_num_siblings; | ||||||
|  | 	__max_logical_packages = DIV_ROUND_UP(nr_cpu_ids, ncpus); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Possibly larger than what we need as the number of apic ids per | ||||||
|  | 	 * package can be smaller than the actual used apic ids. | ||||||
|  | 	 */ | ||||||
|  | 	max_physical_pkg_id = DIV_ROUND_UP(MAX_LOCAL_APIC, ncpus); | ||||||
|  | 	size = max_physical_pkg_id * sizeof(unsigned int); | ||||||
|  | 	physical_to_logical_pkg = kmalloc(size, GFP_KERNEL); | ||||||
|  | 	memset(physical_to_logical_pkg, 0xff, size); | ||||||
|  | 	size = BITS_TO_LONGS(max_physical_pkg_id) * sizeof(unsigned long); | ||||||
|  | 	physical_package_map = kzalloc(size, GFP_KERNEL); | ||||||
|  | 	size = BITS_TO_LONGS(__max_logical_packages) * sizeof(unsigned long); | ||||||
|  | 	logical_package_map = kzalloc(size, GFP_KERNEL); | ||||||
|  | 
 | ||||||
|  | 	pr_info("Max logical packages: %u\n", __max_logical_packages); | ||||||
|  | 
 | ||||||
|  | 	for_each_present_cpu(cpu) { | ||||||
|  | 		unsigned int apicid = apic->cpu_present_to_apicid(cpu); | ||||||
|  | 
 | ||||||
|  | 		if (apicid == BAD_APICID || !apic->apic_id_valid(apicid)) | ||||||
|  | 			continue; | ||||||
|  | 		if (!topology_update_package_map(apicid, cpu)) | ||||||
|  | 			continue; | ||||||
|  | 		pr_warn("CPU %u APICId %x disabled\n", cpu, apicid); | ||||||
|  | 		per_cpu(x86_bios_cpu_apicid, cpu) = BAD_APICID; | ||||||
|  | 		set_cpu_possible(cpu, false); | ||||||
|  | 		set_cpu_present(cpu, false); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void __init smp_store_boot_cpu_info(void) | void __init smp_store_boot_cpu_info(void) | ||||||
| { | { | ||||||
| 	int id = 0; /* CPU 0 */ | 	int id = 0; /* CPU 0 */ | ||||||
|  | @ -258,6 +357,7 @@ void __init smp_store_boot_cpu_info(void) | ||||||
| 
 | 
 | ||||||
| 	*c = boot_cpu_data; | 	*c = boot_cpu_data; | ||||||
| 	c->cpu_index = id; | 	c->cpu_index = id; | ||||||
|  | 	smp_init_package_map(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Thomas Gleixner
						Thomas Gleixner