Skip to content

Commit 7c0edad

Browse files
committed
x86/cpu/topology: Rework possible CPU management
Managing possible CPUs is an unreadable and uncomprehensible maze. Aside of that it's backwards because it applies command line limits after registering all APICs. Rewrite it so that it: - Applies the command line limits upfront so that only the allowed amount of APIC IDs can be registered. - Applies eventual late restrictions in an understandable way - Uses simple min_t() calculations which are trivial to follow. - Provides a separate function for resetting to UP mode late in the bringup process. Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Michael Kelley <[email protected]> Tested-by: Sohil Mehta <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 0e53e7b commit 7c0edad

File tree

6 files changed

+118
-89
lines changed

6 files changed

+118
-89
lines changed

arch/x86/include/asm/apic.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ extern void topology_register_apic(u32 apic_id, u32 acpi_id, bool present);
175175
extern void topology_register_boot_apic(u32 apic_id);
176176
extern int topology_hotplug_apic(u32 apic_id, u32 acpi_id);
177177
extern void topology_hotunplug_apic(unsigned int cpu);
178+
extern void topology_apply_cmdline_limits_early(void);
179+
extern void topology_init_possible_cpus(void);
180+
extern void topology_reset_possible_cpus_up(void);
178181

179182
#else /* !CONFIG_X86_LOCAL_APIC */
180183
static inline void lapic_shutdown(void) { }
@@ -190,6 +193,8 @@ static inline void apic_intr_mode_init(void) { }
190193
static inline void lapic_assign_system_vectors(void) { }
191194
static inline void lapic_assign_legacy_vector(unsigned int i, bool r) { }
192195
static inline bool apic_needs_pit(void) { return true; }
196+
static inline void topology_apply_cmdline_limits_early(void) { }
197+
static inline void topology_init_possible_cpus(void) { }
193198
#endif /* !CONFIG_X86_LOCAL_APIC */
194199

195200
#ifdef CONFIG_X86_X2APIC

arch/x86/include/asm/cpu.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,10 @@
99
#include <linux/percpu.h>
1010
#include <asm/ibt.h>
1111

12-
#ifdef CONFIG_SMP
13-
14-
extern void prefill_possible_map(void);
15-
16-
#else /* CONFIG_SMP */
17-
18-
static inline void prefill_possible_map(void) {}
19-
12+
#ifndef CONFIG_SMP
2013
#define cpu_physical_id(cpu) boot_cpu_physical_apicid
2114
#define cpu_acpi_id(cpu) 0
2215
#define safe_smp_processor_id() 0
23-
2416
#endif /* CONFIG_SMP */
2517

2618
#ifdef CONFIG_HOTPLUG_CPU

arch/x86/include/asm/topology.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ static inline bool topology_is_primary_thread(unsigned int cpu)
191191
{
192192
return cpumask_test_cpu(cpu, cpu_primary_thread_mask);
193193
}
194+
194195
#else /* CONFIG_SMP */
195196
#define topology_max_packages() (1)
196197
static inline int

arch/x86/kernel/cpu/topology.c

Lines changed: 106 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <xen/xen.h>
66

77
#include <asm/apic.h>
8+
#include <asm/io_apic.h>
89
#include <asm/mpspec.h>
910
#include <asm/smp.h>
1011

@@ -85,73 +86,6 @@ early_initcall(smp_init_primary_thread_mask);
8586
static inline void cpu_mark_primary_thread(unsigned int cpu, unsigned int apicid) { }
8687
#endif
8788

