From: sashiko-bot@kernel.org
To: "Tapio Reijonen" <tapio.reijonen@vaisala.com>
Cc: linux-serial@vger.kernel.org
Subject: Re: [PATCH v5 7/8] serial: max310x: drive RTS in software when hardware delays are too short
Date: Tue, 29 Sep 2026 10:08:55 +0000 [thread overview]
Message-ID: <20260929100856.6557F1F00893@smtp.kernel.org> (raw)
In-Reply-To: <20260929-max310x-rs485-sw-delay-v5-7-ae46afa583f2@vaisala.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] serial: max310x: sleeping while atomic in hrtimer on PREEMPT_RT
- [High] serial: max310x: start_tx breaks active software RTS envelope
- [High] serial: max310x: TOCTOU race in RTS idle level settling
--
commit f17935a866e2d71f09696adaa6e429cdd498b4f0
Author: Tapio Reijonen <tapio.reijonen@vaisala.com>
serial: max310x: drive RTS in software when hardware delays are too short
This commit introduces a software-driven RTS path for the MAX310X serial driver
to handle RS485 RTS setup and hold delays that exceed the hardware's
capabilities. It uses a high-resolution timer and work queues to manage the
RTS envelope phases.
> diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
> index 8f8817b127c89..6ad27eb8a3258 100644
> --- a/drivers/tty/serial/max310x.c
> +++ b/drivers/tty/serial/max310x.c
[ ... ]
> @@ -792,6 +837,68 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
> tty_flip_buffer_push(&port->state->port);
> }
>
> +static enum hrtimer_restart max310x_tmr_tx(struct hrtimer *timer)
> +{
> + struct max310x_one *one = container_of(timer, struct max310x_one,
> + tx_delay_tmr);
> +
> + guard(spinlock_irqsave)(&one->port.lock);
[Severity: Critical]
Does acquiring this lock cause a panic on PREEMPT_RT kernels?
The timer is set up with HRTIMER_MODE_REL, meaning this callback runs in
hardirq context. However, port->lock is a sleepable spinlock on PREEMPT_RT,
which will lead to a sleeping while atomic BUG.
[ ... ]
> +/* called with port.lock taken and irqs off */
> static void max310x_start_tx(struct uart_port *port)
> {
> struct max310x_one *one = to_max310x_port(port);
>
> - schedule_work(&one->tx_work);
> + /* A teardown is in progress; nothing may start an envelope or TX. */
> + if (one->tx_teardown)
> + return;
> +
> + if (READ_ONCE(one->sw_rts_during_tx)) {
> + /*
> + * The before- and after-send phases share one delay timer. If an
[ ... ]
> + if (one->tx_teardown)
> + return;
> + }
> +
> + max310x_delayed_start_tx(port);
[Severity: High]
Does this unconditional call to max310x_delayed_start_tx() overwrite the
active tx_state to MAX310X_TX_WAIT_BEFORE_SEND?
If userspace performs fragmented writes to the UART, uart_write() could
call max310x_start_tx() while the driver is actively transmitting (in the
MAX310X_TX_SEND state).
If the state is overwritten here, the after-send delay timer will not be
armed, potentially causing the RTS line to be held indefinitely or dropped
incorrectly, resulting in bus contention and packet loss.
> + } else {
> + schedule_work(&one->tx_work);
> + }
> }
[ ... ]
> @@ -983,12 +1156,48 @@ static void max310x_set_rts_ctl_params(struct max310x_one *one)
[ ... ]
> + if (port->rs485.flags & SER_RS485_ENABLED) {
> + if (sw_rts) {
> + /*
> + * Only settle RTS at idle when no transmission owns it.
> + * A reconfigure while one is in flight - rs_work runs
> + * on every TIOCSRS485 - would otherwise release the
> + * transceiver mid-character.
> + */
> + if (READ_ONCE(one->tx_state) == MAX310X_TX_OFF) {
> + max310x_rts_ctl(port,
> + max310x_rts_level(port, false));
> + /*
> + * serial_core calls set_termios() without
> + * port->lock, so an envelope may have started
> + * while the idle level was written and the
> + * settle can land after its RTS assert.
> + * rts_work re-derives the level from
> + * tx_state; requeue it to converge.
> + */
> + if (READ_ONCE(one->tx_state) != MAX310X_TX_OFF)
> + schedule_work(&one->rts_work);
> + }
[Severity: High]
Can this lockless check and write sequence overwrite the RTS level to idle
during an active transmission envelope?
There appears to be a time-of-check to time-of-use race here between settling
the idle level and max310x_start_tx() starting a new RTS envelope.
If a userspace program changes terminal parameters or triggers an RS485
reconfiguration while data is being written, this could drive the RTS pin to
the idle state while data is actively transmitting, disabling the transceiver
and dropping packets on the wire.
[ ... ]
> @@ -1557,6 +1846,11 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
> INIT_WORK(&s->p[i].md_work, max310x_md_proc);
> /* Initialize queue for changing RS485 mode */
> INIT_WORK(&s->p[i].rs_work, max310x_rs_proc);
> + /* Initialize queue for software-driven RTS toggling */
> + INIT_WORK(&s->p[i].rts_work, max310x_rts_work_proc);
> + hrtimer_setup(&s->p[i].tx_delay_tmr, max310x_tmr_tx,
> + CLOCK_MONOTONIC, HRTIMER_MODE_REL);
[Severity: Critical]
Should this timer be configured to run in softirq context (e.g., using
HRTIMER_MODE_REL_SOFT) since its callback acquires port->lock?
As currently set up, it will run in hardirq context, leading to the PREEMPT_RT
sleep-while-atomic regression noted above.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260929-max310x-rs485-sw-delay-v5-0-ae46afa583f2@vaisala.com?part=7
next prev parent reply other threads:[~2026-09-29 10:08 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 9:37 [PATCH v5 0/8] serial: max310x: RS485 delay and RTS fixes, software-timed delays Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 1/8] serial: max310x: don't clobber the TX break bit in set_termios Tapio Reijonen
2026-09-29 9:49 ` sashiko-bot
2026-09-29 13:40 ` Hugo Villeneuve
2026-10-02 7:25 ` Tapio Reijonen
2026-10-02 15:03 ` Hugo Villeneuve
2026-10-04 10:45 ` Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 2/8] serial: max310x: assert the transceiver during a break Tapio Reijonen
2026-09-29 9:56 ` sashiko-bot
2026-09-29 9:37 ` [PATCH v5 3/8] serial: max310x: convert RS485 delays from milliseconds to bit-times Tapio Reijonen
2026-09-29 10:01 ` sashiko-bot
2026-09-29 13:54 ` Hugo Villeneuve
2026-10-01 19:11 ` Hugo Villeneuve
2026-10-02 7:27 ` Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 4/8] serial: max310x: wait for TX to drain before powering down in shutdown Tapio Reijonen
2026-09-29 9:57 ` sashiko-bot
2026-10-01 20:00 ` Hugo Villeneuve
2026-10-02 7:28 ` Tapio Reijonen
2026-10-02 15:01 ` Hugo Villeneuve
2026-10-04 10:56 ` Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 5/8] serial: max310x: support active-low RTS on the hardware path Tapio Reijonen
2026-09-29 9:56 ` sashiko-bot
2026-09-29 9:38 ` [PATCH v5 6/8] serial: max310x: schedule tx_work directly from the IRQ handler Tapio Reijonen
2026-09-29 9:58 ` sashiko-bot
2026-09-29 9:38 ` [PATCH v5 7/8] serial: max310x: drive RTS in software when hardware delays are too short Tapio Reijonen
2026-09-29 10:08 ` sashiko-bot [this message]
2026-09-29 9:38 ` [PATCH v5 8/8] serial: max310x: don't transmit while an RS485 reconfigure is pending Tapio Reijonen
2026-09-29 10:11 ` sashiko-bot
2026-10-01 8:35 ` [PATCH v5 0/8] serial: max310x: RS485 delay and RTS fixes, software-timed delays Greg Kroah-Hartman
2026-10-01 9:10 ` Tapio Reijonen
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=20260929100856.6557F1F00893@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tapio.reijonen@vaisala.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