Linux Serial subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Biju Das" <biju.das.au@gmail.com>
Cc: linux-serial@vger.kernel.org, kvm@vger.kernel.org,
	wsa+renesas@sang-engineering.com
Subject: Re: [PATCH v2 2/4] serial: sh-sci: Keep TX enabled on TEI while data is pending
Date: Wed, 21 Oct 2026 13:38:48 +0000	[thread overview]
Message-ID: <sashiko-outbox-180317@kernel.org> (raw)
In-Reply-To: <20261021-g2ul_sci-v2-2-b4e038220018@bp.renesas.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] serial: sh-sci: TX stall in sci_tx_end_interrupt() on x_char when stopped
- [High] serial: sh-sci: lock drop race in sci_tx_end_interrupt() before TX handoff
- [Medium] serial: sh-sci: sci_tx_end_interrupt() clears TE without checking TEND
--

commit 24fb279b2944cfab75c45c919854637a4575564b
Author: Biju Das <biju.das.jz@bp.renesas.com>

serial: sh-sci: Keep TX enabled on TEI while data is pending

This commit updates sci_tx_end_interrupt() to check for pending transmit data
under the port lock before disabling the transmitter, handing over to
sci_tx_interrupt() when data is waiting. It also adds tie and teie bit masks
to struct sci_port_params_bits for the supported port variants.

> diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
> index 9eb297dbc9010..58120b523ca1e 100644
> --- a/drivers/tty/serial/sh-sci.c
> +++ b/drivers/tty/serial/sh-sci.c

[ ... ]

