* [PATCH v3 0/2] Add fallback method to handle RPM notification in MPM driver
@ 2026-10-27 13:11 Sneh Mankad
2026-10-27 13:11 ` [PATCH v3 1/2] irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification Sneh Mankad
2026-10-27 13:11 ` [PATCH v3 2/2] arm64: dts: qcom: shikra: Add CPU idle states Sneh Mankad
0 siblings, 2 replies; 5+ messages in thread
From: Sneh Mankad @ 2026-10-27 13:11 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Thomas Gleixner, Shawn Guo, Marc Zyngier,
Catalin Marinas, Will Deacon, Anna-Maria Behnsen,
Frederic Weisbecker
Cc: linux-arm-msm, devicetree, linux-kernel, linux-arm-kernel,
Sneh Mankad
MPM irqchip needs to notify RPM (Resource Power Manager) processor to read
the latest wake up capable interrupts when the CPU cluster is entering the
deepest idle state. This is done by sending IPC interrupt to RPM and is
implemented as .power_off() callback by registering MPM as parent power
domain to CPU cluster.
Such implementation earlier introduced a hard probe dependency between MPM
irqchip and CPU cluster power domains. That is MPM irqchip needs to finish
probe before PSCI power domains are probed.
The above problem has since been resolved after bd7f488a8e5d ("cpuidle:
psci: Fix support for probe deferral by dropping the faux device") / [1].
However, it makes sense to remodel the RPM notification in MPM irqchip due
to general consistency.
MPM is not really a power domain provider, all it needs is a notification
when CPU cluster is going to power collapse to carry out the RPM
notification. We can register for CPU_PM notifications for that purpose.
This way we can avoid such unrelated dependencies.
We cannot register MPM under CPU cluster power domain and rely on
GENPD notifications here, since that way cluster low power modes will be
blocked whenever some consumer of MPM, and in turn MPM itself, are
runtime active. This will add another unnecessary dependency for CPU
cluster low power modes.
For now as first step, register with cpu_pm as a fallback to manage RPM
notification. Trigger the RPM notification when the last CPU in CPU cluster
goes to power collapse.
Add idle states for CPUs and CPU cluster for Shikra.
[1] https://lore.kernel.org/all/20260909134240.685115374@linuxfoundation.org
Signed-off-by: Sneh Mankad <sneh.mankad@oss.qualcomm.com>
---
Changes in v3:
- Skip timer programming for now till we add support to register MPM
under CPU cluster power domain without blocking its idle states.
- Skip modifying DTs other than Shikra to prioritize Shikra LPM
enablement.
- Move genPD/cpu_pm registration after mbox_request_channel().
- Link to v2: https://lore.kernel.org/r/20261015-b4-shikra_lpm_addition-v2-0-ac7f1a0d5638@oss.qualcomm.com
Changes in v2:
- Enable Shikra LPMs in the first patch, following patches concern
MPM driver.
- Register with cpu_pm as a fallback to manage RPM notification.
- Move USECS_TO_CYCLES and xloops_to_cycles to common header delay.h.
- Reformat qcom_mpm_offset() as per feedback.
- Avoid deleting cluster_pd node in qrb2210-rb1.dts.
- Link to v1: https://lore.kernel.org/r/20260713-b4-shikra_lpm_addition-v1-0-3d858df2cbbf@oss.qualcomm.com
---
Sneh Mankad (2):
irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification
arm64: dts: qcom: shikra: Add CPU idle states
arch/arm64/boot/dts/qcom/shikra.dtsi | 93 ++++++++++++++++++++++++++++++-
drivers/irqchip/irq-qcom-mpm.c | 104 ++++++++++++++++++++++++++---------
2 files changed, 170 insertions(+), 27 deletions(-)
---
base-commit: 7777cc195ca1301a28008ca5cdb98bdb2a9d0def
change-id: 20260617-b4-shikra_lpm_addition-4a9bc82bafee
Best regards,
--
Sneh Mankad <sneh.mankad@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v3 1/2] irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification 2026-10-27 13:11 [PATCH v3 0/2] Add fallback method to handle RPM notification in MPM driver Sneh Mankad @ 2026-10-27 13:11 ` Sneh Mankad 2026-10-27 13:32 ` sashiko-bot 2026-10-27 13:11 ` [PATCH v3 2/2] arm64: dts: qcom: shikra: Add CPU idle states Sneh Mankad 1 sibling, 1 reply; 5+ messages in thread From: Sneh Mankad @ 2026-10-27 13:11 UTC (permalink / raw) To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner, Shawn Guo, Marc Zyngier, Catalin Marinas, Will Deacon, Anna-Maria Behnsen, Frederic Weisbecker Cc: linux-arm-msm, devicetree, linux-kernel, linux-arm-kernel, Sneh Mankad MPM irqchip needs to notify RPM (Resource Power Manager) processor to read the latest wake up capable interrupts when the CPU cluster is entering the deepest idle state. This is done by sending IPC interrupt to RPM and is implemented as .power_off() callback by registering MPM as parent power domain to CPU cluster. Such implementation earlier introduced a hard probe dependency between MPM irqchip and CPU cluster power domains. That is MPM irqchip needs to finish probe before PSCI power domains are probed. The above problem has since been resolved after bd7f488a8e5d ("cpuidle: psci: Fix support for probe deferral by dropping the faux device") / [1]. However, it makes sense to remodel the RPM notification in MPM irqchip due to general consistency. MPM is not really a power domain provider, all it needs is a notification when CPU cluster is going to power collapse to carry out the RPM notification. We can register for CPU_PM notifications for that purpose. This way we can avoid such unrelated dependencies. We cannot register MPM under CPU cluster power domain and rely on GENPD notifications here, since that way cluster low power modes will be blocked whenever some consumer of MPM, and in turn MPM itself, are runtime active. This will add another unnecessary dependency for CPU cluster low power modes. For now as first step, register with cpu_pm as a fallback to manage RPM notification. Trigger the RPM notification when the last CPU in CPU cluster goes to power collapse. [1] https://lore.kernel.org/all/20260909134240.685115374@linuxfoundation.org Signed-off-by: Sneh Mankad <sneh.mankad@oss.qualcomm.com> --- drivers/irqchip/irq-qcom-mpm.c | 104 ++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 26 deletions(-) diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c index 181320528a47ac1bde6cf7d2d0a9f79499990092..136d126f0ea24bb10b5183d6ce47b630aa77b0cf 100644 --- a/drivers/irqchip/irq-qcom-mpm.c +++ b/drivers/irqchip/irq-qcom-mpm.c @@ -4,6 +4,7 @@ * Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. */ +#include <linux/cpu_pm.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/init.h> @@ -85,6 +86,8 @@ struct qcom_mpm_priv { unsigned int reg_stride; struct irq_domain *domain; struct generic_pm_domain genpd; + struct notifier_block mpm_pm; + atomic_t cpus_in_pm; }; static u32 qcom_mpm_read(struct qcom_mpm_priv *priv, unsigned int reg, @@ -292,10 +295,8 @@ static irqreturn_t qcom_mpm_handler(int irq, void *dev_id) return ret; } -static int mpm_pd_power_off(struct generic_pm_domain *genpd) +static int handle_rpm_notification(struct qcom_mpm_priv *priv) { - struct qcom_mpm_priv *priv = container_of(genpd, struct qcom_mpm_priv, - genpd); int i, ret; for (i = 0; i < priv->reg_stride; i++) @@ -307,10 +308,52 @@ static int mpm_pd_power_off(struct generic_pm_domain *genpd) return ret; mbox_client_txdone(priv->mbox_chan, 0); - return 0; } +static int mpm_pd_power_off(struct generic_pm_domain *genpd) +{ + struct qcom_mpm_priv *priv = container_of(genpd, struct qcom_mpm_priv, + genpd); + + return handle_rpm_notification(priv); +} + +static int mpm_cpu_pm_callback(struct notifier_block *nfb, unsigned long action, void *v) +{ + struct qcom_mpm_priv *priv = container_of(nfb, struct qcom_mpm_priv, mpm_pm); + int cpus_in_pm; + + switch (action) { + case CPU_PM_ENTER: + cpus_in_pm = atomic_inc_return(&priv->cpus_in_pm); + /* + * NOTE: comments for num_online_cpus() point out that it's + * only a snapshot so we need to be careful. It should be OK + * for us to use, though. It's important for us not to miss + * if we're the last CPU going down so it would only be a + * problem if a CPU went offline right after we did the check + * AND that CPU was not idle AND that CPU was the last non-idle + * CPU. That can't happen. CPUs would have to come out of idle + * before the CPU could go offline. + */ + if (cpus_in_pm < num_online_cpus()) + return NOTIFY_OK; + break; + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + atomic_dec(&priv->cpus_in_pm); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } + + if (handle_rpm_notification(priv)) + return NOTIFY_BAD; + + return NOTIFY_OK; +} + static bool gic_hwirq_is_mapped(struct mpm_gic_map *maps, int cnt, u32 hwirq) { int i; @@ -415,26 +458,6 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare if (irq < 0) return irq; - genpd = &priv->genpd; - genpd->flags = GENPD_FLAG_IRQ_SAFE; - genpd->power_off = mpm_pd_power_off; - - genpd->name = devm_kasprintf(dev, GFP_KERNEL, "%s", dev_name(dev)); - if (!genpd->name) - return -ENOMEM; - - ret = pm_genpd_init(genpd, NULL, false); - if (ret) { - dev_err(dev, "failed to init genpd: %d\n", ret); - return ret; - } - - ret = of_genpd_add_provider_simple(np, genpd); - if (ret) { - dev_err(dev, "failed to add genpd provider: %d\n", ret); - goto remove_genpd; - } - priv->mbox_client.dev = dev; priv->mbox_client.knows_txdone = true; priv->mbox_chan = mbox_request_channel(&priv->mbox_client, 0); @@ -444,6 +467,35 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare return ret; } + if (of_find_property(np, "#power-domain-cells", NULL)) { + genpd = &priv->genpd; + genpd->flags = GENPD_FLAG_IRQ_SAFE; + genpd->power_off = mpm_pd_power_off; + + genpd->name = devm_kasprintf(dev, GFP_KERNEL, "%s", dev_name(dev)); + if (!genpd->name) + return -ENOMEM; + + ret = pm_genpd_init(genpd, NULL, false); + if (ret) { + dev_err(dev, "failed to init genpd: %d\n", ret); + return ret; + } + + ret = of_genpd_add_provider_simple(np, genpd); + if (ret) { + dev_err(dev, "failed to add genpd provider: %d\n", ret); + goto remove_genpd; + } + } else { + priv->mpm_pm.notifier_call = mpm_cpu_pm_callback; + ret = cpu_pm_register_notifier(&priv->mpm_pm); + if (ret) { + dev_err(dev, "failed to register for CPU_PM notifications: %d\n", ret); + return ret; + } + } + parent_domain = irq_find_host(parent); if (!parent_domain) { dev_err(dev, "failed to find MPM parent domain\n"); @@ -473,10 +525,10 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare remove_domain: irq_domain_remove(priv->domain); -free_mbox: - mbox_free_channel(priv->mbox_chan); remove_genpd: pm_genpd_remove(genpd); +free_mbox: + mbox_free_channel(priv->mbox_chan); return ret; } -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 1/2] irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification 2026-10-27 13:11 ` [PATCH v3 1/2] irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification Sneh Mankad @ 2026-10-27 13:32 ` sashiko-bot 0 siblings, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-10-27 13:32 UTC (permalink / raw) To: Sneh Mankad; +Cc: devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [Critical] irqchip/qcom-mpm: broken error handling and UAF in qcom_mpm_probe() - [High] irqchip/qcom-mpm: cpus_in_pm not decremented on failure in mpm_cpu_pm_callback() - [High] irqchip/qcom-mpm: invalid spinlock_t nesting in mpm_cpu_pm_callback() - [High] irqchip: irq-qcom-mpm: cpus_in_pm underflow when probing with idle CPUs -- commit 0b0c9281cd528ac3d6a6f2d4a69ee74beee11616 Author: Sneh Mankad <sneh.mankad@oss.qualcomm.com> irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification This commit updates the Qualcomm MPM irqchip driver to register a CPU PM notifier when the device tree node does not specify #power-domain-cells, notifying the RPM processor when the last online CPU enters idle. > diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c > index 8c575d7ce8a17..b6caea90e9f41 100644 > --- a/drivers/irqchip/irq-qcom-mpm.c > +++ b/drivers/irqchip/irq-qcom-mpm.c [ ... ] > @@ -307,10 +308,52 @@ static int mpm_pd_power_off(struct generic_pm_domain *genpd) > return ret; > > mbox_client_txdone(priv->mbox_chan, 0); > - > return 0; > } > > +static int mpm_pd_power_off(struct generic_pm_domain *genpd) > +{ > + struct qcom_mpm_priv *priv = container_of(genpd, struct qcom_mpm_priv, > + genpd); > + > + return handle_rpm_notification(priv); > +} > + > +static int mpm_cpu_pm_callback(struct notifier_block *nfb, unsigned long action, void *v) > +{ > + struct qcom_mpm_priv *priv = container_of(nfb, struct qcom_mpm_priv, mpm_pm); > + int cpus_in_pm; > + > + switch (action) { > + case CPU_PM_ENTER: > + cpus_in_pm = atomic_inc_return(&priv->cpus_in_pm); > + /* > + * NOTE: comments for num_online_cpus() point out that it's > + * only a snapshot so we need to be careful. It should be OK > + * for us to use, though. It's important for us not to miss > + * if we're the last CPU going down so it would only be a > + * problem if a CPU went offline right after we did the check > + * AND that CPU was not idle AND that CPU was the last non-idle > + * CPU. That can't happen. CPUs would have to come out of idle > + * before the CPU could go offline. > + */ > + if (cpus_in_pm < num_online_cpus()) > + return NOTIFY_OK; > + break; > + case CPU_PM_ENTER_FAILED: > + case CPU_PM_EXIT: > + atomic_dec(&priv->cpus_in_pm); > + return NOTIFY_OK; [Severity: High] Can priv->cpus_in_pm underflow if other CPUs are already in idle when qcom_mpm_probe() registers this notifier? Because MPM is no longer a power domain provider for the PSCI cluster domain, psci_cpuidle_domain_probe() and psci_cpuidle_probe() in drivers/cpuidle/cpuidle-psci.c can initialize cpuidle before qcom_mpm_probe() runs (for example, when CONFIG_QCOM_MPM, CONFIG_QCOM_SMD_RPM, or CONFIG_QCOM_APCS_IPC is built as a module or defers probe). If one or more CPUs have already entered idle via cpu_pm_enter() when qcom_mpm_probe() calls cpu_pm_register_notifier(&priv->mpm_pm) with priv->cpus_in_pm initialized to 0, those idle CPUs will later wake up and invoke cpu_pm_exit() -> mpm_cpu_pm_callback(CPU_PM_EXIT) without a matching CPU_PM_ENTER. That decrements priv->cpus_in_pm below zero when all CPUs are active, so subsequent CPU_PM_ENTER transitions will never reach num_online_cpus() and handle_rpm_notification() will never be called. > + default: > + return NOTIFY_DONE; > + } > + > + if (handle_rpm_notification(priv)) > + return NOTIFY_BAD; [Severity: High] If handle_rpm_notification(priv) fails here in mpm_cpu_pm_callback() in drivers/irqchip/irq-qcom-mpm.c, does priv->cpus_in_pm remain permanently incremented by one? On the CPU_PM_ENTER path, priv->cpus_in_pm is incremented before calling handle_rpm_notification(priv). In kernel/cpu_pm.c, cpu_pm_enter() calls cpu_pm_notify_robust(), which uses raw_notifier_call_chain_robust() to send CPU_PM_ENTER_FAILED only to the callbacks that succeeded prior to the failing callback: kernel/notifier.c:notifier_call_chain_robust() ret = nb->notifier_call(nb, val_up, v); if (ret & NOTIFY_STOP_MASK) { notifier_call_chain(nl, val_down, v, nr - 1, NULL); ... } Because mpm_cpu_pm_callback() will not receive CPU_PM_ENTER_FAILED (and the caller aborts idle entry without calling cpu_pm_exit()), returning NOTIFY_BAD without calling atomic_dec(&priv->cpus_in_pm) leaves priv->cpus_in_pm skewed by +1, causing future idle entries to trigger handle_rpm_notification() while a CPU is still active. [Severity: High] Also, is it valid to call handle_rpm_notification(priv) from mpm_cpu_pm_callback() while holding the raw spinlock in cpu_pm_notify_robust()? In kernel/cpu_pm.c, cpu_pm_notify_robust() holds cpu_pm_notifier.lock (a raw_spinlock_t) across the entire notifier chain: kernel/cpu_pm.c:cpu_pm_notify_robust() raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags); ret = raw_notifier_call_chain_robust(&cpu_pm_notifier.chain, event_up, event_down, NULL); raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags); Inside mpm_cpu_pm_callback(), handle_rpm_notification() invokes mbox_send_message() and mbox_client_txdone() in drivers/mailbox/mailbox.c, where add_to_rbuf(), msg_submit(), and tx_tick() acquire chan->lock (a non-raw spinlock_t). In addition, msg_submit() calls qcom_apcs_ipc_send_data() in drivers/mailbox/qcom-apcs-ipc-mailbox.c, which calls regmap_write() on a regmap without use_raw_spinlock set, acquiring map->spinlock (another non-raw spinlock_t). Acquiring a spinlock_t while holding a raw_spinlock_t violates lockdep wait-type nesting rules under CONFIG_PROVE_RAW_LOCK_NESTING=y and sleeps in atomic idle context on PREEMPT_RT kernels. > + > + return NOTIFY_OK; > +} > + > static bool gic_hwirq_is_mapped(struct mpm_gic_map *maps, int cnt, u32 hwirq) > { > int i; [ ... ] > @@ -444,6 +467,35 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare > return ret; > } > > + if (of_find_property(np, "#power-domain-cells", NULL)) { > + genpd = &priv->genpd; > + genpd->flags = GENPD_FLAG_IRQ_SAFE; > + genpd->power_off = mpm_pd_power_off; > + > + genpd->name = devm_kasprintf(dev, GFP_KERNEL, "%s", dev_name(dev)); > + if (!genpd->name) > + return -ENOMEM; > + > + ret = pm_genpd_init(genpd, NULL, false); > + if (ret) { > + dev_err(dev, "failed to init genpd: %d\n", ret); > + return ret; > + } > + > + ret = of_genpd_add_provider_simple(np, genpd); > + if (ret) { > + dev_err(dev, "failed to add genpd provider: %d\n", ret); > + goto remove_genpd; > + } > + } else { > + priv->mpm_pm.notifier_call = mpm_cpu_pm_callback; > + ret = cpu_pm_register_notifier(&priv->mpm_pm); > + if (ret) { > + dev_err(dev, "failed to register for CPU_PM notifications: %d\n", ret); > + return ret; > + } > + } > + > parent_domain = irq_find_host(parent); > if (!parent_domain) { > dev_err(dev, "failed to find MPM parent domain\n"); > @@ -471,10 +523,10 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare > > remove_domain: > irq_domain_remove(priv->domain); > -free_mbox: > - mbox_free_channel(priv->mbox_chan); > remove_genpd: > pm_genpd_remove(genpd); > +free_mbox: > + mbox_free_channel(priv->mbox_chan); > return ret; > } [Severity: Critical] Does the error cleanup in qcom_mpm_probe() in drivers/irqchip/irq-qcom-mpm.c mishandle failure paths in both the genpd and cpu_pm branches? There appear to be four related issues on probe failure: 1. In qcom_mpm_probe(), the local variable genpd is declared without initialization: struct generic_pm_domain *genpd; When #power-domain-cells is absent, the else branch is taken and genpd remains uninitialized. If devm_request_irq() subsequently fails and jumps to remove_domain, execution falls through to remove_genpd and calls pm_genpd_remove(genpd) with an uninitialized pointer. 2. In the else branch, cpu_pm_register_notifier(&priv->mpm_pm) registers the notifier block embedded in devm-allocated priv into the global cpu_pm_notifier chain. If irq_find_host(), irq_domain_create_hierarchy(), or devm_request_irq() fails, cpu_pm_unregister_notifier(&priv->mpm_pm) is never called before devres frees priv, leaving a dangling notifier in the global chain. 3. Because remove_genpd now precedes free_mbox, if #power-domain-cells is present and irq_find_host() or irq_domain_create_hierarchy() fails, jumping to free_mbox skips remove_genpd (and of_genpd_del_provider(np)), leaving priv->genpd registered after priv is freed. 4. Because mbox_request_channel() is now called before the genpd and cpu_pm setup, the direct return statements on !genpd->name, pm_genpd_init() failure, and cpu_pm_register_notifier() failure return without calling mbox_free_channel(priv->mbox_chan), leaking the mailbox channel with a dangling client pointer once priv is freed. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261027-b4-shikra_lpm_addition-v3-0-424abcdcf824@oss.qualcomm.com?part=1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 2/2] arm64: dts: qcom: shikra: Add CPU idle states 2026-10-27 13:11 [PATCH v3 0/2] Add fallback method to handle RPM notification in MPM driver Sneh Mankad 2026-10-27 13:11 ` [PATCH v3 1/2] irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification Sneh Mankad @ 2026-10-27 13:11 ` Sneh Mankad 2026-10-27 13:25 ` sashiko-bot 1 sibling, 1 reply; 5+ messages in thread From: Sneh Mankad @ 2026-10-27 13:11 UTC (permalink / raw) To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner, Shawn Guo, Marc Zyngier, Catalin Marinas, Will Deacon, Anna-Maria Behnsen, Frederic Weisbecker Cc: linux-arm-msm, devicetree, linux-kernel, linux-arm-kernel, Sneh Mankad Add idle states for the CPUs as well as the whole cluster. This enables deeper-than-WFI cpuidle. Remove #power-domain-cells from MPM device so that the driver follows the cpu_pm notifications path to handle RPM notification. This avoids other probe dependencies that arise due to MPM being a power domain provider. Signed-off-by: Sneh Mankad <sneh.mankad@oss.qualcomm.com> --- arch/arm64/boot/dts/qcom/shikra.dtsi | 93 +++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/shikra.dtsi b/arch/arm64/boot/dts/qcom/shikra.dtsi index 4e5bc9e17c8ed8914733ac9fdc9b69128b40216e..74be82ae50d16c7dcc7bc7af4deb28794efd47e8 100644 --- a/arch/arm64/boot/dts/qcom/shikra.dtsi +++ b/arch/arm64/boot/dts/qcom/shikra.dtsi @@ -40,6 +40,8 @@ cpu0: cpu@0 { compatible = "arm,cortex-a55"; reg = <0x0 0x0>; enable-method = "psci"; + power-domains = <&cpu_pd0>; + power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -50,6 +52,8 @@ cpu1: cpu@100 { compatible = "arm,cortex-a55"; reg = <0x0 0x100>; enable-method = "psci"; + power-domains = <&cpu_pd1>; + power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -60,6 +64,8 @@ cpu2: cpu@200 { compatible = "arm,cortex-a55"; reg = <0x0 0x200>; enable-method = "psci"; + power-domains = <&cpu_pd2>; + power-domain-names = "psci"; next-level-cache = <&l3>; capacity-dmips-mhz = <1024>; dynamic-power-coefficient = <100>; @@ -70,6 +76,8 @@ cpu3: cpu@300 { compatible = "arm,cortex-a78c"; reg = <0x0 0x300>; enable-method = "psci"; + power-domains = <&cpu_pd3>; + power-domain-names = "psci"; next-level-cache = <&l2_3>; capacity-dmips-mhz = <1946>; dynamic-power-coefficient = <489>; @@ -105,6 +113,61 @@ core0 { }; }; + idle-states { + entry-method = "psci"; + + little_cpu_sleep_0: cpu-sleep-0-0 { + compatible = "arm,idle-state"; + idle-state-name = "little-power-down"; + arm,psci-suspend-param = <0x40000003>; + entry-latency-us = <549>; + exit-latency-us = <901>; + min-residency-us = <1774>; + local-timer-stop; + }; + + little_cpu_sleep_1: cpu-sleep-0-1 { + compatible = "arm,idle-state"; + idle-state-name = "little-pll-power-down"; + arm,psci-suspend-param = <0x40000004>; + entry-latency-us = <702>; + exit-latency-us = <915>; + min-residency-us = <4001>; + local-timer-stop; + }; + + big_cpu_sleep_0: cpu-sleep-1-0 { + compatible = "arm,idle-state"; + idle-state-name = "big-power-down"; + arm,psci-suspend-param = <0x40000003>; + entry-latency-us = <523>; + exit-latency-us = <1244>; + min-residency-us = <2207>; + local-timer-stop; + }; + + big_cpu_sleep_1: cpu-sleep-1-1 { + compatible = "arm,idle-state"; + idle-state-name = "big-pll-power-down"; + arm,psci-suspend-param = <0x40000004>; + entry-latency-us = <526>; + exit-latency-us = <1854>; + min-residency-us = <5555>; + local-timer-stop; + }; + }; + + domain_idle_states: domain-idle-states { + cluster_sleep_apss_off: cluster-sleep-0 { + compatible = "domain-idle-state"; + idle-state-name = "cluster-power-down"; + arm,psci-suspend-param = <0x41000044>; + entry-latency-us = <2752>; + exit-latency-us = <3038>; + min-residency-us = <6118>; + }; + }; + l3: l3-cache { compatible = "cache"; cache-level = <3>; @@ -144,6 +207,35 @@ pmu-a78c { psci { compatible = "arm,psci-1.0"; method = "smc"; + + cpu_pd0: power-domain-cpu0 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&little_cpu_sleep_0>, <&little_cpu_sleep_1>; + }; + + cpu_pd1: power-domain-cpu1 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&little_cpu_sleep_0>, <&little_cpu_sleep_1>; + }; + + cpu_pd2: power-domain-cpu2 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&little_cpu_sleep_0>, <&little_cpu_sleep_1>; + }; + + cpu_pd3: power-domain-cpu3 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&big_cpu_sleep_0>, <&big_cpu_sleep_1>; + }; + + cluster_pd: power-domain-cluster { + #power-domain-cells = <0>; + domain-idle-states = <&cluster_sleep_apss_off>; + }; }; rpm: remoteproc { @@ -217,7 +309,6 @@ mpm: interrupt-controller { mboxes = <&apcs_glb 1>; interrupt-controller; #interrupt-cells = <2>; - #power-domain-cells = <0>; interrupt-parent = <&intc>; qcom,mpm-pin-count = <96>; qcom,mpm-pin-map = <2 275>, /* TSENS0 uplow */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 2/2] arm64: dts: qcom: shikra: Add CPU idle states 2026-10-27 13:11 ` [PATCH v3 2/2] arm64: dts: qcom: shikra: Add CPU idle states Sneh Mankad @ 2026-10-27 13:25 ` sashiko-bot 0 siblings, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-10-27 13:25 UTC (permalink / raw) To: Sneh Mankad; +Cc: devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] irqchip/qcom-mpm: uninitialized genpd and notifier UAF in qcom_mpm_probe() - [High] arm64: dts: qcom: shikra: invalid lock nesting and premature MPM notification - [Medium] irqchip/qcom-mpm: cpus_in_pm counter leak on error in mpm_cpu_pm_callback() -- commit bc4ed1a6ee147014bad47b72745f44c888126bd9 Author: Sneh Mankad <sneh.mankad@oss.qualcomm.com> arm64: dts: qcom: shikra: Add CPU idle states This commit adds PSCI CPU and cluster idle states for Shikra and removes #power-domain-cells from the MPM node so the driver uses CPU PM notifications instead of acting as a power domain provider. > diff --git a/arch/arm64/boot/dts/qcom/shikra.dtsi b/arch/arm64/boot/dts/qcom/shikra.dtsi > index 92d22e3b6931c..aeb608fd7180a 100644 > --- a/arch/arm64/boot/dts/qcom/shikra.dtsi > +++ b/arch/arm64/boot/dts/qcom/shikra.dtsi [ ... ] > @@ -334,7 +426,6 @@ mpm: interrupt-controller { > mboxes = <&apcs_glb 1>; > interrupt-controller; > #interrupt-cells = <2>; > - #power-domain-cells = <0>; [Severity: High] When #power-domain-cells is removed from the mpm node, qcom_mpm_probe() in drivers/irqchip/irq-qcom-mpm.c takes the else branch and registers the CPU PM notifier instead of initializing genpd: struct generic_pm_domain *genpd; ... priv->mbox_chan = mbox_request_channel(&priv->mbox_client, 0); ... if (of_find_property(np, "#power-domain-cells", NULL)) { genpd = &priv->genpd; ... } else { priv->mpm_pm.notifier_call = mpm_cpu_pm_callback; ret = cpu_pm_register_notifier(&priv->mpm_pm); if (ret) { dev_err(dev, "failed to register for CPU_PM notifications: %d\n", ret); return ret; } } If a subsequent step in qcom_mpm_probe() fails, the error cleanup path runs: remove_domain: irq_domain_remove(priv->domain); remove_genpd: pm_genpd_remove(genpd); free_mbox: mbox_free_channel(priv->mbox_chan); return ret; If devm_request_irq() fails, execution jumps to remove_domain and falls through to remove_genpd, passing the uninitialized stack pointer genpd to pm_genpd_remove(). Furthermore, if irq_find_host(), irq_domain_create_hierarchy(), or devm_request_irq() fails after cpu_pm_register_notifier(&priv->mpm_pm) succeeds, the error path never calls cpu_pm_unregister_notifier(&priv->mpm_pm). When devres frees priv on probe failure, &priv->mpm_pm remains linked in the global cpu_pm_notifier chain and will be dereferenced after free on the next CPU idle entry. And if cpu_pm_register_notifier() itself fails, returning directly leaks priv->mbox_chan. Could qcom_mpm_probe() initialize genpd to NULL, guard pm_genpd_remove(), unregister the CPU PM notifier on error, and jump to free_mbox when cpu_pm_register_notifier() fails before switching Shikra to this path? [Severity: Medium] Switching the mpm node to the mpm_cpu_pm_callback() path also exposes an error handling issue in drivers/irqchip/irq-qcom-mpm.c when handle_rpm_notification() fails: static int mpm_cpu_pm_callback(struct notifier_block *nfb, unsigned long action, void *v) { struct qcom_mpm_priv *priv = container_of(nfb, struct qcom_mpm_priv, mpm_pm); int cpus_in_pm; switch (action) { case CPU_PM_ENTER: cpus_in_pm = atomic_inc_return(&priv->cpus_in_pm); ... if (cpus_in_pm < num_online_cpus()) return NOTIFY_OK; break; case CPU_PM_ENTER_FAILED: case CPU_PM_EXIT: atomic_dec(&priv->cpus_in_pm); return NOTIFY_OK; default: return NOTIFY_DONE; } if (handle_rpm_notification(priv)) return NOTIFY_BAD; return NOTIFY_OK; } During cpu_pm_enter(), raw_notifier_call_chain_robust() invokes notifier_call_chain_robust() in kernel/notifier.c: ret = notifier_call_chain(nl, val_up, v, -1, &nr); if (ret & NOTIFY_STOP_MASK) notifier_call_chain(nl, val_down, v, nr-1, NULL); When a callback returns NOTIFY_BAD, CPU_PM_ENTER_FAILED is only sent to the nr - 1 callbacks that preceded the failing one, and cpu_pm_enter() returns an error so cpu_pm_exit() is not called. If handle_rpm_notification(priv) fails (for example, if mbox_send_message() returns -ENOBUFS), wouldn't returning NOTIFY_BAD without decrementing priv->cpus_in_pm leave priv->cpus_in_pm permanently skewed by +1? [Severity: High] With #power-domain-cells removed from mpm while adding both per-CPU PSCI idle states (little-power-down, little-pll-power-down, big-power-down, big-pll-power-down) and a cluster idle state (cluster-power-down), the call chain on CPU idle entry becomes: cpu_pm_enter() -> cpu_pm_notify_robust() raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags); -> mpm_cpu_pm_callback() -> handle_rpm_notification() -> mbox_send_message() -> add_to_rbuf() guard(spinlock_irqsave)(&chan->lock); Because cpu_pm_notifier.lock is a raw_spinlock_t and chan->lock in drivers/mailbox/mailbox.c is a non-raw spinlock_t, won't acquiring chan->lock inside mpm_cpu_pm_callback() trigger an invalid wait context bug under CONFIG_PROVE_RAW_LOCK_NESTING and sleep in atomic context from the idle task on PREEMPT_RT? In addition, cpuidle-psci calls cpu_pm_enter() for every non-WFI per-CPU idle state (via psci_enter_idle_state()) and before pm_runtime_put_sync_suspend() determines whether cluster_pd actually enters cluster-power-down (in __psci_enter_domain_idle_state()). Won't mpm_cpu_pm_callback() clear MPM_REG_STATUS and notify RPM whenever all CPUs enter shallow per-CPU idle states, even when the cluster does not power down? -- Sashiko AI review · https://sashiko.dev/#/patchset/20261027-b4-shikra_lpm_addition-v3-0-424abcdcf824@oss.qualcomm.com?part=2 ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-27 13:32 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-27 13:11 [PATCH v3 0/2] Add fallback method to handle RPM notification in MPM driver Sneh Mankad 2026-10-27 13:11 ` [PATCH v3 1/2] irqchip: irq-qcom-mpm: Register MPM with cpu_pm for RPM notification Sneh Mankad 2026-10-27 13:32 ` sashiko-bot 2026-10-27 13:11 ` [PATCH v3 2/2] arm64: dts: qcom: shikra: Add CPU idle states Sneh Mankad 2026-10-27 13:25 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox