From: sashiko-bot@kernel.org
To: "Jingyuan Liang" <jingyliang@chromium.org>
Cc: dmitry.torokhov@gmail.com, linux-input@vger.kernel.org,
conor+dt@kernel.org, devicetree@vger.kernel.org, robh@kernel.org
Subject: Re: [PATCH v4 10/11] HID: spi-hid: add power management implementation
Date: Tue, 09 Jun 2026 05:00:04 +0000 [thread overview]
Message-ID: <20260609050005.549721F00893@smtp.kernel.org> (raw)
In-Reply-To: <20260609-send-upstream-v4-10-b843d5e6ced3@chromium.org>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
New issues:
- [High] `spi_hid_resume()` calls `hid_driver_reset_resume()` synchronously before the device signals readiness.
- [High] Error path in `spi_hid_resume()` creates unbalanced IRQ disable depth for wakeup-capable devices.
- [High] Missing synchronization between system suspend and `reset_work` causes a TOCTOU race on `power_state`.
- [High] System suspend cuts device power without synchronizing against ongoing SPI HID requests.
- [High] Unbalanced `enable_irq()` in `spi_hid_resume()` triggers a kernel warning if the device was already `HIDSPI_OFF`.
- [Medium] Conditional cleanup of the wake IRQ leads to a memory leak if userspace disables wakeup.
Pre-existing issues:
- [Critical] Returning `IRQ_HANDLED` for an unpowered device causes an infinite IRQ storm.
--
commit 57585bfd26214992cdd5246d2df2a4622693a915
Author: Jingyuan Liang <jingyliang@chromium.org>
HID: spi-hid: add power management implementation
This commit implements HID over SPI driver power management callbacks, adding
suspend and resume functions for system power state transitions.
> diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-hid-core.c
> index 698e72102c11e..517f069134775 100644
> --- a/drivers/hid/spi-hid/spi-hid-core.c
> +++ b/drivers/hid/spi-hid/spi-hid-core.c
[ ... ]
> @@ -245,6 +247,96 @@ static const char *spi_hid_power_mode_string(enum hidspi_power_state power_state
> }
> }
>
> +static int spi_hid_suspend(struct spi_hid *shid)
> +{
> + int error;
> + struct device *dev = &shid->spi->dev;
> +
> + guard(mutex)(&shid->power_lock);
[Severity: High]
Does this suspend routine need to synchronize against ongoing SPI I/O?
HID I/O routines like spi_hid_ll_output_report() and spi_hid_ll_raw_request()
acquire output_lock and io_lock to serialize transfers, but they do not check
or lock power_state. Because spi_hid_suspend() acquires power_lock to disable
the device's regulator but does not acquire output_lock or io_lock, nor does
it clear SPI_HID_READY, could it cut hardware power concurrently while a kernel
thread is actively executing spi_write() on another CPU, leading to hardware
bus timeouts?
[Severity: High]
Does this function also need to flush or cancel the asynchronous reset_work
before cutting device power?
If it doesn't synchronize with the workqueue, could there be a race where
spi_hid_reset_response() locklessly reads power_state as HIDSPI_ON, gets
preempted, and allows spi_hid_suspend() to power down the device? If the
workqueue then resumes and calls spi_hid_sync_request(), it would initiate
SPI transmissions to an unpowered device.
> + if (shid->power_state == HIDSPI_OFF)
> + return 0;
[Severity: High]
If this early return is taken, will the subsequent resume cause an unbalanced
IRQ enable warning?
In spi_hid_suspend(), this early return completely bypasses the call to
disable_irq(shid->spi->irq). However, upon system resume, spi_hid_resume()
unconditionally calls enable_irq(shid->spi->irq). If the IRQ disable depth
was not incremented during suspend, could this unconditional enable drop the
depth below zero and trigger a WARN(1, "Unbalanced enable for IRQ %d\n")?
> +
> + if (shid->hid) {
> + error = hid_driver_suspend(shid->hid, PMSG_SUSPEND);
> + if (error) {
> + dev_err(dev, "%s failed to suspend hid driver: %d\n",
> + __func__, error);
> + return error;
> + }
> + }
> +
> + disable_irq(shid->spi->irq);
> +
> + if (!device_may_wakeup(dev)) {
> + set_bit(SPI_HID_RESET_PENDING, &shid->flags);
> +
> + shid->ops->assert_reset(shid->ops);
> +
> + error = shid->ops->power_down(shid->ops);
> + if (error) {
> + dev_err(dev, "%s: could not power down\n", __func__);
> + shid->regulator_error_count++;
> + shid->regulator_last_error = error;
> + /* Undo partial suspend before returning error */
> + shid->ops->deassert_reset(shid->ops);
> + clear_bit(SPI_HID_RESET_PENDING, &shid->flags);
> + enable_irq(shid->spi->irq);
> + if (shid->hid)
> + hid_driver_reset_resume(shid->hid);
> + return error;
> + }
> +
> + shid->power_state = HIDSPI_OFF;
> + }
> + return 0;
> +}
> +
> +static int spi_hid_resume(struct spi_hid *shid)
> +{
> + int error;
> + struct device *dev = &shid->spi->dev;
> +
> + guard(mutex)(&shid->power_lock);
> +
> + if (!device_may_wakeup(dev)) {
> + if (shid->power_state == HIDSPI_OFF) {
> + shid->ops->assert_reset(shid->ops);
> +
> + shid->ops->sleep_minimal_reset_delay(shid->ops);
> +
> + error = shid->ops->power_up(shid->ops);
> + if (error) {
> + dev_err(dev, "%s: could not power up\n", __func__);
> + shid->regulator_error_count++;
> + shid->regulator_last_error = error;
> + return error;
> + }
> + shid->power_state = HIDSPI_ON;
> + shid->ops->deassert_reset(shid->ops);
> + }
> + }
> +
> + enable_irq(shid->spi->irq);
> +
> + if (shid->hid) {
> + error = hid_driver_reset_resume(shid->hid);
[Severity: High]
Does this code need to wait for the device's asynchronous RESET_RESPONSE
before sending further commands?
During a full resume cycle, the device is powered up, its reset line is
deasserted, and enable_irq() is called. The hardware then begins booting
and will signal readiness via an asynchronous RESET_RESPONSE interrupt.
However, spi_hid_resume() does not wait for this interrupt and synchronously
executes hid_driver_reset_resume() immediately. Because the SPI_HID_READY
flag is left set during suspend, any HID initialization commands generated
by the upper layer's resume callback will be immediately transmitted over
the SPI bus to a device that is still booting. Could this result in bus
timeouts and failed resume cycles?
> + if (error) {
> + dev_err(dev, "%s: failed to reset resume hid driver: %d\n",
> + __func__, error);
> + /* Undo partial resume before returning error */
> + disable_irq(shid->spi->irq);
> + if (!device_may_wakeup(dev)) {
> + set_bit(SPI_HID_RESET_PENDING, &shid->flags);
> + shid->ops->assert_reset(shid->ops);
> + shid->ops->power_down(shid->ops);
> + shid->power_state = HIDSPI_OFF;
> + }
[Severity: High]
Can this error path create an unbalanced IRQ disable depth for
wakeup-capable devices?
If a device is wakeup-capable, spi_hid_suspend() skips powering down the
device (leaving power_state at HIDSPI_ON) but still calls disable_irq().
In spi_hid_resume(), enable_irq() is called, bringing the IRQ depth to 0.
If hid_driver_reset_resume() subsequently fails, this error recovery path
calls disable_irq() to undo the resume, but skips setting HIDSPI_OFF due
to the if (!device_may_wakeup(dev)) condition. Because the PM core ignores
resume errors and continues, the device remains at HIDSPI_ON.
On the next system suspend cycle, spi_hid_suspend() will see HIDSPI_ON and
execute disable_irq() a second time. A later successful resume will only call
enable_irq() once, leaving the device's interrupt permanently disabled.
> + return error;
> + }
> + }
> + return 0;
> +}
[ ... ]
> @@ -795,6 +887,11 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shid)
> trace_spi_hid_header_transfer(shid);
>
> scoped_guard(mutex, &shid->io_lock) {
> + if (shid->power_state == HIDSPI_OFF) {
> + dev_warn(dev, "Device is off, ignoring interrupt\n");
> + goto out;
> + }
[Severity: Critical]
This isn't a bug introduced by this patch, but should this path return IRQ_NONE
instead of IRQ_HANDLED?
In spi_hid_error_handler(), if the device exceeds maximum reset attempts,
it is aborted and powered off (HIDSPI_OFF). The function then returns,
releasing guard(disable_irq)(&shid->spi->irq) and explicitly re-enabling
the IRQ line.
If the active-low interrupt line floats low due to power loss, it immediately
triggers. This threaded handler detects HIDSPI_OFF, logs a warning, and
returns IRQ_HANDLED via goto out.
Returning IRQ_HANDLED circumvents the kernel's spurious interrupt detector.
Because the physical line remains asserted and the handler claims success
without clearing the hardware source, the genirq subsystem immediately
re-fires the interrupt. Can this lead to a 100% CPU lockup?
[ ... ]
> @@ -1251,10 +1343,19 @@ int spi_hid_core_probe(struct spi_device *spi, struct spihid_ops *ops,
> dev_err(dev, "%s: unable to request threaded IRQ\n", __func__);
> return error;
> }
> + if (device_may_wakeup(dev)) {
> + error = dev_pm_set_wake_irq(dev, spi->irq);
> + if (error) {
> + dev_err(dev, "%s: failed to set wake IRQ\n", __func__);
> + return error;
> + }
> + }
>
> error = shid->ops->power_up(shid->ops);
> if (error) {
> dev_err(dev, "%s: could not power up\n", __func__);
> + if (device_may_wakeup(dev))
> + dev_pm_clear_wake_irq(dev);
[Severity: Medium]
Will this conditional cleanup leak memory if userspace alters the wakeup
settings?
In spi_hid_core_probe(), dev_pm_set_wake_irq() is executed if
device_may_wakeup(dev) is true. The cleanup path conditionally calls
dev_pm_clear_wake_irq(dev) based on the same check.
However, device_may_wakeup() evaluates dev->power.should_wakeup, which is
mutable state controllable by userspace via sysfs
(/sys/devices/.../power/wakeup). If userspace disables wakeup after the
device is probed, the check fails, bypassing the cleanup and permanently
leaking the allocated wake_irq memory while leaving a dangling pointer in
dev->power.wakeirq. Should dev_pm_clear_wake_irq() be called unconditionally
since it handles missing wake IRQs gracefully?
[ ... ]
> @@ -1284,9 +1385,31 @@ void spi_hid_core_remove(struct spi_device *spi)
> error = shid->ops->power_down(shid->ops);
> if (error)
> dev_err(dev, "failed to disable regulator\n");
> +
> + if (device_may_wakeup(dev))
> + dev_pm_clear_wake_irq(dev);
[Severity: Medium]
As noted above, should dev_pm_clear_wake_irq() be called unconditionally here
to avoid leaking the wake_irq structure if userspace disables device wakeup?
> }
> EXPORT_SYMBOL_GPL(spi_hid_core_remove);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-send-upstream-v4-0-b843d5e6ced3@chromium.org?part=10
next prev parent reply other threads:[~2026-06-09 5:00 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-09 4:40 [PATCH v4 00/11] Add spi-hid transport driver Jingyuan Liang
2026-06-09 4:40 ` [PATCH v4 01/11] Documentation: Correction in HID output_report callback description Jingyuan Liang
2026-06-09 4:40 ` [PATCH v4 02/11] HID: Add BUS_SPI support and define HID_SPI_DEVICE macro Jingyuan Liang
2026-06-09 4:40 ` [PATCH v4 03/11] HID: spi-hid: add transport driver skeleton for HID over SPI bus Jingyuan Liang
2026-06-09 4:56 ` sashiko-bot
2026-06-09 4:40 ` [PATCH v4 04/11] HID: spi-hid: add spi-hid driver HID layer Jingyuan Liang
2026-06-09 4:54 ` sashiko-bot
2026-06-09 4:40 ` [PATCH v4 05/11] HID: spi-hid: add HID SPI protocol implementation Jingyuan Liang
2026-06-09 5:00 ` sashiko-bot
2026-06-09 4:40 ` [PATCH v4 06/11] HID: spi_hid: add spi_hid traces Jingyuan Liang
2026-06-09 4:55 ` sashiko-bot
2026-06-09 4:40 ` [PATCH v4 07/11] HID: spi_hid: add ACPI support for SPI over HID Jingyuan Liang
2026-06-09 5:01 ` sashiko-bot
2026-06-09 4:40 ` [PATCH v4 08/11] HID: spi_hid: add device tree " Jingyuan Liang
2026-06-09 4:54 ` sashiko-bot
2026-06-09 4:41 ` [PATCH v4 09/11] dt-bindings: input: Document hid-over-spi DT schema Jingyuan Liang
2026-06-09 4:50 ` sashiko-bot
2026-06-09 4:41 ` [PATCH v4 10/11] HID: spi-hid: add power management implementation Jingyuan Liang
2026-06-09 5:00 ` sashiko-bot [this message]
2026-06-09 4:41 ` [PATCH v4 11/11] HID: spi-hid: add panel follower support Jingyuan Liang
2026-06-09 4:58 ` 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=20260609050005.549721F00893@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=jingyliang@chromium.org \
--cc=linux-input@vger.kernel.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