2024-05-29 21:55:10

by Samuel Holland

[permalink] [raw]
Subject: [PATCH] irqchip/sifive-plic: Chain to parent IRQ after handlers are ready

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 <[email protected]>
Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/
Signed-off-by: Samuel Holland <[email protected]>
---

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



2024-05-30 07:13:45

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH] irqchip/sifive-plic: Chain to parent IRQ after handlers are ready

Hi Samuel,

On Wed, May 29, 2024 at 11:55 PM Samuel Holland
<[email protected]> 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 <[email protected]>
> Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/
> Signed-off-by: Samuel Holland <[email protected]>

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 <[email protected]>

Gr{oetje,eeting}s,

Geert


--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68korg

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

2024-05-30 14:28:45

by Anup Patel

[permalink] [raw]
Subject: Re: [PATCH] irqchip/sifive-plic: Chain to parent IRQ after handlers are ready

On Thu, May 30, 2024 at 3:25 AM Samuel Holland
<[email protected]> 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 <[email protected]>
> Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/
> Signed-off-by: Samuel Holland <[email protected]>

LGTM.

Reviewed-by: Anup Patel <[email protected]>

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

Subject: [tip: irq/urgent] irqchip/sifive-plic: Chain to parent IRQ after handlers are ready

The following commit has been merged into the irq/urgent branch of tip:

Commit-ID: e306a894bd511804ba9db7c00ca9cc05b55df1f2
Gitweb: https://git.kernel.org/tip/e306a894bd511804ba9db7c00ca9cc05b55df1f2
Author: Samuel Holland <[email protected]>
AuthorDate: Wed, 29 May 2024 14:54:56 -07:00
Committer: Thomas Gleixner <[email protected]>
CommitterDate: Mon, 03 Jun 2024 13:53:12 +02:00

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 <[email protected]>
Signed-off-by: Samuel Holland <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
Tested-by: Geert Uytterhoeven <[email protected]>
Reviewed-by: Anup Patel <[email protected]>
Cc: [email protected]
Link: https://lore.kernel.org/r/[email protected]
Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/
---
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 8fb183c..9e22f7e 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 @@ 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;
}
}