x86, apic, kexec: Add disable_cpu_apicid kernel parameter
authorHATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
Wed, 15 Jan 2014 06:44:58 +0000 (15:44 +0900)
committerH. Peter Anvin <hpa@zytor.com>
Wed, 15 Jan 2014 17:19:20 +0000 (09:19 -0800)
Add disable_cpu_apicid kernel parameter. To use this kernel parameter,
specify an initial APIC ID of the corresponding CPU you want to
disable.

This is mostly used for the kdump 2nd kernel to disable BSP to wake up
multiple CPUs without causing system reset or hang due to sending INIT
from AP to BSP.

Kdump users first figure out initial APIC ID of the BSP, CPU0 in the
1st kernel, for example from /proc/cpuinfo and then set up this kernel
parameter for the 2nd kernel using the obtained APIC ID.

However, doing this procedure at each boot time manually is awkward,
which should be automatically done by user-land service scripts, for
example, kexec-tools on fedora/RHEL distributions.

This design is more flexible than disabling BSP in kernel boot time
automatically in that in kernel boot time we have no choice but
referring to ACPI/MP table to obtain initial APIC ID for BSP, meaning
that the method is not applicable to the systems without such BIOS
tables.

One assumption behind this design is that users get initial APIC ID of
the BSP in still healthy state and so BSP is uniquely kept in
CPU0. Thus, through the kernel parameter, only one initial APIC ID can
be specified.

In a comparison with disabled_cpu_apicid, we use read_apic_id(), not
boot_cpu_physical_apicid, because on some platforms, the variable is
modified to the apicid reported as BSP through MP table and this
function is executed with the temporarily modified
boot_cpu_physical_apicid. As a result, disabled_cpu_apicid kernel
parameter doesn't work well for apicids of APs.

Fixing the wrong handling of boot_cpu_physical_apicid requires some
reviews and tests beyond some platforms and it could take some
time. The fix here is a kind of workaround to focus on the main topic
of this patch.

Signed-off-by: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
Link: http://lkml.kernel.org/r/20140115064458.1545.38775.stgit@localhost6.localdomain6
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Documentation/kernel-parameters.txt
arch/x86/kernel/apic/apic.c

index b9e9bd85429801efa537bc1ea85f773fc92099f8..6fdbf8c968e6ec1d0a3e309dc986afa52a9518be 100644 (file)
@@ -774,6 +774,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        disable=        [IPV6]
                        See Documentation/networking/ipv6.txt.
 
+       disable_cpu_apicid= [X86,APIC,SMP]
+                       Format: <int>
+                       The number of initial APIC ID for the
+                       corresponding CPU to be disabled at boot,
+                       mostly used for the kdump 2nd kernel to
+                       disable BSP to wake up multiple CPUs without
+                       causing system reset or hang due to sending
+                       INIT from AP to BSP.
+
        disable_ddw     [PPC/PSERIES]
                        Disable Dynamic DMA Window support. Use this if
                        to workaround buggy firmware.
index 4ec1dd64022a8b4be944c95e1663556cff09cade..e78ab8c8ac2e5c1fc9d14bfde288e054847b3e9c 100644 (file)
@@ -74,6 +74,13 @@ unsigned int max_physical_apicid;
  */
 physid_mask_t phys_cpu_present_map;
 
+/*
+ * Processor to be disabled specified by kernel parameter
+ * disable_cpu_apicid=<int>, mostly used for the kdump 2nd kernel to
+ * avoid undefined behaviour caused by sending INIT from AP to BSP.
+ */
+unsigned int disabled_cpu_apicid = BAD_APICID;
+
 /*
  * Map cpu index to physical APIC ID
  */
@@ -2113,6 +2120,39 @@ int generic_processor_info(int apicid, int version)
        bool boot_cpu_detected = physid_isset(boot_cpu_physical_apicid,
                                phys_cpu_present_map);
 
+       /*
+        * boot_cpu_physical_apicid is designed to have the apicid
+        * returned by read_apic_id(), i.e, the apicid of the
+        * currently booting-up processor. However, on some platforms,
+        * it is temporarilly modified by the apicid reported as BSP
+        * through MP table. Concretely:
+        *
+        * - arch/x86/kernel/mpparse.c: MP_processor_info()
+        * - arch/x86/mm/amdtopology.c: amd_numa_init()
+        * - arch/x86/platform/visws/visws_quirks.c: MP_processor_info()
+        *
+        * This function is executed with the modified
+        * boot_cpu_physical_apicid. So, disabled_cpu_apicid kernel
+        * parameter doesn't work to disable APs on kdump 2nd kernel.
+        *
+        * Since fixing handling of boot_cpu_physical_apicid requires
+        * another discussion and tests on each platform, we leave it
+        * for now and here we use read_apic_id() directly in this
+        * function, generic_processor_info().
+        */
+       if (disabled_cpu_apicid != BAD_APICID &&
+           disabled_cpu_apicid != read_apic_id() &&
+           disabled_cpu_apicid == apicid) {
+               int thiscpu = num_processors + disabled_cpus;
+
+               pr_warning("ACPI: Disabling requested cpu."
+                          " Processor %d/0x%x ignored.\n",
+                          thiscpu, apicid);
+
+               disabled_cpus++;
+               return -ENODEV;
+       }
+
        /*
         * If boot cpu has not been detected yet, then only allow upto
         * nr_cpu_ids - 1 processors and keep one slot free for boot cpu
@@ -2591,3 +2631,12 @@ static int __init lapic_insert_resource(void)
  * that is using request_resource
  */
 late_initcall(lapic_insert_resource);
+
+static int __init apic_set_disabled_cpu_apicid(char *arg)
+{
+       if (!arg || !get_option(&arg, &disabled_cpu_apicid))
+               return -EINVAL;
+
+       return 0;
+}
+early_param("disable_cpu_apicid", apic_set_disabled_cpu_apicid);