[PATCH] sc16is7xx: switch to threaded irq for fixing RT issues

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux