Skip to content

Commit d633da5

Browse files
James Morsectmarinas
authored andcommitted
irqchip/gic-v3: Add support for ACPI's disabled but 'online capable' CPUs
To support virtual CPU hotplug, ACPI has added an 'online capable' bit to the MADT GICC entries. This indicates a disabled CPU entry may not be possible to online via PSCI until firmware has set enabled bit in _STA. This means that a "usable" GIC redistributor is one that is marked as either enabled, or online capable. The meaning of the acpi_gicc_is_usable() would become less clear than just checking the pair of flags at call sites. As such, drop that helper function. The test in gic_acpi_match_gicc() remains as testing just the enabled bit so the count of enabled distributors is correct. What about the redistributor in the GICC entry? ACPI doesn't want to say. Assume the worst: When a redistributor is described in the GICC entry, but the entry is marked as disabled at boot, assume the redistributor is inaccessible. The GICv3 driver doesn't support late online of redistributors, so this means the corresponding CPU can't be brought online either. Rather than modifying cpu masks that may already have been used, register a new cpuhp callback to fail this case. This must run earlier than the main gic_starting_cpu() so that this case can be rejected before the section of cpuhp that runs on the CPU that is coming up as that is not allowed to fail. This solution keeps the handling of this broken firmware corner case local to the GIC driver. As precise ordering of this callback doesn't need to be controlled as long as it is in that initial prepare phase, use CPUHP_BP_PREPARE_DYN. Systems that want CPU hotplug in a VM can ensure their redistributors are always-on, and describe them that way with a GICR entry in the MADT. Suggested-by: Marc Zyngier <[email protected]> Signed-off-by: James Morse <[email protected]> Signed-off-by: Russell King (Oracle) <[email protected]> Tested-by: Miguel Luis <[email protected]> Co-developed-by: Jonathan Cameron <[email protected]> Signed-off-by: Jonathan Cameron <[email protected]> Acked-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent fa2dabe commit d633da5

File tree

4 files changed

+46
-11
lines changed

4 files changed

+46
-11
lines changed

arch/arm64/kernel/smp.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,8 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
530530
{
531531
u64 hwid = processor->arm_mpidr;
532532

533-
if (!acpi_gicc_is_usable(processor)) {
533+
if (!(processor->flags &
534+
(ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE))) {
534535
pr_debug("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid);
535536
return;
536537
}

drivers/acpi/processor_core.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry,
9090
struct acpi_madt_generic_interrupt *gicc =
9191
container_of(entry, struct acpi_madt_generic_interrupt, header);
9292

93-
if (!acpi_gicc_is_usable(gicc))
93+
if (!(gicc->flags &
94+
(ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
9495
return -ENODEV;
9596

9697
/* device_declaration means Device object in DSDT, in the

drivers/irqchip/irq-gic-v3.c

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444

4545
#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
4646

47+
static struct cpumask broken_rdists __read_mostly;
48+
4749
struct redist_region {
4850
void __iomem *redist_base;
4951
phys_addr_t phys_base;
@@ -1293,6 +1295,18 @@ static void gic_cpu_init(void)
12931295
#define MPIDR_TO_SGI_RS(mpidr) (MPIDR_RS(mpidr) << ICC_SGI1R_RS_SHIFT)
12941296
#define MPIDR_TO_SGI_CLUSTER_ID(mpidr) ((mpidr) & ~0xFUL)
12951297

1298+
/*
1299+
* gic_starting_cpu() is called after the last point where cpuhp is allowed
1300+
* to fail. So pre check for problems earlier.
1301+
*/
1302+
static int gic_check_rdist(unsigned int cpu)
1303+
{
1304+
if (cpumask_test_cpu(cpu, &broken_rdists))
1305+
return -EINVAL;
1306+
1307+
return 0;
1308+
}
1309+
12961310
static int gic_starting_cpu(unsigned int cpu)
12971311
{
12981312
gic_cpu_init();
@@ -1384,6 +1398,10 @@ static void __init gic_smp_init(void)
13841398
};
13851399
int base_sgi;
13861400

1401+
cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,
1402+
"irqchip/arm/gicv3:checkrdist",
1403+
gic_check_rdist, NULL);
1404+
13871405
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
13881406
"irqchip/arm/gicv3:starting",
13891407
gic_starting_cpu, NULL);
@@ -2365,8 +2383,24 @@ gic_acpi_parse_madt_gicc(union acpi_subtable_headers *header,
23652383
u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2;
23662384
void __iomem *redist_base;
23672385

2368-
if (!acpi_gicc_is_usable(gicc))
2386+
/* Neither enabled or online capable means it doesn't exist, skip it */
2387+
if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
2388+
return 0;
2389+
2390+
/*
2391+
* Capable but disabled CPUs can be brought online later. What about
2392+
* the redistributor? ACPI doesn't want to say!
2393+
* Virtual hotplug systems can use the MADT's "always-on" GICR entries.
2394+
* Otherwise, prevent such CPUs from being brought online.
2395+
*/
2396+
if (!(gicc->flags & ACPI_MADT_ENABLED)) {
2397+
int cpu = get_cpu_for_acpi_id(gicc->uid);
2398+
2399+
pr_warn("CPU %u's redistributor is inaccessible: this CPU can't be brought online\n", cpu);
2400+
if (cpu >= 0)
2401+
cpumask_set_cpu(cpu, &broken_rdists);
23692402
return 0;
2403+
}
23702404

23712405
redist_base = ioremap(gicc->gicr_base_address, size);
23722406
if (!redist_base)
@@ -2413,9 +2447,12 @@ static int __init gic_acpi_match_gicc(union acpi_subtable_headers *header,
24132447

24142448
/*
24152449
* If GICC is enabled and has valid gicr base address, then it means
2416-
* GICR base is presented via GICC
2450+
* GICR base is presented via GICC. The redistributor is only known to
2451+
* be accessible if the GICC is marked as enabled. If this bit is not
2452+
* set, we'd need to add the redistributor at runtime, which isn't
2453+
* supported.
24172454
*/
2418-
if (acpi_gicc_is_usable(gicc) && gicc->gicr_base_address)
2455+
if (gicc->flags & ACPI_MADT_ENABLED && gicc->gicr_base_address)
24192456
acpi_data.enabled_rdists++;
24202457

24212458
return 0;
@@ -2474,7 +2511,8 @@ static int __init gic_acpi_parse_virt_madt_gicc(union acpi_subtable_headers *hea
24742511
int maint_irq_mode;
24752512
static int first_madt = true;
24762513

2477-
if (!acpi_gicc_is_usable(gicc))
2514+
if (!(gicc->flags &
2515+
(ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
24782516
return 0;
24792517

24802518
maint_irq_mode = (gicc->flags & ACPI_MADT_VGIC_IRQ_MODE) ?

include/linux/acpi.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,6 @@ acpi_table_parse_cedt(enum acpi_cedt_type id,
237237
int acpi_parse_mcfg (struct acpi_table_header *header);
238238
void acpi_table_print_madt_entry (struct acpi_subtable_header *madt);
239239

240-
static inline bool acpi_gicc_is_usable(struct acpi_madt_generic_interrupt *gicc)
241-
{
242-
return gicc->flags & ACPI_MADT_ENABLED;
243-
}
244-
245240
#if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH)
246241
void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa);
247242
#else

0 commit comments

Comments
 (0)