From: sashiko-bot@kernel.org
To: "Emfox Zhou" <emfox@debian.org>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v3 2/5] HID: intel-thc-hid: intel-ipts: receive reports
Date: Sat, 14 Nov 2026 19:10:28 +0000 [thread overview]
Message-ID: <sashiko-outbox-214954@kernel.org> (raw)
In-Reply-To: <20261114183624.2379573-3-emfox@debian.org>
Thank you for your contribution! Sashiko AI review found 5 potential
issues to consider:
- [Medium] HID: intel-thc-hid: intel-ipts: false IRQ_HANDLED in
thc_ipts_hardirq()
- [Medium] HID: intel-thc-hid: intel-ipts: shared IRQ race unmasks
thc_ipts_thread()
- [Medium] HID: intel-thc-hid: intel-ipts: stopping race in
thc_ipts_port_stop()
- [Medium] HID: intel-thc-hid: intel-ipts: PRD descriptors not reset in
thc_ipts_recover()
- [Medium] HID: intel-ipts: thc_dma_write() still stalls when recovery
masks IRQ
--
commit b17ea629fa85b2d8eee1f00ee19bff9913e9e83b
Author: Emfox Zhou <emfox@debian.org>
HID: intel-thc-hid: intel-ipts: receive reports
This commit configures the RXDMA2 and TXDMA engines on the THC port,
registers a shared threaded interrupt handler to receive and log incoming
frames, and adds a recovery work item to reset RXDMA on transfer errors.
> diff --git a/drivers/hid/intel-thc-hid/intel-ipts/ipts.c b/drivers/hid/intel-thc-hid/intel-ipts/ipts.c
> index 264479630ff6e..ac0074a85bfa4 100644
> --- a/drivers/hid/intel-thc-hid/intel-ipts/ipts.c
> +++ b/drivers/hid/intel-thc-hid/intel-ipts/ipts.c
[ ... ]
> @@ -127,6 +139,209 @@ static int thc_ipts_touch_set(struct thc_ipts *idev, bool enable)
[ ... ]
> +irqreturn_t thc_ipts_hardirq(int irq, void *data)
> +{
> + struct thc_ipts *idev = data;
> +
> + /*
> + * The pin is shared and level triggered: mask first, or it stays
> + * asserted on the way out. The cause itself can only be read in the
> + * thread.
> + */
> + thc_interrupt_enable(idev->thc, false);
> +
> + /* A reset in flight owns the engine, and the interrupt is ours. */
> + if (READ_ONCE(idev->recovering))
> + return IRQ_HANDLED;
> +
> + /*
> + * Nothing of ours is pending in these two states and the port is
> + * masked: the interrupt belongs to another function on the line.
> + */
> + if (READ_ONCE(idev->stopping) || !READ_ONCE(idev->dma_ready))
> + return IRQ_NONE;
> +
> + return IRQ_WAKE_THREAD;
> +}
[Severity: Medium]
Should thc_ipts_hardirq() and thc_ipts_thread() return IRQ_NONE instead of
IRQ_HANDLED when idev->recovering is set?
idev->recovering is only set in thc_ipts_thread() after thc_ipts_hardirq()
has already masked the port via thc_interrupt_enable(idev->thc, false),
and the port remains masked until thc_ipts_recover() finishes. While
masked, this port cannot assert the shared INTx line, so any interrupt
arriving during recovery comes from another device on the shared pin.
Furthermore, if thc_rxdma_reset() fails in thc_ipts_recover() (or if
thc_ipts_port_stop() cancels recover_work), idev->recovering stays true
forever and causes thc_ipts_hardirq() to claim every subsequent interrupt
on the shared line, defeating spurious-interrupt detection.
[ ... ]
> +void thc_ipts_recover(struct work_struct *work)
> +{
> + struct thc_ipts *idev = container_of(work, struct thc_ipts,
> + recover_work);
> +
> + if (thc_rxdma_reset(idev->thc)) {
> + /* Nothing left: keep the port masked until it restarts. */
> + dev_err(idev->dev, "cannot reset the receive engine\n");
> + return;
> + }
[Severity: Medium]
Does thc_rxdma_reset() leave mutated PRD descriptors in the RXDMA2 ring
after an error?
When RXDMA2 writes a frame into a PRD table, the hardware updates each
consumed entry's len, sets end_of_prd on the final entry, and sets the
PRD table hw_status. During normal operation, thc_dma_read() calls
thc_copy_one_sgl_to_prd() to restore those fields before advancing the
write pointer. When thc_ipts_thread() sets need_recover on a transfer
error and skips thc_ipts_read_frames() (or when read_dma_buffer() fails
before thc_copy_one_sgl_to_prd() runs), thc_rxdma_reset() only calls
thc_dma_unconfigure() and thc_dma_configure(), which reset the hardware
ring pointers without calling thc_copy_sgls_to_prd(). Those unconsumed
PRD tables retain truncated entry lengths and stale end_of_prd bits, so
later larger frames can trigger PRD buffer overrun errors.
> +
> + WRITE_ONCE(idev->recovering, false);
> + if (!READ_ONCE(idev->stopping))
> + thc_interrupt_enable(idev->thc, true);
> +}
> +
> +irqreturn_t thc_ipts_thread(int irq, void *data)
> +{
[ ... ]
> + /*
> + * The work item resets the engine and unmasks the port again; a frame
> + * read while it rewrites the ring would corrupt the descriptors, so
> + * ->recovering keeps the handler off the line until it is done.
> + */
> + if (need_recover) {
> + WRITE_ONCE(idev->recovering, true);
> + schedule_work(&idev->recover_work);
> + return IRQ_HANDLED;
> + }
[Severity: Medium]
Can a concurrent thc_dma_write() still stall until its timeout when
recovery masks the port?
thc_rxdma_reset() is deferred to recover_work because thc_dma_write()
holds dev->thc_bus_lock while waiting on dev->write_complete_wait for
thc_interrupt_handler() to process THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS.
However, when need_recover is true, thc_ipts_thread() sets idev->recovering
and returns with the port interrupt still masked, and both
thc_ipts_hardirq() and thc_ipts_thread() ignore further interrupts until
thc_ipts_recover() finishes. If an in-flight TxDMA transfer completes
after the polling loop in thc_ipts_thread() exits (or if a caller enters
thc_dma_write() and takes dev->thc_bus_lock before recover_work runs),
thc_ipts_recover() blocks on dev->thc_bus_lock while the TxDMA completion
interrupt stays masked, stalling thc_dma_write() for its full timeout.
> +
> + if (!READ_ONCE(idev->stopping))
> + thc_interrupt_enable(idev->thc, true);
> +
> + /* An interrupt with no cause of ours is not ours to claim. */
> + return ret;
> +}
[Severity: Medium]
Can a shared interrupt race with port unmasking and run thc_ipts_thread()
while the port interrupt is enabled?
thc_ipts_probe() requests the interrupt with IRQF_SHARED |
IRQF_COND_ONESHOT, which leaves IRQS_ONESHOT unset when no earlier handler
on the line requested IRQF_ONESHOT, so thc_ipts_hardirq() can run on
another CPU while thc_ipts_thread() is executing:
CPU0 CPU1
thc_ipts_thread()
for (pass = 0; ...)
thc_interrupt_handler()
<hardirq: shared INTx>
thc_ipts_hardirq()
thc_interrupt_enable(thc, false)
return IRQ_WAKE_THREAD
(sets IRQTF_RUNTHREAD)
thc_interrupt_enable(thc, true)
thc_ipts_thread() <-- runs with port unmasked
The same inverted state also occurs even with IRQS_ONESHOT if a shared
interrupt fires between WRITE_ONCE(idev->recovering, false) and
thc_interrupt_enable(idev->thc, true) in thc_ipts_recover(), or between
WRITE_ONCE(idev->dma_ready, true) and thc_interrupt_enable(idev->thc, true)
in thc_ipts_port_start(). If that second thc_ipts_thread() run hits an
error and takes the need_recover path, neither it nor thc_ipts_recover()
masks the port, so thc_rxdma_reset() runs with interrupts enabled on the
port.
[ ... ]
> @@ -220,6 +451,16 @@ void thc_ipts_port_stop(struct thc_ipts *idev)
> if (!idev->thc)
> return;
>
> + /*
> + * Mask the line, then wait for a handler that may be holding a
> + * frame for the HID device which is about to go away.
> + */
> + WRITE_ONCE(idev->stopping, true);
> + thc_interrupt_enable(idev->thc, false);
> + synchronize_irq(idev->irq);
> + cancel_work_sync(&idev->recover_work);
[Severity: Medium]
Can thc_ipts_thread() or thc_ipts_recover() re-enable interrupts during
thc_ipts_port_stop()?
CPU0 CPU1
thc_ipts_thread()
if (!READ_ONCE(idev->stopping))
thc_ipts_port_stop()
WRITE_ONCE(idev->stopping, true)
thc_interrupt_enable(thc, false)
thc_interrupt_enable(thc, true) <-- re-enables IRQ during stop
synchronize_irq(idev->irq)
cancel_work_sync(&idev->recover_work)
Because thc_ipts_port_stop() masks the port before synchronize_irq() and
cancel_work_sync(), an in-flight thread or recovery work item that has
already passed its !READ_ONCE(idev->stopping) check will unmask the port
again, leaving THC_M_PRT_INT_EN_GBL_INT_EN set while the controller and
DMA buffers are torn down. Calling thc_interrupt_enable(idev->thc, false)
after synchronize_irq() and cancel_work_sync() as well would close this
window.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114183624.2379573-1-emfox@debian.org?part=2
next prev parent reply other threads:[~2026-11-14 19:10 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-14 18:36 [PATCH v3 0/5] HID: intel-thc-hid: intel-ipts: support THC IPTS ports Emfox Zhou
2026-11-14 18:36 ` [PATCH v3 1/5] HID: intel-thc-hid: intel-ipts: bring up the port Emfox Zhou
2026-11-14 18:55 ` sashiko-bot
2026-11-14 18:36 ` [PATCH v3 2/5] HID: intel-thc-hid: intel-ipts: receive reports Emfox Zhou
2026-11-14 19:10 ` sashiko-bot [this message]
2026-11-14 18:36 ` [PATCH v3 3/5] HID: intel-thc-hid: intel-ipts: register the HID device Emfox Zhou
2026-11-14 18:59 ` sashiko-bot
2026-11-14 18:36 ` [PATCH v3 4/5] HID: intel-thc-hid: intel-ipts: handle system sleep Emfox Zhou
2026-11-14 19:02 ` sashiko-bot
2026-11-14 18:36 ` [PATCH v3 5/5] HID: intel-thc-hid: intel-ipts: document the IPTS mode Emfox Zhou
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=sashiko-outbox-214954@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=emfox@debian.org \
--cc=linux-input@vger.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.