All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: zjzhao@edatec.cn
Cc: robh@kernel.org, linux-serial@vger.kernel.org,
	devicetree@vger.kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver
Date: Tue, 08 Sep 2026 10:44:13 +0000	[thread overview]
Message-ID: <20260908104417.7EF0D1F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260908103129.58085-3-zjzhao@edatec.cn>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] UART transmit FIFO data corruption and stats race due to missing port lock in wk2xxx_handle_tx().
- [High] TTY buffer flood due to persistent overrun flag in batch RX loop.
- [High] The tx_empty callback performs a synchronous SPI read which sleeps while the serial core holds the port spinlock.
- [High] Early return on SPI error in wk2xxx_shutdown skips essential software state cleanup, causing open_ports to permanently leak.
- [Medium] OF node reference leak when parsing DT nodes for non-RS485 variants.
- [Medium] Commit message claims to apply serial properties from DT subnodes, but they are not applied.
- [Low] The driver intentionally breaks the build on architectures with L1_CACHE_BYTES < 31 (e.g., m68k).
--

commit 8ec8660308853d2cff61b90024208e8b3e1bafdb
Author: Zi Jie Zhao <zjzhao@edatec.cn>
Subject: serial: wk2xxx: Add WK2xxx SPI UART driver

This commit introduces a new driver for the WK2xxx SPI to UART bridge ICs.
It models the driver after the NXP sc16is7xx driver, utilizing a kthread
worker for SPI register accesses and supporting polled or interrupt-driven
operation.

> diff --git a/drivers/tty/serial/wk2xxx.c b/drivers/tty/serial/wk2xxx.c
> index 0000000000000..15869fbdeab07
> --- /dev/null
> +++ b/drivers/tty/serial/wk2xxx.c
[ ... ]
> +	/*
> +	 * Shared SPI transfer buffers. All SPI accesses are serialized by
> +	 * s->reg_lock, so these are never used concurrently. Each buffer is
> +	 * cache-line aligned and sized to a full cache line, so DMA cache
> +	 * maintenance on one of them can never invalidate a line shared
> +	 * with any other field (notably the p[] array below).
> +	 */
> +	u8				spi_tx[L1_CACHE_BYTES] ____cacheline_aligned;
> +	u8				spi_rx[L1_CACHE_BYTES] ____cacheline_aligned;
> +	struct wk2xxx_one		p[];
> +};
> +
> +static_assert(WK2XXX_MAX_SPI_LEN + 1 <= L1_CACHE_BYTES);

[Severity: Low]
Does this intentionally break the build for architectures with a cache line 
size smaller than 31 bytes (such as m68k where L1_CACHE_BYTES is 16)? 

Might it be better to size the array to something like 
max(L1_CACHE_BYTES, 32) instead of failing the compilation?