> @@ -2012,6 +2016,7 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr)
>  {
>  	struct uart_port *port = ptr;
>  	struct sci_port *s = to_sci_port(port);
> +	struct tty_port *tport = &port->state->port;
>  	const struct sci_common_regs *regs = s->params->common_regs;
>  	unsigned long flags;
>  	u32 ctrl;
> @@ -2020,8 +2025,17 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr)
>  		return sci_tx_interrupt(irq, ptr);
>  
>  	uart_port_lock_irqsave(port, &flags);
> -	ctrl = s->ops->read_reg(port, regs->control) &
> -		~(s->params->param_bits->te_clear);
> +	ctrl = s->ops->read_reg(port, regs->control);
> +	if (port->x_char || (!kfifo_is_empty(&tport->xmit_fifo) &&
> +			     !uart_tx_stopped(port))) {
> +		ctrl &= ~(s->params->param_bits->teie);
> +		ctrl |= s->params->param_bits->tie;
> +		s->ops->write_reg(port, regs->control, ctrl);

[Severity: High]
What happens here when port->x_char is non-zero while uart_tx_stopped(port)
is true and tport->xmit_fifo is not empty?

In that case, sci_tx_end_interrupt() clears param_bits->teie, sets
param_bits->tie, and calls sci_tx_interrupt() -> s->ops->transmit_chars(port).
In both drivers/tty/serial/sh-sci.c:sci_transmit_chars() and
drivers/tty/serial/rsci.c:rsci_transmit_chars(), the loop sends port->x_char,
clears port->x_char = 0, and breaks out on the next iteration because stopped
is true:

	do {
		unsigned char c;

		if (port->x_char) {
			c = port->x_char;
			port->x_char = 0;
		} else if (stopped || !kfifo_get(&tport->xmit_fifo, &c)) {
			break;
		}

However, at the end of sci_transmit_chars() and rsci_transmit_chars(),
param_bits->tie is only cleared and param_bits->teie is only re-enabled when
tport->xmit_fifo is empty:

drivers/tty/serial/sh-sci.c:sci_transmit_chars() {
	...
	if (kfifo_is_empty(&tport->xmit_fifo)) {
		if (s->type == PORT_SCI) {
			ctrl = sci_serial_in(port, SCSCR);
			ctrl &= ~SCSCR_TIE;
			ctrl |= SCSCR_TEIE;
			sci_serial_out(port, SCSCR, ctrl);
		}

		sci_stop_tx(port);
	}
}

drivers/tty/serial/rsci.c:rsci_transmit_chars() {
	...
	if (kfifo_is_empty(&tport->xmit_fifo)) {
		ctrl = rsci_serial_in(port, CCR0);
		ctrl &= ~CCR0_TIE;
		ctrl |= CCR0_TEIE;
		rsci_serial_out(port, CCR0, ctrl);
	}
}

Because tport->xmit_fifo is still non-empty while the port is stopped,
param_bits->tie remains set and param_bits->teie remains cleared after
port->x_char has been written:

1. On sci ports without hardware status auto-clear, when the transmit data
   register empties and triggers sci_tx_interrupt() again with
   port->x_char == 0 and stopped == 1, sci_transmit_chars() breaks out of the
   loop without writing a new byte and calls s->ops->clear_SCxSR(), causing
   the hardware to repeatedly re-transmit the stale byte in the data register
   in an interrupt loop.

2. On both sci and rsci ports, because param_bits->teie is cleared,
   sci_tx_end_interrupt() never fires after port->x_char finishes shifting
   out, so transmit-enable is never cleared. When the port is later unstopped
   and uart_start() calls sci_start_tx() or rsci_start_tx(), transmit-enable
   and param_bits->tie are already set in regs->control, producing no rising
   edge to trigger the transmit interrupt and stalling the remaining data in
   tport->xmit_fifo.

Could sci_transmit_chars() and rsci_transmit_chars() also switch back from
param_bits->tie to param_bits->teie when stopped is true?

> +		uart_port_unlock_irqrestore(port, flags);
> +		return sci_tx_interrupt(irq, ptr);
> +	}

[Severity: High]
Does dropping port->lock here before calling sci_tx_interrupt() introduce a
race window with concurrent port operations?

sci_tx_interrupt() immediately re-acquires port->lock to invoke
s->ops->transmit_chars(port):

drivers/tty/serial/sh-sci.c:sci_tx_interrupt() {
	...
	uart_port_lock_irqsave(port, &flags);
	s->ops->transmit_chars(port);
	uart_port_unlock_irqrestore(port, flags);
	...
}

Between uart_port_unlock_irqrestore() in sci_tx_end_interrupt() and
uart_port_lock_irqsave() in sci_tx_interrupt(), another processor can acquire
port->lock:

1. In drivers/tty/serial/sh-sci.c:sci_shutdown():

	uart_port_lock_irqsave(port, &flags);
	s->port.ops->stop_rx(port);
	s->port.ops->stop_tx(port);
	s->ops->shutdown_complete(port);
	uart_port_unlock_irqrestore(port, flags);

   If sci_shutdown() runs during the unlock window before sci_free_irq(s),
   sci_tx_interrupt() will subsequently run s->ops->transmit_chars(port)
   after shutdown_complete(), writing to the transmit data register and
   re-arming transmit interrupts on a shut-down port.

2. If uart_stop() runs in the window, it sets tty->flow.stopped and calls
   stop_tx() to clear param_bits->tie. When s->ops->transmit_chars(port)
   then runs with stopped set and a non-empty tport->xmit_fifo, no byte is
   written and param_bits->teie is not set, leaving transmit-enable set with
   both param_bits->tie and param_bits->teie cleared so a later
   sci_start_tx() or rsci_start_tx() sees no rising edge on transmit-enable
   and stalls.

3. If uart_flush_buffer() or a concurrent sci_tx_interrupt() drains
   tport->xmit_fifo during the window, s->ops->transmit_chars(port) runs with
   an empty fifo and clears status without writing a new byte.

Could sci_tx_end_interrupt() call s->ops->transmit_chars(port) directly while
still holding port->lock instead of unlocking and calling sci_tx_interrupt()?

> +
> +	ctrl &= ~(s->params->param_bits->te_clear);
>  	s->ops->write_reg(port, regs->control, ctrl);
>  	uart_port_unlock_irqrestore(port, flags);

[Severity: Medium]
Can clearing param_bits->te_clear here cut off a byte that was just written
to the transmit register by a concurrent sci_tx_interrupt()?

When sci_transmit_chars() or rsci_transmit_chars() writes the final byte from
tport->xmit_fifo, it clears param_bits->tie and sets param_bits->teie while
the byte is still draining. If uart_write() queues a new byte before that
transmission finishes, drivers/tty/serial/sh-sci.c:sci_start_tx():

		if (s->type == PORT_SCI)
			ctrl |= SCSCR_TE;

		sci_serial_out(port, SCSCR, ctrl | SCSCR_TIE);

and drivers/tty/serial/rsci.c:rsci_start_tx():

	ctrl = rsci_serial_in(port, CCR0);
	ctrl |= CCR0_TIE | CCR0_TE;
	rsci_serial_out(port, CCR0, ctrl);

set param_bits->tie without clearing param_bits->teie.

If the previous byte finishes shifting out and triggers
sci_tx_end_interrupt() while sci_tx_interrupt() runs on another processor and
moves the newly queued byte from tport->xmit_fifo into the data register
(leaving tport->xmit_fifo empty again), sci_tx_end_interrupt() will see
port->x_char == 0 and kfifo_is_empty(&tport->xmit_fifo) and clear
param_bits->te_clear while the new byte is still in the hardware register.

Should sci_tx_end_interrupt() verify that param_bits->poll_sent_bits is still
set in regs->status before clearing param_bits->te_clear (or should
sci_start_tx() and rsci_start_tx() clear param_bits->teie)?

>  

>  	return IRQ_HANDLED;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261021-g2ul_sci-v2-0-b4e038220018@bp.renesas.com?part=2

  reply	other threads:[~2026-10-21 13:38 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21 13:13 [PATCH v2 0/4] Fix transmit handling on SCI/RSCI ports Biju Das
2026-10-21 13:13 ` [PATCH v2 1/4] serial: sh-sci: Drop TE disable at end of transmission on SCI Biju Das
2026-10-21 13:18   ` sashiko-bot
2026-10-21 13:13 ` [PATCH v2 2/4] serial: sh-sci: Keep TX enabled on TEI while data is pending Biju Das
2026-10-21 13:38   ` sashiko-bot [this message]
2026-10-22 10:28     ` Biju Das
2026-10-21 13:13 ` [PATCH v2 3/4] serial: sh-sci: Skip TDxE clear when hardware clears it automatically Biju Das
2026-10-21 13:22   ` sashiko-bot
2026-10-22 10:29     ` Biju Das
2026-10-21 13:13 ` [PATCH v2 4/4] serial: sh-sci: Toggle TE when restoring SCSCR in console write on SCI Biju Das
2026-10-21 13:19   ` 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=sashiko-outbox-180317@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=biju.das.au@gmail.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=wsa+renesas@sang-engineering.com \
    /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