fri2 serial port fixes for preempt-rt
cover fri2 serial port fixes for preempt-rt
These patches address are lockup when attempting login over serial on the fri2 when running the preempt-rt kernel. I have sent the first patch out for review, but have not yet heard back. The serial port works and appears fairly stable. However, I would prefer to keep these patches in yocto/preempt-rt/fri2 until they are merged upstream in their final form. Please apply to a new branch:
yocto/preempt-rt/fri2
The following changes since commit c004dd798ce68af4d7677939426b083575523db5:
pch_uart: Add eg20t_port lock field, avoid recursive spinlock on port->lock (2012-05-30 23:22:37 -0700)
are available in the git repository at:
git://git.yoctoproject.org/linux-yocto-2.6.37-contrib dvhart/standard/preempt-rt/fri2
http://git.yoctoproject.org/cgit.cgi/linux-yocto-2.6.37-contrib/log/?h=dvhart/standard/preempt-rt/fri2
Darren Hart (2):
pch_uart: Add eg20t_port lock field, avoid recursive spinlocks [RT] pch_uart: Correct spin_lock and irqsave usage in pch_console_write
drivers/tty/serial/pch_uart.c | 26 ++++++++++++++++++-------- 1 files changed, 18 insertions(+), 8 deletions(-)
Signature · 1 line
1.7.5.4
PATCH 1/2 pch_uart: Add eg20t_port lock field, avoid recursive spinlocks
pch_uart_interrupt() takes priv->port.lock which leads to two recursive spinlock calls if low_latency==1 or CONFIG_PREEMPT_RT_FULL=y (one otherwise):
pch_uart_interrupt
spin_lock_irqsave(priv->port.lock, flags)
case PCH_UART_IID_RDR_TO (data ready)
handle_rx_to
push_rx
tty_port_tty_get
spin_lock_irqsave(&port->lock, flags) <--- already hold this lock
...
tty_flip_buffer_push
...
flush_to_ldisc
spin_lock_irqsave(&tty->buf.lock)
spin_lock_irqsave(&tty->buf.lock)
disc->ops->receive_buf(tty, char_buf)
n_tty_receive_buf
tty->ops->flush_chars()
uart_flush_chars
uart_start
spin_lock_irqsave(&port->lock) <--- already hold this lockAvoid this by using a dedicated lock to protect the eg20t_port structure and IO access to its membase. This is more consistent with the 8250 driver. Ensure priv->lock is always take prior to priv->port.lock when taken at the same time.
--- drivers/tty/serial/pch_uart.c | 22 ++++++++++++++++------ 1 files changed, 16 insertions(+), 6 deletions(-)
drivers/tty/serial/pch_uart.c +16 -6
@@ -246,6 +246,8 @@ struct eg20t_port {int tx_dma_use;void *rx_buf_virt;dma_addr_t rx_buf_dma; +/* protect the eg20t_port private structure and io access to membase */ +spinlock_t lock; };/**
@@ -999,7 +1001,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)unsigned int iid;unsigned long flags; -spin_lock_irqsave (&priv->port.lock, flags); +spin_lock_irqsave (&priv->lock, flags); handled =0 ;while ((iid =pch_uart_hal_get_iid (priv)) >1 ) {switch (iid) {
@@ -1050,7 +1052,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) priv->int_dis_flag =0 ; } -spin_unlock_irqrestore (&priv->port.lock, flags); +spin_unlock_irqrestore (&priv->lock, flags);return IRQ_RETVAL (handled); }
@@ -1163,9 +1165,9 @@ static void pch_uart_break_ctl(struct uart_port *port, int ctl)unsigned long flags; priv = container_of(port, struct eg20t_port, port); -spin_lock_irqsave (&port->lock, flags); +spin_lock_irqsave (&priv->lock, flags);pch_uart_hal_set_break (priv, ctl); -spin_unlock_irqrestore (&port->lock, flags); +spin_unlock_irqrestore (&priv->lock, flags); }/* Grab any interrupt resources and initialise any low level driver state. */
@@ -1314,7 +1316,8 @@ static void pch_uart_set_termios(struct uart_port *port, baud =uart_get_baud_rate (port, termios, old,0 , port->uartclk /16 ); -spin_lock_irqsave (&port->lock, flags); +spin_lock_irqsave (&priv->lock, flags); +spin_lock (&port->lock);uart_update_timeout (port, termios->c_cflag, baud); rtn =pch_uart_hal_set_line (priv, baud, parity, bits, stb);
@@ -1327,7 +1330,8 @@ static void pch_uart_set_termios(struct uart_port *port,tty_termios_encode_baud_rate (termios, baud, baud); out: -spin_unlock_irqrestore (&port->lock, flags); +spin_unlock (&port->lock); +spin_unlock_irqrestore (&priv->lock, flags); }static const char *pch_uart_type (struct uart_port *port)
@@ -1486,6 +1490,7 @@ pch_console_write(struct console *co, const char *s, unsigned int count)touch_nmi_watchdog ();local_irq_save (flags); +spin_lock (&priv->lock);if (priv->port.sysrq) {/* serial8250_handle_port() already took the lock */ locked =0 ;
@@ -1494,6 +1499,7 @@ pch_console_write(struct console *co, const char *s, unsigned int count) }else spin_lock (&priv->port.lock); +/* * First save the IER then disable the interrupts */
@@ -1512,7 +1518,9 @@ pch_console_write(struct console *co, const char *s, unsigned int count)if (locked)spin_unlock (&priv->port.lock); +spin_unlock (&priv->lock);local_irq_restore (flags); + }static int __init pch_console_setup (struct console *co,char *options)
@@ -1608,6 +1616,8 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,pci_enable_msi (pdev);pci_set_master (pdev); +spin_lock_init (&priv->lock); + iobase =pci_resource_start (pdev,0 ); mapbase =pci_resource_start (pdev,1 ); priv->mapbase = mapbase;
Signature · 1 line
1.7.5.4
PATCH 2/2 pch_uart: Correct spin_lock and irqsave usage in pch_console_write
In reply to Darren Hartpch_console_write was calling:
local_irq_save()
spin_lock(a)
spin_lock(b)
...
spin_unlock(b)
spin_unlock(a)
local_irq_restore()Which trips the rtmutex debug code:
BUG: sleeping function called from invalid context at kernel/rtmutex.c:646
Update the function to use the _nort versions of local_irq* (thanks Peterz for the suggestion).
local_irq_save_nort()
spin_lock(a)
spin_lock(b)
...
spin_unlock(b)
spin_unlock(a)
local_irq_restore_nort()--- drivers/tty/serial/pch_uart.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
drivers/tty/serial/pch_uart.c +2 -2
@@ -1489,7 +1489,7 @@ pch_console_write(struct console *co, const char *s, unsigned int count)touch_nmi_watchdog (); -local_irq_save (flags); +local_irq_save_nort (flags);spin_lock (&priv->lock);if (priv->port.sysrq) {/* serial8250_handle_port() already took the lock */
@@ -1519,7 +1519,7 @@ pch_console_write(struct console *co, const char *s, unsigned int count)if (locked)spin_unlock (&priv->port.lock);spin_unlock (&priv->lock); -local_irq_restore (flags); +local_irq_restore_nort (flags); }
Signature · 1 line
1.7.5.4
Quoted 7 lines · Darren Hart
These patches address are lockup when attempting login over serial on the fri2 when running the preempt-rt kernel. I have sent the first patch out for review, but have not yet heard back. The serial port works and appears fairly stable. However, I would prefer to keep these patches in yocto/preempt-rt/fri2 until they are merged upstream in their final form. Please apply to a new branch:
yocto/preempt-rt/fri2
Ack. I'm fine with that as well, I'll create the branch and lock the patches in there.
Also updating the -rt patch at the same time.
Cheers,
Bruce
Quoted 17 lines
The following changes since commit
c004dd798ce68af4d7677939426b083575523db5:pch_uart: Add eg20t_port lock field, avoid recursive spinlock on port->lock (2012-05-30 23:22:37 -0700)are available in the git repository at:
git://git.yoctoproject.org/linux-yocto-2.6.37-contrib dvhart/standard/preempt-rt/fri2 http://git.yoctoproject.org/cgit.cgi/linux-yocto-2.6.37-contrib/log/?h=dvhart/standard/preempt-rt/fri2Darren Hart (2):
pch_uart: Addeg20t_portlock field, avoid recursive spinlocks [RT]pch_uart: Correctspin_lockand irqsave usage inpch_console_writedrivers/tty/serial/pch_uart.c | 26 ++++++++++++++++++-------- 1 files changed, 18 insertions(+), 8 deletions(-)