Working with RT patches reveals this driver have some spin_lock issues. spin_lock moved from sc16is7xx_reg_proc to protect to call to uart_handle_cts_change as it's the only call that isn't already protected in serial-core. Attached kernel dump: -----------[ cut here ]------------ Kernel BUG at 80484398 [verbose debug info unavailable] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM Modules linked in: sc16is7xx regmap_spi CPU: 0 PID: 463 Comm: sc16is7xx Tainted: G O 4.1.15-rt17-00096-gbd1cd22 #1 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) task: aa991cc0 ti: aa640000 task.ti: aa640000 PC is at rt_spin_lock_slowlock+0x254/0x2c0 LR is at rt_spin_lock_slowlock+0x54/0x2c0 pc : [<80484398>] lr : [<80484198>] psr: 60070193 sp : aa641db0 ip : aa991cc1 fp : aa68dcdc r10: 00000000 r9 : aa991cc0 r8 : 00000000 r7 : 00000000 r6 : aa641db0 r5 : 00000001 r4 : aa68dcc4 r3 : aa991cc0 r2 : 00000000 r1 : aa991cc0 r0 : 00000000 Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 3a60004a DAC: 00000015 Process sc16is7xx (pid: 463, stack limit = 0xaa640210) Stack: (0xaa641db0 to 0xaa642000) 1da0: aa641db0 aa8c4f40 00000000 aa641dbc 1dc0: aa641e24 aa641e60 00000000 00000000 802c9301 00000000 aa68dcc4 aa68dcf0 1de0: 0000006a ab0ce300 806e02cd 804858c8 00000000 8004018c 7f051258 aa5c8b80 1e00: 00000000 7f051268 7f051258 80062624 00000000 00000000 00000000 ab0ce300 1e20: aa5c8b80 00000005 ab11ba10 00000001 80731908 806b0cc4 aa68dcdc 80062774 1e40: 00020002 ab0ce300 00000020 80065bd8 0000006a 80061cb8 00000020 8023217c 1e60: aa641dc0 aa641e24 00000000 a8948800 aa8c4f40 ab11ba10 806b8948 ab0cdd80 1e80: 00000000 00000001 aa641ee8 ab008000 aa68dcdc 802322c8 00000063 00000000 1ea0: 00000063 80061cb8 806a3030 80061f80 f400010c 00000056 806b0fe0 aa641ee8 1ec0: f4000100 aa68dcdc 80723f68 80009468 804858c8 60070013 ffffffff aa641f1c 1ee0: 00000001 80013240 aa68dcc4 aa991cc0 aa640000 aa68dcd0 80723f68 aa68dcc4 1f00: aa640000 00000000 00000001 aa68dcdc 80723f68 aa68dcdc 00000000 aa641f34 1f20: 8003ffbc 804858c8 60070013 ffffffff aa68dcf0 80723f68 aa640000 aa641f38 1f40: aa68dcc4 aab04480 00000000 aa68dcc4 8003feb8 00000000 00000000 00000000 1f60: 00000000 80040104 00000000 00000000 00000000 aa68dcc4 00000000 00000000 1f80: aa641f80 aa641f80 00000000 00000000 aa641f90 aa641f90 aa641fac aab04480 1fa0: 80040034 00000000 00000000 8000f528 00000000 00000000 00000000 00000000 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000 3bf5a811 3bf5ac11 [<80484398>] (rt_spin_lock_slowlock) from [<8004018c>] (queue_kthread_work+0x18/0x74) [<8004018c>] (queue_kthread_work) from [<7f051268>] (sc16is7xx_irq+0x10/0x18 [sc16is7xx]) [<7f051268>] (sc16is7xx_irq [sc16is7xx]) from [<80062624>] (handle_irq_event_percpu+0x70/0x158) [<80062624>] (handle_irq_event_percpu) from [<80062774>] (handle_irq_event+0x68/0xa8) [<80062774>] (handle_irq_event) from [<80065bd8>] (handle_level_irq+0x10c/0x184) [<80065bd8>] (handle_level_irq) from [<80061cb8>] (generic_handle_irq+0x2c/0x3c) [<80061cb8>] (generic_handle_irq) from [<8023217c>] (mxc_gpio_irq_handler+0x3c/0x108) [<8023217c>] (mxc_gpio_irq_handler) from [<802322c8>] (mx3_gpio_irq_handler+0x80/0xcc) [<802322c8>] (mx3_gpio_irq_handler) from [<80061cb8>] (generic_handle_irq+0x2c/0x3c) [<80061cb8>] (generic_handle_irq) from [<80061f80>] (__handle_domain_irq+0x7c/0xe8) [<80061f80>] (__handle_domain_irq) from [<80009468>] (gic_handle_irq+0x24/0x5c) [<80009468>] (gic_handle_irq) from [<80013240>] (__irq_svc+0x40/0x88) Exception stack(0xaa641ee8 to 0xaa641f30) 1ee0: aa68dcc4 aa991cc0 aa640000 aa68dcd0 80723f68 aa68dcc4 1f00: aa640000 00000000 00000001 aa68dcdc 80723f68 aa68dcdc 00000000 aa641f34 1f20: 8003ffbc 804858c8 60070013 ffffffff [<80013240>] (__irq_svc) from [<804858c8>] (rt_spin_unlock+0x1c/0x68) [<804858c8>] (rt_spin_unlock) from [<8003ffbc>] (kthread_worker_fn+0x104/0x17c) [<8003ffbc>] (kthread_worker_fn) from [<80040104>] (kthread+0xd0/0xe8) [<80040104>] (kthread) from [<8000f528>] (ret_from_fork+0x14/0x2c) Code: 0a000002 e59d3000 e1530006 0a000000 (e7f001f2) ---[ end trace 0000000000000002 ]--- Kernel panic - not syncing: Fatal exception in interrupt CPU1: stopping CPU: 1 PID: 0 Comm: swapper/1 Tainted: G D O 4.1.15-rt17-00096-gbd1cd22 #1 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) [<80016718>] (unwind_backtrace) from [<80012700>] (show_stack+0x10/0x14) [<80012700>] (show_stack) from [<80481808>] (dump_stack+0x7c/0x90) [<80481808>] (dump_stack) from [<800155c8>] (handle_IPI+0x180/0x1dc) [<800155c8>] (handle_IPI) from [<8000949c>] (gic_handle_irq+0x58/0x5c) [<8000949c>] (gic_handle_irq) from [<80013240>] (__irq_svc+0x40/0x88) Exception stack(0xab0a3f58 to 0xab0a3fa0) 3f40: 00000000 ab0a3f68 3f60: 00000001 ab0a2000 00000001 806b7f70 533b4c3a 00002c5b ab729128 00000001 3f80: 678b9ec8 00002c5b 00000000 ab0a3fa0 80080730 80338560 20070013 ffffffff [<80013240>] (__irq_svc) from [<80338560>] (cpuidle_enter_state+0xb8/0x1ec) [<80338560>] (cpuidle_enter_state) from [<80059c0c>] (cpu_startup_entry+0x234/0x37c) [<80059c0c>] (cpu_startup_entry) from [<1000952c>] (0x1000952c) Signed-off-by: Sean Nyekjaer <sean.nyekjaer@xxxxxxxxx> --- drivers/tty/serial/sc16is7xx.c | 73 ++++++++---------------------------------- 1 file changed, 14 insertions(+), 59 deletions(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index e78fa99..f8b9ef1 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -316,8 +316,6 @@ struct sc16is7xx_one_config { struct sc16is7xx_one { struct uart_port port; u8 line; - struct kthread_work tx_work; - struct kthread_work reg_work; struct sc16is7xx_one_config config; }; @@ -329,9 +327,6 @@ struct sc16is7xx_port { struct gpio_chip gpio; #endif unsigned char buf[SC16IS7XX_FIFO_SIZE]; - struct kthread_worker kworker; - struct task_struct *kworker_task; - struct kthread_work irq_work; struct sc16is7xx_one p[0]; }; @@ -661,9 +656,10 @@ static void sc16is7xx_handle_tx(struct uart_port *port) uart_write_wakeup(port); } -static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) +static void sc16is7xx_port_ist(struct sc16is7xx_port *s, int portno) { struct uart_port *port = &s->p[portno].port; + unsigned long irqflags; do { unsigned int iir, msr, rxlen; @@ -686,8 +682,10 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) case SC16IS7XX_IIR_CTSRTS_SRC: msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG); + spin_lock_irqsave(&port->lock, irqflags); uart_handle_cts_change(port, !!(msr & SC16IS7XX_MSR_DCTS_BIT)); + spin_unlock_irqrestore(&port->lock, irqflags); break; case SC16IS7XX_IIR_THRI_SRC: sc16is7xx_handle_tx(port); @@ -701,28 +699,19 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) } while (1); } -static void sc16is7xx_ist(struct kthread_work *ws) +static irqreturn_t sc16is7xx_ist(int irq, void *dev_id) { - struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work); + struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; int i; for (i = 0; i < s->devtype->nr_uart; ++i) - sc16is7xx_port_irq(s, i); -} - -static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) -{ - struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; - - queue_kthread_work(&s->kworker, &s->irq_work); + sc16is7xx_port_ist(s, i); return IRQ_HANDLED; } -static void sc16is7xx_tx_proc(struct kthread_work *ws) +static void sc16is7xx_tx_proc(struct uart_port *port) { - struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port); - if ((port->rs485.flags & SER_RS485_ENABLED) && (port->rs485.delay_rts_before_send > 0)) msleep(port->rs485.delay_rts_before_send); @@ -750,16 +739,12 @@ static void sc16is7xx_reconf_rs485(struct uart_port *port) sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr); } -static void sc16is7xx_reg_proc(struct kthread_work *ws) +static void sc16is7xx_reg_proc(struct sc16is7xx_one *one) { - struct sc16is7xx_one *one = to_sc16is7xx_one(ws, reg_work); struct sc16is7xx_one_config config; - unsigned long irqflags; - spin_lock_irqsave(&one->port.lock, irqflags); config = one->config; memset(&one->config, 0, sizeof(one->config)); - spin_unlock_irqrestore(&one->port.lock, irqflags); if (config.flags & SC16IS7XX_RECONF_MD) { sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, @@ -785,12 +770,11 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws) static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); one->config.flags |= SC16IS7XX_RECONF_IER; one->config.ier_clear |= bit; - queue_kthread_work(&s->kworker, &one->reg_work); + sc16is7xx_reg_proc(one); } static void sc16is7xx_stop_tx(struct uart_port *port) @@ -805,10 +789,7 @@ static void sc16is7xx_stop_rx(struct uart_port *port) static void sc16is7xx_start_tx(struct uart_port *port) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - - queue_kthread_work(&s->kworker, &one->tx_work); + sc16is7xx_tx_proc(port); } static unsigned int sc16is7xx_tx_empty(struct uart_port *port) @@ -836,11 +817,10 @@ static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); one->config.flags |= SC16IS7XX_RECONF_MD; - queue_kthread_work(&s->kworker, &one->reg_work); + sc16is7xx_reg_proc(one); } static void sc16is7xx_break_ctl(struct uart_port *port, int break_state) @@ -944,7 +924,6 @@ static void sc16is7xx_set_termios(struct uart_port *port, static int sc16is7xx_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); if (rs485->flags & SER_RS485_ENABLED) { @@ -969,7 +948,7 @@ static int sc16is7xx_config_rs485(struct uart_port *port, port->rs485 = *rs485; one->config.flags |= SC16IS7XX_RECONF_RS485; - queue_kthread_work(&s->kworker, &one->reg_work); + sc16is7xx_reg_proc(one); return 0; } @@ -1030,8 +1009,6 @@ static int sc16is7xx_startup(struct uart_port *port) static void sc16is7xx_shutdown(struct uart_port *port) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - /* Disable all interrupts */ sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0); /* Disable TX/RX */ @@ -1042,8 +1019,6 @@ static void sc16is7xx_shutdown(struct uart_port *port) SC16IS7XX_EFCR_TXDISABLE_BIT); sc16is7xx_power(port, 0); - - flush_kthread_worker(&s->kworker); } static const char *sc16is7xx_type(struct uart_port *port) @@ -1161,7 +1136,6 @@ static int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, struct regmap *regmap, int irq, unsigned long flags) { - struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 }; unsigned long freq, *pfreq = dev_get_platdata(dev); int i, ret; struct sc16is7xx_port *s; @@ -1193,16 +1167,6 @@ static int sc16is7xx_probe(struct device *dev, s->devtype = devtype; dev_set_drvdata(dev, s); - init_kthread_worker(&s->kworker); - init_kthread_work(&s->irq_work, sc16is7xx_ist); - s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker, - "sc16is7xx"); - if (IS_ERR(s->kworker_task)) { - ret = PTR_ERR(s->kworker_task); - goto out_clk; - } - sched_setscheduler(s->kworker_task, SCHED_FIFO, &sched_param); - #ifdef CONFIG_GPIOLIB if (devtype->nr_gpio) { /* Setup GPIO cotroller */ @@ -1246,9 +1210,6 @@ static int sc16is7xx_probe(struct device *dev, sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, SC16IS7XX_EFCR_RXDISABLE_BIT | SC16IS7XX_EFCR_TXDISABLE_BIT); - /* Initialize kthread work structs */ - init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc); - init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc); /* Register port */ uart_add_one_port(&sc16is7xx_uart, &s->p[i].port); /* Go to suspend mode */ @@ -1256,7 +1217,7 @@ static int sc16is7xx_probe(struct device *dev, } /* Setup interrupt */ - ret = devm_request_irq(dev, irq, sc16is7xx_irq, + ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_ist, IRQF_ONESHOT | flags, dev_name(dev), s); if (!ret) return 0; @@ -1273,9 +1234,6 @@ out_ports: out_thread: #endif - kthread_stop(s->kworker_task); - -out_clk: if (!IS_ERR(s->clk)) clk_disable_unprepare(s->clk); @@ -1298,9 +1256,6 @@ static int sc16is7xx_remove(struct device *dev) sc16is7xx_power(&s->p[i].port, 0); } - flush_kthread_worker(&s->kworker); - kthread_stop(s->kworker_task); - if (!IS_ERR(s->clk)) clk_disable_unprepare(s->clk); -- 2.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html