Message ID | 20240529215458.937817-1-samuel.holland@sifive.com (mailing list archive) |
---|---|
State | Handled Elsewhere |
Headers | show |
Series | irqchip/sifive-plic: Chain to parent IRQ after handlers are ready | expand |
Hi Samuel, On Wed, May 29, 2024 at 11:55 PM Samuel Holland <samuel.holland@sifive.com> wrote: > Now that the PLIC uses a platform driver, the driver 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(). 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> > Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/ > Signed-off-by: Samuel Holland <samuel.holland@sifive.com> Thanks for your patch! This fixes the issue I was seering on Starlight, and does not seem to cause regressions on RZ/Five, Icicle, K210, or VexRiscV. Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
On Thu, May 30, 2024 at 3:25 AM Samuel Holland <samuel.holland@sifive.com> wrote: > > Now that the PLIC uses a platform driver, the driver 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(). 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> > Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/ > Signed-off-by: Samuel Holland <samuel.holland@sifive.com> LGTM. Reviewed-by: Anup Patel <anup@brainfault.org> Thanks for fixing this. Regards, Anup > --- > > drivers/irqchip/irq-sifive-plic.c | 34 +++++++++++++++---------------- > 1 file changed, 17 insertions(+), 17 deletions(-) > > diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c > index 8fb183ced1e7..9e22f7e378f5 100644 > --- a/drivers/irqchip/irq-sifive-plic.c > +++ b/drivers/irqchip/irq-sifive-plic.c > @@ -85,7 +85,7 @@ struct plic_handler { > struct plic_priv *priv; > }; > static int plic_parent_irq __ro_after_init; > -static bool plic_cpuhp_setup_done __ro_after_init; > +static bool plic_global_setup_done __ro_after_init; > static DEFINE_PER_CPU(struct plic_handler, plic_handlers); > > static int plic_irq_set_type(struct irq_data *d, unsigned int type); > @@ -487,10 +487,8 @@ static int plic_probe(struct platform_device *pdev) > unsigned long plic_quirks = 0; > struct plic_handler *handler; > u32 nr_irqs, parent_hwirq; > - struct irq_domain *domain; > struct plic_priv *priv; > irq_hw_number_t hwirq; > - bool cpuhp_setup; > > if (is_of_node(dev->fwnode)) { > const struct of_device_id *id; > @@ -549,14 +547,6 @@ static int plic_probe(struct platform_device *pdev) > continue; > } > > - /* Find parent domain and register chained handler */ > - domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); > - if (!plic_parent_irq && domain) { > - plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); > - if (plic_parent_irq) > - irq_set_chained_handler(plic_parent_irq, plic_handle_irq); > - } > - > /* > * When running in M-mode we need to ignore the S-mode handler. > * Here we assume it always comes later, but that might be a > @@ -597,25 +587,35 @@ static int plic_probe(struct platform_device *pdev) > goto fail_cleanup_contexts; > > /* > - * We can have multiple PLIC instances so setup cpuhp state > + * We can have multiple PLIC instances so setup global state > * and register syscore operations only once after context > * handlers of all online CPUs are initialized. > */ > - if (!plic_cpuhp_setup_done) { > - cpuhp_setup = true; > + if (!plic_global_setup_done) { > + struct irq_domain *domain; > + bool global_setup = true; > + > for_each_online_cpu(cpu) { > handler = per_cpu_ptr(&plic_handlers, cpu); > if (!handler->present) { > - cpuhp_setup = false; > + global_setup = false; > break; > } > } > - if (cpuhp_setup) { > + > + if (global_setup) { > + /* Find parent domain and register chained handler */ > + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); > + if (domain) > + plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); > + if (plic_parent_irq) > + irq_set_chained_handler(plic_parent_irq, plic_handle_irq); > + > cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, > "irqchip/sifive/plic:starting", > plic_starting_cpu, plic_dying_cpu); > register_syscore_ops(&plic_irq_syscore_ops); > - plic_cpuhp_setup_done = true; > + plic_global_setup_done = true; > } > } > > -- > 2.44.1 > >
diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 8fb183ced1e7..9e22f7e378f5 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -85,7 +85,7 @@ struct plic_handler { struct plic_priv *priv; }; static int plic_parent_irq __ro_after_init; -static bool plic_cpuhp_setup_done __ro_after_init; +static bool plic_global_setup_done __ro_after_init; static DEFINE_PER_CPU(struct plic_handler, plic_handlers); static int plic_irq_set_type(struct irq_data *d, unsigned int type); @@ -487,10 +487,8 @@ static int plic_probe(struct platform_device *pdev) unsigned long plic_quirks = 0; struct plic_handler *handler; u32 nr_irqs, parent_hwirq; - struct irq_domain *domain; struct plic_priv *priv; irq_hw_number_t hwirq; - bool cpuhp_setup; if (is_of_node(dev->fwnode)) { const struct of_device_id *id; @@ -549,14 +547,6 @@ static int plic_probe(struct platform_device *pdev) continue; } - /* Find parent domain and register chained handler */ - domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); - if (!plic_parent_irq && domain) { - plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); - if (plic_parent_irq) - irq_set_chained_handler(plic_parent_irq, plic_handle_irq); - } - /* * When running in M-mode we need to ignore the S-mode handler. * Here we assume it always comes later, but that might be a @@ -597,25 +587,35 @@ static int plic_probe(struct platform_device *pdev) goto fail_cleanup_contexts; /* - * We can have multiple PLIC instances so setup cpuhp state + * We can have multiple PLIC instances so setup global state * and register syscore operations only once after context * handlers of all online CPUs are initialized. */ - if (!plic_cpuhp_setup_done) { - cpuhp_setup = true; + if (!plic_global_setup_done) { + struct irq_domain *domain; + bool global_setup = true; + for_each_online_cpu(cpu) { handler = per_cpu_ptr(&plic_handlers, cpu); if (!handler->present) { - cpuhp_setup = false; + global_setup = false; break; } } - if (cpuhp_setup) { + + if (global_setup) { + /* Find parent domain and register chained handler */ + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); + if (domain) + plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); + if (plic_parent_irq) + irq_set_chained_handler(plic_parent_irq, plic_handle_irq); + cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, "irqchip/sifive/plic:starting", plic_starting_cpu, plic_dying_cpu); register_syscore_ops(&plic_irq_syscore_ops); - plic_cpuhp_setup_done = true; + plic_global_setup_done = true; } }
Now that the PLIC uses a platform driver, the driver 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(). 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> Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/ Signed-off-by: Samuel Holland <samuel.holland@sifive.com> --- drivers/irqchip/irq-sifive-plic.c | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-)