From: sashiko-bot@kernel.org
To: "Emfox Zhou" <emfox@debian.org>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v5 2/5] HID: intel-thc-hid: intel-ipts: receive reports
Date: Sun, 15 Nov 2026 00:07:03 +0000 [thread overview]
Message-ID: <sashiko-outbox-215139@kernel.org> (raw)
In-Reply-To: <20261114234242.177387-3-emfox@debian.org>
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] HID: intel-thc-hid: intel-ipts: shared IRQ race unmasks
thc_ipts_thread()
- [Medium] HID: intel-thc-hid: intel-ipts: DMA buffer leak on
thc_dma_allocate() failure
--
commit bc2c54076d407e032aad5ea9b6b5fb2d73e4ce2a
Author: Emfox Zhou <emfox@debian.org>
HID: intel-thc-hid: intel-ipts: receive reports
This commit configures the Rx and Tx DMA engines for Intel IPTS ports,
registers a threaded interrupt handler to drain incoming frames, and adds a
work item to recover from 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 b0e3fe8ea8aef..76224f4444828 100644
> --- a/drivers/hid/intel-thc-hid/intel-ipts/ipts.c
> +++ b/drivers/hid/intel-thc-hid/intel-ipts/ipts.c
[ ... ]
> @@ -122,6 +132,198 @@ static int thc_ipts_touch_set(struct thc_ipts *idev, bool enable)
[ ... ]
> +static int thc_ipts_dma_start(struct thc_ipts *idev)
> +{
> + int ret;
> +
> + /* Allocated once: a start that failed must not leave a NULL buffer. */
> + if (!idev->rx_frame) {
> + /* The core rounds the packet size up to a page. */
> + idev->rx_frame = devm_kzalloc(idev->dev,
> + round_up(idev->max_rx, SZ_4K),
> + GFP_KERNEL);
> + if (!idev->rx_frame)
> + return -ENOMEM;
> + }
> +
> + ret = thc_dma_set_max_packet_sizes(idev->thc, 0, idev->max_rx,
> + idev->max_tx, 0);
> + if (ret)
> + return ret;
> +
> + ret = thc_dma_allocate(idev->thc);
> + if (ret)
> + return ret;
[Severity: Medium]
Does this leak config->prd_tbls and scatterlist buffers when
thc_dma_allocate() fails?
idev->max_rx is decoded from a 14-bit field (up to 1 MiB) and only checked
against THC_IPTS_RX_HDR_SIZE in thc_ipts_port_start(). If idev->max_rx
exceeds PRD_ENTRIES_NUM * SZ_4K (64 KiB on 4K-page systems) or sgl_alloc()
fails, setup_dma_buffers() returns -ENOMEM after allocating config->prd_tbls
and any earlier config->sgls[i], while thc_dma_allocate()'s rollback skips
the failing channel:
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c:setup_dma_buffers() {
...
config->prd_tbls = cpu_addr;
config->prd_tbls_dma_handle = dma_handle;
buf_sz = dma_get_max_packet_size(dev, config);
/* Allocate and map the scatter-gather lists, one for each PRD table */
for (i = 0; i < config->prd_tbl_num; i++) {
config->sgls[i] = sgl_alloc(buf_sz, GFP_KERNEL, &nent);
if (!config->sgls[i] || nent > PRD_ENTRIES_NUM) {
dev_err_once(dev->dev, "sgl_alloc (%uth) failed, nent %u\n",
i, nent);
return -ENOMEM;
}
...
}
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c:thc_dma_allocate() {
...
release_bufs:
while (chan--)
release_dma_buffers(dev, &dev->dma_ctx->dma_config[chan]);
return ret;
}
Because thc_ipts_dma_start() returns before setting idev->dma_ready,
thc_ipts_dma_stop() also skips thc_dma_release(), leaking the unmanaged
coherent PRD table and scatterlists.
> +
> + ret = thc_dma_configure(idev->thc);
> + if (ret) {
> + thc_dma_release(idev->thc);
> + return ret;
> + }
> +
> + WRITE_ONCE(idev->dma_ready, true);
> +
> + return 0;
> +}
> +
> +irqreturn_t thc_ipts_hardirq(int irq, void *data)
> +{
> + struct thc_ipts *idev = data;
> +
> + /* Level triggered and shared: mask first, or it stays asserted. */
> + thc_interrupt_enable(idev->thc, false);
> +
> + /* A reset in flight: the port is masked, so this is not ours. */
> + if (READ_ONCE(idev->recovering))
> + return IRQ_NONE;
> +
> + /* Nothing of ours is pending: the cause belongs to a sharer. */
> + if (READ_ONCE(idev->stopping) || !READ_ONCE(idev->dma_ready))
> + return IRQ_NONE;
> +
> + return IRQ_WAKE_THREAD;
> +}
[ ... ]
> +void thc_ipts_recover(struct work_struct *work)
> +{
> + struct thc_ipts *idev = container_of(work, struct thc_ipts,
> + recover_work);
> +
> + /* A handler may have unmasked after the error; keep it masked. */
> + thc_interrupt_enable(idev->thc, false);
> +
> + 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;
> + }
> +
> + 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)
> +{
[ ... ]
> + if (!READ_ONCE(idev->stopping))
> + thc_interrupt_enable(idev->thc, true);
> +
> + return ret;
> +}
[Severity: Medium]
Can a shared interrupt cause thc_ipts_thread() or recovery to run with the
THC port interrupt unmasked?
thc_ipts_probe() requests the interrupt with IRQF_SHARED | IRQF_COND_ONESHOT,
which leaves IRQS_ONESHOT unset unless an earlier sharer requested
IRQF_ONESHOT. While thc_ipts_thread() is running on CPU0, an interrupt from
another device sharing the INTx line can run thc_ipts_hardirq() on CPU1:
CPU0 CPU1
irq_thread()
thc_ipts_thread()
for (pass = 0; ...; pass++)
...
thc_ipts_hardirq()
thc_interrupt_enable(..., false)
return IRQ_WAKE_THREAD
/* sets IRQTF_RUNTHREAD */
thc_interrupt_enable(..., true)
/* sees IRQTF_RUNTHREAD set */
thc_ipts_thread()
/* runs with port unmasked */ <-- invariant violated
The second invocation of thc_ipts_thread() runs without thc_ipts_hardirq()
masking the port in between. In addition, thc_ipts_recover() clears
idev->recovering before calling thc_interrupt_enable(idev->thc, true), and
thc_ipts_dma_start() sets idev->dma_ready before thc_ipts_port_start()
unmasks the port; a shared IRQ in either window can wake thc_ipts_thread()
(and even enter thc_ipts_request_recover()) before the delayed
thc_interrupt_enable(idev->thc, true) re-enables the port interrupt.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114234242.177387-1-emfox@debian.org?part=2
next prev parent reply other threads:[~2026-11-15 0:07 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-14 23:42 [PATCH v5 0/5] HID: intel-thc-hid: intel-ipts: support THC IPTS ports Emfox Zhou
2026-11-14 23:42 ` [PATCH v5 1/5] HID: intel-thc-hid: intel-ipts: bring up the port Emfox Zhou
2026-11-14 23:42 ` [PATCH v5 2/5] HID: intel-thc-hid: intel-ipts: receive reports Emfox Zhou
2026-11-15 0:07 ` sashiko-bot [this message]
2026-11-14 23:42 ` [PATCH v5 3/5] HID: intel-thc-hid: intel-ipts: register the HID device Emfox Zhou
2026-11-14 23:54 ` sashiko-bot
2026-11-14 23:42 ` [PATCH v5 4/5] HID: intel-thc-hid: intel-ipts: handle system sleep Emfox Zhou
2026-11-14 23:42 ` [PATCH v5 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-215139@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox