irqchip/sifive-plic: Chain to parent IRQ after handlers are ready

Now that the PLIC uses a platform driver, the driver is probed later in the
boot process, where interrupts from peripherals might already be pending.

As a result, plic_handle_irq() may be called as early as the call to
irq_set_chained_handler() completes. But this call happens before the
per-context handler is completely set up, so there is a window where
plic_handle_irq() can see incomplete per-context state and crash.

Avoid this by delaying the call to irq_set_chained_handler() until all
handlers from all PLICs are initialized.

Fixes: 8ec99b033147 ("irqchip/sifive-plic: Convert PLIC driver into a platform driver")
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Anup Patel <anup@brainfault.org>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20240529215458.937817-1-samuel.holland@sifive.com
Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/

authored by Samuel Holland and committed by Thomas Gleixner e306a894 0110c4b1

+17 -17
+17 -17
drivers/irqchip/irq-sifive-plic.c
··· 85 struct plic_priv *priv; 86 }; 87 static int plic_parent_irq __ro_after_init; 88 - static bool plic_cpuhp_setup_done __ro_after_init; 89 static DEFINE_PER_CPU(struct plic_handler, plic_handlers); 90 91 static int plic_irq_set_type(struct irq_data *d, unsigned int type); ··· 487 unsigned long plic_quirks = 0; 488 struct plic_handler *handler; 489 u32 nr_irqs, parent_hwirq; 490 - struct irq_domain *domain; 491 struct plic_priv *priv; 492 irq_hw_number_t hwirq; 493 - bool cpuhp_setup; 494 495 if (is_of_node(dev->fwnode)) { 496 const struct of_device_id *id; ··· 547 continue; 548 } 549 550 - /* Find parent domain and register chained handler */ 551 - domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); 552 - if (!plic_parent_irq && domain) { 553 - plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); 554 - if (plic_parent_irq) 555 - irq_set_chained_handler(plic_parent_irq, plic_handle_irq); 556 - } 557 - 558 /* 559 * When running in M-mode we need to ignore the S-mode handler. 560 * Here we assume it always comes later, but that might be a ··· 587 goto fail_cleanup_contexts; 588 589 /* 590 - * We can have multiple PLIC instances so setup cpuhp state 591 * and register syscore operations only once after context 592 * handlers of all online CPUs are initialized. 593 */ 594 - if (!plic_cpuhp_setup_done) { 595 - cpuhp_setup = true; 596 for_each_online_cpu(cpu) { 597 handler = per_cpu_ptr(&plic_handlers, cpu); 598 if (!handler->present) { 599 - cpuhp_setup = false; 600 break; 601 } 602 } 603 - if (cpuhp_setup) { 604 cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, 605 "irqchip/sifive/plic:starting", 606 plic_starting_cpu, plic_dying_cpu); 607 register_syscore_ops(&plic_irq_syscore_ops); 608 - plic_cpuhp_setup_done = true; 609 } 610 } 611
··· 85 struct plic_priv *priv; 86 }; 87 static int plic_parent_irq __ro_after_init; 88 + static bool plic_global_setup_done __ro_after_init; 89 static DEFINE_PER_CPU(struct plic_handler, plic_handlers); 90 91 static int plic_irq_set_type(struct irq_data *d, unsigned int type); ··· 487 unsigned long plic_quirks = 0; 488 struct plic_handler *handler; 489 u32 nr_irqs, parent_hwirq; 490 struct plic_priv *priv; 491 irq_hw_number_t hwirq; 492 493 if (is_of_node(dev->fwnode)) { 494 const struct of_device_id *id; ··· 549 continue; 550 } 551 552 /* 553 * When running in M-mode we need to ignore the S-mode handler. 554 * Here we assume it always comes later, but that might be a ··· 597 goto fail_cleanup_contexts; 598 599 /* 600 + * We can have multiple PLIC instances so setup global state 601 * and register syscore operations only once after context 602 * handlers of all online CPUs are initialized. 603 */ 604 + if (!plic_global_setup_done) { 605 + struct irq_domain *domain; 606 + bool global_setup = true; 607 + 608 for_each_online_cpu(cpu) { 609 handler = per_cpu_ptr(&plic_handlers, cpu); 610 if (!handler->present) { 611 + global_setup = false; 612 break; 613 } 614 } 615 + 616 + if (global_setup) { 617 + /* Find parent domain and register chained handler */ 618 + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); 619 + if (domain) 620 + plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); 621 + if (plic_parent_irq) 622 + irq_set_chained_handler(plic_parent_irq, plic_handle_irq); 623 + 624 cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, 625 "irqchip/sifive/plic:starting", 626 plic_starting_cpu, plic_dying_cpu); 627 register_syscore_ops(&plic_irq_syscore_ops); 628 + plic_global_setup_done = true; 629 } 630 } 631