Skip to contentSkip to conversation

x86/apic/flat64: Add back the early_param("apic", parse_apic)

PATCH Superseded Dexuan Cui 1 participant 2 messages May 31, 2020 – Jun 15, 2020

Subsystems (4)
lore
Share & tools
lore

Dexuan Cui

author maintainer
#0 lore raw

PATCH v3 1/1 x86/apic/flat64: Add back the early_param("apic", parse_apic)

parse_apic() allows the user to try a different APIC driver than the default one that's automatically chosen. It works for X86-32, but doesn't work for X86-64 because it was removed in 2009 for X86-64 by commit 7b38725318f4 ("x86: remove subarchitecture support code"), whose changelog doesn't explicitly describe the removal for X86-64.

The patch adds back the functionality for X86-64. The intent is mainly to work around an APIC emulation bug in Hyper-V in the case of kdump:
currently Hyper-V does not honor the disabled state of the local APICs, so all the IOAPIC-based interrupts may not be delivered to the correct virtual CPU, if the logical-mode APIC driver is used (the kdump kernel usually uses the logical-mode APIC driver, since typically only 1 CPU is active). Luckily the kdump issue can be worked around by forcing the kdump kernel to use physical mode, before the fix to Hyper-V becomes widely available.

The current algorithm of choosing an APIC driver is:

1. The global pointer "struct apic *apic" has a default value, i.e "apic_default" on X86-32, and "apic_flat" on X86-64.

2. If the early_param "apic=" is specified, parse_apic() is called and the pointer "apic" is changed if a matching APIC driver is found.

3. default_acpi_madt_oem_check() calls the acpi_madt_oem_check() method of all APIC drivers, which may override the "apic" pointer.

4. default_setup_apic_routing() may override the "apic" pointer, e.g. by calling the probe() method of all APIC drivers. Note: refer to the order of the APIC drivers specified in arch/x86/kernel/apic/Makefile.

The patch is safe because if the apic= early param is not specified, the current algorithm of choosing an APIC driver is unchanged; when the param is specified (e.g. on X86-64, "apic=physical flat"), the kernel still tries to find a "more suitable" APIC driver in the above step 3 and 4: e.g. if the BIOS/firmware requires that apic_x2apic_phys should be used, the above step 4 will override the APIC driver to apic_x2apic_phys, even if an early_param "apic=physical flat" is specified.

On Hyper-V, when a Linux VM has <= 8 virtual CPUs, if we use "apic=physical flat", sending IPIs to multiple vCPUs is still fast because Linux VM uses the para-virtualized IPI hypercalls: see hv_apic_init().

The patch adds the __init tag for flat_acpi_madt_oem_check() and physflat_acpi_madt_oem_check() to avoid a warning seen with "make W=1":
flat_acpi_madt_oem_check() accesses cmdline_apic, which has a __initdata tag.

Fixes 7b38725318f4 ("x86: remove subarchitecture support code")

---

Changes in v2:
Updated Documentation/admin-guide/kernel-parameters.txt. [Randy Dunlap] Changed apic_set_verbosity().
Enhanced the changelog.

Changes in v3:
Added the __init tag for flat_acpi_madt_oem_check() and
physflat_acpi_madt_oem_check() to avoid a warning seen with "make W=1".
(Thanks to kbuild test robot <[email protected]>).

Updated the changelog for the __init tag.

 .../admin-guide/kernel-parameters.txt         | 11 +++++--
 arch/x86/kernel/apic/apic.c                   | 11 +++----
 arch/x86/kernel/apic/apic_flat_64.c           | 31 +++++++++++++++++--
 3 files changed, 40 insertions(+), 13 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +8 -3
index 7bc83f3d9bdf..c4503fff9348 100644
@@ -341,10 +341,15 @@
 			Format: { quiet (default) | verbose | debug }
 			Change the amount of debugging information output
 			when initialising the APIC and IO-APIC components.
-			For X86-32, this can also be used to specify an APIC
-			driver name.
+			This can also be used to specify an APIC driver name.
 			Format: apic=driver_name
-			Examples: apic=bigsmp
+			Examples:
+			  On X86-32:  apic=bigsmp
+			  On X86-64: "apic=physical flat"
+			  Note: the available driver names depend on the
+			  architecture and the kernel config; the setting may
+			  be overridden by the acpi_madt_oem_check() and probe()
+			  methods of other APIC drivers.
 
 	apic_extnmi=	[APIC,X86] External NMI delivery setting
 			Format: { bsp (default) | all | none }
