Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Herman van Hazendonk" <github.com@herrie.org>
Cc: conor+dt@kernel.org, robh@kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH v2 2/2] irqchip: add MSM8x60 MPM wakeup interrupt controller driver
Date: Sun, 31 May 2026 04:32:13 +0000	[thread overview]
Message-ID: <20260531043213.D18801F00893@smtp.kernel.org> (raw)
In-Reply-To: <0133fce127c7507bbb907d0258ad69ac8d753218.1780195817.git.github.com@herrie.org>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Unprotected read-modify-write operations on shared vMPM registers lead to lost configuration updates.
- [High] Calling mbox_send_message() from irqchip callbacks causes a sleeping-while-atomic deadlock on PREEMPT_RT.
- [High] Identity confusion between MPM pin numbers and parent GIC SPI numbers leads to allocation failures or dropped interrupts.
- [High] Toggling the hardware ENABLE bit inside irq_set_wake inadvertently disables unmasked interrupts upon resume.
- [High] Missing suppress_bind_attrs allows sysfs unbind, causing Use-After-Free via irq_domain_remove and concurrent probes.
- [Medium] Missing read-back flush after clearing MPM interrupt status using writel_relaxed.
- [Low] Phandle resolution ignores the actual target node, leading to incorrect device linking.
--

commit c09bf824269ef596633ac65838f83c2f22429764
Author: Herman van Hazendonk <github.com@herrie.org>

irqchip: add MSM8x60 MPM wakeup interrupt controller driver

Add a driver for the MSM Power Manager (MPM) on the MSM8x60 family
(MSM8260/MSM8660/APQ8060). The MPM latches edge-triggered interrupts
during APPS power collapse and signals the RPM to wake the APPS back up.

> diff --git a/drivers/irqchip/irq-msm8660-mpm.c b/drivers/irqchip/irq-msm8660-mpm.c
> new file mode 100644
> index 0000000000000..b8394d9c076d2
> --- /dev/null
> +++ b/drivers/irqchip/irq-msm8660-mpm.c
> @@ -0,0 +1,740 @@

[ ... ]

