]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
irqchip/sifive-plic: Chain to parent IRQ after handlers are ready
authorSamuel Holland <samuel.holland@sifive.com>
Wed, 29 May 2024 21:54:56 +0000 (14:54 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Jun 2024 12:40:30 +0000 (14:40 +0200)
commit e306a894bd511804ba9db7c00ca9cc05b55df1f2 upstream.

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/
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/irqchip/irq-sifive-plic.c

index f3d4cb9e34f7d8b824ad4fbb8e2e9b10b71232d2..d246b7230bd9c7da2ee449dec5f6ac8f3be93836 100644 (file)
@@ -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);
@@ -490,10 +490,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;
@@ -552,14 +550,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
@@ -600,25 +590,35 @@ done:
                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;
                }
        }