Skip to content

Commit 8679328

Browse files
l1kgregkh
authored andcommitted
serial: Reduce spinlocked portion of uart_rs485_config()
Commit 44b27ae ("serial: core, 8250: set RS485 termination GPIO in serial core") enabled support for RS485 termination GPIOs behind i2c expanders by setting the GPIO outside of the critical section protected by the port spinlock. Access to the i2c expander may sleep, which caused a splat with the port spinlock held. Commit 7c7f9bc ("serial: Deassert Transmit Enable on probe in driver-specific way") erroneously regressed that by spinlocking the GPIO manipulation again. Fix by moving uart_rs485_config() (the function manipulating the GPIO) outside of the spinlocked section and acquiring the spinlock inside of uart_rs485_config() for the invocation of ->rs485_config() only. This gets us one step closer to pushing the spinlock down into the ->rs485_config() callbacks which actually need it. (Some callbacks do not want to be spinlocked because they perform sleepable register accesses, see e.g. sc16is7xx_config_rs485().) Stack trace for posterity: Voluntary context switch within RCU read-side critical section! WARNING: CPU: 0 PID: 56 at kernel/rcu/tree_plugin.h:318 rcu_note_context_switch Call trace: rcu_note_context_switch __schedule schedule schedule_timeout wait_for_completion_timeout bcm2835_i2c_xfer __i2c_transfer i2c_transfer i2c_transfer_buffer_flags regmap_i2c_write _regmap_raw_write_impl _regmap_bus_raw_write _regmap_write _regmap_update_bits regmap_update_bits_base pca953x_gpio_set_value gpiod_set_raw_value_commit gpiod_set_value_nocheck gpiod_set_value_cansleep uart_rs485_config uart_add_one_port pl011_register_port pl011_probe Fixes: 7c7f9bc ("serial: Deassert Transmit Enable on probe in driver-specific way") Suggested-by: Lino Sanfilippo <LinoSanfilippo@gmx.de> Signed-off-by: Lukas Wunner <lukas@wunner.de> Cc: stable@vger.kernel.org # v6.1+ Link: https://lore.kernel.org/r/f3a35967c28b32f3c6432d0aa5936e6a9908282d.1695307688.git.lukas@wunner.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8a749fd commit 8679328

File tree

1 file changed

+9
-4
lines changed

1 file changed

+9
-4
lines changed

drivers/tty/serial/serial_core.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,12 +1404,18 @@ static void uart_set_rs485_termination(struct uart_port *port,
14041404
static int uart_rs485_config(struct uart_port *port)
14051405
{
14061406
struct serial_rs485 *rs485 = &port->rs485;
1407+
unsigned long flags;
14071408
int ret;
14081409

1410+
if (!(rs485->flags & SER_RS485_ENABLED))
1411+
return 0;
1412+
14091413
uart_sanitize_serial_rs485(port, rs485);
14101414
uart_set_rs485_termination(port, rs485);
14111415

1416+
spin_lock_irqsave(&port->lock, flags);
14121417
ret = port->rs485_config(port, NULL, rs485);
1418+
spin_unlock_irqrestore(&port->lock, flags);
14131419
if (ret)
14141420
memset(rs485, 0, sizeof(*rs485));
14151421

@@ -2474,11 +2480,10 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
24742480
if (ret == 0) {
24752481
if (tty)
24762482
uart_change_line_settings(tty, state, NULL);
2483+
uart_rs485_config(uport);
24772484
spin_lock_irq(&uport->lock);
24782485
if (!(uport->rs485.flags & SER_RS485_ENABLED))
24792486
ops->set_mctrl(uport, uport->mctrl);
2480-
else
2481-
uart_rs485_config(uport);
24822487
ops->start_tx(uport);
24832488
spin_unlock_irq(&uport->lock);
24842489
tty_port_set_initialized(port, true);
@@ -2587,10 +2592,10 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
25872592
port->mctrl &= TIOCM_DTR;
25882593
if (!(port->rs485.flags & SER_RS485_ENABLED))
25892594
port->ops->set_mctrl(port, port->mctrl);
2590-
else
2591-
uart_rs485_config(port);
25922595
spin_unlock_irqrestore(&port->lock, flags);
25932596

2597+
uart_rs485_config(port);
2598+
25942599
/*
25952600
* If this driver supports console, and it hasn't been
25962601
* successfully registered yet, try to re-register it.

0 commit comments

Comments
 (0)