> +static void msm8660_mpm_doorbell(struct msm8660_mpm *mpm)
> +{
> +	int ret;
> +
> +	if (!mpm->mbox_chan)
> +		return;
> +
> +	ret = mbox_send_message(mpm->mbox_chan, NULL);

[Severity: High]
Does calling mbox_send_message() from within an irqchip callback cause a
sleeping-while-atomic deadlock on PREEMPT_RT?

The irq_mask, irq_unmask, and irq_set_type callbacks run with the
raw_spinlock_t irq_desc->lock held.

The mailbox core conditionally waits and takes chan->lock, which is a spinlock_t
that gets converted to a sleepable RT-mutex on PREEMPT_RT.

Deferring the mailbox communication using irq_bus_lock and irq_bus_sync_unlock
could avoid this deadlock.

[ ... ]

> +static irqreturn_t msm8660_mpm_irq(int irq, void *data)
> +{

[ ... ]

> +		/*
> +		 * Clear before dispatching: a new edge latched on this pin
> +		 * after this point will set the pending bit again and we
> +		 * will service it on the next IPC IRQ. Clearing AFTER the
> +		 * handler would race with that new latch and silently lose
> +		 * the new edge.
> +		 */
> +		if (pending[i])
> +			msm8660_mpm_write(mpm,
> +				MSM8660_MPM_REG_CLEAR + i * 4, pending[i]);

[Severity: Medium]
Can a delayed write in the CPU store buffer overwrite newly latched edges and
drop interrupts?

Using writel_relaxed does not flush the write over the interconnect. If a
child driver triggers a new edge on a peripheral during the handler, the MPM
hardware will latch it.

If the delayed REG_CLEAR write arrives afterward, it will incorrectly wipe out
the newly latched edge. Adding a dummy read from the MPM register space ensures
the write completes before executing the handler.

[ ... ]

> +static int msm8660_mpm_hwirq_to_pin(struct msm8660_mpm *mpm, unsigned int hwirq)
> +{
> +	int i;
> +
> +	for (i = 0; i < mpm->pin_map_count; i++) {
> +		if (mpm->pin_map[i].hwirq == hwirq)

[Severity: High]
Is the GIC SPI number being confused with the MPM pin number here?

When msm8660_mpm_set_type calls msm8660_mpm_hwirq_to_pin(mpm, d->hwirq),
it searches for mpm->pin_map[i].hwirq == hwirq.

Because d->hwirq is the MPM pin and pin_map[i].hwirq is the GIC SPI, this lookup
will fail and return -1, causing set_type to return -ENOENT.

> +			return mpm->pin_map[i].pin;
> +	}
> +	return -1;
> +}
> +
> +static void msm8660_mpm_enable_hwirq(struct irq_data *d, bool enable)
> +{
> +	struct msm8660_mpm *mpm = irq_data_get_irq_chip_data(d);
> +	int pin;
> +	u32 val, mask;
> +
> +	pin = msm8660_mpm_hwirq_to_pin(mpm, d->hwirq);
> +	if (pin < 0)
> +		return;
> +
> +	mask = BIT(pin % 32);
> +	val = msm8660_mpm_read(mpm,
> +		MSM8660_MPM_REG_ENABLE + (pin / 32) * 4);

[Severity: High]
Could this concurrent read-modify-write corrupt the shared register state?

The driver manipulates shared 32-bit registers (ENABLE, DETECT_CTL,
POLARITY) to configure individual pins.

Because the IRQ subsystem calls irq_mask and irq_unmask with the per-interrupt
irq_desc->lock held, concurrent mask/unmask operations on different interrupts
within the same bank will execute without mutual exclusion.

A driver-local raw_spinlock_t is needed to serialize these accesses.

> +	if (enable)
> +		val |= mask;
> +	else
> +		val &= ~mask;
> +	msm8660_mpm_write(mpm,
> +		MSM8660_MPM_REG_ENABLE + (pin / 32) * 4, val);
> +
> +	msm8660_mpm_doorbell(mpm);

[Severity: High]
Is it safe to invoke the doorbell from a context where a raw_spinlock_t
is held?

The irq_mask and irq_unmask callbacks execute with irq_desc->lock held.

Calling msm8660_mpm_doorbell() here will invoke mbox_send_message(), which
sleeps on PREEMPT_RT, causing a scheduling-while-atomic deadlock.

[ ... ]

> +static int msm8660_mpm_set_type(struct irq_data *d, unsigned int type)
> +{
> +	struct msm8660_mpm *mpm = irq_data_get_irq_chip_data(d);
> +	u32 detect, polarity, mask;
> +	int pin;
> +
> +	pin = msm8660_mpm_hwirq_to_pin(mpm, d->hwirq);
> +	if (pin < 0)
> +		return -ENOENT;
> +
> +	mask = BIT(pin % 32);
> +	detect = msm8660_mpm_read(mpm,
> +		MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4);

[Severity: High]
Does this unlocked read-modify-write of the DETECT_CTL and POLARITY registers
race with other set_type calls?

Similar to the ENABLE register, these are shared across multiple pins and
need mutual exclusion to prevent lost configuration updates.

[ ... ]

> +static int msm8660_mpm_set_wake(struct irq_data *d, unsigned int on)
> +{
> +	struct msm8660_mpm *mpm = irq_data_get_irq_chip_data(d);
> +	int pin;
> +
> +	pin = msm8660_mpm_hwirq_to_pin(mpm, d->hwirq);
> +	if (pin < 0)
> +		return -ENOENT;
> +
> +	msm8660_mpm_enable_hwirq(d, !!on);

[Severity: High]
Will toggling the hardware ENABLE bit here cause the interrupt to remain
permanently disabled after the first system resume?

When the system suspends, IRQCHIP_MASK_ON_SUSPEND ensures non-wake
interrupts are logically masked. During resume, device drivers call
irq_set_wake(..., 0) to disable wakeup.

Since this writes 0 to the hardware ENABLE bit, but the IRQ core assumes
the interrupt is still logically unmasked, it will not call irq_unmask()
and the hardware state will remain disabled.

[ ... ]

> +static int msm8660_mpm_domain_alloc(struct irq_domain *domain,
> +				    unsigned int virq, unsigned int nr_irqs,
> +				    void *data)
> +{

[ ... ]

> +	parent_fwspec.fwnode = domain->parent->fwnode;
> +	parent_fwspec.param_count = 3;
> +	parent_fwspec.param[0] = 0;
> +	parent_fwspec.param[1] = hwirq;

[Severity: High]
Does passing the MPM pin number directly to the parent GIC domain map the
wrong interrupt?

The parent GIC domain expects a GIC SPI number in param[1], not the MPM pin
number extracted from fwspec->param[0]. The correct mapped SPI should be
looked up and passed here instead.

[ ... ]

> +struct msm8660_mpm *msm8660_mpm_get(struct device *consumer,
> +				    struct device_node *np,
> +				    const char *propname)
> +{
> +	struct device_node *mpm_np;
> +	struct device_link *link;
> +
> +	if (!msm8660_mpm_global)
> +		return ERR_PTR(-EPROBE_DEFER);
> +
> +	if (np && propname) {
> +		mpm_np = of_parse_phandle(np, propname, 0);
> +		if (!mpm_np)
> +			return ERR_PTR(-ENOENT);
> +		of_node_put(mpm_np);

[Severity: Low]
Should this function verify that the resolved mpm_np actually matches
msm8660_mpm_global->dev->of_node?

If a consumer's DT property points to a completely different node by mistake,
it appears this code will still drop the reference and successfully return
the global MPM handle anyway.

> +	}
> +
> +	if (!consumer)
> +		return msm8660_mpm_global;
> +
> +	link = device_link_add(consumer, msm8660_mpm_global->dev,
> +			       DL_FLAG_AUTOREMOVE_CONSUMER);

[Severity: High]
Can msm8660_mpm_global->dev be dereferenced safely here without a lock?

If this races with a sysfs unbind of the MPM driver, msm8660_mpm_global
could be set to NULL or freed, leading to a use-after-free.

[ ... ]

> +int msm8660_mpm_enable_pin(struct msm8660_mpm *mpm, unsigned int pin,
> +			   bool enable)
> +{
> +	u32 val;
> +
> +	if (!mpm || pin >= MSM8660_MPM_PIN_COUNT)
> +		return -EINVAL;
> +
> +	val = msm8660_mpm_read(mpm,
> +		MSM8660_MPM_REG_ENABLE + (pin / 32) * 4);

[Severity: High]
Does this raw-pin consumer API modify the ENABLE register without locking?

Concurrent calls from different consumer drivers could overwrite each other's
enable states without mutual exclusion.

[ ... ]

> +static void msm8660_mpm_remove(struct platform_device *pdev)
> +{
> +	struct msm8660_mpm *mpm = platform_get_drvdata(pdev);
> +
> +	/*
> +	 * Tear down in strict reverse order: drop the singleton so new
> +	 * consumers cannot grab a handle, free the IRQ so the handler
> +	 * cannot fire again, free the mailbox channel, then remove the
> +	 * domain. Consumer device_links established in msm8660_mpm_get()
> +	 * prevent the parent device from being unbound while a consumer
> +	 * still holds a handle.
> +	 */
> +	msm8660_mpm_global = NULL;
> +	free_irq(mpm->parent_irq, mpm);
> +	if (mpm->mbox_chan)
> +		mbox_free_channel(mpm->mbox_chan);
> +	irq_domain_remove(mpm->domain);

[Severity: High]
Does allowing sysfs unbind lead to a use-after-free in the core IRQ subsystem?

While consumer drivers are unbound due to the device_link, their virtual IRQ
mappings persist in the core IRQ subsystem because they do not automatically
call irq_dispose_mapping().

Removing the domain here frees the domain memory, but the core IRQ subsystem
retains dangling pointers to it in irq_data structures.

> +}

[ ... ]

> +static struct platform_driver msm8660_mpm_driver = {
> +	.probe		= msm8660_mpm_probe,
> +	.remove		= msm8660_mpm_remove,
> +	.driver		= {
> +		.name	= "msm8660-mpm",

[Severity: High]
Should this driver set .suppress_bind_attrs = true?

Without it, the driver can be unbound via sysfs, which triggers the
irq_domain_remove() use-after-free issue described above.

> +		.of_match_table = msm8660_mpm_of_match,
> +	},
> +};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1780195817.git.github.com@herrie.org?part=2

  reply	other threads:[~2026-05-31  4:32 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-30 13:59 [PATCH 0/2] clk: qcom: add MSM8x60 LPASS Clock Controller Herman van Hazendonk
2026-05-30 13:58 ` [PATCH 1/3] dt-bindings: clock: qcom: add mmcc-msm8660 clock IDs Herman van Hazendonk
2026-05-30 13:58 ` [PATCH 2/3] dt-bindings: reset: qcom: add mmcc-msm8660 reset IDs Herman van Hazendonk
2026-05-30 13:59 ` [PATCH 0/3] clk: qcom: add MSM8x60 Multimedia Clock Controller Herman van Hazendonk
2026-05-30 13:59 ` [PATCH 1/2] dt-bindings: clock: qcom: add lcc-msm8660 LPASS clock IDs Herman van Hazendonk
2026-05-30 14:15   ` sashiko-bot
2026-05-30 13:59 ` [PATCH 2/2] clk: qcom: add MSM8x60 LCC (LPASS) driver Herman van Hazendonk
2026-05-30 14:25   ` sashiko-bot
2026-05-30 14:00 ` [PATCH 0/2] interconnect: qcom: add MSM8x60 NoC driver Herman van Hazendonk
2026-05-30 14:00 ` [PATCH 1/2] dt-bindings: interconnect: qcom: add msm8660 fabric IDs Herman van Hazendonk
2026-05-30 14:00 ` [PATCH 2/2] interconnect: qcom: add MSM8x60 NoC driver Herman van Hazendonk
2026-05-30 14:14   ` sashiko-bot
2026-05-30 14:00 ` [PATCH 1/2] dt-bindings: interrupt-controller: qcom: add msm8660-mpm Herman van Hazendonk
2026-05-30 14:00 ` [PATCH 2/2] irqchip: add MSM8x60 MPM wakeup interrupt controller driver Herman van Hazendonk
2026-05-30 14:22   ` sashiko-bot
2026-05-30 14:00 ` [PATCH 0/2] thermal: qcom: add PM8901 PMIC temperature-alarm driver Herman van Hazendonk
2026-05-30 14:00 ` [PATCH 1/2] dt-bindings: thermal: qcom: add pm8901-temp-alarm Herman van Hazendonk
2026-05-30 14:08   ` sashiko-bot
2026-05-30 20:48   ` Rob Herring (Arm)
2026-05-30 14:00 ` [PATCH 2/2] thermal: qcom: add PM8901 PMIC temperature-alarm driver Herman van Hazendonk
2026-05-30 14:16   ` sashiko-bot
2026-05-31  4:08 ` [PATCH v2 0/3] clk: qcom: add MSM8x60 LPASS Clock Controller Herman van Hazendonk
2026-05-31  4:09   ` [PATCH v2 1/3] dt-bindings: clock: qcom,lcc: add MSM8x60 family compatibles Herman van Hazendonk
2026-05-31  4:14     ` sashiko-bot
2026-05-31  7:58     ` Krzysztof Kozlowski
2026-05-31  4:09   ` [PATCH v2 2/3] dt-bindings: clock: qcom: add lcc-msm8660 LPASS clock IDs Herman van Hazendonk
2026-05-31  4:23     ` sashiko-bot
2026-05-31  4:09   ` [PATCH v2 3/3] clk: qcom: add MSM8x60 LCC (LPASS) driver Herman van Hazendonk
2026-05-31  4:33     ` sashiko-bot
2026-05-31  4:09 ` [PATCH v2 0/2] irqchip: add MSM8x60 MPM wakeup interrupt controller Herman van Hazendonk
2026-05-31  4:09   ` [PATCH v2 1/2] dt-bindings: interrupt-controller: qcom: add msm8660-mpm Herman van Hazendonk
2026-05-31  4:20     ` sashiko-bot
2026-05-31  8:01     ` Krzysztof Kozlowski
2026-05-31  4:09   ` [PATCH v2 2/2] irqchip: add MSM8x60 MPM wakeup interrupt controller driver Herman van Hazendonk
2026-05-31  4:32     ` sashiko-bot [this message]
2026-05-31  4:09 ` [PATCH v2 0/3] thermal: qcom: add PM8901 PMIC temperature-alarm driver Herman van Hazendonk
2026-05-31  4:09   ` [PATCH v2 0/2] interconnect: qcom: add MSM8x60 NoC driver Herman van Hazendonk
2026-05-31  4:09   ` [PATCH v2 1/2] dt-bindings: interconnect: qcom: add msm8660 fabric IDs Herman van Hazendonk
2026-05-31  8:00     ` Krzysztof Kozlowski
2026-05-31  4:09   ` [PATCH v2 2/2] interconnect: qcom: add MSM8x60 NoC driver Herman van Hazendonk
2026-05-31  4:34     ` sashiko-bot
2026-05-31  4:09   ` [PATCH v2 1/3] dt-bindings: mfd: qcom-pm8xxx: allow temp-alarm subnode Herman van Hazendonk
2026-05-31  7:59     ` Krzysztof Kozlowski
2026-05-31  4:09   ` [PATCH v2 2/3] dt-bindings: thermal: qcom: add pm8901-temp-alarm Herman van Hazendonk
2026-05-31  4:09   ` [PATCH v2 3/3] thermal: qcom: add PM8901 PMIC temperature-alarm driver Herman van Hazendonk
2026-05-31  4:41     ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260531043213.D18801F00893@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=github.com@herrie.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox