Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] x86_64: New heuristics to find out hotpluggable CPUs.

With a NR_CPUS==128 kernel with CPU hotplug enabled we would waste 4MB
on per CPU data of all possible CPUs. The reason was that HOTPLUG
always set up possible map to NR_CPUS cpus and then we need to allocate
that much (each per CPU data is roughly ~32k now)

The underlying problem is that ACPI didn't tell us how many hotplug CPUs
the platform supports. So the old code just assumed all, which would
lead to this memory wastage.

This implements some new heuristics:

- If the BIOS specified disabled CPUs in the ACPI/mptables assume they
can be enabled later (this is bending the ACPI specification a bit,
but seems like a obvious extension)
- The user can overwrite it with a new additionals_cpus=NUM option
- Otherwise use half of the available CPUs or 2, whatever is more.

Cc: ashok.raj@intel.com
Cc: len.brown@intel.com

Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Andi Kleen and committed by
Linus Torvalds
420f8f68 485832a5

+47 -5
+3
Documentation/x86_64/boot-options.txt
··· 122 122 123 123 cpumask=MASK only use cpus with bits set in mask 124 124 125 + additional_cpus=NUM Allow NUM more CPUs for hotplug 126 + (defaults are specified by the BIOS or half the available CPUs) 127 + 125 128 NUMA 126 129 127 130 numa=off Only set up a single NUMA node spanning all memory.
+6 -2
arch/x86_64/kernel/mpparse.c
··· 65 65 /* Processor that is doing the boot up */ 66 66 unsigned int boot_cpu_id = -1U; 67 67 /* Internal processor count */ 68 - static unsigned int num_processors = 0; 68 + unsigned int num_processors __initdata = 0; 69 + 70 + unsigned disabled_cpus __initdata; 69 71 70 72 /* Bitmask of physically existing CPUs */ 71 73 physid_mask_t phys_cpu_present_map = PHYSID_MASK_NONE; ··· 111 109 int ver, cpu; 112 110 static int found_bsp=0; 113 111 114 - if (!(m->mpc_cpuflag & CPU_ENABLED)) 112 + if (!(m->mpc_cpuflag & CPU_ENABLED)) { 113 + disabled_cpus++; 115 114 return; 115 + } 116 116 117 117 printk(KERN_INFO "Processor #%d %d:%d APIC version %d\n", 118 118 m->mpc_apicid,
+36 -3
arch/x86_64/kernel/smpboot.c
··· 880 880 } 881 881 882 882 #ifdef CONFIG_HOTPLUG_CPU 883 + 884 + int additional_cpus __initdata = -1; 885 + 883 886 /* 884 887 * cpu_possible_map should be static, it cannot change as cpu's 885 888 * are onlined, or offlined. The reason is per-cpu data-structures ··· 891 888 * cpu_present_map on the other hand can change dynamically. 892 889 * In case when cpu_hotplug is not compiled, then we resort to current 893 890 * behaviour, which is cpu_possible == cpu_present. 894 - * If cpu-hotplug is supported, then we need to preallocate for all 895 - * those NR_CPUS, hence cpu_possible_map represents entire NR_CPUS range. 896 891 * - Ashok Raj 892 + * 893 + * Three ways to find out the number of additional hotplug CPUs: 894 + * - If the BIOS specified disabled CPUs in ACPI/mptables use that. 895 + * - otherwise use half of the available CPUs or 2, whatever is more. 896 + * - The user can overwrite it with additional_cpus=NUM 897 + * We do this because additional CPUs waste a lot of memory. 898 + * -AK 897 899 */ 898 900 __init void prefill_possible_map(void) 899 901 { 900 902 int i; 901 - for (i = 0; i < NR_CPUS; i++) 903 + int possible; 904 + 905 + if (additional_cpus == -1) { 906 + if (disabled_cpus > 0) { 907 + additional_cpus = disabled_cpus; 908 + } else { 909 + additional_cpus = num_processors / 2; 910 + if (additional_cpus == 0) 911 + additional_cpus = 2; 912 + } 913 + } 914 + possible = num_processors + additional_cpus; 915 + if (possible > NR_CPUS) 916 + possible = NR_CPUS; 917 + 918 + printk(KERN_INFO "SMP: Allowing %d CPUs, %d hotplug CPUs\n", 919 + possible, 920 + max_t(int, possible - num_processors, 0)); 921 + 922 + for (i = 0; i < possible; i++) 902 923 cpu_set(i, cpu_possible_map); 903 924 } 904 925 #endif ··· 1177 1150 } 1178 1151 printk(KERN_ERR "CPU %u didn't die...\n", cpu); 1179 1152 } 1153 + 1154 + static __init int setup_additional_cpus(char *s) 1155 + { 1156 + return get_option(&s, &additional_cpus); 1157 + } 1158 + __setup("additional_cpus=", setup_additional_cpus); 1180 1159 1181 1160 #else /* ... !CONFIG_HOTPLUG_CPU */ 1182 1161
+2
include/asm-x86_64/smp.h
··· 81 81 extern int __cpu_disable(void); 82 82 extern void __cpu_die(unsigned int cpu); 83 83 extern void prefill_possible_map(void); 84 + extern unsigned num_processors; 85 + extern unsigned disabled_cpus; 84 86 85 87 #endif /* !ASSEMBLY */ 86 88