Here is the first version of prototype for cpu-hotplug on ARM64.
The first part is patch 1~3 for MADT parse. MADT is being parsed and the cpu possible map and cpu present map are prefilled.
I kept the terminology consistent with x86 as Al and Andrea's advice.
CPU possible map is equal to the GIC entries in MADT no matter enabled or disabled, and cpu present map limits to the enabled cpu in MADT. This is the base for cpu hot-plug.
The second part is patch 4~10 for ACPI cpu hot-plug drivers. ACPI driver for CPU hot-plug is implemented in this part, when the cpu logical online/offline is ready, the cpu hot-plug can work on arm64.
Please give some comments, thanks.
Hanjun Guo (10): ARM64 / ACPI: MADT for armv8 foundation model ARM64 / ACPI: Core functions for MADT parse ARM64 / ACPI: Prefill cpu possible and present maps and map logical cpu id to APIC id ARM64 / ACPI: Declare processors in ACPI namespace in the DSDT ARM64 / ACPI: Introduce map_gic_id() to get apic id from MADT or _MAT method ARM64 / ACPI: Get logic cpu id to see if the cpu is a new added cpu ARM64 / ACPI: Implement acpi_map/unmap_lsapic for cpu hot-plug ARM64 / ACPI: Fix the variable declared but not defined ARM64 / ACPI: Introduce arch_register/unregister_cpu() for arm64 ARM64 / CPU hot-plug: Skeleton logic cpu online/offline for cpu hot-plug
arch/arm64/Kconfig | 7 + arch/arm64/boot/asl/foundation-v8.acpi/apic.asl | 165 +++++++------ arch/arm64/boot/asl/foundation-v8.acpi/dsdt.asl | 205 +++++++++++++++- arch/arm64/include/asm/acpi.h | 12 + arch/arm64/include/asm/cpu.h | 5 + arch/arm64/include/asm/smp.h | 3 + arch/arm64/kernel/setup.c | 16 +- arch/arm64/kernel/smp.c | 23 ++ arch/arm64/kernel/topology.c | 26 +++ drivers/acpi/plat/arm/boot.c | 286 +++++++++++++++++++++-- drivers/acpi/processor_core.c | 50 ++-- drivers/acpi/tables.c | 21 ++ 12 files changed, 694 insertions(+), 125 deletions(-)
Implement the MADT for armv8 foundation model, There are 8 GIC struct entries in MADT, which stands for 8 cpus in the system. since 4 cpus was supported on armv8 foundation model, so I disabled 4 cpus to test the cpu topology and cpu hot-plug.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/boot/asl/foundation-v8.acpi/apic.asl | 165 +++++++++++------------ 1 file changed, 79 insertions(+), 86 deletions(-)
diff --git a/arch/arm64/boot/asl/foundation-v8.acpi/apic.asl b/arch/arm64/boot/asl/foundation-v8.acpi/apic.asl index b301712..ba63ce6 100644 --- a/arch/arm64/boot/asl/foundation-v8.acpi/apic.asl +++ b/arch/arm64/boot/asl/foundation-v8.acpi/apic.asl @@ -9,7 +9,7 @@
[0004] Signature : "APIC" [0004] Table Length : 000000F6 -[0001] Revision : 01 +[0001] Revision : 03 [0001] Checksum : B0 [0006] Oem ID : "LINARO" [0008] Oem Table ID : "FOUNDATI" @@ -17,117 +17,110 @@ [0004] Asl Compiler ID : "INTL" [0004] Asl Compiler Revision : 20110623
-[0004] Local Apic Address : 00000000 +[0004] Local Apic Address : 2C002000 [0004] Flags (decoded below) : 00000001 - PC-AT Compatibility : 1 + PC-AT Compatibility : 0
-[0001] Subtable Type : 00 [Processor Local APIC] -[0001] Length : 08 -[0001] Processor ID : 00 -[0001] Local Apic ID : 00 +[0001] Subtable Type : 0B [Generic Interrupt Controller] +[0001] Length : 28 +[0002] Reserved : 0000 +[0004] Local GIC Hardware ID : 00000000 /* Should be equal to FDT provided or CPU hardware ID */ +[0004] Processor UID : 00000000 [0004] Flags (decoded below) : 00000001 Processor Enabled : 1 +[0004] Parking Protocol Version : 00000000 +[0004] Performance Interrupt : 00000000 +[0008] Parked Address : 0000000000000000 +[0008] Base Address : 000000002C002000 /* this is the foundation model's GIC address */
-[0001] Subtable Type : 01 [I/O APIC] -[0001] Length : 0C -[0001] I/O Apic ID : 01 -[0001] Reserved : 00 -[0004] Address : 00000000 -[0004] Interrupt : 00000000 - -[0001] Subtable Type : 02 [Interrupt Source Override] -[0001] Length : 0A -[0001] Bus : 00 -[0001] Source : 00 -[0004] Interrupt : 00000000 -[0002] Flags (decoded below) : 0000 - Polarity : 0 - Trigger Mode : 0 - -[0001] Subtable Type : 03 [NMI Source] -[0001] Length : 08 -[0002] Flags (decoded below) : 000D - Polarity : 1 - Trigger Mode : 3 -[0004] Interrupt : 00000001 - -[0001] Subtable Type : 04 [Local APIC NMI] -[0001] Length : 06 -[0001] Processor ID : 00 -[0002] Flags (decoded below) : 0005 - Polarity : 1 - Trigger Mode : 1 -[0001] Interrupt Input LINT : 01 - -[0001] Subtable Type : 05 [Local APIC Address Override] -[0001] Length : 0C +[0001] Subtable Type : 0B [Generic Interrupt Controller] +[0001] Length : 28 [0002] Reserved : 0000 -[0008] APIC Address : 0000000000000000 - -[0001] Subtable Type : 06 [I/O SAPIC] -[0001] Length : 10 -[0001] I/O Sapic ID : 00 -[0001] Reserved : 00 -[0004] Interrupt Base : 00000000 -[0008] Address : 0000000000000000 - -[0001] Subtable Type : 07 [Local SAPIC] -[0001] Length : 16 -[0001] Processor ID : 00 -[0001] Local Sapic ID : 00 -[0001] Local Sapic EID : 00 -[0003] Reserved : 000000 +[0004] Local GIC Hardware ID : 00000001 +[0004] Processor UID : 00000001 [0004] Flags (decoded below) : 00000001 Processor Enabled : 1 -[0004] Processor UID : 00000000 -[0006] Processor UID String : "\CPU0" +[0004] Parking Protocol Version : 00000000 +[0004] Performance Interrupt : 00000000 +[0008] Parked Address : 0000000000000000 +[0008] Base Address : 000000002C002000
-[0001] Subtable Type : 08 [Platform Interrupt Sources] -[0001] Length : 10 -[0002] Flags (decoded below) : 0005 - Polarity : 1 - Trigger Mode : 1 -[0001] InterruptType : 00 -[0001] Processor ID : 00 -[0001] Processor EID : 00 -[0001] I/O Sapic Vector : 00 -[0004] Interrupt : 00000001 +[0001] Subtable Type : 0B [Generic Interrupt Controller] +[0001] Length : 28 +[0002] Reserved : 0000 +[0004] Local GIC Hardware ID : 00000002 +[0004] Processor UID : 00000002 [0004] Flags (decoded below) : 00000001 - CPEI Override : 1 + Processor Enabled : 1 +[0004] Parking Protocol Version : 00000000 +[0004] Performance Interrupt : 00000000 +[0008] Parked Address : 0000000000000000 +[0008] Base Address : 000000002C002000
-[0001] Subtable Type : 09 [Processor Local x2APIC] -[0001] Length : 10 +[0001] Subtable Type : 0B [Generic Interrupt Controller] +[0001] Length : 28 [0002] Reserved : 0000 -[0004] Processor x2Apic ID : 00000000 +[0004] Local GIC Hardware ID : 00000003 +[0004] Processor UID : 00000003 [0004] Flags (decoded below) : 00000001 Processor Enabled : 1 -[0004] Processor UID : 00000000 +[0004] Parking Protocol Version : 00000000 +[0004] Performance Interrupt : 00000000 +[0008] Parked Address : 0000000000000000 +[0008] Base Address : 000000002C002000
-[0001] Subtable Type : 0A [Local x2APIC NMI] -[0001] Length : 0C -[0002] Flags (decoded below) : 0005 - Polarity : 1 - Trigger Mode : 1 -[0004] Processor UID : 00000000 -[0001] Interrupt Input LINT : 00 -[0003] Reserved : 000000 +[0001] Subtable Type : 0B [Generic Interrupt Controller] +[0001] Length : 28 +[0002] Reserved : 0000 +[0004] Local GIC Hardware ID : 00000004 +[0004] Processor UID : 00000004 +[0004] Flags (decoded below) : 00000000 + Processor Enabled : 0 +[0004] Parking Protocol Version : 00000000 +[0004] Performance Interrupt : 00000000 +[0008] Parked Address : 0000000000000000 +[0008] Base Address : 000000002C002000
[0001] Subtable Type : 0B [Generic Interrupt Controller] [0001] Length : 28 [0002] Reserved : 0000 -[0004] Local GIC Hardware ID : 00000000 -[0004] Processor UID : 00000000 -[0004] Flags (decoded below) : 00000001 - Processor Enabled : 1 +[0004] Local GIC Hardware ID : 00000005 +[0004] Processor UID : 00000005 +[0004] Flags (decoded below) : 00000000 + Processor Enabled : 0 +[0004] Parking Protocol Version : 00000000 +[0004] Performance Interrupt : 00000000 +[0008] Parked Address : 0000000000000000 +[0008] Base Address : 000000002C002000 + +[0001] Subtable Type : 0B [Generic Interrupt Controller] +[0001] Length : 28 +[0002] Reserved : 0000 +[0004] Local GIC Hardware ID : 00000006 +[0004] Processor UID : 00000006 +[0004] Flags (decoded below) : 00000000 + Processor Enabled : 0 +[0004] Parking Protocol Version : 00000000 +[0004] Performance Interrupt : 00000000 +[0008] Parked Address : 0000000000000000 +[0008] Base Address : 000000002C002000 + +[0001] Subtable Type : 0B [Generic Interrupt Controller] +[0001] Length : 28 +[0002] Reserved : 0000 +[0004] Local GIC Hardware ID : 00000007 +[0004] Processor UID : 00000007 +[0004] Flags (decoded below) : 00000000 + Processor Enabled : 0 [0004] Parking Protocol Version : 00000000 [0004] Performance Interrupt : 00000000 [0008] Parked Address : 0000000000000000 -[0008] Base Address : 0000000000000000 +[0008] Base Address : 000000002C002000
[0001] Subtable Type : 0C [Generic Interrupt Distributor] [0001] Length : 18 [0002] Reserved : 0000 [0004] Local GIC Hardware ID : 00000000 -[0008] Base Address : 0000000000000000 +[0008] Base Address : 000000002C001000 /* armv8 foundation model's GIC distributor base addr */ [0004] Interrupt Base : 00000000 [0004] Reserved : 00000000
Implement core functions for GIC and GIC distributor entries in MADT, after this patch, we can parse MADT for ARM now. Here is the boot information for MADT parse:
ACPI: GIC (acpi_id[0x0000] gic_id[0x0000] enabled) ACPI: GIC (acpi_id[0x0001] gic_id[0x0001] enabled) ACPI: GIC (acpi_id[0x0002] gic_id[0x0002] enabled) ACPI: GIC (acpi_id[0x0003] gic_id[0x0003] enabled) ACPI: GIC (acpi_id[0x0004] gic_id[0x0004] disabled) ACPI: GIC (acpi_id[0x0005] gic_id[0x0005] disabled) ACPI: GIC (acpi_id[0x0006] gic_id[0x0006] disabled) ACPI: GIC (acpi_id[0x0007] gic_id[0x0007] disabled) ACPI: GIC Distributor (id[0x0000] address[0x2c001000] gsi_base[0]) Using ACPI (MADT) for SMP configuration information
NOTE: Parse GIC distributor for irq system is not implemented, should fix it in the future.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/include/asm/acpi.h | 3 + arch/arm64/kernel/setup.c | 4 + drivers/acpi/plat/arm/boot.c | 165 ++++++++++++++++++++++++++++++++++++++++- drivers/acpi/tables.c | 21 ++++++ 4 files changed, 190 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 605478e..b43ddba 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -34,6 +34,9 @@ #define COMPILER_DEPENDENT_INT64 long long #define COMPILER_DEPENDENT_UINT64 unsigned long long
+#define MAX_LOCAL_APIC 256 +#define MAX_IO_APICS 64 + /* * Calling conventions: * diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index cb5a299..7e37ba0 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -299,6 +299,10 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con; #endif #endif + +#ifdef CONFIG_ACPI + acpi_boot_init(); +#endif }
static int __init arm64_device_init(void) diff --git a/drivers/acpi/plat/arm/boot.c b/drivers/acpi/plat/arm/boot.c index a6645ff..f85d4da 100644 --- a/drivers/acpi/plat/arm/boot.c +++ b/drivers/acpi/plat/arm/boot.c @@ -70,6 +70,7 @@ int acpi_sci_override_gsi __initdata; int acpi_skip_timer_override __initdata; int acpi_use_timer_override __initdata; int acpi_fix_pin2_polarity __initdata; +static u64 acpi_lapic_addr __initdata;
struct acpi_arm_root acpi_arm_rsdp_info; /* info about RSDP from FDT */
@@ -117,6 +118,74 @@ void __init __acpi_unmap_table(char *map, unsigned long size) return; }
+static int __init acpi_parse_madt(struct acpi_table_header *table) +{ + struct acpi_table_madt *madt = NULL; + + madt = (struct acpi_table_madt *)table; + if (!madt) { + pr_warn(PREFIX "Unable to map MADT\n"); + return -ENODEV; + } + + if (madt->address) { + acpi_lapic_addr = (u64) madt->address; + + pr_debug(PREFIX "Local APIC address 0x%08x\n", madt->address); + } + + return 0; +} + +/* Local APIC = GIC cpu interface on ARM */ +static void __cpuinit acpi_register_lapic(int id, u8 enabled) +{ + return; +} + +static int __init +acpi_parse_gic(struct acpi_subtable_header *header, const unsigned long end) +{ + struct acpi_madt_generic_interrupt *processor = NULL; + + processor = (struct acpi_madt_generic_interrupt *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* + * We need to register disabled CPU as well to permit + * counting disabled CPUs. This allows us to size + * cpus_possible_map more accurately, to permit + * to not preallocating memory for all NR_CPUS + * when we use CPU hotplug. + */ + acpi_register_lapic(processor->gic_id, + processor->flags & ACPI_MADT_ENABLED); + + return 0; +} + +static int __init +acpi_parse_gic_distributor(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_distributor *distributor = NULL; + + distributor = (struct acpi_madt_generic_distributor *)header; + + if (BAD_MADT_ENTRY(distributor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* TODO: handle with the base_address and irq_base for irq system */ + + return 0; +} + /* * acpi_pic_sci_set_trigger() * @@ -227,9 +296,9 @@ void __init acpi_set_irq_model_pic(void) acpi_ioapic = 0; }
-void __init acpi_set_irq_model_ioapic(void) +void __init acpi_set_irq_model_gic(void) { - acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC; + acpi_irq_model = ACPI_IRQ_MODEL_GIC; __acpi_register_gsi = acpi_register_gsi_ioapic; acpi_ioapic = 1; } @@ -437,17 +506,108 @@ late_initcall(hpet_insert_resource); #define acpi_parse_hpet NULL #endif
+/* Local APIC = GIC cpu interface on ARM */ +static int __init acpi_parse_madt_lapic_entries(void) +{ + int count; + + /* + * do a partial walk of MADT to determine how many CPUs + * we have including disabled CPUs + */ + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, + acpi_parse_gic, MAX_LOCAL_APIC); + + if (!count) { + pr_err(PREFIX "No LAPIC entries present\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return -ENODEV; + } else if (count < 0) { + pr_err(PREFIX "Error parsing LAPIC entry\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return count; + } + + return 0; +} + static int __init acpi_parse_fadt(struct acpi_table_header *table) { return 0; }
+/* + * Parse IOAPIC related entries in MADT + * returns 0 on success, < 0 on error + * IOAPIC = GIC distributor on ARM + */ +static int __init acpi_parse_madt_ioapic_entries(void) +{ + int count; + + /* + * ACPI interpreter is required to complete interrupt setup, + * so if it is off, don't enumerate the io-apics with ACPI. + * If MPS is present, it will handle them, + * otherwise the system will stay in PIC mode + */ + if (acpi_disabled || acpi_noirq) + return -ENODEV; + + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, + acpi_parse_gic_distributor, MAX_IO_APICS); + + if (!count) { + pr_err(PREFIX "No IOAPIC entries present\n"); + return -ENODEV; + } else if (count < 0) { + pr_err(PREFIX "Error parsing IOAPIC entry\n"); + return count; + } + + return 0; +} + static void __init early_acpi_process_madt(void) { + /* should I introduce CONFIG_ARM_LOCAL_APIC like x86 does? */ + acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt); }
static void __init acpi_process_madt(void) { + /* should I introduce CONFIG_ARM_LOCAL_APIC like x86 does? */ + int error; + + if (!acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt)) { + + /* + * Parse MADT LAPIC entries + */ + error = acpi_parse_madt_lapic_entries(); + if (!error) { + acpi_lapic = 1; + + /* + * Parse MADT IO-APIC entries + */ + error = acpi_parse_madt_ioapic_entries(); + if (!error) + acpi_set_irq_model_gic(); + } + } + + /* + * ACPI supports both logical (e.g. Hyper-Threading) and physical + * processors, where MPS only supports physical. + */ + if (acpi_lapic && acpi_ioapic) + pr_info("Using ACPI (MADT) for SMP configuration " + "information\n"); + else if (acpi_lapic) + pr_info("Using ACPI for processor (LAPIC) " + "configuration information\n"); + return; }
@@ -719,4 +879,3 @@ int __acpi_release_global_lock(unsigned int *lock) } while (unlikely (val != old)); return old & 0x1; } - diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index d67a1fe..4613e8f 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -191,6 +191,27 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) } break;
+ case ACPI_MADT_TYPE_GENERIC_INTERRUPT: + { + struct acpi_madt_generic_interrupt *p = + (struct acpi_madt_generic_interrupt *)header; + printk(KERN_INFO PREFIX + "GIC (acpi_id[0x%04x] gic_id[0x%04x] %s)\n", + p->uid, p->gic_id, + (p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + } + break; + + case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR: + { + struct acpi_madt_generic_distributor *p = + (struct acpi_madt_generic_distributor *)header; + printk(KERN_INFO PREFIX + "GIC Distributor (id[0x%04x] address[0x%08llx] gsi_base[%d])\n", + p->gic_id, p->base_address, p->global_irq_base); + } + break; + default: printk(KERN_WARNING PREFIX "Found unsupported MADT entry (type = 0x%x)\n",
When boot the kernel with MADT, the cpu possible and present maps should be prefilled for cpu topology and cpu hot-plug.
The logic cpu id maps to GIC id is also implemented, then we can use it for ACPI driver for cpu hot-plug.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/include/asm/acpi.h | 8 +++ arch/arm64/kernel/setup.c | 2 + arch/arm64/kernel/smp.c | 2 + drivers/acpi/plat/arm/boot.c | 113 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 122 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index b43ddba..c40148b 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -77,6 +77,12 @@ extern int acpi_noirq; extern int acpi_pci_disabled; extern int acpi_strict;
+/* map logic cpu id to physical APIC id + * APIC = GIC cpu interface on ARM + */ +extern volatile int arm_cpu_to_apicid[NR_CPUS]; +extern int boot_cpu_apic_id; + struct acpi_arm_root { phys_addr_t phys_address; unsigned long size; @@ -86,6 +92,8 @@ extern struct acpi_arm_root acpi_arm_rsdp_info; /* Low-level suspend routine. */ extern int acpi_suspend_lowlevel(void);
+extern void prefill_possible_map(void); + /* Physical address to resume after wakeup */ /* BOZO: was... #define acpi_wakeup_address ((unsigned long)(real_mode_header->wakeup_start)) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 7e37ba0..d91ecb3 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -301,7 +301,9 @@ void __init setup_arch(char **cmdline_p) #endif
#ifdef CONFIG_ACPI + boot_cpu_apic_id = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; acpi_boot_init(); + prefill_possible_map(); #endif }
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 5d54e37..fe4ed77 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -418,7 +418,9 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (err) continue;
+#ifndef CONFIG_ACPI set_cpu_present(cpu, true); +#endif max_cpus--; } } diff --git a/drivers/acpi/plat/arm/boot.c b/drivers/acpi/plat/arm/boot.c index f85d4da..aaacfbb 100644 --- a/drivers/acpi/plat/arm/boot.c +++ b/drivers/acpi/plat/arm/boot.c @@ -51,6 +51,15 @@ u32 acpi_rsdt_forced; int acpi_disabled; EXPORT_SYMBOL(acpi_disabled);
+/* available_cpus here means enabled cpu in MADT */ +static int available_cpus __initdata; + +/* Map logic cpu id to physical APIC id. + * APIC = GIC cpu interface on ARM + */ +volatile int arm_cpu_to_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 }; +int boot_cpu_apic_id = -1; + #define BAD_MADT_ENTRY(entry, end) ( \ (!entry) || (unsigned long)entry + sizeof(*entry) > end || \ ((struct acpi_subtable_header *)entry)->length < sizeof(*entry)) @@ -131,7 +140,7 @@ static int __init acpi_parse_madt(struct acpi_table_header *table) if (madt->address) { acpi_lapic_addr = (u64) madt->address;
- pr_debug(PREFIX "Local APIC address 0x%08x\n", madt->address); + pr_info(PREFIX "Local APIC address 0x%08x\n", madt->address); }
return 0; @@ -140,7 +149,35 @@ static int __init acpi_parse_madt(struct acpi_table_header *table) /* Local APIC = GIC cpu interface on ARM */ static void __cpuinit acpi_register_lapic(int id, u8 enabled) { - return; + int cpu; + + if (id >= MAX_LOCAL_APIC) { + pr_info(PREFIX "skipped apicid that is too big\n"); + return; + } + + total_cpus++; + if (!enabled) + return; + + available_cpus++; + + /* allocate a logic cpu id for the new comer */ + if (boot_cpu_apic_id == id) { + /* + * boot_cpu_init() already hold bit 0 in cpu_present_mask + * for BSP, no need to allocte again. + */ + cpu = 0; + } else { + cpu = cpumask_next_zero(-1, cpu_present_mask); + } + + /* map the logic cpu id to APIC id */ + arm_cpu_to_apicid[cpu] = id; + + set_cpu_present(cpu, true); + set_cpu_possible(cpu, true); }
static int __init @@ -303,6 +340,65 @@ void __init acpi_set_irq_model_gic(void) acpi_ioapic = 1; }
+static int __initdata setup_possible_cpus = -1; +static int __init _setup_possible_cpus(char *str) +{ + get_option(&str, &setup_possible_cpus); + return 0; +} +early_param("possible_cpus", _setup_possible_cpus); + +/* + * cpu_possible_mask should be static, it cannot change as cpu's + * are onlined, or offlined. The reason is per-cpu data-structures + * are allocated by some modules at init time, and dont expect to + * do this dynamically on cpu arrival/departure. + * cpu_present_mask on the other hand can change dynamically. + * In case when cpu_hotplug is not compiled, then we resort to current + * behaviour, which is cpu_possible == cpu_present. + * - Ashok Raj + * + * Three ways to find out the number of additional hotplug CPUs: + * - If the BIOS specified disabled CPUs in ACPI/mptables use that. + * - The user can overwrite it with possible_cpus=NUM + * - Otherwise don't reserve additional CPUs. + * We do this because additional CPUs waste a lot of memory. + * -AK + */ +__init void prefill_possible_map(void) +{ + int i; + int possible, disabled_cpus; + + disabled_cpus = total_cpus - available_cpus; + + if (setup_possible_cpus == -1) { + if (disabled_cpus > 0) + setup_possible_cpus = disabled_cpus; + else + setup_possible_cpus = 0; + } + + possible = available_cpus + setup_possible_cpus; + + pr_info("SMP: the system is limited to %d CPUs\n", nr_cpu_ids); + + /* + * On armv8 foundation model --cores=4 lets nr_cpu_ids=4, so we can't + * get possible map correctly when more than 4 APIC entries in MADT. + */ + if (possible > nr_cpu_ids) + possible = nr_cpu_ids; + + pr_info("SMP: Allowing %d CPUs, %d hotplug CPUs\n", + possible, max((possible - available_cpus), 0)); + + for (i = 0; i < possible; i++) + set_cpu_possible(i, true); + for (; i < NR_CPUS; i++) + set_cpu_possible(i, false); +} + /* * ACPI based hotplug support for CPU */ @@ -528,6 +624,18 @@ static int __init acpi_parse_madt_lapic_entries(void) return count; }
+#ifdef CONFIG_SMP + if (available_cpus == 0) { + pr_info(PREFIX "Found 0 CPUS; assuming 1\n"); + /* FIXME: should be the real GIC id read from hardware */ + arm_cpu_to_apicid[available_cpus] = 0; + available_cpus = 1; /* We've got at least one of these */ + } +#endif + /* Make boot-up look pretty */ + pr_info("%d CPUs available, %d CPUs total\n", available_cpus, + total_cpus); + return 0; }
@@ -717,7 +825,6 @@ void __init acpi_arm_blob_relocate(void) * bit of fixup work on the offsets to turn them into kernel * virtual addresses so we can pass them on for later use. */ - void __init acpi_boot_table_init(void) { /*
Four processors are declared in the dsdt table.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/boot/asl/foundation-v8.acpi/dsdt.asl | 205 ++++++++++++++++++++++- 1 file changed, 202 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/boot/asl/foundation-v8.acpi/dsdt.asl b/arch/arm64/boot/asl/foundation-v8.acpi/dsdt.asl index 7f06af0..2dfd07d 100644 --- a/arch/arm64/boot/asl/foundation-v8.acpi/dsdt.asl +++ b/arch/arm64/boot/asl/foundation-v8.acpi/dsdt.asl @@ -14,9 +14,208 @@ DefinitionBlock ( "FOUNDATI", // table ID 0x00000002) // OEM revision { - Scope (_PR) + Scope (_SB) { - Processor (CPU0, 0x01, 0x00000410, 0x06) {} - Processor (CPU1, 0x02, 0x00000410, 0x06) {} + Device (SCK0) + { + Name (_HID, "ACPI0004") + Name (_UID, 0x00) + Method (_STA, 0, NotSerialized) + { + Return (0x0F) + } + + Device (PRC0) + { + Name (_HID, "ACPI0007") + Name (_UID, 0x00) + + /* CPU0 will be always present */ + Method (_STA, 0, NotSerialized) + { + Return (0x0F) + } + + Name (MAT0, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* type, len, reserved, gic_id */ + /* 0008 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* uid, flags */ + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* parking_version, performance_interrupt */ + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* parked_address */ + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* base_address */ + }) + + Name (MAT1, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0008 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }) + + Method (_MAT, 0, NotSerialized) + { + If (_STA()) + { + Return (MAT0) + } + Else + { + Return (MAT1) + } + } + } + + Device (PRC1) + { + Name (_HID, "ACPI0007") + Name (_UID, 0x01) + + Name (STA1, 0x0F) + Method (_STA, 0, NotSerialized) + { + Return (STA1) + } + + Method (_EJ0, 1, NotSerialized) + { + If (LEqual (STA1, 0x0F)) + { + Store (0x00, STA1) + } + } + + Name (MAT0, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* 0008 */ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }) + + Name (MAT1, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* 0008 */ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }) + + Method (_MAT, 0, NotSerialized) + { + If (_STA()) + { + Return (MAT0) + } + Else + { + Return (MAT1) + } + } + } + + Device (PRC2) + { + Name (_HID, "ACPI0007") + Name (_UID, 0x02) + + Name (STA2, 0x0F) + Method (_STA, 0, NotSerialized) + { + Return (STA2) + } + + Method (_EJ0, 1, NotSerialized) + { + If (LEqual (STA2, 0x0F)) + { + Store (0x00, STA2) + } + } + + Name (MAT0, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + /* 0008 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }) + + Name (MAT1, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + /* 0008 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }) + + Method (_MAT, 0, NotSerialized) + { + If (_STA()) + { + Return (MAT0) + } + Else + { + Return (MAT1) + } + } + } + + Device (PRC3) + { + Name (_HID, "ACPI0007") + Name (_UID, 0x03) + + Name (STA3, 0x0F) + Method (_STA, 0, NotSerialized) + { + Return (STA3) + } + + Method (_EJ0, 1, NotSerialized) + { + If (LEqual (STA3, 0x0F)) + { + Store (0x00, STA3) + } + } + + Name (MAT0, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + /* 0008 */ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }) + + Name (MAT1, Buffer (0x28) + { + /* 0000 */ 0x0B, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + /* 0008 */ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + /* 0010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0018 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }) + + Method (_MAT, 0, NotSerialized) + { + If (_STA()) + { + Return (MAT0) + } + Else + { + Return (MAT1) + } + } + } + } } }
Get apic id from MADT or _MAT method is not implemented on arm, and ACPI 5.0 introduces GIC Structure for it, so we just introduce map_gic_id() to get apic id followed the ACPI 5.0.
Note: When _MAT appears under a Processor object, GIC Structure will be ignored by OSPM in ACPI 5.0, so the ACPI 5.0 (chapter 6.2.9) should be updated for ARM.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/acpi/processor_core.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 09e5e4f..ef225e5 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -90,6 +90,27 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, return 1; }
+static int map_gic_id(struct acpi_subtable_header *entry, + int device_declaration, u32 acpi_id, int *apic_id) +{ + struct acpi_madt_generic_interrupt *gic = + (struct acpi_madt_generic_interrupt *)entry; + + if (!(gic->flags & ACPI_MADT_ENABLED)) + return 0; + + /* In the GIC interrupt model, logical processors are + * required to have a Processor Device object in the DSDT, + * so we should check device_declaration here + */ + if (device_declaration && (gic->uid == acpi_id)) { + *apic_id = gic->gic_id; + return 1; + } + + return 0; +} + static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; @@ -125,6 +146,9 @@ static int map_madt_entry(int type, u32 acpi_id) } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { if (map_lsapic_id(header, type, acpi_id, &apic_id)) break; + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + if (map_gic_id(header, type, acpi_id, &apic_id)) + break; } entry += header->length; } @@ -155,6 +179,8 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) map_lapic_id(header, acpi_id, &apic_id); } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { map_lsapic_id(header, type, acpi_id, &apic_id); + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + map_gic_id(header, type, acpi_id, &apic_id); }
exit:
Since enabled cpus were allocated logic cpu id to them and mapped to the APIC id when boot, we can get the cpu id in the ACPI driver, the cpu is a new added cpu if cpu id is -1, then we can hot-add it later.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/include/asm/acpi.h | 1 + drivers/acpi/processor_core.c | 24 ++++++------------------ 2 files changed, 7 insertions(+), 18 deletions(-)
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index c40148b..3d37c0e 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -82,6 +82,7 @@ extern int acpi_strict; */ extern volatile int arm_cpu_to_apicid[NR_CPUS]; extern int boot_cpu_apic_id; +#define cpu_physical_id(cpu) arm_cpu_to_apicid[cpu]
struct acpi_arm_root { phys_addr_t phys_address; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index ef225e5..39ac781 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -190,6 +190,9 @@ exit:
int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) { +#ifdef CONFIG_SMP + int i; +#endif int apic_id = -1;
apic_id = map_mat_entry(handle, type, acpi_id); @@ -222,23 +225,10 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) return apic_id; }
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) - - /* - * BOZO: need to abstract this out to have it make sense -- - * it's not that ARM has no equivalent, it's that apic_id is - * arch-specific - */ - -#else - #ifdef CONFIG_SMP - { - int i; - for_each_possible_cpu(i) { - if (cpu_physical_id(i) == apic_id) - return i; - } + for_each_possible_cpu(i) { + if (cpu_physical_id(i) == apic_id) + return i; } #else /* In UP kernel, only processor 0 is valid */ @@ -246,8 +236,6 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) return apic_id; #endif
-#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */ - return -1; } EXPORT_SYMBOL_GPL(acpi_get_cpuid);
When cpu was hot-added or hot removed, the cpu_present_map and APIC id map to logic cpu id should be updated. acpi_map/unmap_lsapic() will do this.
available_cpus will be used for ACPI driver, so remove __initdata attribute for it, and change it into global for future use too.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/acpi/plat/arm/boot.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/drivers/acpi/plat/arm/boot.c b/drivers/acpi/plat/arm/boot.c index aaacfbb..82b7bd6 100644 --- a/drivers/acpi/plat/arm/boot.c +++ b/drivers/acpi/plat/arm/boot.c @@ -52,7 +52,7 @@ int acpi_disabled; EXPORT_SYMBOL(acpi_disabled);
/* available_cpus here means enabled cpu in MADT */ -static int available_cpus __initdata; +int available_cpus;
/* Map logic cpu id to physical APIC id. * APIC = GIC cpu interface on ARM @@ -463,10 +463,7 @@ static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu) goto free_tmp_map;
cpumask_copy(tmp_map, cpu_present_mask); -#ifdef CONFIG_X86 - /* BOZO: ?? */ acpi_register_lapic(physid, ACPI_MADT_ENABLED); -#endif
/* * If acpi_register_lapic successfully generates a new logical cpu @@ -474,7 +471,7 @@ static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu) */ cpumask_andnot(new_map, cpu_present_mask, tmp_map); if (cpumask_empty(new_map)) { - printk("Unable to map lapic to logical cpu number\n"); + pr_err("Unable to map lapic to logical cpu number\n"); retval = -EINVAL; goto free_new_map; } @@ -504,12 +501,9 @@ EXPORT_SYMBOL(acpi_map_lsapic);
int acpi_unmap_lsapic(int cpu) { -#ifdef CONFIG_X86 - /* BOZO: ??? */ - per_cpu(x86_cpu_to_apicid, cpu) = -1; + arm_cpu_to_apicid[cpu] = -1; set_cpu_present(cpu, false); - num_processors--; -#endif + available_cpus--;
return 0; }
cpudata with type struct cpuinfo_arm was declared in cpu.h, but not defined at any file, so fix it.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/kernel/setup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index d91ecb3..627c6d6 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -56,7 +56,7 @@ #include <asm/traps.h> #include <asm/memblock.h> #include <asm/psci.h> - +#include <asm/cpu.h> #ifdef CONFIG_ACPI #include <asm/acpi.h> #endif @@ -315,16 +315,16 @@ static int __init arm64_device_init(void) } arch_initcall(arm64_device_init);
-static DEFINE_PER_CPU(struct cpu, cpu_data); +DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data);
static int __init topology_init(void) { int i;
for_each_possible_cpu(i) { - struct cpu *cpu = &per_cpu(cpu_data, i); - cpu->hotpluggable = 1; - register_cpu(cpu, i); + struct cpuinfo_arm *cpuinfo = &per_cpu(cpu_data, i); + cpuinfo->cpu.hotpluggable = 1; + register_cpu(&cpuinfo->cpu, i); }
return 0;
Introduce arch_register/unregister_cpu() for ACPI driver, which can register/unregister cpu interfaces in /sys/devices/system/cpu/cpux.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/include/asm/cpu.h | 5 +++++ arch/arm64/kernel/topology.c | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index dbeb98d..5613e09 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -20,6 +20,11 @@ struct cpuinfo_arm { #endif };
+#ifdef CONFIG_HOTPLUG_CPU +extern int arch_register_cpu(int cpu); +extern void arch_unregister_cpu(int cpu); +#endif + DECLARE_PER_CPU(struct cpuinfo_arm, cpu_data);
#endif diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index cb548f1..5c8e69c 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -18,3 +18,29 @@ void arch_fix_phys_package_id(int num, u32 slot) } EXPORT_SYMBOL_GPL(arch_fix_phys_package_id);
+#ifdef CONFIG_HOTPLUG_CPU +int __ref arch_register_cpu(int cpu) +{ + struct cpuinfo_arm *cpuinfo = &per_cpu(cpu_data, cpu); + + /* BSP cann't be taken down on arm */ + if (cpu) + cpuinfo->cpu.hotpluggable = 1; + + return register_cpu(&cpuinfo->cpu, cpu); +} +EXPORT_SYMBOL(arch_register_cpu); + +void arch_unregister_cpu(int cpu) +{ + unregister_cpu(&per_cpu(cpu_data, cpu).cpu); +} +EXPORT_SYMBOL(arch_unregister_cpu); +#else /* CONFIG_HOTPLUG_CPU */ + +static int __init arch_register_cpu(int cpu) +{ + return register_cpu(&per_cpu(cpu_data, cpu).cpu, cpu); +} +#endif /* CONFIG_HOTPLUG_CPU */ +
CPU logic online/offline was not implemented on arm64 which is essential for cpu hot-plug, so skeleton the logic cpu online/offline to make arm64 code compile when CONFIG_HOTPLUG_CPU was introduced.
It should be fully implemented at later date. Will discuss it with power management team in LCE13.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- arch/arm64/Kconfig | 7 +++++++ arch/arm64/include/asm/smp.h | 3 +++ arch/arm64/kernel/smp.c | 21 +++++++++++++++++++++ 3 files changed, 31 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 9b9cde0..c967b11 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -150,6 +150,13 @@ config NR_CPUS depends on SMP default "4"
+config HOTPLUG_CPU + bool "Support for hot-pluggable CPUs" + depends on SMP && HOTPLUG + help + Say Y here to experiment with turning CPUs off and on. CPUs + can be controlled through /sys/devices/system/cpu. + source kernel/Kconfig.preempt
config HZ diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index 4b8023c..95c9f58 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h @@ -63,6 +63,9 @@ extern struct secondary_data secondary_data; extern void secondary_holding_pen(void); extern volatile unsigned long secondary_holding_pen_release;
+extern int __cpu_disable(void); +extern void __cpu_die(unsigned int cpu); + extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index fe4ed77..5ec1723 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -257,6 +257,27 @@ static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name) return NULL; }
+#ifdef CONFIG_HOTPLUG_CPU +/* + * __cpu_disable runs on the processor to be shutdown. + */ +int __cpuinit __cpu_disable(void) +{ + pr_info("entered __cpu_disable(), assume always success\n"); + return 0; +} + +/* + * called on the thread which is asking for a CPU to be shutdown - + * waits until shutdown has completed, or it is timed out. + */ +void __cpuinit __cpu_die(unsigned int cpu) +{ + pr_info("entered __cpu_disable(), cpu %d will go away\n", cpu); + return; +} +#endif /* CONFIG_HOTPLUG_CPU */ + /* * Enumerate the possible CPU set from the device tree and build the * cpu logical map array containing MPIDR values related to logical
On 2013-7-3 19:15, Hanjun Guo wrote:
CPU logic online/offline was not implemented on arm64 which is essential for cpu hot-plug, so skeleton the logic cpu online/offline to make arm64 code compile when CONFIG_HOTPLUG_CPU was introduced.
It should be fully implemented at later date. Will discuss it with power management team in LCE13.
Test result: (FOR TEST ONLY! will lead to panic because of CPU logic online/offline was not implemented, but it shows that ACPI CPU hot-plug is working! if you don't trigger the hot-remove, the kernel will runs well :) )
root@genericarmv8:/sys/bus/acpi/devices# ls ACPI0004:00 ACPI0007:01 ACPI0007:03 device:00 ACPI0007:00 ACPI0007:02 LNXSYSTM:00 device:01
root@genericarmv8:/sys/bus/acpi/devices# cd ACPI0007:03 root@genericarmv8:/sys/bus/acpi/devices/ACPI0007:03# ls driver hid path sysdev uevent eject modalias subsystem thermal_cooling uid
We have a *eject* interface for cpu now, which can trigger a hot-remove request from OS!
root@genericarmv8:/sys/bus/acpi/devices/ACPI0007:03# echo 1 > eject root@genericarmv8:/sys/bus/acpi/devices/ACPI0007:03# entered __cpu_disable(), assume always success BUG: failure at kernel/cpu.c:321/_cpu_down()! Kernel panic - not syncing: BUG! CPU: 0 PID: 275 Comm: kworker/0:1 Not tainted 3.10.0-rc6+ #56 Workqueue: kacpi_hotplug acpi_os_execute_deferred Call trace: [<ffffffc00008724c>] dump_backtrace+0x0/0x12c [<ffffffc00008738c>] show_stack+0x14/0x1c [<ffffffc0003f2954>] dump_stack+0x20/0x28 [<ffffffc0003ef7fc>] panic+0xe8/0x214 [<ffffffc0003eab78>] cpu_down+0x0/0x40 [<ffffffc0003eab9c>] cpu_down+0x24/0x40 [<ffffffc0002c0520>] acpi_processor_remove+0x60/0x14c [<ffffffc0002847f8>] acpi_device_remove+0x78/0x98 [<ffffffc0002f5720>] __device_release_driver+0x50/0xa8 [<ffffffc0002f579c>] device_release_driver+0x24/0x38 [<ffffffc0002853c0>] acpi_bus_device_detach+0x4c/0x58 [<ffffffc000285414>] acpi_bus_trim+0x48/0x8c [<ffffffc0002859b8>] acpi_scan_hot_remove+0x80/0x1b4 [<ffffffc000285bf4>] acpi_bus_hot_remove_device+0x30/0x70 [<ffffffc000281664>] acpi_os_execute_deferred+0x2c/0x40 [<ffffffc0000ad474>] process_one_work+0x11c/0x340 [<ffffffc0000adaf8>] worker_thread+0x14c/0x38c [<ffffffc0000b3238>] kthread+0xb4/0xbc
From the dump trace, we can see that the ACPI CPU hot-plug driver
is working fine, so, when the CPU logic online/offline is ready, the physical CPU hot-plug for arm64 is ready too!
Hi Hanjun,
These look good to me, I think we should push to ACPI tree before Connect so you can use them in discussion with other groups.
I have a small "hack" on top of these patches to make armv7 compile again, basically just copy same changes to armv7 version of acpi.h
Graeme
On 03/07/13 12:15, Hanjun Guo wrote:
Here is the first version of prototype for cpu-hotplug on ARM64.
The first part is patch 1~3 for MADT parse. MADT is being parsed and the cpu possible map and cpu present map are prefilled.
I kept the terminology consistent with x86 as Al and Andrea's advice.
CPU possible map is equal to the GIC entries in MADT no matter enabled or disabled, and cpu present map limits to the enabled cpu in MADT. This is the base for cpu hot-plug.
The second part is patch 4~10 for ACPI cpu hot-plug drivers. ACPI driver for CPU hot-plug is implemented in this part, when the cpu logical online/offline is ready, the cpu hot-plug can work on arm64.
Please give some comments, thanks.
Hanjun Guo (10): ARM64 / ACPI: MADT for armv8 foundation model ARM64 / ACPI: Core functions for MADT parse ARM64 / ACPI: Prefill cpu possible and present maps and map logical cpu id to APIC id ARM64 / ACPI: Declare processors in ACPI namespace in the DSDT ARM64 / ACPI: Introduce map_gic_id() to get apic id from MADT or _MAT method ARM64 / ACPI: Get logic cpu id to see if the cpu is a new added cpu ARM64 / ACPI: Implement acpi_map/unmap_lsapic for cpu hot-plug ARM64 / ACPI: Fix the variable declared but not defined ARM64 / ACPI: Introduce arch_register/unregister_cpu() for arm64 ARM64 / CPU hot-plug: Skeleton logic cpu online/offline for cpu hot-plug
arch/arm64/Kconfig | 7 + arch/arm64/boot/asl/foundation-v8.acpi/apic.asl | 165 +++++++------ arch/arm64/boot/asl/foundation-v8.acpi/dsdt.asl | 205 +++++++++++++++- arch/arm64/include/asm/acpi.h | 12 + arch/arm64/include/asm/cpu.h | 5 + arch/arm64/include/asm/smp.h | 3 + arch/arm64/kernel/setup.c | 16 +- arch/arm64/kernel/smp.c | 23 ++ arch/arm64/kernel/topology.c | 26 +++ drivers/acpi/plat/arm/boot.c | 286 +++++++++++++++++++++-- drivers/acpi/processor_core.c | 50 ++-- drivers/acpi/tables.c | 21 ++ 12 files changed, 694 insertions(+), 125 deletions(-)