88-
static int __initdata setup_possible_cpus = -1;
89-
90-
/*
91-
* cpu_possible_mask should be static, it cannot change as cpu's
92-
* are onlined, or offlined. The reason is per-cpu data-structures
93-
* are allocated by some modules at init time, and don't expect to
94-
* do this dynamically on cpu arrival/departure.
95-
* cpu_present_mask on the other hand can change dynamically.
96-
* In case when cpu_hotplug is not compiled, then we resort to current
97-
* behaviour, which is cpu_possible == cpu_present.
98-
* - Ashok Raj
99-
*
100-
* Three ways to find out the number of additional hotplug CPUs:
101-
* - If the BIOS specified disabled CPUs in ACPI/mptables use that.
102-
* - The user can overwrite it with possible_cpus=NUM
103-
* - Otherwise don't reserve additional CPUs.
104-
* We do this because additional CPUs waste a lot of memory.
105-
* -AK
106-
*/
107-
__init void prefill_possible_map(void)
108-
{
109-
unsigned int num_processors = topo_info.nr_assigned_cpus;
110-
unsigned int disabled_cpus = topo_info.nr_disabled_cpus;
111-
int i, possible;
112-
113-
i = setup_max_cpus ?: 1;
114-
if (setup_possible_cpus == -1) {
115-
possible = topo_info.nr_assigned_cpus;
116-
#ifdef CONFIG_HOTPLUG_CPU
117-
if (setup_max_cpus)
118-
possible += num_processors;
119-
#else
120-
if (possible > i)
121-
possible = i;
122-
#endif
123-
} else
124-
possible = setup_possible_cpus;
125-
126-
total_cpus = max_t(int, possible, num_processors + disabled_cpus);
127-
128-
/* nr_cpu_ids could be reduced via nr_cpus= */
129-
if (possible > nr_cpu_ids) {
130-
pr_warn("%d Processors exceeds NR_CPUS limit of %u\n",
131-
possible, nr_cpu_ids);
132-
possible = nr_cpu_ids;
133-
}
134-
135-
#ifdef CONFIG_HOTPLUG_CPU
136-
if (!setup_max_cpus)
137-
#endif
138-
if (possible > i) {
139-
pr_warn("%d Processors exceeds max_cpus limit of %u\n",
140-
possible, setup_max_cpus);
141-
possible = i;
142-
}
143-
144-
set_nr_cpu_ids(possible);
145-
146-
pr_info("Allowing %d CPUs, %d hotplug CPUs\n",
147-
possible, max_t(int, possible - num_processors, 0));
148-
149-
reset_cpu_possible_mask();
150-
151-
for (i = 0; i < possible; i++)
152-
set_cpu_possible(i, true);
153-
}
154-
15589
static int topo_lookup_cpuid(u32 apic_id)
15690
{
15791
int i;
@@ -293,12 +227,114 @@ void topology_hotunplug_apic(unsigned int cpu)
293227
}
294228
#endif
295229

