Skip to content

Commit 5c5682b

Browse files
committed
x86/cpu: Detect real BSP on crash kernels
When a kdump kernel is started from a crashing CPU then there is no guarantee that this CPU is the real boot CPU (BSP). If the kdump kernel tries to online the BSP then the INIT sequence will reset the machine. There is a command line option to prevent this, but in case of nested kdump kernels this is wrong. But that command line option is not required at all because the real BSP is enumerated as the first CPU by firmware. Support for the only known system which was different (Voyager) got removed long ago. Detect whether the boot CPU APIC ID is the first APIC ID enumerated by the firmware. If the first APIC ID enumerated is not matching the boot CPU APIC ID then skip registering it. 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 7c0edad commit 5c5682b

File tree

3 files changed

+61
-52
lines changed

3 files changed

+61
-52
lines changed

Documentation/admin-guide/kdump/kdump.rst

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,7 @@ Dump-capture kernel config options (Arch Dependent, i386 and x86_64)
191191
CPU is enough for kdump kernel to dump vmcore on most of systems.
192192

193193
However, you can also specify nr_cpus=X to enable multiple processors
194-
in kdump kernel. In this case, "disable_cpu_apicid=" is needed to
195-
tell kdump kernel which cpu is 1st kernel's BSP. Please refer to
196-
admin-guide/kernel-parameters.txt for more details.
194+
in kdump kernel.
197195

198196
With CONFIG_SMP=n, the above things are not related.
199197

@@ -454,8 +452,7 @@ Notes on loading the dump-capture kernel:
454452
to use multi-thread programs with it, such as parallel dump feature of
455453
makedumpfile. Otherwise, the multi-thread program may have a great
456454
performance degradation. To enable multi-cpu support, you should bring up an
457-
SMP dump-capture kernel and specify maxcpus/nr_cpus, disable_cpu_apicid=[X]
458-
options while loading it.
455+
SMP dump-capture kernel and specify maxcpus/nr_cpus options while loading it.
459456

460457
* For s390x there are two kdump modes: If a ELF header is specified with
461458
the elfcorehdr= kernel parameter, it is used by the kdump kernel as it

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,15 +1100,6 @@
11001100
Disable TLBIE instruction. Currently does not work
11011101
with KVM, with HASH MMU, or with coherent accelerators.
11021102

1103-
disable_cpu_apicid= [X86,APIC,SMP]
1104-
Format: <int>
1105-
The number of initial APIC ID for the
1106-
corresponding CPU to be disabled at boot,
1107-
mostly used for the kdump 2nd kernel to
1108-
disable BSP to wake up multiple CPUs without
1109-
causing system reset or hang due to sending
1110-
INIT from AP to BSP.
1111-
11121103
disable_ddw [PPC/PSERIES]
11131104
Disable Dynamic DMA Window support. Use this
11141105
to workaround buggy firmware.

arch/x86/kernel/cpu/topology.c

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,13 @@ static struct {
3232
unsigned int nr_disabled_cpus;
3333
unsigned int nr_rejected_cpus;
3434
u32 boot_cpu_apic_id;
35+
u32 real_bsp_apic_id;
3536
} topo_info __read_mostly = {
3637
.nr_assigned_cpus = 1,
3738
.boot_cpu_apic_id = BAD_APICID,
39+
.real_bsp_apic_id = BAD_APICID,
3840
};
3941

40-
/*
41-
* Processor to be disabled specified by kernel parameter
42-
* disable_cpu_apicid=<int>, mostly used for the kdump 2nd kernel to
43-
* avoid undefined behaviour caused by sending INIT from AP to BSP.
44-
*/
45-
static u32 disabled_cpu_apicid __ro_after_init = BAD_APICID;
46-
4742
bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
4843
{
4944
return phys_id == (u64)cpuid_to_apicid[cpu];
@@ -123,6 +118,60 @@ static void topo_set_cpuids(unsigned int cpu, u32 apic_id, u32 acpi_id)
123118
cpu_mark_primary_thread(cpu, apic_id);
124119
}
125120

