]> git.ipfire.org Git - thirdparty/linux.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)
committerThomas Gleixner <tglx@linutronix.de>
Mon, 3 Jun 2024 11:53:12 +0000 (13:53 +0200)
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/
drivers/irqchip/irq-sifive-plic.c

index 8fb183ced1e7781a00e6eac880481e198d60843f..9e22f7e378f5020e3d3a762769d944285a3ad46f 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);
@@ -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 @@ 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;
                }
        }