Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

serial: sc16is7xx: split into core and I2C/SPI parts (sc16is7xx_lines)

Before, sc16is7xx_lines was checked for a free (zero) bit first, and then
later it was set only if UART port registration succeeded. Now that
sc16is7xx_lines is shared for the I2C and SPI drivers, there is a
possibility that the two drivers can simultaneously try to reserve the same
line bit at the same time.

To prevent this, make sure line allocation is reserved atomically, and use
a new variable to hold the status of UART port registration.

Now that we no longer need to search if a bit is set, it is now possible
to simplify sc16is7xx_lines allocation by using the IDA framework.

Link: https://lore.kernel.org/all/20231212150302.a9ec5d085a4ba65e89ca41af@hugovil.com/
Link: https://lore.kernel.org/all/CAHp75VebCZckUrNraYQj9k=Mrn2kbYs1Lx26f5-8rKJ3RXeh-w@mail.gmail.com/
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240409154253.3043822-5-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Hugo Villeneuve and committed by
Greg Kroah-Hartman
cf9c3753 d4921643

+21 -11
+21 -11
drivers/tty/serial/sc16is7xx.c
··· 10 10 #undef DEFAULT_SYMBOL_NAMESPACE 11 11 #define DEFAULT_SYMBOL_NAMESPACE SERIAL_NXP_SC16IS7XX 12 12 13 - #include <linux/bitops.h> 14 13 #include <linux/clk.h> 15 14 #include <linux/delay.h> 16 15 #include <linux/device.h> 17 16 #include <linux/export.h> 18 17 #include <linux/gpio/driver.h> 18 + #include <linux/idr.h> 19 19 #include <linux/kthread.h> 20 20 #include <linux/mod_devicetable.h> 21 21 #include <linux/module.h> ··· 345 345 struct sc16is7xx_one p[]; 346 346 }; 347 347 348 - static DECLARE_BITMAP(sc16is7xx_lines, SC16IS7XX_MAX_DEVS); 348 + static DEFINE_IDA(sc16is7xx_lines); 349 349 350 350 static struct uart_driver sc16is7xx_uart = { 351 351 .owner = THIS_MODULE, ··· 1462 1462 u32 uartclk = 0; 1463 1463 int i, ret; 1464 1464 struct sc16is7xx_port *s; 1465 + bool port_registered[SC16IS7XX_MAX_PORTS]; 1465 1466 1466 1467 for (i = 0; i < devtype->nr_uart; i++) 1467 1468 if (IS_ERR(regmaps[i])) ··· 1527 1526 regmap_write(regmaps[0], SC16IS7XX_IOCONTROL_REG, 1528 1527 SC16IS7XX_IOCONTROL_SRESET_BIT); 1529 1528 1529 + /* Mark each port line and status as uninitialised. */ 1530 1530 for (i = 0; i < devtype->nr_uart; ++i) { 1531 - s->p[i].port.line = find_first_zero_bit(sc16is7xx_lines, 1532 - SC16IS7XX_MAX_DEVS); 1533 - if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { 1534 - ret = -ERANGE; 1531 + s->p[i].port.line = SC16IS7XX_MAX_DEVS; 1532 + port_registered[i] = false; 1533 + } 1534 + 1535 + for (i = 0; i < devtype->nr_uart; ++i) { 1536 + ret = ida_alloc_max(&sc16is7xx_lines, 1537 + SC16IS7XX_MAX_DEVS - 1, GFP_KERNEL); 1538 + if (ret < 0) 1535 1539 goto out_ports; 1536 - } 1540 + 1541 + s->p[i].port.line = ret; 1537 1542 1538 1543 /* Initialize port data */ 1539 1544 s->p[i].port.dev = dev; ··· 1585 1578 if (ret) 1586 1579 goto out_ports; 1587 1580 1588 - set_bit(s->p[i].port.line, sc16is7xx_lines); 1581 + port_registered[i] = true; 1589 1582 1590 1583 /* Enable EFR */ 1591 1584 sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, ··· 1643 1636 #endif 1644 1637 1645 1638 out_ports: 1646 - for (i = 0; i < devtype->nr_uart; i++) 1647 - if (test_and_clear_bit(s->p[i].port.line, sc16is7xx_lines)) 1639 + for (i = 0; i < devtype->nr_uart; i++) { 1640 + if (s->p[i].port.line < SC16IS7XX_MAX_DEVS) 1641 + ida_free(&sc16is7xx_lines, s->p[i].port.line); 1642 + if (port_registered[i]) 1648 1643 uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); 1644 + } 1649 1645 1650 1646 kthread_stop(s->kworker_task); 1651 1647 ··· 1671 1661 1672 1662 for (i = 0; i < s->devtype->nr_uart; i++) { 1673 1663 kthread_cancel_delayed_work_sync(&s->p[i].ms_work); 1674 - clear_bit(s->p[i].port.line, sc16is7xx_lines); 1664 + ida_free(&sc16is7xx_lines, s->p[i].port.line); 1675 1665 uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); 1676 1666 sc16is7xx_power(&s->p[i].port, 0); 1677 1667 }