296-
static int __init _setup_possible_cpus(char *str)
230+
#ifdef CONFIG_SMP
231+
static unsigned int max_possible_cpus __initdata = NR_CPUS;
232+
233+
/**
234+
* topology_apply_cmdline_limits_early - Apply topology command line limits early
235+
*
236+
* Ensure that command line limits are in effect before firmware parsing
237+
* takes place.
238+
*/
239+
void __init topology_apply_cmdline_limits_early(void)
297240
{
298-
get_option(&str, &setup_possible_cpus);
241+
unsigned int possible = nr_cpu_ids;
242+
243+
/* 'maxcpus=0' 'nosmp' 'nolapic' 'disableapic' 'noapic' */
244+
if (!setup_max_cpus || ioapic_is_disabled || apic_is_disabled)
245+
possible = 1;
246+
247+
/* 'possible_cpus=N' */
248+
possible = min_t(unsigned int, max_possible_cpus, possible);
249+
250+
if (possible < nr_cpu_ids) {
251+
pr_info("Limiting to %u possible CPUs\n", possible);
252+
set_nr_cpu_ids(possible);
253+
}
254+
}
255+
256+
static __init bool restrict_to_up(void)
257+
{
258+
if (!smp_found_config || ioapic_is_disabled)
259+
return true;
260+
/*
261+
* XEN PV is special as it does not advertise the local APIC
262+
* properly, but provides a fake topology for it so that the
263+
* infrastructure works. So don't apply the restrictions vs. APIC
264+
* here.
265+
*/
266+
if (xen_pv_domain())
267+
return false;
268+
269+
return apic_is_disabled;
270+
}
271+
272+
void __init topology_init_possible_cpus(void)
273+
{
274+
unsigned int assigned = topo_info.nr_assigned_cpus;
275+
unsigned int disabled = topo_info.nr_disabled_cpus;
276+
unsigned int total = assigned + disabled;
277+
unsigned int cpu, allowed = 1;
278+
279+
if (!restrict_to_up()) {
280+
if (WARN_ON_ONCE(assigned > nr_cpu_ids)) {
281+
disabled += assigned - nr_cpu_ids;
282+
assigned = nr_cpu_ids;
283+
}
284+
allowed = min_t(unsigned int, total, nr_cpu_ids);
285+
}
286+
287+
if (total > allowed)
288+
pr_warn("%u possible CPUs exceed the limit of %u\n", total, allowed);
289+
290+
assigned = min_t(unsigned int, allowed, assigned);
291+
disabled = allowed - assigned;
292+
293+
topo_info.nr_assigned_cpus = assigned;
294+
topo_info.nr_disabled_cpus = disabled;
295+
296+
total_cpus = allowed;
297+
set_nr_cpu_ids(allowed);
298+
299+
pr_info("Allowing %u present CPUs plus %u hotplug CPUs\n", assigned, disabled);
300+
if (topo_info.nr_rejected_cpus)
301+
pr_info("Rejected CPUs %u\n", topo_info.nr_rejected_cpus);
302+
303+
init_cpu_present(cpumask_of(0));
304+
init_cpu_possible(cpumask_of(0));
305+
306+
for (cpu = 0; cpu < allowed; cpu++) {
307+
u32 apicid = cpuid_to_apicid[cpu];
308+
309+
set_cpu_possible(cpu, true);
310+
311+
if (apicid == BAD_APICID)
312+
continue;
313+
314+
set_cpu_present(cpu, test_bit(apicid, phys_cpu_present_map));
315+
}
316+
}
317+
318+
/*
319+
* Late SMP disable after sizing CPU masks when APIC/IOAPIC setup failed.
320+
*/
321+
void __init topology_reset_possible_cpus_up(void)
322+
{
323+
init_cpu_present(cpumask_of(0));
324+
init_cpu_possible(cpumask_of(0));
325+
326+
bitmap_zero(phys_cpu_present_map, MAX_LOCAL_APIC);
327+
if (topo_info.boot_cpu_apic_id != BAD_APICID)
328+
set_bit(topo_info.boot_cpu_apic_id, phys_cpu_present_map);
329+
}
330+
331+
static int __init setup_possible_cpus(char *str)
332+
{
333+
get_option(&str, &max_possible_cpus);
299334
return 0;
300335
}
301-
early_param("possible_cpus", _setup_possible_cpus);
336+
early_param("possible_cpus", setup_possible_cpus);
337+
#endif
302338

303339
static int __init apic_set_disabled_cpu_apicid(char *arg)
304340
{

arch/x86/kernel/setup.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,20 +1131,19 @@ void __init setup_arch(char **cmdline_p)
11311131

11321132
early_quirks();
11331133

1134+
topology_apply_cmdline_limits_early();
1135+
11341136
/*
11351137
* Parse SMP configuration. Try ACPI first and then the platform
11361138
* specific parser.
11371139
*/
11381140
acpi_boot_init();
11391141
x86_init.mpparse.parse_smp_cfg();
11401142

1141-
/*
1142-
* Systems w/o ACPI and mptables might not have it mapped the local
1143-
* APIC yet, but prefill_possible_map() might need to access it.
1144-
*/
1143+
/* Last opportunity to detect and map the local APIC */
11451144
init_apic_mappings();
11461145

1147-
prefill_possible_map();
1146+
topology_init_possible_cpus();
11481147

11491148
init_cpu_to_node();
11501149
init_gi_nodes();

arch/x86/kernel/smpboot.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,11 +1147,7 @@ static __init void disable_smp(void)
11471147
pr_info("SMP disabled\n");
11481148

11491149
disable_ioapic_support();
1150-
1151-
init_cpu_present(cpumask_of(0));
1152-
init_cpu_possible(cpumask_of(0));
1153-
1154-
reset_phys_cpu_present_map(smp_found_config ? boot_cpu_physical_apicid : 0);
1150+
topology_reset_possible_cpus_up();
11551151

11561152
cpumask_set_cpu(0, topology_sibling_cpumask(0));
11571153
cpumask_set_cpu(0, topology_core_cpumask(0));

0 commit comments

Comments
 (0)