Skip to content

Commit 8a1060c

Browse files
hvilleneuvedoogregkh
authored andcommitted
serial: sc16is7xx: fix invalid sc16is7xx_lines bitfield in case of probe error
If an error occurs during probing, the sc16is7xx_lines bitfield may be left in a state that doesn't represent the correct state of lines allocation. For example, in a system with two SC16 devices, if an error occurs only during probing of channel (port) B of the second device, sc16is7xx_lines final state will be 00001011b instead of the expected 00000011b. This is caused in part because of the "i--" in the for/loop located in the out_ports: error path. Fix this by checking the return value of uart_add_one_port() and set line allocation bit only if this was successful. This allows the refactor of the obfuscated for(i--...) loop in the error path, and properly call uart_remove_one_port() only when needed, and properly unset line allocation bits. Also use same mechanism in remove() when calling uart_remove_one_port(). Fixes: c643497 ("sc16is7xx: support multiple devices") Cc: <[email protected]> Cc: Yury Norov <[email protected]> Signed-off-by: Hugo Villeneuve <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0c2a5f4 commit 8a1060c

File tree

1 file changed

+18
-26
lines changed

1 file changed

+18
-26
lines changed

drivers/tty/serial/sc16is7xx.c

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -407,19 +407,6 @@ static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
407407
regmap_update_bits(one->regmap, reg, mask, val);
408408
}
409409

410-
static int sc16is7xx_alloc_line(void)
411-
{
412-
int i;
413-
414-
BUILD_BUG_ON(SC16IS7XX_MAX_DEVS > BITS_PER_LONG);
415-
416-
for (i = 0; i < SC16IS7XX_MAX_DEVS; i++)
417-
if (!test_and_set_bit(i, &sc16is7xx_lines))
418-
break;
419-
420-
return i;
421-
}
422-
423410
static void sc16is7xx_power(struct uart_port *port, int on)
424411
{
425412
sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
@@ -1550,6 +1537,13 @@ static int sc16is7xx_probe(struct device *dev,
15501537
SC16IS7XX_IOCONTROL_SRESET_BIT);
15511538

15521539
for (i = 0; i < devtype->nr_uart; ++i) {
1540+
s->p[i].port.line = find_first_zero_bit(&sc16is7xx_lines,
1541+
SC16IS7XX_MAX_DEVS);
1542+
if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) {
1543+
ret = -ERANGE;
1544+
goto out_ports;
1545+
}
1546+
15531547
/* Initialize port data */
15541548
s->p[i].port.dev = dev;
15551549
s->p[i].port.irq = irq;
@@ -1569,14 +1563,8 @@ static int sc16is7xx_probe(struct device *dev,
15691563
s->p[i].port.rs485_supported = sc16is7xx_rs485_supported;
15701564
s->p[i].port.ops = &sc16is7xx_ops;
15711565
s->p[i].old_mctrl = 0;
1572-
s->p[i].port.line = sc16is7xx_alloc_line();
15731566
s->p[i].regmap = regmaps[i];
15741567

1575-
if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) {
1576-
ret = -ENOMEM;
1577-
goto out_ports;
1578-
}
1579-
15801568
mutex_init(&s->p[i].efr_lock);
15811569

15821570
ret = uart_get_rs485_mode(&s->p[i].port);
@@ -1594,8 +1582,13 @@ static int sc16is7xx_probe(struct device *dev,
15941582
kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
15951583
kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
15961584
kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc);
1585+
15971586
/* Register port */
1598-
uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
1587+
ret = uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
1588+
if (ret)
1589+
goto out_ports;
1590+
1591+
set_bit(s->p[i].port.line, &sc16is7xx_lines);
15991592

16001593
/* Enable EFR */
16011594
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG,
@@ -1653,10 +1646,9 @@ static int sc16is7xx_probe(struct device *dev,
16531646
#endif
16541647

16551648
out_ports:
1656-
for (i--; i >= 0; i--) {
1657-
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
1658-
clear_bit(s->p[i].port.line, &sc16is7xx_lines);
1659-
}
1649+
for (i = 0; i < devtype->nr_uart; i++)
1650+
if (test_and_clear_bit(s->p[i].port.line, &sc16is7xx_lines))
1651+
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
16601652

16611653
kthread_stop(s->kworker_task);
16621654

@@ -1678,8 +1670,8 @@ static void sc16is7xx_remove(struct device *dev)
16781670

16791671
for (i = 0; i < s->devtype->nr_uart; i++) {
16801672
kthread_cancel_delayed_work_sync(&s->p[i].ms_work);
1681-
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
1682-
clear_bit(s->p[i].port.line, &sc16is7xx_lines);
1673+
if (test_and_clear_bit(s->p[i].port.line, &sc16is7xx_lines))
1674+
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
16831675
sc16is7xx_power(&s->p[i].port, 0);
16841676
}
16851677

0 commit comments

Comments
 (0)