arch/x86/kernel/apic/apic.c +4 -7
index e53dda210cd7..6f7d75b6358b 100644
@@ -2855,13 +2855,10 @@ static int __init apic_set_verbosity(char *arg)
 		apic_verbosity = APIC_DEBUG;
 	else if (strcmp("verbose", arg) == 0)
 		apic_verbosity = APIC_VERBOSE;
-#ifdef CONFIG_X86_64
-	else {
-		pr_warn("APIC Verbosity level %s not recognised"
-			" use apic=verbose or apic=debug\n", arg);
-		return -EINVAL;
-	}
-#endif
+
+	/* Ignore unrecognized verbosity level setting. */
+
+	pr_info("APIC Verbosity level is %d\n", apic_verbosity);
 
 	return 0;
 }
arch/x86/kernel/apic/apic_flat_64.c +28 -3
index 7862b152a052..da8f3640453f 100644
@@ -23,9 +23,34 @@ static struct apic apic_flat;
 struct apic *apic __ro_after_init = &apic_flat;
 EXPORT_SYMBOL_GPL(apic);
 
-static int flat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+static int cmdline_apic __initdata;
+static int __init parse_apic(char *arg)
 {
-	return 1;
+	struct apic **drv;
+
+	if (!arg)
+		return -EINVAL;
+
+	for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
+		if (!strcmp((*drv)->name, arg)) {
+			apic = *drv;
+			cmdline_apic = 1;
+			return 0;
+		}
+	}
+
+	/* Parsed again by __setup for debug/verbose */
+	return 0;
+}
+early_param("apic", parse_apic);
+
+
+static int __init flat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+	if (!cmdline_apic)
+		return 1;
+
+	return apic == &apic_flat;
 }
 
 /*
@@ -157,7 +182,7 @@ static struct apic apic_flat __ro_after_init = {
  * We cannot use logical delivery in this case because the mask
  * overflows, so use physical mode.
  */
