aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial
diff options
context:
space:
mode:
authorThomas Abraham <thomas.abraham@linaro.org>2012-11-22 07:36:28 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-11-26 18:36:39 -0500
commitc15c3747ee32b6969f3cfbc86dc94923e3742d0a (patch)
tree2c2f9b77abcaa8f116e6f87bf91208e6013d3fee /drivers/tty/serial
parentdaa74a25cc7351a9fb01ba7611af5ef5df80ae4e (diff)
serial: samsung: fix potential soft lockup during uart write
Certain tty line discipline implementations such slip and bluetooth hci invoke the serial core uart_write() api in their write_wakeup callback. This leads to a soft lockup with samsung serial driver since the uart port lock is taken in the driver's interrupt handler and uart_write() attempts to take the same lock again. Fix this issue by releasing the uart port lock before the call to uart_write_wakeup() in the tx handler. Also move the spin-lock/unlock sequence from s3c64xx_serial_handle_irq() function into the tx and rx irq handlers so that this change is applicable to s3c24xx platforms as well. Reported-by: Kyungmin Park <kyungmin.park@samsung.com> Reported-by: Hyeonkook Kim <hk619.kim@samsung.com> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r--drivers/tty/serial/samsung.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 82b48f60aa0c..9368c3e81cc8 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -223,8 +223,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
223 struct uart_port *port = &ourport->port; 223 struct uart_port *port = &ourport->port;
224 struct tty_struct *tty = port->state->port.tty; 224 struct tty_struct *tty = port->state->port.tty;
225 unsigned int ufcon, ch, flag, ufstat, uerstat; 225 unsigned int ufcon, ch, flag, ufstat, uerstat;
226 unsigned long flags;
226 int max_count = 64; 227 int max_count = 64;
227 228
229 spin_lock_irqsave(&port->lock, flags);
230
228 while (max_count-- > 0) { 231 while (max_count-- > 0) {
229 ufcon = rd_regl(port, S3C2410_UFCON); 232 ufcon = rd_regl(port, S3C2410_UFCON);
230 ufstat = rd_regl(port, S3C2410_UFSTAT); 233 ufstat = rd_regl(port, S3C2410_UFSTAT);
@@ -299,6 +302,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
299 tty_flip_buffer_push(tty); 302 tty_flip_buffer_push(tty);
300 303
301 out: 304 out:
305 spin_unlock_irqrestore(&port->lock, flags);
302 return IRQ_HANDLED; 306 return IRQ_HANDLED;
303} 307}
304 308
@@ -307,8 +311,11 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
307 struct s3c24xx_uart_port *ourport = id; 311 struct s3c24xx_uart_port *ourport = id;
308 struct uart_port *port = &ourport->port; 312 struct uart_port *port = &ourport->port;
309 struct circ_buf *xmit = &port->state->xmit; 313 struct circ_buf *xmit = &port->state->xmit;
314 unsigned long flags;
310 int count = 256; 315 int count = 256;
311 316
317 spin_lock_irqsave(&port->lock, flags);
318
312 if (port->x_char) { 319 if (port->x_char) {
313 wr_regb(port, S3C2410_UTXH, port->x_char); 320 wr_regb(port, S3C2410_UTXH, port->x_char);
314 port->icount.tx++; 321 port->icount.tx++;
@@ -336,13 +343,17 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
336 port->icount.tx++; 343 port->icount.tx++;
337 } 344 }
338 345
339 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 346 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
347 spin_unlock(&port->lock);
340 uart_write_wakeup(port); 348 uart_write_wakeup(port);
349 spin_lock(&port->lock);
350 }
341 351
342 if (uart_circ_empty(xmit)) 352 if (uart_circ_empty(xmit))
343 s3c24xx_serial_stop_tx(port); 353 s3c24xx_serial_stop_tx(port);
344 354
345 out: 355 out:
356 spin_unlock_irqrestore(&port->lock, flags);
346 return IRQ_HANDLED; 357 return IRQ_HANDLED;
347} 358}
348 359
@@ -352,10 +363,8 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
352 struct s3c24xx_uart_port *ourport = id; 363 struct s3c24xx_uart_port *ourport = id;
353 struct uart_port *port = &ourport->port; 364 struct uart_port *port = &ourport->port;
354 unsigned int pend = rd_regl(port, S3C64XX_UINTP); 365 unsigned int pend = rd_regl(port, S3C64XX_UINTP);
355 unsigned long flags;
356 irqreturn_t ret = IRQ_HANDLED; 366 irqreturn_t ret = IRQ_HANDLED;
357 367
358 spin_lock_irqsave(&port->lock, flags);
359 if (pend & S3C64XX_UINTM_RXD_MSK) { 368 if (pend & S3C64XX_UINTM_RXD_MSK) {
360 ret = s3c24xx_serial_rx_chars(irq, id); 369 ret = s3c24xx_serial_rx_chars(irq, id);
361 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); 370 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);
@@ -364,7 +373,6 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
364 ret = s3c24xx_serial_tx_chars(irq, id); 373 ret = s3c24xx_serial_tx_chars(irq, id);
365 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); 374 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);
366 } 375 }
367 spin_unlock_irqrestore(&port->lock, flags);
368 return ret; 376 return ret;
369} 377}
370 378