121+
static __init bool check_for_real_bsp(u32 apic_id)
122+
{
123+
/*
124+
* There is no real good way to detect whether this a kdump()
125+
* kernel, but except on the Voyager SMP monstrosity which is not
126+
* longer supported, the real BSP APIC ID is the first one which is
127+
* enumerated by firmware. That allows to detect whether the boot
128+
* CPU is the real BSP. If it is not, then do not register the APIC
129+
* because sending INIT to the real BSP would reset the whole
130+
* system.
131+
*
132+
* The first APIC ID which is enumerated by firmware is detectable
133+
* because the boot CPU APIC ID is registered before that without
134+
* invoking this code.
135+
*/
136+
if (topo_info.real_bsp_apic_id != BAD_APICID)
137+
return false;
138+
139+
if (apic_id == topo_info.boot_cpu_apic_id) {
140+
topo_info.real_bsp_apic_id = apic_id;
141+
return false;
142+
}
143+
144+
pr_warn("Boot CPU APIC ID not the first enumerated APIC ID: %x > %x\n",
145+
topo_info.boot_cpu_apic_id, apic_id);
146+
pr_warn("Crash kernel detected. Disabling real BSP to prevent machine INIT\n");
147+
148+
topo_info.real_bsp_apic_id = apic_id;
149+
return true;
150+
}
151+
152+
static __init void topo_register_apic(u32 apic_id, u32 acpi_id, bool present)
153+
{
154+
int cpu;
155+
156+
if (present) {
157+
set_bit(apic_id, phys_cpu_present_map);
158+
159+
/*
160+
* Double registration is valid in case of the boot CPU
161+
* APIC because that is registered before the enumeration
162+
* of the APICs via firmware parsers or VM guest
163+
* mechanisms.
164+
*/
165+
if (apic_id == topo_info.boot_cpu_apic_id)
166+
cpu = 0;
167+
else
168+
cpu = topo_get_cpunr(apic_id);
169+
topo_set_cpuids(cpu, apic_id, acpi_id);
170+
} else {
171+
topo_info.nr_disabled_cpus++;
172+
}
173+
}
174+
126175
/**
127176
* topology_register_apic - Register an APIC in early topology maps
128177
* @apic_id: The APIC ID to set up
@@ -131,16 +180,13 @@ static void topo_set_cpuids(unsigned int cpu, u32 apic_id, u32 acpi_id)
131180
*/
132181
void __init topology_register_apic(u32 apic_id, u32 acpi_id, bool present)
133182
{
134-
int cpu;
135-
136183
if (apic_id >= MAX_LOCAL_APIC) {
137184
pr_err_once("APIC ID %x exceeds kernel limit of: %x\n", apic_id, MAX_LOCAL_APIC - 1);
138185
topo_info.nr_rejected_cpus++;
139186
return;
140187
}
141188

142-
if (disabled_cpu_apicid == apic_id) {
143-
pr_info("Disabling CPU as requested via 'disable_cpu_apicid=0x%x'.\n", apic_id);
189+
if (check_for_real_bsp(apic_id)) {
144190
topo_info.nr_rejected_cpus++;
145191
return;
146192
}
@@ -152,23 +198,7 @@ void __init topology_register_apic(u32 apic_id, u32 acpi_id, bool present)
152198
return;
153199
}
154200

155-
if (present) {
156-
set_bit(apic_id, phys_cpu_present_map);
157-
158-
/*
159-
* Double registration is valid in case of the boot CPU
160-
* APIC because that is registered before the enumeration
161-
* of the APICs via firmware parsers or VM guest
162-
* mechanisms.
163-
*/
164-
if (apic_id == topo_info.boot_cpu_apic_id)
165-
cpu = 0;
166-
else
167-
cpu = topo_get_cpunr(apic_id);
168-
topo_set_cpuids(cpu, apic_id, acpi_id);
169-
} else {
170-
topo_info.nr_disabled_cpus++;
171-
}
201+
topo_register_apic(apic_id, acpi_id, present);
172202
}
173203

174204
/**
@@ -182,7 +212,7 @@ void __init topology_register_boot_apic(u32 apic_id)
182212
WARN_ON_ONCE(topo_info.boot_cpu_apic_id != BAD_APICID);
183213

184214
topo_info.boot_cpu_apic_id = apic_id;
185-
topology_register_apic(apic_id, CPU_ACPIID_INVALID, true);
215+
topo_register_apic(apic_id, CPU_ACPIID_INVALID, true);
186216
}
187217

188218
#ifdef CONFIG_ACPI_HOTPLUG_CPU
@@ -335,12 +365,3 @@ static int __init setup_possible_cpus(char *str)
335365
}
336366
early_param("possible_cpus", setup_possible_cpus);
337367
#endif
338-
339-
static int __init apic_set_disabled_cpu_apicid(char *arg)
340-
{
341-
if (!arg || !get_option(&arg, &disabled_cpu_apicid))
342-
return -EINVAL;
343-
344-
return 0;
345-
}
346-
early_param("disable_cpu_apicid", apic_set_disabled_cpu_apicid);

0 commit comments

Comments
 (0)