diff mbox series

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

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

Checks

Context Check Description
conchuod/vmtest-for-next-PR success PR summary
conchuod/patch-1-test-1 success .github/scripts/patches/tests/build_rv32_defconfig.sh
conchuod/patch-1-test-2 success .github/scripts/patches/tests/build_rv64_clang_allmodconfig.sh
conchuod/patch-1-test-3 success .github/scripts/patches/tests/build_rv64_gcc_allmodconfig.sh
conchuod/patch-1-test-4 success .github/scripts/patches/tests/build_rv64_nommu_k210_defconfig.sh
conchuod/patch-1-test-5 success .github/scripts/patches/tests/build_rv64_nommu_virt_defconfig.sh
conchuod/patch-1-test-6 success .github/scripts/patches/tests/checkpatch.sh
conchuod/patch-1-test-7 success .github/scripts/patches/tests/dtb_warn_rv64.sh
conchuod/patch-1-test-8 success .github/scripts/patches/tests/header_inline.sh
conchuod/patch-1-test-9 success .github/scripts/patches/tests/kdoc.sh
conchuod/patch-1-test-10 success .github/scripts/patches/tests/module_param.sh
conchuod/patch-1-test-11 success .github/scripts/patches/tests/verify_fixes.sh
conchuod/patch-1-test-12 success .github/scripts/patches/tests/verify_signedoff.sh

Commit Message

Samuel Holland May 29, 2024, 9:54 p.m. UTC
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(-)

Comments

Geert Uytterhoeven May 30, 2024, 7:13 a.m. UTC | #1
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
Anup Patel May 30, 2024, 2:28 p.m. UTC | #2
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 mbox series

Patch

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;
 		}
 	}