* [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable()
@ 2026-08-05 19:27 Fabio Estevam
2026-08-05 19:27 ` [PATCH v3 2/2] irqchip/imx-irqsteer: Validate IRQ count before creating domain Fabio Estevam
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Fabio Estevam @ 2026-08-05 19:27 UTC (permalink / raw)
To: tglx; +Cc: radu, Frank.Li, imx, linux-kernel, Fabio Estevam
From: Fabio Estevam <festevam@nabladev.com>
imx_irqsteer_probe() enables runtime PM, but imx_irqsteer_remove() does
not disable it. Consequently, runtime PM remains enabled after unbinding
the device, and rebinding it triggers:
Unbalanced pm_runtime_enable!
Use devm_pm_runtime_enable() to automatically disable runtime PM when
the device is removed. Set up runtime PM before creating the IRQ domain
and registering chained handlers so that a failure cannot leave either
resource pointing at freed driver data.
Fixes: 4730d2233311 ("irqchip/imx-irqsteer: Add runtime PM support")
Signed-off-by: Fabio Estevam <festevam@nabladev.com>
---
Changes since v1:
- Move devm_pm_runtime_enable() prior to irq_domain_create_linear(). (Frank)
drivers/irqchip/irq-imx-irqsteer.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 87b07f517be3..653e25115083 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -236,6 +236,11 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
if (irqsteer_has_chanctrl(data->devtype_data))
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
+ pm_runtime_set_active(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ goto out;
+
data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32,
&imx_irqsteer_domain_ops, data);
if (!data->domain) {
@@ -262,9 +267,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
- pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
-
return 0;
out:
clk_disable_unprepare(data->ipg_clk);
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v3 2/2] irqchip/imx-irqsteer: Validate IRQ count before creating domain 2026-08-05 19:27 [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() Fabio Estevam @ 2026-08-05 19:27 ` Fabio Estevam 2026-08-05 20:37 ` Frank Li 2026-08-05 20:36 ` [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() Frank Li 2026-08-06 21:28 ` Radu Rendec 2 siblings, 1 reply; 7+ messages in thread From: Fabio Estevam @ 2026-08-05 19:27 UTC (permalink / raw) To: tglx; +Cc: radu, Frank.Li, imx, linux-kernel, Fabio Estevam From: Fabio Estevam <festevam@nabladev.com> The IRQ count is validated after creating the IRQ domain. If it is invalid, probe returns without removing the domain, leaving its host data pointing at devm-managed memory that is freed on probe failure. Validate the count before allocating resources to avoid the leak and dangling pointer. Fixes: 28528fca4908 ("irqchip/imx-irqsteer: Add multi output interrupts support") Signed-off-by: Fabio Estevam <festevam@nabladev.com> --- Changes since v2: - Newly introduced. drivers/irqchip/irq-imx-irqsteer.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 653e25115083..55aec60dee40 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -217,6 +217,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev) */ data->irq_count = DIV_ROUND_UP(irqs_num, 64); data->reg_num = irqs_num / 32; + if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) + return -EINVAL; if (IS_ENABLED(CONFIG_PM)) { data->saved_reg = devm_kzalloc(&pdev->dev, @@ -250,11 +252,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) } irq_domain_set_pm_device(data->domain, &pdev->dev); - if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) { - ret = -EINVAL; - goto out; - } - for (i = 0; i < data->irq_count; i++) { data->irq[i] = irq_of_parse_and_map(np, i); if (!data->irq[i]) -- 2.43.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] irqchip/imx-irqsteer: Validate IRQ count before creating domain 2026-08-05 19:27 ` [PATCH v3 2/2] irqchip/imx-irqsteer: Validate IRQ count before creating domain Fabio Estevam @ 2026-08-05 20:37 ` Frank Li 0 siblings, 0 replies; 7+ messages in thread From: Frank Li @ 2026-08-05 20:37 UTC (permalink / raw) To: Fabio Estevam; +Cc: tglx, radu, Frank.Li, imx, linux-kernel, Fabio Estevam On Wed, Aug 05, 2026 at 04:27:43PM -0300, Fabio Estevam wrote: > From: Fabio Estevam <festevam@nabladev.com> > > The IRQ count is validated after creating the IRQ domain. If it is > invalid, probe returns without removing the domain, leaving its host > data pointing at devm-managed memory that is freed on probe failure. > > Validate the count before allocating resources to avoid the leak and > dangling pointer. > > Fixes: 28528fca4908 ("irqchip/imx-irqsteer: Add multi output interrupts support") > Signed-off-by: Fabio Estevam <festevam@nabladev.com> > --- Reviewed-by: Frank Li <Frank.Li@nxp.com> > Changes since v2: > - Newly introduced. > > drivers/irqchip/irq-imx-irqsteer.c | 7 ++----- > 1 file changed, 2 insertions(+), 5 deletions(-) > > diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c > index 653e25115083..55aec60dee40 100644 > --- a/drivers/irqchip/irq-imx-irqsteer.c > +++ b/drivers/irqchip/irq-imx-irqsteer.c > @@ -217,6 +217,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > */ > data->irq_count = DIV_ROUND_UP(irqs_num, 64); > data->reg_num = irqs_num / 32; > + if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) > + return -EINVAL; > > if (IS_ENABLED(CONFIG_PM)) { > data->saved_reg = devm_kzalloc(&pdev->dev, > @@ -250,11 +252,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > } > irq_domain_set_pm_device(data->domain, &pdev->dev); > > - if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) { > - ret = -EINVAL; > - goto out; > - } > - > for (i = 0; i < data->irq_count; i++) { > data->irq[i] = irq_of_parse_and_map(np, i); > if (!data->irq[i]) > -- > 2.43.0 > > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() 2026-08-05 19:27 [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() Fabio Estevam 2026-08-05 19:27 ` [PATCH v3 2/2] irqchip/imx-irqsteer: Validate IRQ count before creating domain Fabio Estevam @ 2026-08-05 20:36 ` Frank Li 2026-08-06 21:28 ` Radu Rendec 2 siblings, 0 replies; 7+ messages in thread From: Frank Li @ 2026-08-05 20:36 UTC (permalink / raw) To: Fabio Estevam; +Cc: tglx, radu, Frank.Li, imx, linux-kernel, Fabio Estevam On Wed, Aug 05, 2026 at 04:27:42PM -0300, Fabio Estevam wrote: > From: Fabio Estevam <festevam@nabladev.com> > > imx_irqsteer_probe() enables runtime PM, but imx_irqsteer_remove() does > not disable it. Consequently, runtime PM remains enabled after unbinding > the device, and rebinding it triggers: > > Unbalanced pm_runtime_enable! > > Use devm_pm_runtime_enable() to automatically disable runtime PM when > the device is removed. Set up runtime PM before creating the IRQ domain > and registering chained handlers so that a failure cannot leave either > resource pointing at freed driver data. > > Fixes: 4730d2233311 ("irqchip/imx-irqsteer: Add runtime PM support") > Signed-off-by: Fabio Estevam <festevam@nabladev.com> > --- > Changes since v1: > - Move devm_pm_runtime_enable() prior to irq_domain_create_linear(). (Frank) Reviewed-by: Frank Li <Frank.Li@nxp.com> > > drivers/irqchip/irq-imx-irqsteer.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c > index 87b07f517be3..653e25115083 100644 > --- a/drivers/irqchip/irq-imx-irqsteer.c > +++ b/drivers/irqchip/irq-imx-irqsteer.c > @@ -236,6 +236,11 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > if (irqsteer_has_chanctrl(data->devtype_data)) > writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); > > + pm_runtime_set_active(&pdev->dev); > + ret = devm_pm_runtime_enable(&pdev->dev); > + if (ret) > + goto out; > + > data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32, > &imx_irqsteer_domain_ops, data); > if (!data->domain) { > @@ -262,9 +267,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > > platform_set_drvdata(pdev, data); > > - pm_runtime_set_active(&pdev->dev); > - pm_runtime_enable(&pdev->dev); > - > return 0; > out: > clk_disable_unprepare(data->ipg_clk); > -- > 2.43.0 > > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() 2026-08-05 19:27 [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() Fabio Estevam 2026-08-05 19:27 ` [PATCH v3 2/2] irqchip/imx-irqsteer: Validate IRQ count before creating domain Fabio Estevam 2026-08-05 20:36 ` [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() Frank Li @ 2026-08-06 21:28 ` Radu Rendec 2026-09-11 19:32 ` Frank Li 2 siblings, 1 reply; 7+ messages in thread From: Radu Rendec @ 2026-08-06 21:28 UTC (permalink / raw) To: Fabio Estevam, tglx; +Cc: Frank.Li, imx, linux-kernel, Fabio Estevam On Wed, 2026-08-05 at 16:27 -0300, Fabio Estevam wrote: > From: Fabio Estevam <festevam@nabladev.com> > > imx_irqsteer_probe() enables runtime PM, but imx_irqsteer_remove() does > not disable it. Consequently, runtime PM remains enabled after unbinding > the device, and rebinding it triggers: > > Unbalanced pm_runtime_enable! > > Use devm_pm_runtime_enable() to automatically disable runtime PM when > the device is removed. Set up runtime PM before creating the IRQ domain > and registering chained handlers so that a failure cannot leave either > resource pointing at freed driver data. > > Fixes: 4730d2233311 ("irqchip/imx-irqsteer: Add runtime PM support") > Signed-off-by: Fabio Estevam <festevam@nabladev.com> > --- > Changes since v1: > - Move devm_pm_runtime_enable() prior to irq_domain_create_linear(). (Frank) > > drivers/irqchip/irq-imx-irqsteer.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c > index 87b07f517be3..653e25115083 100644 > --- a/drivers/irqchip/irq-imx-irqsteer.c > +++ b/drivers/irqchip/irq-imx-irqsteer.c > @@ -236,6 +236,11 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > if (irqsteer_has_chanctrl(data->devtype_data)) > writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); > > + pm_runtime_set_active(&pdev->dev); > + ret = devm_pm_runtime_enable(&pdev->dev); > + if (ret) > + goto out; > + > data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32, > &imx_irqsteer_domain_ops, data); > if (!data->domain) { > @@ -262,9 +267,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > > platform_set_drvdata(pdev, data); > > - pm_runtime_set_active(&pdev->dev); > - pm_runtime_enable(&pdev->dev); > - > return 0; > out: > clk_disable_unprepare(data->ipg_clk); I still believe there is something off with the way pm_runtime is handled, and that the double clock disable is possible. Since (like I said) I have very limited understanding of the runtime_pm framework, I decided to make a little experiment. With the dummy module below, I see this: [ 548.253591] pm_dummy pm_dummy: pm_dummy_probe() executed [ 548.254209] pm_dummy pm_dummy: clock enabled; refcount: 1 [ 548.255028] pm_dummy pm_dummy: pm_dummy_runtime_suspend() triggered [ 548.255845] pm_dummy pm_dummy: clock disabled; refcount: 0 [ 554.261820] pm_dummy pm_dummy: pm_dummy_runtime_resume() triggered [ 554.263100] pm_dummy pm_dummy: clock enabled; refcount: 1 [ 554.264006] pm_dummy pm_dummy: pm_dummy_runtime_suspend() triggered [ 554.264997] pm_dummy pm_dummy: clock disabled; refcount: 0 [ 554.265819] pm_dummy pm_dummy: pm_dummy_remove() executed [ 554.266609] pm_dummy pm_dummy: clock disabled; refcount: -1 [ 554.267468] pm_dummy pm_dummy: ************************************************** [ 554.268554] pm_dummy pm_dummy: [BUG DETECTED] Clock disable count underflow! (-1) [ 554.269479] pm_dummy pm_dummy: ************************************************** What I find interesting is that the device is suspended immediately during probe(), then it's automatically resumed and immediately suspended again right before remove(). The latter is probably a side effect of devm_pm_runtime_enable(). But in any case, the clock *is* disabled twice, and that's even without any explicit suspend or resume, it's just by loading and unloading the module. #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Radu Rendec <radu@rendec.net>"); MODULE_DESCRIPTION("runtime_pm playground"); static int mock_clk_count = 0; static int mock_clk_prepare_enable(struct device *dev) { mock_clk_count++; dev_info(dev, "clock enabled; refcount: %d\n", mock_clk_count); return 0; } static void mock_clk_disable_unprepare(struct device *dev) { mock_clk_count--; dev_info(dev, "clock disabled; refcount: %d\n", mock_clk_count); if (mock_clk_count < 0) { dev_err(dev, "**************************************************\n"); dev_err(dev, "[BUG DETECTED] Clock disable count underflow! (%d)\n", mock_clk_count); dev_err(dev, "**************************************************\n"); } } static int pm_dummy_runtime_suspend(struct device *dev) { dev_info(dev, "%s() triggered\n", __func__); mock_clk_disable_unprepare(dev); return 0; } static int pm_dummy_runtime_resume(struct device *dev) { dev_info(dev, "%s() triggered\n", __func__); return mock_clk_prepare_enable(dev); } static const struct dev_pm_ops pm_dummy_pm_ops = { SET_RUNTIME_PM_OPS(pm_dummy_runtime_suspend, pm_dummy_runtime_resume, NULL) }; static int pm_dummy_probe(struct platform_device *pdev) { dev_info(&pdev->dev, "%s() executed\n", __func__); mock_clk_prepare_enable(&pdev->dev); pm_runtime_set_active(&pdev->dev); devm_pm_runtime_enable(&pdev->dev); return 0; } static void pm_dummy_remove(struct platform_device *pdev) { dev_info(&pdev->dev, "%s() executed\n", __func__); mock_clk_disable_unprepare(&pdev->dev); } static struct platform_driver pm_dummy = { .probe = pm_dummy_probe, .remove = pm_dummy_remove, .driver = { .name = "pm_dummy", .pm = &pm_dummy_pm_ops, }, }; static struct platform_device *pdev; static int __init pm_demo_init(void) { int ret; ret = platform_driver_register(&pm_dummy); if (ret) return ret; pdev = platform_device_register_simple("pm_dummy", -1, NULL, 0); if (IS_ERR(pdev)) { platform_driver_unregister(&pm_dummy); return PTR_ERR(pdev); } return 0; } static void __exit pm_demo_exit(void) { platform_device_unregister(pdev); platform_driver_unregister(&pm_dummy); } module_init(pm_demo_init); module_exit(pm_demo_exit); ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() 2026-08-06 21:28 ` Radu Rendec @ 2026-09-11 19:32 ` Frank Li 2026-09-13 17:40 ` Radu Rendec 0 siblings, 1 reply; 7+ messages in thread From: Frank Li @ 2026-09-11 19:32 UTC (permalink / raw) To: Radu Rendec Cc: Fabio Estevam, tglx, Frank.Li, imx, linux-kernel, Fabio Estevam On Thu, Aug 06, 2026 at 05:28:59PM -0400, Radu Rendec wrote: > On Wed, 2026-08-05 at 16:27 -0300, Fabio Estevam wrote: > > From: Fabio Estevam <festevam@nabladev.com> > > > > imx_irqsteer_probe() enables runtime PM, but imx_irqsteer_remove() does > > not disable it. Consequently, runtime PM remains enabled after unbinding > > the device, and rebinding it triggers: > > > > Unbalanced pm_runtime_enable! > > > > Use devm_pm_runtime_enable() to automatically disable runtime PM when > > the device is removed. Set up runtime PM before creating the IRQ domain > > and registering chained handlers so that a failure cannot leave either > > resource pointing at freed driver data. > > > > Fixes: 4730d2233311 ("irqchip/imx-irqsteer: Add runtime PM support") > > Signed-off-by: Fabio Estevam <festevam@nabladev.com> > > --- > > Changes since v1: > > - Move devm_pm_runtime_enable() prior to irq_domain_create_linear(). (Frank) > > > > drivers/irqchip/irq-imx-irqsteer.c | 8 +++++--- > > 1 file changed, 5 insertions(+), 3 deletions(-) > > > > diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c > > index 87b07f517be3..653e25115083 100644 > > --- a/drivers/irqchip/irq-imx-irqsteer.c > > +++ b/drivers/irqchip/irq-imx-irqsteer.c > > @@ -236,6 +236,11 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > > if (irqsteer_has_chanctrl(data->devtype_data)) > > writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); > > > > + pm_runtime_set_active(&pdev->dev); > > + ret = devm_pm_runtime_enable(&pdev->dev); > > + if (ret) > > + goto out; > > + > > data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32, > > &imx_irqsteer_domain_ops, data); > > if (!data->domain) { > > @@ -262,9 +267,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > > > > platform_set_drvdata(pdev, data); > > > > - pm_runtime_set_active(&pdev->dev); > > - pm_runtime_enable(&pdev->dev); > > - > > return 0; > > out: > > clk_disable_unprepare(data->ipg_clk); > > I still believe there is something off with the way pm_runtime is > handled, and that the double clock disable is possible. > > Since (like I said) I have very limited understanding of the runtime_pm > framework, I decided to make a little experiment. > > With the dummy module below, I see this: > [ 548.253591] pm_dummy pm_dummy: pm_dummy_probe() executed > [ 548.254209] pm_dummy pm_dummy: clock enabled; refcount: 1 > [ 548.255028] pm_dummy pm_dummy: pm_dummy_runtime_suspend() triggered https://elixir.bootlin.com/linux/v7.2.2/source/drivers/base/dd.c#L827 after probe, pm_request_idle(dev), which trigger pm_dummy_runtime_suspend() > [ 548.255845] pm_dummy pm_dummy: clock disabled; refcount: 0 > [ 554.261820] pm_dummy pm_dummy: pm_dummy_runtime_resume() triggered > [ 554.263100] pm_dummy pm_dummy: clock enabled; refcount: 1 > [ 554.264006] pm_dummy pm_dummy: pm_dummy_runtime_suspend() triggered > [ 554.264997] pm_dummy pm_dummy: clock disabled; refcount: 0 > [ 554.265819] pm_dummy pm_dummy: pm_dummy_remove() executed You should set runtime_resume() at remove function to match probe's state. if your driver remove() did disable clock. It is trick between runtime pm and devm clock management. The beneafit of keep runtime active in probe, driver can work when disable CONFIG_PM, but complex at tear down. keep inactive in probe, code will be simple, but it will not work if disable CONFIG_PM because clock have not enabled Frank > [ 554.266609] pm_dummy pm_dummy: clock disabled; refcount: -1 > [ 554.267468] pm_dummy pm_dummy: ************************************************** > [ 554.268554] pm_dummy pm_dummy: [BUG DETECTED] Clock disable count underflow! (-1) > [ 554.269479] pm_dummy pm_dummy: ************************************************** > > What I find interesting is that the device is suspended immediately > during probe(), then it's automatically resumed and immediately > suspended again right before remove(). The latter is probably a side > effect of devm_pm_runtime_enable(). But in any case, the clock *is* > disabled twice, and that's even without any explicit suspend or resume, > it's just by loading and unloading the module. > > #include <linux/module.h> > #include <linux/kernel.h> > #include <linux/init.h> > #include <linux/platform_device.h> > #include <linux/pm_runtime.h> > > MODULE_LICENSE("GPL"); > MODULE_AUTHOR("Radu Rendec <radu@rendec.net>"); > MODULE_DESCRIPTION("runtime_pm playground"); > > static int mock_clk_count = 0; > > static int mock_clk_prepare_enable(struct device *dev) > { > mock_clk_count++; > dev_info(dev, "clock enabled; refcount: %d\n", mock_clk_count); > return 0; > } > > static void mock_clk_disable_unprepare(struct device *dev) > { > mock_clk_count--; > dev_info(dev, "clock disabled; refcount: %d\n", mock_clk_count); > > if (mock_clk_count < 0) { > dev_err(dev, "**************************************************\n"); > dev_err(dev, "[BUG DETECTED] Clock disable count underflow! (%d)\n", mock_clk_count); > dev_err(dev, "**************************************************\n"); > } > } > > static int pm_dummy_runtime_suspend(struct device *dev) > { > dev_info(dev, "%s() triggered\n", __func__); > mock_clk_disable_unprepare(dev); > return 0; > } > > static int pm_dummy_runtime_resume(struct device *dev) > { > dev_info(dev, "%s() triggered\n", __func__); > return mock_clk_prepare_enable(dev); > } > > static const struct dev_pm_ops pm_dummy_pm_ops = { > SET_RUNTIME_PM_OPS(pm_dummy_runtime_suspend, pm_dummy_runtime_resume, NULL) > }; > > static int pm_dummy_probe(struct platform_device *pdev) > { > dev_info(&pdev->dev, "%s() executed\n", __func__); > > mock_clk_prepare_enable(&pdev->dev); > pm_runtime_set_active(&pdev->dev); > devm_pm_runtime_enable(&pdev->dev); > > return 0; > } > > static void pm_dummy_remove(struct platform_device *pdev) > { > dev_info(&pdev->dev, "%s() executed\n", __func__); > > mock_clk_disable_unprepare(&pdev->dev); > } > > static struct platform_driver pm_dummy = { > .probe = pm_dummy_probe, > .remove = pm_dummy_remove, > .driver = { > .name = "pm_dummy", > .pm = &pm_dummy_pm_ops, > }, > }; > > static struct platform_device *pdev; > > static int __init pm_demo_init(void) > { > int ret; > > ret = platform_driver_register(&pm_dummy); > if (ret) > return ret; > > pdev = platform_device_register_simple("pm_dummy", -1, NULL, 0); > if (IS_ERR(pdev)) { > platform_driver_unregister(&pm_dummy); > return PTR_ERR(pdev); > } > > return 0; > } > > static void __exit pm_demo_exit(void) > { > platform_device_unregister(pdev); > platform_driver_unregister(&pm_dummy); > } > > module_init(pm_demo_init); > module_exit(pm_demo_exit); ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() 2026-09-11 19:32 ` Frank Li @ 2026-09-13 17:40 ` Radu Rendec 0 siblings, 0 replies; 7+ messages in thread From: Radu Rendec @ 2026-09-13 17:40 UTC (permalink / raw) To: Frank Li; +Cc: Fabio Estevam, tglx, Frank.Li, imx, linux-kernel, Fabio Estevam On Fri, 2026-09-11 at 15:32 -0400, Frank Li wrote: > On Thu, Aug 06, 2026 at 05:28:59PM -0400, Radu Rendec wrote: > > On Wed, 2026-08-05 at 16:27 -0300, Fabio Estevam wrote: > > > From: Fabio Estevam <festevam@nabladev.com> > > > > > > imx_irqsteer_probe() enables runtime PM, but imx_irqsteer_remove() does > > > not disable it. Consequently, runtime PM remains enabled after unbinding > > > the device, and rebinding it triggers: > > > > > > Unbalanced pm_runtime_enable! > > > > > > Use devm_pm_runtime_enable() to automatically disable runtime PM when > > > the device is removed. Set up runtime PM before creating the IRQ domain > > > and registering chained handlers so that a failure cannot leave either > > > resource pointing at freed driver data. > > > > > > Fixes: 4730d2233311 ("irqchip/imx-irqsteer: Add runtime PM support") > > > Signed-off-by: Fabio Estevam <festevam@nabladev.com> > > > --- > > > Changes since v1: > > > - Move devm_pm_runtime_enable() prior to irq_domain_create_linear(). (Frank) > > > > > > drivers/irqchip/irq-imx-irqsteer.c | 8 +++++--- > > > 1 file changed, 5 insertions(+), 3 deletions(-) > > > > > > diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c > > > index 87b07f517be3..653e25115083 100644 > > > --- a/drivers/irqchip/irq-imx-irqsteer.c > > > +++ b/drivers/irqchip/irq-imx-irqsteer.c > > > @@ -236,6 +236,11 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > > > if (irqsteer_has_chanctrl(data->devtype_data)) > > > writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); > > > > > > + pm_runtime_set_active(&pdev->dev); > > > + ret = devm_pm_runtime_enable(&pdev->dev); > > > + if (ret) > > > + goto out; > > > + > > > data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32, > > > &imx_irqsteer_domain_ops, data); > > > if (!data->domain) { > > > @@ -262,9 +267,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) > > > > > > platform_set_drvdata(pdev, data); > > > > > > - pm_runtime_set_active(&pdev->dev); > > > - pm_runtime_enable(&pdev->dev); > > > - > > > return 0; > > > out: > > > clk_disable_unprepare(data->ipg_clk); > > > > I still believe there is something off with the way pm_runtime is > > handled, and that the double clock disable is possible. > > > > Since (like I said) I have very limited understanding of the runtime_pm > > framework, I decided to make a little experiment. > > > > With the dummy module below, I see this: > > [ 548.253591] pm_dummy pm_dummy: pm_dummy_probe() executed > > [ 548.254209] pm_dummy pm_dummy: clock enabled; refcount: 1 > > [ 548.255028] pm_dummy pm_dummy: pm_dummy_runtime_suspend() triggered > > https://elixir.bootlin.com/linux/v7.2.2/source/drivers/base/dd.c#L827 > > after probe, pm_request_idle(dev), which trigger pm_dummy_runtime_suspend() > > > [ 548.255845] pm_dummy pm_dummy: clock disabled; refcount: 0 > > > [ 554.261820] pm_dummy pm_dummy: pm_dummy_runtime_resume() triggered > > [ 554.263100] pm_dummy pm_dummy: clock enabled; refcount: 1 > > [ 554.264006] pm_dummy pm_dummy: pm_dummy_runtime_suspend() triggered > > [ 554.264997] pm_dummy pm_dummy: clock disabled; refcount: 0 > > [ 554.265819] pm_dummy pm_dummy: pm_dummy_remove() executed > > You should set runtime_resume() at remove function to match probe's state. > if your driver remove() did disable clock. > > It is trick between runtime pm and devm clock management. > > The beneafit of keep runtime active in probe, driver can work when disable > CONFIG_PM, but complex at tear down. > > keep inactive in probe, code will be simple, but it will not work if > disable CONFIG_PM because clock have not enabled Thanks, Frank! I appreciate you took the time to explain this in detail. The dummy driver followed the exact same logic/sequence as the proposed patch, and the purpose was to observe the behavior of runtime_pm in isolation and prove that something wasn't quite right about the clock management. I believe this is now handled correctly in Zhipeng's consolidated patch series against imx-irqsteer. > > [ 554.266609] pm_dummy pm_dummy: clock disabled; refcount: -1 > > [ 554.267468] pm_dummy pm_dummy: ************************************************** > > [ 554.268554] pm_dummy pm_dummy: [BUG DETECTED] Clock disable count underflow! (-1) > > [ 554.269479] pm_dummy pm_dummy: ************************************************** > > > > What I find interesting is that the device is suspended immediately > > during probe(), then it's automatically resumed and immediately > > suspended again right before remove(). The latter is probably a side > > effect of devm_pm_runtime_enable(). But in any case, the clock *is* > > disabled twice, and that's even without any explicit suspend or resume, > > it's just by loading and unloading the module. > > > > #include <linux/module.h> > > #include <linux/kernel.h> > > #include <linux/init.h> > > #include <linux/platform_device.h> > > #include <linux/pm_runtime.h> > > > > MODULE_LICENSE("GPL"); > > MODULE_AUTHOR("Radu Rendec <radu@rendec.net>"); > > MODULE_DESCRIPTION("runtime_pm playground"); > > > > static int mock_clk_count = 0; > > > > static int mock_clk_prepare_enable(struct device *dev) > > { > > mock_clk_count++; > > dev_info(dev, "clock enabled; refcount: %d\n", mock_clk_count); > > return 0; > > } > > > > static void mock_clk_disable_unprepare(struct device *dev) > > { > > mock_clk_count--; > > dev_info(dev, "clock disabled; refcount: %d\n", mock_clk_count); > > > > if (mock_clk_count < 0) { > > dev_err(dev, "**************************************************\n"); > > dev_err(dev, "[BUG DETECTED] Clock disable count underflow! (%d)\n", mock_clk_count); > > dev_err(dev, "**************************************************\n"); > > } > > } > > > > static int pm_dummy_runtime_suspend(struct device *dev) > > { > > dev_info(dev, "%s() triggered\n", __func__); > > mock_clk_disable_unprepare(dev); > > return 0; > > } > > > > static int pm_dummy_runtime_resume(struct device *dev) > > { > > dev_info(dev, "%s() triggered\n", __func__); > > return mock_clk_prepare_enable(dev); > > } > > > > static const struct dev_pm_ops pm_dummy_pm_ops = { > > SET_RUNTIME_PM_OPS(pm_dummy_runtime_suspend, pm_dummy_runtime_resume, NULL) > > }; > > > > static int pm_dummy_probe(struct platform_device *pdev) > > { > > dev_info(&pdev->dev, "%s() executed\n", __func__); > > > > mock_clk_prepare_enable(&pdev->dev); > > pm_runtime_set_active(&pdev->dev); > > devm_pm_runtime_enable(&pdev->dev); > > > > return 0; > > } > > > > static void pm_dummy_remove(struct platform_device *pdev) > > { > > dev_info(&pdev->dev, "%s() executed\n", __func__); > > > > mock_clk_disable_unprepare(&pdev->dev); > > } > > > > static struct platform_driver pm_dummy = { > > .probe = pm_dummy_probe, > > .remove = pm_dummy_remove, > > .driver = { > > .name = "pm_dummy", > > .pm = &pm_dummy_pm_ops, > > }, > > }; > > > > static struct platform_device *pdev; > > > > static int __init pm_demo_init(void) > > { > > int ret; > > > > ret = platform_driver_register(&pm_dummy); > > if (ret) > > return ret; > > > > pdev = platform_device_register_simple("pm_dummy", -1, NULL, 0); > > if (IS_ERR(pdev)) { > > platform_driver_unregister(&pm_dummy); > > return PTR_ERR(pdev); > > } > > > > return 0; > > } > > > > static void __exit pm_demo_exit(void) > > { > > platform_device_unregister(pdev); > > platform_driver_unregister(&pm_dummy); > > } > > > > module_init(pm_demo_init); > > module_exit(pm_demo_exit); -- Best regards, Radu ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-13 17:46 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-08-05 19:27 [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() Fabio Estevam 2026-08-05 19:27 ` [PATCH v3 2/2] irqchip/imx-irqsteer: Validate IRQ count before creating domain Fabio Estevam 2026-08-05 20:37 ` Frank Li 2026-08-05 20:36 ` [PATCH v3 1/2] irqchip/imx-irqsteer: Convert to devm_pm_runtime_enable() Frank Li 2026-08-06 21:28 ` Radu Rendec 2026-09-11 19:32 ` Frank Li 2026-09-13 17:40 ` Radu Rendec
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®