All of lore.kernel.org
 help / color / mirror / Atom feed
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

  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 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.