-static int physflat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+static int __init physflat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
 {
 #ifdef CONFIG_ACPI
 	/*
Signature · 1 line
2.19.1
author maintainer
#1 lore raw
Quoted 179 lines

From: Dexuan Cui <[email protected]>
Sent: Sunday, May 31, 2020 9:49 AM
To: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Dexuan Cui <[email protected]>; [email protected]; Michael Kelley <[email protected]>; Long Li <[email protected]>
Cc: [email protected]; [email protected]
Subject: [PATCH v3] x86/apic/flat64: Add back the early_param("apic", parse_apic)

parse_apic() allows the user to try a different APIC driver than the default one that's automatically chosen. It works for X86-32, but doesn't work for X86-64 because it was removed in 2009 for X86-64 by commit 7b38725318f4 ("x86: remove subarchitecture support code"), whose changelog doesn't explicitly describe the removal for X86-64.

The patch adds back the functionality for X86-64. The intent is mainly to work around an APIC emulation bug in Hyper-V in the case of kdump:
currently Hyper-V does not honor the disabled state of the local APICs, so all the IOAPIC-based interrupts may not be delivered to the correct virtual CPU, if the logical-mode APIC driver is used (the kdump kernel usually uses the logical-mode APIC driver, since typically only 1 CPU is active). Luckily the kdump issue can be worked around by forcing the kdump kernel to use physical mode, before the fix to Hyper-V becomes widely available.

The current algorithm of choosing an APIC driver is:

1. The global pointer "struct apic *apic" has a default value, i.e "apic_default" on X86-32, and "apic_flat" on X86-64.

2. If the early_param "apic=" is specified, parse_apic() is called and the pointer "apic" is changed if a matching APIC driver is found.

3. default_acpi_madt_oem_check() calls the acpi_madt_oem_check() method of all APIC drivers, which may override the "apic" pointer.

4. default_setup_apic_routing() may override the "apic" pointer, e.g. by calling the probe() method of all APIC drivers. Note: refer to the order of the APIC drivers specified in arch/x86/kernel/apic/Makefile.

The patch is safe because if the apic= early param is not specified, the current algorithm of choosing an APIC driver is unchanged; when the param is specified (e.g. on X86-64, "apic=physical flat"), the kernel still tries to find a "more suitable" APIC driver in the above step 3 and 4: e.g. if the BIOS/firmware requires that apic_x2apic_phys should be used, the above step 4 will override the APIC driver to apic_x2apic_phys, even if an early_param "apic=physical flat" is specified.

On Hyper-V, when a Linux VM has <= 8 virtual CPUs, if we use "apic=physical flat", sending IPIs to multiple vCPUs is still fast because Linux VM uses the para-virtualized IPI hypercalls: see hv_apic_init().

The patch adds the __init tag for flat_acpi_madt_oem_check() and physflat_acpi_madt_oem_check() to avoid a warning seen with "make W=1":
flat_acpi_madt_oem_check() accesses cmdline_apic, which has a __initdata tag.

Fixes 7b38725318f4 ("x86: remove subarchitecture support code")

---

Changes in v2:
Updated Documentation/admin-guide/kernel-parameters.txt. [Randy
Dunlap]
Changed apic_set_verbosity().
Enhanced the changelog.

Changes in v3:
Added the __init tag for flat_acpi_madt_oem_check() and
physflat_acpi_madt_oem_check() to avoid a warning seen with "make W=1".
(Thanks to kbuild test robot <[email protected]>).

Updated the changelog for the __init tag.

 .../admin-guide/kernel-parameters.txt         | 11 +++++--
 arch/x86/kernel/apic/apic.c                   | 11 +++----
 arch/x86/kernel/apic/apic_flat_64.c           | 31 +++++++++++++++++--
 3 files changed, 40 insertions(+), 13 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +8 -3
index 7bc83f3d9bdf..c4503fff9348 100644
@@ -341,10 +341,15 @@
 			Format: { quiet (default) | verbose | debug }
 			Change the amount of debugging information output
 			when initialising the APIC and IO-APIC components.
-			For X86-32, this can also be used to specify an APIC
-			driver name.
+			This can also be used to specify an APIC driver name.
 			Format: apic=driver_name
-			Examples: apic=bigsmp
+			Examples:
+			  On X86-32:  apic=bigsmp
+			  On X86-64: "apic=physical flat"
+			  Note: the available driver names depend on the
+			  architecture and the kernel config; the setting may
+			  be overridden by the acpi_madt_oem_check() and probe()
+			  methods of other APIC drivers.
 
 	apic_extnmi=	[APIC,X86] External NMI delivery setting
 			Format: { bsp (default) | all | none }
arch/x86/kernel/apic/apic.c +4 -7
index e53dda210cd7..6f7d75b6358b 100644
@@ -2855,13 +2855,10 @@ static int __init apic_set_verbosity(char *arg)
 		apic_verbosity = APIC_DEBUG;
 	else if (strcmp("verbose", arg) == 0)
 		apic_verbosity = APIC_VERBOSE;
-#ifdef CONFIG_X86_64
-	else {
-		pr_warn("APIC Verbosity level %s not recognised"
-			" use apic=verbose or apic=debug\n", arg);
-		return -EINVAL;
-	}
-#endif
+
+	/* Ignore unrecognized verbosity level setting. */
+
+	pr_info("APIC Verbosity level is %d\n", apic_verbosity);
 
 	return 0;
 }
arch/x86/kernel/apic/apic_flat_64.c +27 -2
index 7862b152a052..da8f3640453f 100644
@@ -23,9 +23,34 @@ static struct apic apic_flat;
 struct apic *apic __ro_after_init = &apic_flat;
 EXPORT_SYMBOL_GPL(apic);
 
-static int flat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+static int cmdline_apic __initdata;
+static int __init parse_apic(char *arg)
 {
-	return 1;
+	struct apic **drv;
+
+	if (!arg)
+		return -EINVAL;
+
+	for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
+		if (!strcmp((*drv)->name, arg)) {
+			apic = *drv;
+			cmdline_apic = 1;
+			return 0;
+		}
+	}
+
+	/* Parsed again by __setup for debug/verbose */
+	return 0;
+}
+early_param("apic", parse_apic);
+
+
+static int __init flat_acpi_madt_oem_check(char *oem_id, char
 *oem_table_id)
+{
+	if (!cmdline_apic)
+		return 1;
+
+	return apic == &apic_flat;
 }
 

/*

arch/x86/kernel/apic/apic_flat_64.c +1 -1
@@ -157,7 +182,7 @@ static struct apic apic_flat __ro_after_init = {
  * We cannot use logical delivery in this case because the mask
  * overflows, so use physical mode.
  */
-static int physflat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+static int __init physflat_acpi_madt_oem_check(char *oem_id, char
 *oem_table_id)
 {
 #ifdef CONFIG_ACPI
Quoted 2 lines

/*
--

Hi tglx and all,
Since v5.8-rc1 has been out, I guess you may have some cycles to take a look at this patch?

Thanks,
-- Dexuan