[ ... ]
> +static void wk2xxx_handle_tx(struct uart_port *port)
> +{
[ ... ]
> +	to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
> +	sent = 0;
> +	while (to_send) {
> +		unsigned int chunk = min_t(unsigned int, to_send,
> +					   WK2XXX_MAX_SPI_LEN);
> +
> +		if (wk2xxx_fifo_write(s, portno, tail, chunk))
> +			break;
> +		tail += chunk;
> +		to_send -= chunk;
> +		sent += chunk;
> +	}
> +	uart_xmit_advance(port, sent);

[Severity: High]
Is it safe to manipulate the xmit_fifo pointers and call uart_xmit_advance() 
here without holding the port lock?

If a concurrent operation like a tcflush() ioctl resets the FIFO via 
uart_flush_buffer() (which zeroes the in and out indices), could this 
lockless uart_xmit_advance() call advance the out index beyond the in index, 
causing a substantial underflow and subsequent data corruption?

[ ... ]
> +static void wk2xxx_handle_rx(struct uart_port *port)
> +{
[ ... ]
> +	/* Map the FIFO status register error flags to line status. */
> +	if (fsr & WK2XXX_FSR_ERR_MASK) {
> +		if (fsr & WK2XXX_FSR_RFPE_BIT) {
> +			port->icount.parity++;
> +			lsr |= WK2XXX_LSR_PE_BIT;
> +			flag = TTY_PARITY;
> +		}
> +		if (fsr & WK2XXX_FSR_RFFE_BIT) {
> +			port->icount.frame++;
> +			lsr |= WK2XXX_LSR_FE_BIT;
> +			flag = TTY_FRAME;
> +		}
> +		if (fsr & WK2XXX_FSR_RFOE_BIT) {
> +			port->icount.overrun++;
> +			lsr |= WK2XXX_LSR_OE_BIT;
> +			flag = TTY_OVERRUN;
> +		}
[ ... ]
> +	for (i = 0; i < rxlen; ++i) {
> +		u8 ch = one->buf[i];
> +
> +		if (uart_handle_sysrq_char(port, ch))
> +			continue;
> +
> +		if (lsr & port->ignore_status_mask)
> +			continue;
> +
> +		uart_insert_char(port, lsr, WK2XXX_LSR_OE_BIT, ch, flag);
> +	}

[Severity: High]
Could this loop insert an excessive number of dummy overrun characters into 
the TTY buffer?

Since lsr is modified once outside the loop when the WK2XXX_FSR_RFOE_BIT is 
set, the WK2XXX_LSR_OE_BIT remains active for the entire iteration. When 
uart_insert_char() evaluates this bit, it will insert a dummy overrun byte 
for every single character in this chunk (up to 256 times) for a single 
overrun event.

[ ... ]
> +static unsigned int wk2xxx_tx_empty(struct uart_port *port)
> +{
> +	struct wk2xxx_port *s = dev_get_drvdata(port->dev);
> +	u8 fsr = 0;
> +
> +	if (wk2xxx_port_reg_read(s, port->iobase, WK2XXX_FSR_REG, &fsr))
> +		return TIOCSER_TEMT;

[Severity: High]
Can this sequence sleep while executing in an atomic context?

The serial core routinely invokes the ops->tx_empty() callback while holding 
the port spinlock with interrupts disabled (for instance, during the 
TIOCSERGETLSR ioctl). However, wk2xxx_port_reg_read() performs a synchronous 
spi_sync() transfer which can sleep.

[ ... ]
> +static void wk2xxx_shutdown(struct uart_port *port)
> +{
> +	struct wk2xxx_port *s = dev_get_drvdata(port->dev);
> +	unsigned int portno = port->iobase;
> +	u8 reg;
> +
> +	scoped_guard(mutex, &s->reg_lock) {
> +		/* Disable the sub-UART interrupt in the global mask. */
> +		if (wk2xxx_raw_read(s, WK2XXX_GIER_REG, &reg))
> +			return;

[Severity: High]
If this SPI read fails, the function returns early. Does this leave the 
software state permanently out of sync?

By returning here, the function skips the atomic_dec_return(&s->open_ports) 
and kthread_flush_worker() cleanup steps at the end of wk2xxx_shutdown(). 
This leaves the open_ports counter permanently elevated and strands the 
polling kthread so it can never be stopped.

[ ... ]
> +static int wk2xxx_probe(struct spi_device *spi)
> +{
[ ... ]
> +		/* Locate the matching "serial@i" DT subnode, if any. */
> +		for_each_available_child_of_node(dev->of_node, child) {
> +			u32 reg;
> +
> +			if (!of_node_name_eq(child, "serial"))
> +				continue;
> +			if (of_property_read_u32(child, "reg", &reg))
> +				continue;
> +			if (reg == i) {
> +				port_np = child;
> +				break;
> +			}
> +		}
[ ... ]
> +		/*
> +		 * Temporarily retarget dev's fwnode to the per-port subnode
> +		 * so uart_get_rs485_mode() picks up the per-port properties.
> +		 */
> +		if (port_np && devtype->has_rs485) {
> +			device_set_node(dev, of_fwnode_handle(port_np));
> +			ret = uart_get_rs485_mode(&s->p[i].port);
> +			device_set_node(dev, saved_fwnode);
> +			of_node_put(port_np);
> +			if (ret)
> +				goto out_ports;
> +		}

[Severity: Medium]
Is there a device tree node reference leak here for non-RS485 variants?

The port_np reference is acquired via the for_each_available_child_of_node() 
loop earlier. However, if devtype->has_rs485 evaluates to false (such as for 
the WK2132 or WK2202 variants), this block is entirely bypassed and 
of_node_put(port_np) is never called.

[Severity: Medium]
The commit message states the driver will "apply serial and RS-485 properties 
from each serial@N DT subnode to the corresponding channel."

While uart_get_rs485_mode() is called in this block, should there also be a 
call to uart_read_port_properties() to ensure the generic serial properties 
(like tx-trigger or rx-trigger) are actually applied as described?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260908103129.58085-1-zjzhao@edatec.cn?part=2

  reply	other threads:[~2026-09-08 10:44 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-08 10:31 [PATCH v4 0/2] WK2xxx SPI to UART bridge driver zjzhao
2026-09-08 10:31 ` [PATCH v4 1/2] dt-bindings: serial: Document WK2xxx SPI UART zjzhao
2026-09-08 10:38   ` sashiko-bot
2026-09-08 17:49   ` Conor Dooley
2026-09-08 10:31 ` [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver zjzhao
2026-09-08 10:44   ` sashiko-bot [this message]
2026-09-08 17:51   ` Hugo Villeneuve

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=20260908104417.7EF0D1F00A3A@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=zjzhao@edatec.cn \
    /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.