From 19ffd68f816878aed456d5e87697f43bd9e3bd2b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 5 Feb 2013 16:08:50 -0500 Subject: pty: Remove redundant itty reset port->itty has already been reset by release_tty() before pty_cleanup() is called. Call stack: release_tty() tty_kref_put() queue_release_one_tty() release_one_tty() : workqueue tty->ops->cleanup() pty_cleanup() Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index c24b4db243b..71e456aa636 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -413,7 +413,6 @@ static void pty_unix98_shutdown(struct tty_struct *tty) static void pty_cleanup(struct tty_struct *tty) { - tty->port->itty = NULL; tty_port_put(tty->port); } -- cgit v1.2.3-18-g5258 From 84e819220468e989a0dde33bf1121888c5e749b1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 4 Mar 2013 09:59:00 +0530 Subject: serial: tegra: Convert to devm_ioremap_resource() Use the newly introduced devm_ioremap_resource() instead of devm_request_and_ioremap() which provides more consistent error handling. devm_ioremap_resource() provides its own error messages; so all explicit error messages can be removed from the failure code paths. Signed-off-by: Sachin Kamat Reviewed-by: Thierry Reding Cc: Laxman Dewangan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 372de8ade76..9799d043a9b 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1301,11 +1302,9 @@ static int tegra_uart_probe(struct platform_device *pdev) } u->mapbase = resource->start; - u->membase = devm_request_and_ioremap(&pdev->dev, resource); - if (!u->membase) { - dev_err(&pdev->dev, "memregion/iomap address req failed\n"); - return -EADDRNOTAVAIL; - } + u->membase = devm_ioremap_resource(&pdev->dev, resource); + if (IS_ERR(u->membase)) + return PTR_ERR(u->membase); tup->uart_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(tup->uart_clk)) { -- cgit v1.2.3-18-g5258 From 82b231323e419dcd61de9ff38d66dd7e82564594 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 4 Mar 2013 14:24:39 +0530 Subject: serial: vt8500_serial: Convert to devm_ioremap_resource() Use the newly introduced devm_ioremap_resource() instead of devm_request_and_ioremap() which provides more consistent error handling. Signed-off-by: Sachin Kamat Acked-by: Tony Prisk Reviewed-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/vt8500_serial.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index a3f9dd5c9df..f15f53f18ca 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -35,6 +35,7 @@ #include #include #include +#include /* * UART Register offsets @@ -585,9 +586,9 @@ static int vt8500_serial_probe(struct platform_device *pdev) if (!vt8500_port) return -ENOMEM; - vt8500_port->uart.membase = devm_request_and_ioremap(&pdev->dev, mmres); - if (!vt8500_port->uart.membase) - return -EADDRNOTAVAIL; + vt8500_port->uart.membase = devm_ioremap_resource(&pdev->dev, mmres); + if (IS_ERR(vt8500_port->uart.membase)) + return PTR_ERR(vt8500_port->uart.membase); vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); if (IS_ERR(vt8500_port->clk)) { -- cgit v1.2.3-18-g5258 From fec6bee367357d9dd3ab3a7c56293214e49c371c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 5 Mar 2013 12:29:20 +0900 Subject: TTY: amiserial, use module_platform_driver_probe() This patch uses module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index fc700342d43..083710e0236 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1798,19 +1798,7 @@ static struct platform_driver amiga_serial_driver = { }, }; -static int __init amiga_serial_init(void) -{ - return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe); -} - -module_init(amiga_serial_init); - -static void __exit amiga_serial_exit(void) -{ - platform_driver_unregister(&amiga_serial_driver); -} - -module_exit(amiga_serial_exit); +module_platform_driver_probe(amiga_serial_driver, amiga_serial_probe); #if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE) -- cgit v1.2.3-18-g5258 From ef44d28c4fd94166ec6be054359ae26ba73b0291 Mon Sep 17 00:00:00 2001 From: Liang Li Date: Tue, 5 Mar 2013 22:30:38 +0800 Subject: serial: pch_uart: add console poll support Implement console poll for pch_uart, this could enable KGDBoC when on pch-uart console. Signed-off-by: Liang Li Acked-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 103 ++++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 7a6c989924b..21a7e179edf 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1493,29 +1493,6 @@ static int pch_uart_verify_port(struct uart_port *port, return 0; } -static struct uart_ops pch_uart_ops = { - .tx_empty = pch_uart_tx_empty, - .set_mctrl = pch_uart_set_mctrl, - .get_mctrl = pch_uart_get_mctrl, - .stop_tx = pch_uart_stop_tx, - .start_tx = pch_uart_start_tx, - .stop_rx = pch_uart_stop_rx, - .enable_ms = pch_uart_enable_ms, - .break_ctl = pch_uart_break_ctl, - .startup = pch_uart_startup, - .shutdown = pch_uart_shutdown, - .set_termios = pch_uart_set_termios, -/* .pm = pch_uart_pm, Not supported yet */ -/* .set_wake = pch_uart_set_wake, Not supported yet */ - .type = pch_uart_type, - .release_port = pch_uart_release_port, - .request_port = pch_uart_request_port, - .config_port = pch_uart_config_port, - .verify_port = pch_uart_verify_port -}; - -#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE - /* * Wait for transmitter & holding register to empty */ @@ -1547,6 +1524,84 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits) } } +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for communicate via uart while + * in an interrupt or debug context. + */ +static int pch_uart_get_poll_char(struct uart_port *port) +{ + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + u8 lsr = ioread8(priv->membase + UART_LSR); + + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; + + return ioread8(priv->membase + PCH_UART_RBR); +} + + +static void pch_uart_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + + /* + * First save the IER then disable the interrupts + */ + ier = ioread8(priv->membase + UART_IER); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + + wait_for_xmitr(priv, UART_LSR_THRE); + /* + * Send the character out. + * If a LF, also do CR... + */ + iowrite8(c, priv->membase + PCH_UART_THR); + if (c == 10) { + wait_for_xmitr(priv, UART_LSR_THRE); + iowrite8(13, priv->membase + PCH_UART_THR); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(priv, BOTH_EMPTY); + iowrite8(ier, priv->membase + UART_IER); +} +#endif /* CONFIG_CONSOLE_POLL */ + +static struct uart_ops pch_uart_ops = { + .tx_empty = pch_uart_tx_empty, + .set_mctrl = pch_uart_set_mctrl, + .get_mctrl = pch_uart_get_mctrl, + .stop_tx = pch_uart_stop_tx, + .start_tx = pch_uart_start_tx, + .stop_rx = pch_uart_stop_rx, + .enable_ms = pch_uart_enable_ms, + .break_ctl = pch_uart_break_ctl, + .startup = pch_uart_startup, + .shutdown = pch_uart_shutdown, + .set_termios = pch_uart_set_termios, +/* .pm = pch_uart_pm, Not supported yet */ +/* .set_wake = pch_uart_set_wake, Not supported yet */ + .type = pch_uart_type, + .release_port = pch_uart_release_port, + .request_port = pch_uart_request_port, + .config_port = pch_uart_config_port, + .verify_port = pch_uart_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = pch_uart_get_poll_char, + .poll_put_char = pch_uart_put_poll_char, +#endif +}; + +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE + static void pch_console_putchar(struct uart_port *port, int ch) { struct eg20t_port *priv = @@ -1655,7 +1710,7 @@ static struct console pch_console = { #define PCH_CONSOLE (&pch_console) #else #define PCH_CONSOLE NULL -#endif +#endif /* CONFIG_SERIAL_PCH_UART_CONSOLE */ static struct uart_driver pch_uart_driver = { .owner = THIS_MODULE, -- cgit v1.2.3-18-g5258 From f3c8279d694a5c2c455cdcb3323e2349b40c542f Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Wed, 6 Mar 2013 01:03:22 +0530 Subject: tty: ipwireless: Remove redundant NULL check before kfree kfree on NULL pointer is a no-op. Signed-off-by: Syam Sidhardhan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/ipwireless/hardware.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index 97a511f4185..2c14842541d 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1732,8 +1732,7 @@ void ipwireless_hardware_free(struct ipw_hardware *hw) flush_work(&hw->work_rx); for (i = 0; i < NL_NUM_OF_ADDRESSES; i++) - if (hw->packet_assembler[i] != NULL) - kfree(hw->packet_assembler[i]); + kfree(hw->packet_assembler[i]); for (i = 0; i < NL_NUM_OF_PRIORITIES; i++) list_for_each_entry_safe(tp, tq, &hw->tx_queue[i], queue) { -- cgit v1.2.3-18-g5258 From ea648a47e83d7cda0832f96de215464e2c35b2c2 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 07:20:53 -0500 Subject: tty: Refactor session leader SIGHUP from __tty_hangup() Reduce complexity of __tty_hangup(); separate SIGHUP signalling into tty_signal_session_leader(). Signed-off-by: Peter Hurley Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 81 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 05400acbc45..706c23b9cb9 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -532,6 +532,51 @@ void tty_wakeup(struct tty_struct *tty) EXPORT_SYMBOL_GPL(tty_wakeup); +/** + * tty_signal_session_leader - sends SIGHUP to session leader + * + * Send SIGHUP and SIGCONT to the session leader and its + * process group. + * + * Returns the number of processes in the session with this tty + * as their controlling terminal. This value is used to drop + * tty references for those processes. + */ +static int tty_signal_session_leader(struct tty_struct *tty) +{ + struct task_struct *p; + unsigned long flags; + int refs = 0; + + read_lock(&tasklist_lock); + if (tty->session) { + do_each_pid_task(tty->session, PIDTYPE_SID, p) { + spin_lock_irq(&p->sighand->siglock); + if (p->signal->tty == tty) { + p->signal->tty = NULL; + /* We defer the dereferences outside fo + the tasklist lock */ + refs++; + } + if (!p->signal->leader) { + spin_unlock_irq(&p->sighand->siglock); + continue; + } + __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); + __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); + put_pid(p->signal->tty_old_pgrp); /* A noop */ + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->pgrp) + p->signal->tty_old_pgrp = get_pid(tty->pgrp); + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock_irq(&p->sighand->siglock); + } while_each_pid_task(tty->session, PIDTYPE_SID, p); + } + read_unlock(&tasklist_lock); + + return refs; +} + /** * __tty_hangup - actual handler for hangup events * @work: tty device @@ -558,11 +603,10 @@ static void __tty_hangup(struct tty_struct *tty) { struct file *cons_filp = NULL; struct file *filp, *f = NULL; - struct task_struct *p; struct tty_file_private *priv; int closecount = 0, n; unsigned long flags; - int refs = 0; + int refs; if (!tty) return; @@ -605,31 +649,10 @@ static void __tty_hangup(struct tty_struct *tty) */ tty_ldisc_hangup(tty); - read_lock(&tasklist_lock); - if (tty->session) { - do_each_pid_task(tty->session, PIDTYPE_SID, p) { - spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) { - p->signal->tty = NULL; - /* We defer the dereferences outside fo - the tasklist lock */ - refs++; - } - if (!p->signal->leader) { - spin_unlock_irq(&p->sighand->siglock); - continue; - } - __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); - __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); - put_pid(p->signal->tty_old_pgrp); /* A noop */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->pgrp) - p->signal->tty_old_pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - spin_unlock_irq(&p->sighand->siglock); - } while_each_pid_task(tty->session, PIDTYPE_SID, p); - } - read_unlock(&tasklist_lock); + refs = tty_signal_session_leader(tty); + /* Account for the p->signal references we killed */ + while (refs--) + tty_kref_put(tty); spin_lock_irqsave(&tty->ctrl_lock, flags); clear_bit(TTY_THROTTLED, &tty->flags); @@ -642,10 +665,6 @@ static void __tty_hangup(struct tty_struct *tty) tty->ctrl_status = 0; spin_unlock_irqrestore(&tty->ctrl_lock, flags); - /* Account for the p->signal references we killed */ - while (refs--) - tty_kref_put(tty); - /* * If one of the devices matches a console pointer, we * cannot just call hangup() because that will cause -- cgit v1.2.3-18-g5258 From 20cc225bab6709408840e4400cd1a5c2b28c7a52 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 07:20:54 -0500 Subject: tty: Fix spinlock flavor in non-atomic __tty_hangup() __tty_hangup() and tty_vhangup() cannot be called from atomic context, so locks do not need to preserve the interrupt state (although, still disable interrupts). Signed-off-by: Peter Hurley Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 706c23b9cb9..fb50442fd2a 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -605,7 +605,6 @@ static void __tty_hangup(struct tty_struct *tty) struct file *filp, *f = NULL; struct tty_file_private *priv; int closecount = 0, n; - unsigned long flags; int refs; if (!tty) @@ -654,7 +653,7 @@ static void __tty_hangup(struct tty_struct *tty) while (refs--) tty_kref_put(tty); - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); clear_bit(TTY_THROTTLED, &tty->flags); clear_bit(TTY_PUSH, &tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); @@ -663,7 +662,7 @@ static void __tty_hangup(struct tty_struct *tty) tty->session = NULL; tty->pgrp = NULL; tty->ctrl_status = 0; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock_irq(&tty->ctrl_lock); /* * If one of the devices matches a console pointer, we -- cgit v1.2.3-18-g5258 From bc30c3b23bb953fc6eb59e7ac6ecb48d92962bb0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 07:20:55 -0500 Subject: tty: Use spin_lock() inside existing critical region The interrupt state does not need to be saved, disabled and restored here; interrupts are already off because this lock is bracketed by spin_lock_irq/spin_unlock_irq. Signed-off-by: Peter Hurley Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index fb50442fd2a..2661e86a227 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -545,7 +545,6 @@ EXPORT_SYMBOL_GPL(tty_wakeup); static int tty_signal_session_leader(struct tty_struct *tty) { struct task_struct *p; - unsigned long flags; int refs = 0; read_lock(&tasklist_lock); @@ -565,10 +564,10 @@ static int tty_signal_session_leader(struct tty_struct *tty) __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); put_pid(p->signal->tty_old_pgrp); /* A noop */ - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock(&tty->ctrl_lock); if (tty->pgrp) p->signal->tty_old_pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock(&tty->ctrl_lock); spin_unlock_irq(&p->sighand->siglock); } while_each_pid_task(tty->session, PIDTYPE_SID, p); } -- cgit v1.2.3-18-g5258 From f91e2590410bd992e3f065d17c55329bdaa51b1d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 07:20:56 -0500 Subject: tty: Signal foreground group processes in hangup When the session leader is exiting, signal the foreground group processes as part of the hangup sequence, instead of after the hangup is complete. This prepares for hanging up the line discipline _after_ signalling processes which may be blocking on ldisc i/o. Parameterize __tty_hangup() to distinguish between when the session leader is exiting and all other hangups; signal the foreground group after signalling the session leader and its process group, which preserves the original signal order. Signed-off-by: Peter Hurley Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 65 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 2661e86a227..3feca406dc3 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -534,18 +534,21 @@ EXPORT_SYMBOL_GPL(tty_wakeup); /** * tty_signal_session_leader - sends SIGHUP to session leader + * @tty controlling tty + * @exit_session if non-zero, signal all foreground group processes * - * Send SIGHUP and SIGCONT to the session leader and its - * process group. + * Send SIGHUP and SIGCONT to the session leader and its process group. + * Optionally, signal all processes in the foreground process group. * * Returns the number of processes in the session with this tty * as their controlling terminal. This value is used to drop * tty references for those processes. */ -static int tty_signal_session_leader(struct tty_struct *tty) +static int tty_signal_session_leader(struct tty_struct *tty, int exit_session) { struct task_struct *p; int refs = 0; + struct pid *tty_pgrp = NULL; read_lock(&tasklist_lock); if (tty->session) { @@ -565,6 +568,7 @@ static int tty_signal_session_leader(struct tty_struct *tty) __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); put_pid(p->signal->tty_old_pgrp); /* A noop */ spin_lock(&tty->ctrl_lock); + tty_pgrp = get_pid(tty->pgrp); if (tty->pgrp) p->signal->tty_old_pgrp = get_pid(tty->pgrp); spin_unlock(&tty->ctrl_lock); @@ -573,6 +577,12 @@ static int tty_signal_session_leader(struct tty_struct *tty) } read_unlock(&tasklist_lock); + if (tty_pgrp) { + if (exit_session) + kill_pgrp(tty_pgrp, SIGHUP, exit_session); + put_pid(tty_pgrp); + } + return refs; } @@ -598,7 +608,7 @@ static int tty_signal_session_leader(struct tty_struct *tty) * tasklist_lock to walk task list for hangup event * ->siglock to protect ->signal/->sighand */ -static void __tty_hangup(struct tty_struct *tty) +static void __tty_hangup(struct tty_struct *tty, int exit_session) { struct file *cons_filp = NULL; struct file *filp, *f = NULL; @@ -647,7 +657,7 @@ static void __tty_hangup(struct tty_struct *tty) */ tty_ldisc_hangup(tty); - refs = tty_signal_session_leader(tty); + refs = tty_signal_session_leader(tty, exit_session); /* Account for the p->signal references we killed */ while (refs--) tty_kref_put(tty); @@ -696,7 +706,7 @@ static void do_tty_hangup(struct work_struct *work) struct tty_struct *tty = container_of(work, struct tty_struct, hangup_work); - __tty_hangup(tty); + __tty_hangup(tty, 0); } /** @@ -734,7 +744,7 @@ void tty_vhangup(struct tty_struct *tty) printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); #endif - __tty_hangup(tty); + __tty_hangup(tty, 0); } EXPORT_SYMBOL(tty_vhangup); @@ -757,6 +767,27 @@ void tty_vhangup_self(void) } } +/** + * tty_vhangup_session - hangup session leader exit + * @tty: tty to hangup + * + * The session leader is exiting and hanging up its controlling terminal. + * Every process in the foreground process group is signalled SIGHUP. + * + * We do this synchronously so that when the syscall returns the process + * is complete. That guarantee is necessary for security reasons. + */ + +void tty_vhangup_session(struct tty_struct *tty) +{ +#ifdef TTY_DEBUG_HANGUP + char buf[64]; + + printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf)); +#endif + __tty_hangup(tty, 1); +} + /** * tty_hung_up_p - was tty hung up * @filp: file pointer of tty @@ -814,18 +845,18 @@ void disassociate_ctty(int on_exit) tty = get_current_tty(); if (tty) { - struct pid *tty_pgrp = get_pid(tty->pgrp); - if (on_exit) { - if (tty->driver->type != TTY_DRIVER_TYPE_PTY) - tty_vhangup(tty); - } - tty_kref_put(tty); - if (tty_pgrp) { - kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) + if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) { + tty_vhangup_session(tty); + } else { + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, SIGHUP, on_exit); kill_pgrp(tty_pgrp, SIGCONT, on_exit); - put_pid(tty_pgrp); + put_pid(tty_pgrp); + } } + tty_kref_put(tty); + } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); -- cgit v1.2.3-18-g5258 From 25fdf2435139542759df2eeb59e4998923c13403 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 07:20:57 -0500 Subject: tty: Signal SIGHUP before hanging up ldisc An exiting session leader can hang if a foreground process is blocking for line discipline i/o, eg. in n_tty_read(). This happens because the blocking reader is holding an ldisc reference (indicating the line discipline is in-use) which prevents __tty_hangup() from recycling the line discipline. Although waiters are woken before attempting to gain exclusive access for changing the ldisc, the blocking reader in this case will not exit the i/o loop since it has not yet received SIGHUP (because it has not been sent). Instead, perform signalling first, then recycle the line discipline. Fixes: INFO: task init:1 blocked for more than 120 seconds. "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. init D 00000000001d7180 2688 1 0 0x00000002 ffff8800b9acfba8 0000000000000002 00000000001d7180 ffff8800b9b10048 ffff8800b94cb000 ffff8800b9b10000 00000000001d7180 00000000001d7180 ffff8800b9b10000 ffff8800b9acffd8 00000000001d7180 00000000001d7180 Call Trace: [] __schedule+0x2e9/0x3b0 [] schedule+0x55/0x60 [] schedule_timeout+0x3a/0x370 [] ? mark_held_locks+0xf9/0x130 [] ? down_failed+0x108/0x200 [] ? _raw_spin_unlock_irq+0x2b/0x80 [] ? trace_hardirqs_on_caller+0x128/0x160 [] down_failed+0x131/0x200 [] ? tty_ldisc_lock_pair_timeout+0xcd/0x120 [] ldsem_down_write+0xd3/0x113 [] ? tty_ldisc_lock_pair_timeout+0xcd/0x120 [] ? trace_hardirqs_on+0xd/0x10 [] tty_ldisc_lock_pair_timeout+0xcd/0x120 [] tty_ldisc_hangup+0xd0/0x220 [] __tty_hangup+0x137/0x4f0 [] disassociate_ctty+0x6c/0x230 [] do_exit+0x41c/0x590 [] ? syscall_trace_enter+0x24/0x2e0 [] do_group_exit+0x8a/0xc0 [] sys_exit_group+0x12/0x20 [] tracesys+0xe1/0xe6 1 lock held by init/1: #0: (&tty->ldisc_sem){++++++}, at: [] tty_ldisc_lock_pair_timeout+0xcd/0x120 Reported-by: Sasha Levin Signed-off-by: Peter Hurley Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 3feca406dc3..d3ddb31e363 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -651,17 +651,17 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) } spin_unlock(&tty_files_lock); + refs = tty_signal_session_leader(tty, exit_session); + /* Account for the p->signal references we killed */ + while (refs--) + tty_kref_put(tty); + /* * it drops BTM and thus races with reopen * we protect the race by TTY_HUPPING */ tty_ldisc_hangup(tty); - refs = tty_signal_session_leader(tty, exit_session); - /* Account for the p->signal references we killed */ - while (refs--) - tty_kref_put(tty); - spin_lock_irq(&tty->ctrl_lock); clear_bit(TTY_THROTTLED, &tty->flags); clear_bit(TTY_PUSH, &tty->flags); -- cgit v1.2.3-18-g5258 From afa80ccb4c7d39702dfb0832ce02a054848191a8 Mon Sep 17 00:00:00 2001 From: "zhangwei(Jovi)" Date: Thu, 7 Mar 2013 17:00:02 +0800 Subject: sysrq: fix inconstistent help message of sysrq key Currently help message of /proc/sysrq-trigger highlight its upper-case characters, like below: SysRq : HELP : loglevel(0-9) reBoot Crash terminate-all-tasks(E) memory-full-oom-kill(F) kill-all-tasks(I) ... this would confuse user trigger sysrq by upper-case character, which is inconsistent with the real lower-case character registed key. This inconsistent help message will also lead more confused when 26 upper-case letters put into use in future. This patch fix it. Thanks the comments from Andrew and Randy. Signed-off-by: zhangwei(Jovi) Cc: Andrew Morton Acked-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 3687f0cad64..0a0de333c76 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -101,7 +101,7 @@ static void sysrq_handle_SAK(int key) } static struct sysrq_key_op sysrq_SAK_op = { .handler = sysrq_handle_SAK, - .help_msg = "saK", + .help_msg = "sak(k)", .action_msg = "SAK", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -117,7 +117,7 @@ static void sysrq_handle_unraw(int key) static struct sysrq_key_op sysrq_unraw_op = { .handler = sysrq_handle_unraw, - .help_msg = "unRaw", + .help_msg = "unraw(r)", .action_msg = "Keyboard mode set to system default", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -135,7 +135,7 @@ static void sysrq_handle_crash(int key) } static struct sysrq_key_op sysrq_crash_op = { .handler = sysrq_handle_crash, - .help_msg = "Crash", + .help_msg = "crash(c)", .action_msg = "Trigger a crash", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -148,7 +148,7 @@ static void sysrq_handle_reboot(int key) } static struct sysrq_key_op sysrq_reboot_op = { .handler = sysrq_handle_reboot, - .help_msg = "reBoot", + .help_msg = "reboot(b)", .action_msg = "Resetting", .enable_mask = SYSRQ_ENABLE_BOOT, }; @@ -159,7 +159,7 @@ static void sysrq_handle_sync(int key) } static struct sysrq_key_op sysrq_sync_op = { .handler = sysrq_handle_sync, - .help_msg = "Sync", + .help_msg = "sync(s)", .action_msg = "Emergency Sync", .enable_mask = SYSRQ_ENABLE_SYNC, }; @@ -171,7 +171,7 @@ static void sysrq_handle_show_timers(int key) static struct sysrq_key_op sysrq_show_timers_op = { .handler = sysrq_handle_show_timers, - .help_msg = "show-all-timers(Q)", + .help_msg = "show-all-timers(q)", .action_msg = "Show clockevent devices & pending hrtimers (no others)", }; @@ -181,7 +181,7 @@ static void sysrq_handle_mountro(int key) } static struct sysrq_key_op sysrq_mountro_op = { .handler = sysrq_handle_mountro, - .help_msg = "Unmount", + .help_msg = "unmount(u)", .action_msg = "Emergency Remount R/O", .enable_mask = SYSRQ_ENABLE_REMOUNT, }; @@ -194,7 +194,7 @@ static void sysrq_handle_showlocks(int key) static struct sysrq_key_op sysrq_showlocks_op = { .handler = sysrq_handle_showlocks, - .help_msg = "show-all-locks(D)", + .help_msg = "show-all-locks(d)", .action_msg = "Show Locks Held", }; #else @@ -245,7 +245,7 @@ static void sysrq_handle_showallcpus(int key) static struct sysrq_key_op sysrq_showallcpus_op = { .handler = sysrq_handle_showallcpus, - .help_msg = "show-backtrace-all-active-cpus(L)", + .help_msg = "show-backtrace-all-active-cpus(l)", .action_msg = "Show backtrace of all active CPUs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -260,7 +260,7 @@ static void sysrq_handle_showregs(int key) } static struct sysrq_key_op sysrq_showregs_op = { .handler = sysrq_handle_showregs, - .help_msg = "show-registers(P)", + .help_msg = "show-registers(p)", .action_msg = "Show Regs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -271,7 +271,7 @@ static void sysrq_handle_showstate(int key) } static struct sysrq_key_op sysrq_showstate_op = { .handler = sysrq_handle_showstate, - .help_msg = "show-task-states(T)", + .help_msg = "show-task-states(t)", .action_msg = "Show State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -282,7 +282,7 @@ static void sysrq_handle_showstate_blocked(int key) } static struct sysrq_key_op sysrq_showstate_blocked_op = { .handler = sysrq_handle_showstate_blocked, - .help_msg = "show-blocked-tasks(W)", + .help_msg = "show-blocked-tasks(w)", .action_msg = "Show Blocked State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -296,7 +296,7 @@ static void sysrq_ftrace_dump(int key) } static struct sysrq_key_op sysrq_ftrace_dump_op = { .handler = sysrq_ftrace_dump, - .help_msg = "dump-ftrace-buffer(Z)", + .help_msg = "dump-ftrace-buffer(z)", .action_msg = "Dump ftrace buffer", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -310,7 +310,7 @@ static void sysrq_handle_showmem(int key) } static struct sysrq_key_op sysrq_showmem_op = { .handler = sysrq_handle_showmem, - .help_msg = "show-memory-usage(M)", + .help_msg = "show-memory-usage(m)", .action_msg = "Show Memory", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -341,7 +341,7 @@ static void sysrq_handle_term(int key) } static struct sysrq_key_op sysrq_term_op = { .handler = sysrq_handle_term, - .help_msg = "terminate-all-tasks(E)", + .help_msg = "terminate-all-tasks(e)", .action_msg = "Terminate All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -360,7 +360,7 @@ static void sysrq_handle_moom(int key) } static struct sysrq_key_op sysrq_moom_op = { .handler = sysrq_handle_moom, - .help_msg = "memory-full-oom-kill(F)", + .help_msg = "memory-full-oom-kill(f)", .action_msg = "Manual OOM execution", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -372,7 +372,7 @@ static void sysrq_handle_thaw(int key) } static struct sysrq_key_op sysrq_thaw_op = { .handler = sysrq_handle_thaw, - .help_msg = "thaw-filesystems(J)", + .help_msg = "thaw-filesystems(j)", .action_msg = "Emergency Thaw of all frozen filesystems", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -385,7 +385,7 @@ static void sysrq_handle_kill(int key) } static struct sysrq_key_op sysrq_kill_op = { .handler = sysrq_handle_kill, - .help_msg = "kill-all-tasks(I)", + .help_msg = "kill-all-tasks(i)", .action_msg = "Kill All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -396,7 +396,7 @@ static void sysrq_handle_unrt(int key) } static struct sysrq_key_op sysrq_unrt_op = { .handler = sysrq_handle_unrt, - .help_msg = "nice-all-RT-tasks(N)", + .help_msg = "nice-all-RT-tasks(n)", .action_msg = "Nice All RT Tasks", .enable_mask = SYSRQ_ENABLE_RTNICE, }; -- cgit v1.2.3-18-g5258 From c828f679eed393d6925a2b44a4c3fb80a8d657cb Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:20:51 -0500 Subject: n_tty: Inline check_unthrottle() at lone call site 2-line function check_unthrottle() is now only called from n_tty_read(); merge into caller. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 05e72bea9b0..7fbad56db7c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -188,21 +188,6 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) raw_spin_unlock_irqrestore(&ldata->read_lock, flags); } -/** - * check_unthrottle - allow new receive data - * @tty; tty device - * - * Check whether to call the driver unthrottle functions - * - * Can sleep, may be called under the atomic_read_lock mutex but - * this is not guaranteed. - */ -static void check_unthrottle(struct tty_struct *tty) -{ - if (tty->count) - tty_unthrottle(tty); -} - /** * reset_buffer_flags - reset buffer state * @tty: terminal to reset @@ -1961,7 +1946,8 @@ do_it_again: */ if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { n_tty_set_room(tty); - check_unthrottle(tty); + if (tty->count) + tty_unthrottle(tty); } if (b - buf >= minimum) -- cgit v1.2.3-18-g5258 From 70bc126471af30bb115e635512dcf6d86fe6e29a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:20:52 -0500 Subject: tty: Add safe tty throttle/unthrottle functions The tty driver can become stuck throttled due to race conditions between throttle and unthrottle, when the decision to throttle or unthrottle is conditional. The following example helps to illustrate the race: CPU 0 | CPU 1 | if (condition A) | | | if (!condition A) | unthrottle() throttle() | | Note the converse is also possible; ie., CPU 0 | CPU 1 | | if (!condition A) | if (condition A) | throttle() | | unthrottle() | Add new throttle/unthrottle functions based on the familiar model of task state and schedule/wake. For example, while (1) { tty_set_flow_change(tty, TTY_THROTTLE_SAFE); if (!condition) break; if (!tty_throttle_safe(tty)) break; } __tty_set_flow_change(tty, 0); In this example, if an unthrottle occurs after the condition is evaluated but before tty_throttle_safe(), then tty_throttle_safe() will return non-zero, looping and forcing the re-evaluation of condition. Reported-by: Vincent Pillet Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ioctl.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/tty.h | 18 ++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index d58b92cc187..132d452578b 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -106,6 +106,7 @@ void tty_throttle(struct tty_struct *tty) if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && tty->ops->throttle) tty->ops->throttle(tty); + tty->flow_change = 0; mutex_unlock(&tty->termios_mutex); } EXPORT_SYMBOL(tty_throttle); @@ -129,10 +130,73 @@ void tty_unthrottle(struct tty_struct *tty) if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->ops->unthrottle) tty->ops->unthrottle(tty); + tty->flow_change = 0; mutex_unlock(&tty->termios_mutex); } EXPORT_SYMBOL(tty_unthrottle); +/** + * tty_throttle_safe - flow control + * @tty: terminal + * + * Similar to tty_throttle() but will only attempt throttle + * if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental + * throttle due to race conditions when throttling is conditional + * on factors evaluated prior to throttling. + * + * Returns 0 if tty is throttled (or was already throttled) + */ + +int tty_throttle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->termios_mutex); + if (!test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty->flow_change != TTY_THROTTLE_SAFE) + ret = 1; + else { + __set_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->throttle) + tty->ops->throttle(tty); + } + } + mutex_unlock(&tty->termios_mutex); + + return ret; +} + +/** + * tty_unthrottle_safe - flow control + * @tty: terminal + * + * Similar to tty_unthrottle() but will only attempt unthrottle + * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental + * unthrottle due to race conditions when unthrottling is conditional + * on factors evaluated prior to unthrottling. + * + * Returns 0 if tty is unthrottled (or was already unthrottled) + */ + +int tty_unthrottle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->termios_mutex); + if (test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty->flow_change != TTY_UNTHROTTLE_SAFE) + ret = 1; + else { + __clear_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->unthrottle) + tty->ops->unthrottle(tty); + } + } + mutex_unlock(&tty->termios_mutex); + + return ret; +} + /** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for diff --git a/include/linux/tty.h b/include/linux/tty.h index c75d886b030..189ca80494d 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -258,6 +258,7 @@ struct tty_struct { unsigned char warned:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ + int flow_change; struct tty_struct *link; struct fasync_struct *fasync; @@ -318,6 +319,21 @@ struct tty_file_private { #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) +/* Values for tty->flow_change */ +#define TTY_THROTTLE_SAFE 1 +#define TTY_UNTHROTTLE_SAFE 2 + +static inline void __tty_set_flow_change(struct tty_struct *tty, int val) +{ + tty->flow_change = val; +} + +static inline void tty_set_flow_change(struct tty_struct *tty, int val) +{ + tty->flow_change = val; + smp_mb(); +} + #ifdef CONFIG_TTY extern void console_init(void); extern void tty_kref_put(struct tty_struct *tty); @@ -400,6 +416,8 @@ extern int tty_write_room(struct tty_struct *tty); extern void tty_driver_flush_buffer(struct tty_struct *tty); extern void tty_throttle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty); +extern int tty_throttle_safe(struct tty_struct *tty); +extern int tty_unthrottle_safe(struct tty_struct *tty); extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); extern void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty); -- cgit v1.2.3-18-g5258 From e91e52e42814b130c20d17bc1e2adf813c50db11 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:20:53 -0500 Subject: n_tty: Fix stuck throttled driver As noted in the following comment: /* FIXME: there is a tiny race here if the receive room check runs before the other work executes and empties the buffer (upping the receiving room and unthrottling. We then throttle and get stuck. This has been observed and traced down by Vincent Pillet/ We need to address this when we sort out out the rx path locking */ Use new safe throttle/unthrottle functions to re-evaluate conditions if interrupted by the complement flow control function. Reported-by: Vincent Pillet Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 7fbad56db7c..e3a9321f7f8 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1468,14 +1468,14 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) - tty_throttle(tty); - - /* FIXME: there is a tiny race here if the receive room check runs - before the other work executes and empties the buffer (upping - the receiving room and unthrottling. We then throttle and get - stuck. This has been observed and traced down by Vincent Pillet/ - We need to address this when we sort out out the rx path locking */ + while (1) { + tty_set_flow_change(tty, TTY_THROTTLE_SAFE); + if (tty->receive_room >= TTY_THRESHOLD_THROTTLE) + break; + if (!tty_throttle_safe(tty)) + break; + } + __tty_set_flow_change(tty, 0); } int is_ignored(int sig) @@ -1944,11 +1944,17 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { + while (1) { + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + if (n_tty_chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + break; + if (!tty->count) + break; n_tty_set_room(tty); - if (tty->count) - tty_unthrottle(tty); + if (!tty_unthrottle_safe(tty)) + break; } + __tty_set_flow_change(tty, 0); if (b - buf >= minimum) break; -- cgit v1.2.3-18-g5258 From 8c985d18b136c5d511445d15f0c6650003a8946b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:38:19 -0500 Subject: n_tty: Fix unsafe driver-side signals An ldisc reference is insufficient guarantee the foreground process group is not in the process of being signalled from a hangup. 1) Reads of tty->pgrp must be locked with ctrl_lock 2) The group pid must be referenced for the duration of signalling. Because the driver-side is not process-context, a pid reference must be acquired. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index e3a9321f7f8..61f1bc97ccd 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1017,23 +1017,19 @@ static void eraser(unsigned char c, struct tty_struct *tty) * isig - handle the ISIG optio * @sig: signal * @tty: terminal - * @flush: force flush * - * Called when a signal is being sent due to terminal input. This - * may caus terminal flushing to take place according to the termios - * settings and character used. Called from the driver receive_buf - * path so serialized. + * Called when a signal is being sent due to terminal input. + * Called from the driver receive_buf path so serialized. * - * Locking: ctrl_lock, read_lock (both via flush buffer) + * Locking: ctrl_lock */ -static inline void isig(int sig, struct tty_struct *tty, int flush) +static inline void isig(int sig, struct tty_struct *tty) { - if (tty->pgrp) - kill_pgrp(tty->pgrp, sig, 1); - if (flush || !L_NOFLSH(tty)) { - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, sig, 1); + put_pid(tty_pgrp); } } @@ -1054,7 +1050,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty) if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) { - isig(SIGINT, tty, 1); + isig(SIGINT, tty); + if (!L_NOFLSH(tty)) { + n_tty_flush_buffer(tty); + tty_driver_flush_buffer(tty); + } return; } if (I_PARMRK(tty)) { @@ -1221,11 +1221,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) signal = SIGTSTP; if (c == SUSP_CHAR(tty)) { send_signal: - /* - * Note that we do not use isig() here because we want - * the order to be: - * 1) flush, 2) echo, 3) signal - */ if (!L_NOFLSH(tty)) { n_tty_flush_buffer(tty); tty_driver_flush_buffer(tty); @@ -1236,8 +1231,7 @@ send_signal: echo_char(c, tty); process_echoes(tty); } - if (tty->pgrp) - kill_pgrp(tty->pgrp, signal, 1); + isig(signal, tty); return; } } -- cgit v1.2.3-18-g5258 From 01a5e440c91dc6065cf3dbc38aa7276fc4ce2f26 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:38:20 -0500 Subject: n_tty: Lock access to tty->pgrp for POSIX job control Concurrent access to tty->pgrp must be protected with tty->ctrl_lock. Also, as noted in the comments, reading current->signal->tty is safe because either, 1) current->signal->tty is assigned by current, or 2) current->signal->tty is set to NULL. NB: for reference, tty_check_change() implements a similar POSIX check for the ioctls corresponding to tcflush(), tcdrain(), tcsetattr(), tcsetpgrp(), tcflow() and tcsendbreak(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 61f1bc97ccd..68865d9af8a 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1719,10 +1719,9 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *, * and if appropriate send any needed signals and return a negative * error code if action should be taken. * - * FIXME: - * Locking: None - redirected write test is safe, testing - * current->signal should possibly lock current->sighand - * pgrp locking ? + * Locking: redirected write test is safe + * current->signal->tty check is safe + * ctrl_lock to safely reference tty->pgrp */ static int job_control(struct tty_struct *tty, struct file *file) @@ -1732,19 +1731,22 @@ static int job_control(struct tty_struct *tty, struct file *file) /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ - if (file->f_op->write != redirected_tty_write && - current->signal->tty == tty) { - if (!tty->pgrp) - printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); - else if (task_pgrp(current) != tty->pgrp) { - if (is_ignored(SIGTTIN) || - is_current_pgrp_orphaned()) - return -EIO; - kill_pgrp(task_pgrp(current), SIGTTIN, 1); - set_thread_flag(TIF_SIGPENDING); - return -ERESTARTSYS; - } + if (file->f_op->write == redirected_tty_write || + current->signal->tty != tty) + return 0; + + spin_lock_irq(&tty->ctrl_lock); + if (!tty->pgrp) + printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); + else if (task_pgrp(current) != tty->pgrp) { + spin_unlock_irq(&tty->ctrl_lock); + if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned()) + return -EIO; + kill_pgrp(task_pgrp(current), SIGTTIN, 1); + set_thread_flag(TIF_SIGPENDING); + return -ERESTARTSYS; } + spin_unlock_irq(&tty->ctrl_lock); return 0; } -- cgit v1.2.3-18-g5258 From 6be06e7273c4682a15ca1f4adf1aeae510823530 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:38:21 -0500 Subject: tty: Fix checkpatch errors in tty_ldisc.h Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_ldisc.h | 132 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 455a0d7bf22..58390c73df8 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -9,89 +9,89 @@ * * int (*open)(struct tty_struct *); * - * This function is called when the line discipline is associated - * with the tty. The line discipline can use this as an - * opportunity to initialize any state needed by the ldisc routines. - * + * This function is called when the line discipline is associated + * with the tty. The line discipline can use this as an + * opportunity to initialize any state needed by the ldisc routines. + * * void (*close)(struct tty_struct *); * * This function is called when the line discipline is being - * shutdown, either because the tty is being closed or because - * the tty is being changed to use a new line discipline - * + * shutdown, either because the tty is being closed or because + * the tty is being changed to use a new line discipline + * * void (*flush_buffer)(struct tty_struct *tty); * - * This function instructs the line discipline to clear its - * buffers of any input characters it may have queued to be - * delivered to the user mode process. - * + * This function instructs the line discipline to clear its + * buffers of any input characters it may have queued to be + * delivered to the user mode process. + * * ssize_t (*chars_in_buffer)(struct tty_struct *tty); * - * This function returns the number of input characters the line + * This function returns the number of input characters the line * discipline may have queued up to be delivered to the user mode * process. - * + * * ssize_t (*read)(struct tty_struct * tty, struct file * file, * unsigned char * buf, size_t nr); * - * This function is called when the user requests to read from - * the tty. The line discipline will return whatever characters - * it has buffered up for the user. If this function is not - * defined, the user will receive an EIO error. - * + * This function is called when the user requests to read from + * the tty. The line discipline will return whatever characters + * it has buffered up for the user. If this function is not + * defined, the user will receive an EIO error. + * * ssize_t (*write)(struct tty_struct * tty, struct file * file, - * const unsigned char * buf, size_t nr); - * - * This function is called when the user requests to write to the - * tty. The line discipline will deliver the characters to the - * low-level tty device for transmission, optionally performing - * some processing on the characters first. If this function is - * not defined, the user will receive an EIO error. - * + * const unsigned char * buf, size_t nr); + * + * This function is called when the user requests to write to the + * tty. The line discipline will deliver the characters to the + * low-level tty device for transmission, optionally performing + * some processing on the characters first. If this function is + * not defined, the user will receive an EIO error. + * * int (*ioctl)(struct tty_struct * tty, struct file * file, - * unsigned int cmd, unsigned long arg); + * unsigned int cmd, unsigned long arg); * * This function is called when the user requests an ioctl which - * is not handled by the tty layer or the low-level tty driver. - * It is intended for ioctls which affect line discpline - * operation. Note that the search order for ioctls is (1) tty - * layer, (2) tty low-level driver, (3) line discpline. So a - * low-level driver can "grab" an ioctl request before the line - * discpline has a chance to see it. - * + * is not handled by the tty layer or the low-level tty driver. + * It is intended for ioctls which affect line discpline + * operation. Note that the search order for ioctls is (1) tty + * layer, (2) tty low-level driver, (3) line discpline. So a + * low-level driver can "grab" an ioctl request before the line + * discpline has a chance to see it. + * * long (*compat_ioctl)(struct tty_struct * tty, struct file * file, - * unsigned int cmd, unsigned long arg); + * unsigned int cmd, unsigned long arg); * - * Process ioctl calls from 32-bit process on 64-bit system + * Process ioctl calls from 32-bit process on 64-bit system * * void (*set_termios)(struct tty_struct *tty, struct ktermios * old); * - * This function notifies the line discpline that a change has - * been made to the termios structure. - * + * This function notifies the line discpline that a change has + * been made to the termios structure. + * * int (*poll)(struct tty_struct * tty, struct file * file, - * poll_table *wait); + * poll_table *wait); * - * This function is called when a user attempts to select/poll on a - * tty device. It is solely the responsibility of the line - * discipline to handle poll requests. + * This function is called when a user attempts to select/poll on a + * tty device. It is solely the responsibility of the line + * discipline to handle poll requests. * * void (*receive_buf)(struct tty_struct *, const unsigned char *cp, - * char *fp, int count); - * - * This function is called by the low-level tty driver to send - * characters received by the hardware to the line discpline for - * processing. is a pointer to the buffer of input - * character received by the device. is a pointer to a - * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. - * + * char *fp, int count); + * + * This function is called by the low-level tty driver to send + * characters received by the hardware to the line discpline for + * processing. is a pointer to the buffer of input + * character received by the device. is a pointer to a + * pointer of flag bytes which indicate whether a character was + * received with a parity error, etc. + * * void (*write_wakeup)(struct tty_struct *); * - * This function is called by the low-level tty driver to signal - * that line discpline should try to send more characters to the - * low-level driver for transmission. If the line discpline does - * not have any more data to send, it can just return. + * This function is called by the low-level tty driver to signal + * that line discpline should try to send more characters to the + * low-level driver for transmission. If the line discpline does + * not have any more data to send, it can just return. * * int (*hangup)(struct tty_struct *) * @@ -115,7 +115,7 @@ struct tty_ldisc_ops { char *name; int num; int flags; - + /* * The following routines are called from above. */ @@ -123,19 +123,19 @@ struct tty_ldisc_ops { void (*close)(struct tty_struct *); void (*flush_buffer)(struct tty_struct *tty); ssize_t (*chars_in_buffer)(struct tty_struct *tty); - ssize_t (*read)(struct tty_struct * tty, struct file * file, - unsigned char __user * buf, size_t nr); - ssize_t (*write)(struct tty_struct * tty, struct file * file, - const unsigned char * buf, size_t nr); - int (*ioctl)(struct tty_struct * tty, struct file * file, + ssize_t (*read)(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr); + ssize_t (*write)(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr); + int (*ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); - long (*compat_ioctl)(struct tty_struct * tty, struct file * file, + long (*compat_ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); - void (*set_termios)(struct tty_struct *tty, struct ktermios * old); + void (*set_termios)(struct tty_struct *tty, struct ktermios *old); unsigned int (*poll)(struct tty_struct *, struct file *, struct poll_table_struct *); int (*hangup)(struct tty_struct *tty); - + /* * The following routines are called from below. */ @@ -145,7 +145,7 @@ struct tty_ldisc_ops { void (*dcd_change)(struct tty_struct *, unsigned int); struct module *owner; - + int refcount; }; -- cgit v1.2.3-18-g5258 From b1622e0ac1b18632cff1e9af807fcdcb2397071b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:25 +0100 Subject: TTY: jsm, remove superfluous check data_len in jsm_input cannot be zero as we would jump out early in the function. It also cannot be negative because it is an int and we do bitwise and with 8192. So remove the check. Signed-off-by: Jiri Slaby Cc: Lucas Tavares Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm_tty.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 00f250ae14c..27bb75070c9 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -596,12 +596,6 @@ void jsm_input(struct jsm_channel *ch) jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); - if (data_len <= 0) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_dbg(READ, &ch->ch_bd->pci_dev, "jsm_input 1\n"); - return; - } - len = tty_buffer_request_room(port, data_len); n = len; -- cgit v1.2.3-18-g5258 From 049b539b39977fc9343056435eba568fc7779970 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:26 +0100 Subject: TTY: synclink, remove superfluous check info is obtained by container_of. It can never be NULL. So do not test that. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclink.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 8983276aa35..72d607101f9 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -1058,9 +1058,6 @@ static void mgsl_bh_handler(struct work_struct *work) container_of(work, struct mgsl_struct, task); int action; - if (!info) - return; - if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler(%s) entry\n", __FILE__,__LINE__,info->device_name); -- cgit v1.2.3-18-g5258 From 6865ff222ccab371c04afce17aec1f7d70b17dbc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:27 +0100 Subject: TTY: do not warn about setting speed via SPD_* The warning is there since 2.1.69 and we have not seen anybody reporting it in the past decade. Remove the warning now. tty_get_baud_rate can now be inline. This gives us one less EXPORT_SYMBOL. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ioctl.c | 28 ---------------------------- include/linux/tty.h | 18 ++++++++++++++++-- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 132d452578b..28715e48b2f 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -478,34 +478,6 @@ void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) } EXPORT_SYMBOL_GPL(tty_encode_baud_rate); -/** - * tty_get_baud_rate - get tty bit rates - * @tty: tty to query - * - * Returns the baud rate as an integer for this terminal. The - * termios lock must be held by the caller and the terminal bit - * flags may be updated. - * - * Locking: none - */ - -speed_t tty_get_baud_rate(struct tty_struct *tty) -{ - speed_t baud = tty_termios_baud_rate(&tty->termios); - - if (baud == 38400 && tty->alt_speed) { - if (!tty->warned) { - printk(KERN_WARNING "Use of setserial/setrocket to " - "set SPD_* flags is deprecated\n"); - tty->warned = 1; - } - baud = tty->alt_speed; - } - - return baud; -} -EXPORT_SYMBOL(tty_get_baud_rate); - /** * tty_termios_copy_hw - copy hardware settings * @new: New termios diff --git a/include/linux/tty.h b/include/linux/tty.h index 189ca80494d..63b62865c8e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -255,7 +255,6 @@ struct tty_struct { int count; struct winsize winsize; /* termios mutex */ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; - unsigned char warned:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ int flow_change; @@ -437,13 +436,28 @@ extern void tty_flush_to_ldisc(struct tty_struct *tty); extern void tty_buffer_free_all(struct tty_port *port); extern void tty_buffer_flush(struct tty_struct *tty); extern void tty_buffer_init(struct tty_port *port); -extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); extern void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud); extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud); + +/** + * tty_get_baud_rate - get tty bit rates + * @tty: tty to query + * + * Returns the baud rate as an integer for this terminal. The + * termios lock must be held by the caller and the terminal bit + * flags may be updated. + * + * Locking: none + */ +static inline speed_t tty_get_baud_rate(struct tty_struct *tty) +{ + return tty_termios_baud_rate(&tty->termios); +} + extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old); extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt); -- cgit v1.2.3-18-g5258 From 6982a398426a22166eaf049b79544536fdd6429f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:28 +0100 Subject: TTY: msm_smd_tty, clean up activate/shutdown Do not dig struct smd_tty_info out of tty_struct using tty_port_tty_get. It is unnecessarily too complicated, use simple container_of instead. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_smd_tty.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c index e722ff163d9..1238ac370bf 100644 --- a/drivers/tty/serial/msm_smd_tty.c +++ b/drivers/tty/serial/msm_smd_tty.c @@ -90,13 +90,13 @@ static void smd_tty_notify(void *priv, unsigned event) static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) { + struct smd_tty_info *info = container_of(tport, struct smd_tty_info, + port); int i, res = 0; - int n = tty->index; const char *name = NULL; - struct smd_tty_info *info = smd_tty + n; for (i = 0; i < smd_tty_channels_len; i++) { - if (smd_tty_channels[i].id == n) { + if (smd_tty_channels[i].id == tty->index) { name = smd_tty_channels[i].name; break; } @@ -117,17 +117,13 @@ static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) static void smd_tty_port_shutdown(struct tty_port *tport) { - struct smd_tty_info *info; - struct tty_struct *tty = tty_port_tty_get(tport); + struct smd_tty_info *info = container_of(tport, struct smd_tty_info, + port); - info = tty->driver_data; if (info->ch) { smd_close(info->ch); info->ch = 0; } - - tty->driver_data = 0; - tty_kref_put(tty); } static int smd_tty_open(struct tty_struct *tty, struct file *f) -- cgit v1.2.3-18-g5258 From 6aad04f21374633bd8cecf25024553d1e11a9522 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:29 +0100 Subject: TTY: add tty_port_tty_wakeup helper It allows for cleaning up on a considerable amount of places. They did port_get, wakeup, kref_put. Now the only thing needed is to call tty_port_tty_wakeup which does exactly that. One exception is ifx6x60 where tty_wakeup was open-coded. We now call tty_wakeup properly there. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/um/drivers/line.c | 8 +------- drivers/isdn/capi/capi.c | 7 +------ drivers/isdn/gigaset/interface.c | 6 +----- drivers/net/usb/hso.c | 13 ++----------- drivers/s390/char/sclp_tty.c | 9 ++------- drivers/s390/char/sclp_vt220.c | 8 +------- drivers/staging/fwserial/fwserial.c | 10 ++-------- drivers/staging/serqt_usb2/serqt_usb2.c | 7 +------ drivers/tty/ehv_bytechan.c | 6 +----- drivers/tty/hvc/hvsi.c | 7 +------ drivers/tty/nozomi.c | 6 +----- drivers/tty/serial/ifx6x60.c | 33 ++------------------------------- drivers/tty/tty_port.c | 16 ++++++++++++++++ drivers/usb/class/cdc-acm.c | 7 +------ drivers/usb/serial/digi_acceleport.c | 17 +++-------------- drivers/usb/serial/io_edgeport.c | 28 +++++----------------------- drivers/usb/serial/keyspan_pda.c | 6 ++---- drivers/usb/serial/mos7720.c | 8 ++------ drivers/usb/serial/mos7840.c | 7 ++----- drivers/usb/serial/ti_usb_3410_5052.c | 7 ++----- drivers/usb/serial/usb-serial.c | 10 +--------- include/linux/tty.h | 1 + 22 files changed, 51 insertions(+), 176 deletions(-) diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index f1b38571f94..cc206eda245 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -248,7 +248,6 @@ static irqreturn_t line_write_interrupt(int irq, void *data) { struct chan *chan = data; struct line *line = chan->line; - struct tty_struct *tty; int err; /* @@ -267,12 +266,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data) } spin_unlock(&line->lock); - tty = tty_port_tty_get(&line->port); - if (tty == NULL) - return IRQ_NONE; - - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&line->port); return IRQ_HANDLED; } diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 89562a845f6..ac6f72b455d 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -569,7 +569,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) { struct capidev *cdev = ap->private; #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct tty_struct *tty; struct capiminor *mp; u16 datahandle; struct capincci *np; @@ -627,11 +626,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 2)); kfree_skb(skb); capiminor_del_ack(mp, datahandle); - tty = tty_port_tty_get(&mp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&mp->port); handle_minor_send(mp); } else { diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index e2b539675b6..600c79b030c 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -487,12 +487,8 @@ static const struct tty_operations if_ops = { static void if_wake(unsigned long data) { struct cardstate *cs = (struct cardstate *)data; - struct tty_struct *tty = tty_port_tty_get(&cs->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&cs->port); } /*** interface to common ***/ diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index e2dd3249b6b..a7714b4f29a 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1925,7 +1925,6 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb) { struct hso_serial *serial = urb->context; int status = urb->status; - struct tty_struct *tty; /* sanity check */ if (!serial) { @@ -1941,11 +1940,7 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb) return; } hso_put_activity(serial->parent); - tty = tty_port_tty_get(&serial->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&serial->port); hso_kick_transmit(serial); D1(" "); @@ -2008,12 +2003,8 @@ static void ctrl_callback(struct urb *urb) put_rxbuf_data_and_resubmit_ctrl_urb(serial); spin_unlock(&serial->serial_lock); } else { - struct tty_struct *tty = tty_port_tty_get(&serial->port); hso_put_activity(serial->parent); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&serial->port); /* response to a write command */ hso_kick_transmit(serial); } diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 14b4cb8abcc..7ed7a598781 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -107,7 +107,6 @@ sclp_tty_write_room (struct tty_struct *tty) static void sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) { - struct tty_struct *tty; unsigned long flags; void *page; @@ -125,12 +124,8 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) struct sclp_buffer, list); spin_unlock_irqrestore(&sclp_tty_lock, flags); } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); - /* check if the tty needs a wake up call */ - tty = tty_port_tty_get(&sclp_port); - if (tty != NULL) { - tty_wakeup(tty); - tty_kref_put(tty); - } + + tty_port_tty_wakeup(&sclp_port); } static inline void diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 6c92f62623b..5aaaa2ec8df 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -114,7 +114,6 @@ static struct sclp_register sclp_vt220_register = { static void sclp_vt220_process_queue(struct sclp_vt220_request *request) { - struct tty_struct *tty; unsigned long flags; void *page; @@ -139,12 +138,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) } while (__sclp_vt220_emit(request)); if (request == NULL && sclp_vt220_flush_later) sclp_vt220_emit_current(); - /* Check if the tty needs a wake up call */ - tty = tty_port_tty_get(&sclp_vt220_port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&sclp_vt220_port); } #define SCLP_BUFFER_MAX_RETRY 1 diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 5a6fb44f38a..5c64e3a35b2 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -744,7 +744,6 @@ static void fwtty_tx_complete(struct fw_card *card, int rcode, struct fwtty_transaction *txn) { struct fwtty_port *port = txn->port; - struct tty_struct *tty; int len; fwtty_dbg(port, "rcode: %d", rcode); @@ -769,13 +768,8 @@ static void fwtty_tx_complete(struct fw_card *card, int rcode, port->stats.dropped += txn->dma_pended.len; } - if (len < WAKEUP_CHARS) { - tty = tty_port_tty_get(&port->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } - } + if (len < WAKEUP_CHARS) + tty_port_tty_wakeup(&port->port); } static int fwtty_tx(struct fwtty_port *port, bool drain) diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index b1bb1a6abe8..8a6e5ea476e 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -264,7 +264,6 @@ static void ProcessRxChar(struct usb_serial_port *port, unsigned char data) static void qt_write_bulk_callback(struct urb *urb) { - struct tty_struct *tty; int status; struct quatech_port *quatech_port; @@ -278,11 +277,7 @@ static void qt_write_bulk_callback(struct urb *urb) quatech_port = urb->context; - tty = tty_port_tty_get(&quatech_port->port->port); - - if (tty) - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&quatech_port->port->port); } static void qt_interrupt_callback(struct urb *urb) diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index ed92622b894..6d0c27cd03d 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -472,13 +472,9 @@ static void ehv_bc_tx_dequeue(struct ehv_bc_data *bc) static irqreturn_t ehv_bc_tty_tx_isr(int irq, void *data) { struct ehv_bc_data *bc = data; - struct tty_struct *ttys = tty_port_tty_get(&bc->port); ehv_bc_tx_dequeue(bc); - if (ttys) { - tty_wakeup(ttys); - tty_kref_put(ttys); - } + tty_port_tty_wakeup(&bc->port); return IRQ_HANDLED; } diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index ef95a154854..41901997c0d 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -861,7 +861,6 @@ static void hvsi_write_worker(struct work_struct *work) { struct hvsi_struct *hp = container_of(work, struct hvsi_struct, writer.work); - struct tty_struct *tty; unsigned long flags; #ifdef DEBUG static long start_j = 0; @@ -895,11 +894,7 @@ static void hvsi_write_worker(struct work_struct *work) start_j = 0; #endif /* DEBUG */ wake_up_all(&hp->emptyq); - tty = tty_port_tty_get(&hp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&hp->port); } out: diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 2dff1979615..2e5bbdc09e1 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -791,7 +791,6 @@ static int send_data(enum port_type index, struct nozomi *dc) const u8 toggle = port->toggle_ul; void __iomem *addr = port->ul_addr[toggle]; const u32 ul_size = port->ul_size[toggle]; - struct tty_struct *tty = tty_port_tty_get(&port->port); /* Get data from tty and place in buf for now */ size = kfifo_out(&port->fifo_ul, dc->send_buf, @@ -799,7 +798,6 @@ static int send_data(enum port_type index, struct nozomi *dc) if (size == 0) { DBG4("No more data to send, disable link:"); - tty_kref_put(tty); return 0; } @@ -809,10 +807,8 @@ static int send_data(enum port_type index, struct nozomi *dc) write_mem32(addr, (u32 *) &size, 4); write_mem32(addr + 4, (u32 *) dc->send_buf, size); - if (tty) - tty_wakeup(tty); + tty_port_tty_wakeup(&port->port); - tty_kref_put(tty); return 1; } diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 68d7ce997ed..d723d4193b9 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -442,25 +442,6 @@ static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK; } -/** - * ifx_spi_wakeup_serial - SPI space made - * @port_data: our SPI device - * - * We have emptied the FIFO enough that we want to get more data - * queued into it. Poke the line discipline via tty_wakeup so that - * it will feed us more bits - */ -static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev) -{ - struct tty_struct *tty; - - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); -} - /** * ifx_spi_prepare_tx_buffer - prepare transmit frame * @ifx_dev: our SPI device @@ -506,7 +487,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) tx_count += temp_count; if (temp_count == queue_length) /* poke port to get more data */ - ifx_spi_wakeup_serial(ifx_dev); + tty_port_tty_wakeup(&ifx_dev->tty_port); else /* more data in port, use next SPI message */ ifx_dev->spi_more = 1; } @@ -683,8 +664,6 @@ static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, static void ifx_spi_complete(void *ctx) { struct ifx_spi_device *ifx_dev = ctx; - struct tty_struct *tty; - struct tty_ldisc *ldisc = NULL; int length; int actual_length; unsigned char more; @@ -762,15 +741,7 @@ complete_exit: */ ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_DATA_PENDING); - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (tty) { - ldisc = tty_ldisc_ref(tty); - if (ldisc) { - ldisc->ops->write_wakeup(tty); - tty_ldisc_deref(ldisc); - } - tty_kref_put(tty); - } + tty_port_tty_wakeup(&ifx_dev->tty_port); } } } diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index b7ff59d3db8..8bb757c62ee 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -232,6 +232,22 @@ void tty_port_hangup(struct tty_port *port) } EXPORT_SYMBOL(tty_port_hangup); +/** + * tty_port_tty_wakeup - helper to wake up a tty + * + * @port: tty port + */ +void tty_port_tty_wakeup(struct tty_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); + /** * tty_port_carrier_raised - carrier raised check * @port: tty port diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8ac25adf31b..755766e4b75 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -475,15 +475,10 @@ static void acm_write_bulk(struct urb *urb) static void acm_softint(struct work_struct *work) { struct acm *acm = container_of(work, struct acm, work); - struct tty_struct *tty; dev_vdbg(&acm->data->dev, "%s\n", __func__); - tty = tty_port_tty_get(&acm->port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&acm->port); } /* diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index ebe45fa0ed5..31191581060 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -210,7 +210,6 @@ struct digi_port { /* Local Function Declarations */ -static void digi_wakeup_write(struct usb_serial_port *port); static void digi_wakeup_write_lock(struct work_struct *work); static int digi_write_oob_command(struct usb_serial_port *port, unsigned char *buf, int count, int interruptible); @@ -374,20 +373,10 @@ static void digi_wakeup_write_lock(struct work_struct *work) unsigned long flags; spin_lock_irqsave(&priv->dp_port_lock, flags); - digi_wakeup_write(port); + tty_port_tty_wakeup(&port->port); spin_unlock_irqrestore(&priv->dp_port_lock, flags); } -static void digi_wakeup_write(struct usb_serial_port *port) -{ - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } -} - - /* * Digi Write OOB Command * @@ -1044,7 +1033,7 @@ static void digi_write_bulk_callback(struct urb *urb) } } /* wake up processes sleeping on writes immediately */ - digi_wakeup_write(port); + tty_port_tty_wakeup(&port->port); /* also queue up a wakeup at scheduler time, in case we */ /* lost the race in write_chan(). */ schedule_work(&priv->dp_wakeup_work); @@ -1522,7 +1511,7 @@ static int digi_read_oob_callback(struct urb *urb) /* port must be open to use tty struct */ if (rts) { tty->hw_stopped = 0; - digi_wakeup_write(port); + tty_port_tty_wakeup(&port->port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index b00e5cbf741..44e5208f7c6 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -565,7 +565,6 @@ static void edge_interrupt_callback(struct urb *urb) struct device *dev; struct edgeport_port *edge_port; struct usb_serial_port *port; - struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int length = urb->actual_length; int bytes_avail; @@ -644,12 +643,7 @@ static void edge_interrupt_callback(struct urb *urb) /* tell the tty driver that something has changed */ - tty = tty_port_tty_get( - &edge_port->port->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&edge_port->port->port); /* Since we have more credit, check if more data can be sent */ send_more_port_data(edge_serial, @@ -738,7 +732,6 @@ static void edge_bulk_in_callback(struct urb *urb) static void edge_bulk_out_data_callback(struct urb *urb) { struct edgeport_port *edge_port = urb->context; - struct tty_struct *tty; int status = urb->status; if (status) { @@ -747,14 +740,8 @@ static void edge_bulk_out_data_callback(struct urb *urb) __func__, status); } - tty = tty_port_tty_get(&edge_port->port->port); - - if (tty && edge_port->open) { - /* let the tty driver wakeup if it has a special - write_wakeup function */ - tty_wakeup(tty); - } - tty_kref_put(tty); + if (edge_port->open) + tty_port_tty_wakeup(&edge_port->port->port); /* Release the Write URB */ edge_port->write_in_progress = false; @@ -773,7 +760,6 @@ static void edge_bulk_out_data_callback(struct urb *urb) static void edge_bulk_out_cmd_callback(struct urb *urb) { struct edgeport_port *edge_port = urb->context; - struct tty_struct *tty; int status = urb->status; atomic_dec(&CmdUrbs); @@ -794,13 +780,9 @@ static void edge_bulk_out_cmd_callback(struct urb *urb) return; } - /* Get pointer to tty */ - tty = tty_port_tty_get(&edge_port->port->port); - /* tell the tty driver that something has changed */ - if (tty && edge_port->open) - tty_wakeup(tty); - tty_kref_put(tty); + if (edge_port->open) + tty_port_tty_wakeup(&edge_port->port->port); /* we have completed the command */ edge_port->commandPending = false; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 3b17d5d13dc..2230223978c 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -104,10 +104,8 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) struct keyspan_pda_private *priv = container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty) - tty_wakeup(tty); - tty_kref_put(tty); + + tty_port_tty_wakeup(&port->port); } static void keyspan_pda_request_unthrottle(struct work_struct *work) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index e0ebec3b5d6..e956eae198f 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -932,7 +932,6 @@ static void mos7720_bulk_in_callback(struct urb *urb) static void mos7720_bulk_out_data_callback(struct urb *urb) { struct moschip_port *mos7720_port; - struct tty_struct *tty; int status = urb->status; if (status) { @@ -946,11 +945,8 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) return ; } - tty = tty_port_tty_get(&mos7720_port->port->port); - - if (tty && mos7720_port->open) - tty_wakeup(tty); - tty_kref_put(tty); + if (mos7720_port->open) + tty_port_tty_wakeup(&mos7720_port->port->port); } /* diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 809fb329eca..08284d28e84 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -814,7 +814,6 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) { struct moschip_port *mos7840_port; struct usb_serial_port *port; - struct tty_struct *tty; int status = urb->status; int i; @@ -837,10 +836,8 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) if (mos7840_port_paranoia_check(port, __func__)) return; - tty = tty_port_tty_get(&port->port); - if (tty && mos7840_port->open) - tty_wakeup(tty); - tty_kref_put(tty); + if (mos7840_port->open) + tty_port_tty_wakeup(&port->port); } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 39cb9b807c3..437f2d579cd 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1227,7 +1227,6 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = tty_port_tty_get(&port->port); /* FIXME */ unsigned long flags; spin_lock_irqsave(&tport->tp_lock, flags); @@ -1268,14 +1267,12 @@ static void ti_send(struct ti_port *tport) } /* more room in the buffer for new writes, wakeup */ - if (tty) - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&port->port); + wake_up_interruptible(&tport->tp_write_wait); return; unlock: spin_unlock_irqrestore(&tport->tp_lock, flags); - tty_kref_put(tty); return; } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a19ed74d770..2df84845baf 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -541,16 +541,8 @@ static void usb_serial_port_work(struct work_struct *work) { struct usb_serial_port *port = container_of(work, struct usb_serial_port, work); - struct tty_struct *tty; - tty = tty_port_tty_get(&port->port); - if (!tty) - return; - - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&port->port); } static void kill_traffic(struct usb_serial_port *port) diff --git a/include/linux/tty.h b/include/linux/tty.h index 63b62865c8e..b6e890a87eb 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -534,6 +534,7 @@ extern int tty_port_carrier_raised(struct tty_port *port); extern void tty_port_raise_dtr_rts(struct tty_port *port); extern void tty_port_lower_dtr_rts(struct tty_port *port); extern void tty_port_hangup(struct tty_port *port); +extern void tty_port_tty_wakeup(struct tty_port *port); extern int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp); extern int tty_port_close_start(struct tty_port *port, -- cgit v1.2.3-18-g5258 From e4408ce3c23f8451eff7a2954694598fb8fce833 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:31 +0100 Subject: TTY: quatech2, remove unneeded is_open tty->ops->break_ctl cannot be called outside the gap between open and close. So there is no need to check whether the port is open in break_ctl in quatech2. Remove the check and also that member completely. Signed-off-by: Jiri Slaby Cc: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 00e6c9bac8a..d8531047b41 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -116,7 +116,6 @@ struct qt2_serial_private { }; struct qt2_port_private { - bool is_open; u8 device_port; spinlock_t urb_lock; @@ -398,7 +397,6 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) return status; } - port_priv->is_open = true; port_priv->device_port = (u8) device_port; if (tty) @@ -418,8 +416,6 @@ static void qt2_close(struct usb_serial_port *port) serial = port->serial; port_priv = usb_get_serial_port_data(port); - port_priv->is_open = false; - spin_lock_irqsave(&port_priv->urb_lock, flags); usb_kill_urb(port_priv->write_urb); port_priv->urb_in_use = false; @@ -905,12 +901,6 @@ static void qt2_break_ctl(struct tty_struct *tty, int break_state) port_priv = usb_get_serial_port_data(port); - if (!port_priv->is_open) { - dev_err(&port->dev, - "%s - port is not open\n", __func__); - return; - } - val = (break_state == -1) ? 1 : 0; status = qt2_control_msg(port->serial->dev, QT2_BREAK_CONTROL, -- cgit v1.2.3-18-g5258 From aa27a094e2c2e0cc59914e56113b860f524f4479 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:30 +0100 Subject: TTY: add tty_port_tty_hangup helper It allows for cleaning up on a considerable amount of places. They did port_get, hangup, kref_put. Now the only thing needed is to call tty_port_tty_hangup which does exactly that. And they can also decide whether to consider CLOCAL or completely ignore that. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/um/drivers/chan_kern.c | 6 +----- drivers/mmc/card/sdio_uart.c | 13 ++--------- drivers/net/usb/hso.c | 7 +----- drivers/tty/cyclades.c | 10 ++------- drivers/tty/moxa.c | 19 ++++++---------- drivers/tty/n_gsm.c | 6 +----- drivers/tty/nozomi.c | 9 +++----- drivers/tty/rocket.c | 7 +----- drivers/tty/serial/ifx6x60.c | 21 ++---------------- drivers/tty/tty_port.c | 17 +++++++++++++++ drivers/usb/class/cdc-acm.c | 24 ++++++--------------- drivers/usb/serial/keyspan.c | 43 +++++++++---------------------------- drivers/usb/serial/option.c | 9 ++------ drivers/usb/serial/sierra.c | 8 ++----- include/linux/tty.h | 1 + net/irda/ircomm/ircomm_tty_attach.c | 6 +----- 16 files changed, 58 insertions(+), 148 deletions(-) diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 15c553c239a..bf42825ba54 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -568,11 +568,7 @@ void chan_interrupt(struct line *line, int irq) reactivate_fd(chan->fd, irq); if (err == -EIO) { if (chan->primary) { - struct tty_struct *tty = tty_port_tty_get(&line->port); - if (tty != NULL) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&line->port, false); if (line->chan_out != chan) close_one_chan(line->chan_out, 1); } diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index c931dfe6a59..f093cea0d06 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -134,7 +134,6 @@ static void sdio_uart_port_put(struct sdio_uart_port *port) static void sdio_uart_port_remove(struct sdio_uart_port *port) { struct sdio_func *func; - struct tty_struct *tty; BUG_ON(sdio_uart_table[port->index] != port); @@ -155,12 +154,8 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) sdio_claim_host(func); port->func = NULL; mutex_unlock(&port->func_lock); - tty = tty_port_tty_get(&port->port); /* tty_hangup is async so is this safe as is ?? */ - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&port->port, false); mutex_unlock(&port->port.mutex); sdio_release_irq(func); sdio_disable_func(func); @@ -492,11 +487,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) wake_up_interruptible(&port->port.open_wait); else { /* DCD drop - hang up if tty attached */ - tty = tty_port_tty_get(&port->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&port->port, false); } } if (status & UART_MSR_DCTS) { diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index a7714b4f29a..cba1d46e672 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -3124,18 +3124,13 @@ static void hso_serial_ref_free(struct kref *ref) static void hso_free_interface(struct usb_interface *interface) { struct hso_serial *hso_dev; - struct tty_struct *tty; int i; for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] && (serial_table[i]->interface == interface)) { hso_dev = dev2ser(serial_table[i]); - tty = tty_port_tty_get(&hso_dev->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&hso_dev->port, false); mutex_lock(&hso_dev->parent->mutex); hso_dev->parent->usb_gone = 1; mutex_unlock(&hso_dev->parent->mutex); diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 345bd0e0884..33f83fee9fa 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1124,14 +1124,8 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) readl(&info->u.cyz.ch_ctrl->rs_status); if (dcd & C_RS_DCD) wake_up_interruptible(&info->port.open_wait); - else { - struct tty_struct *tty; - tty = tty_port_tty_get(&info->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + else + tty_port_tty_hangup(&info->port, false); } break; case C_CM_MCTS: diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index adeac255e52..1deaca4674e 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -913,16 +913,12 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) /* pci hot-un-plug support */ for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) { - struct tty_struct *tty = tty_port_tty_get( - &brd->ports[a].port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) + tty_port_tty_hangup(&brd->ports[a].port, false); + for (a = 0; a < MAX_PORTS_PER_BOARD; a++) tty_port_destroy(&brd->ports[a].port); + while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) @@ -1365,7 +1361,6 @@ static void moxa_hangup(struct tty_struct *tty) static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) { - struct tty_struct *tty; unsigned long flags; dcd = !!dcd; @@ -1373,10 +1368,8 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) if (dcd != p->DCDState) { p->DCDState = dcd; spin_unlock_irqrestore(&p->port.lock, flags); - tty = tty_port_tty_get(&p->port); - if (tty && !C_CLOCAL(tty) && !dcd) - tty_hangup(tty); - tty_kref_put(tty); + if (!dcd) + tty_port_tty_hangup(&p->port, true); } else spin_unlock_irqrestore(&p->port.lock, flags); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 4a43ef5d796..74d9a0258d7 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1418,11 +1418,7 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) pr_debug("DLCI %d goes closed.\n", dlci->addr); dlci->state = DLCI_CLOSED; if (dlci->addr != 0) { - struct tty_struct *tty = tty_port_tty_get(&dlci->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&dlci->port, false); kfifo_reset(dlci->fifo); } else dlci->gsm->dead = 1; diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 2e5bbdc09e1..d6080c3831e 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1501,12 +1501,9 @@ static void tty_exit(struct nozomi *dc) DBG1(" "); - for (i = 0; i < MAX_PORT; ++i) { - struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port); - if (tty && list_empty(&tty->hangup_work.entry)) - tty_hangup(tty); - tty_kref_put(tty); - } + for (i = 0; i < MAX_PORT; ++i) + tty_port_tty_hangup(&dc->port[i].port, false); + /* Racy below - surely should wait for scheduled work to be done or complete off a hangup method ? */ while (dc->open_ttys) diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 1d270034bfc..bbffd7a431e 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -521,15 +521,10 @@ static void rp_handle_port(struct r_port *info) (ChanStatus & CD_ACT) ? "on" : "off"); #endif if (!(ChanStatus & CD_ACT) && info->cd_status) { - struct tty_struct *tty; #ifdef ROCKET_DEBUG_HANGUP printk(KERN_INFO "CD drop, calling hangup.\n"); #endif - tty = tty_port_tty_get(&info->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&info->port, false); } info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; wake_up_interruptible(&info->port.open_wait); diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index d723d4193b9..2c77fed31a7 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -269,23 +269,6 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev) mrdy_set_high(ifx_dev); } -/** - * ifx_spi_hangup - hang up an IFX device - * @ifx_dev: our SPI device - * - * Hang up the tty attached to the IFX device if one is currently - * open. If not take no action - */ -static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev) -{ - struct tty_port *pport = &ifx_dev->tty_port; - struct tty_struct *tty = tty_port_tty_get(pport); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } -} - /** * ifx_spi_timeout - SPI timeout * @arg: our SPI device @@ -298,7 +281,7 @@ static void ifx_spi_timeout(unsigned long arg) struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg; dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); mrdy_set_low(ifx_dev); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); } @@ -933,7 +916,7 @@ static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); if (!solreset) { /* unsolicited reset */ - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); } } else { /* exited reset */ diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 8bb757c62ee..7f38eeaafac 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -232,6 +232,23 @@ void tty_port_hangup(struct tty_port *port) } EXPORT_SYMBOL(tty_port_hangup); +/** + * tty_port_tty_hangup - helper to hang up a tty + * + * @port: tty port + * @check_clocal: hang only ttys with CLOCAL unset? + */ +void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty && (!check_clocal || !C_CLOCAL(tty))) { + tty_hangup(tty); + tty_kref_put(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_hangup); + /** * tty_port_tty_wakeup - helper to wake up a tty * diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 755766e4b75..27a18743275 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -292,7 +292,6 @@ static void acm_ctrl_irq(struct urb *urb) { struct acm *acm = urb->context; struct usb_cdc_notification *dr = urb->transfer_buffer; - struct tty_struct *tty; unsigned char *data; int newctrl; int retval; @@ -327,17 +326,12 @@ static void acm_ctrl_irq(struct urb *urb) break; case USB_CDC_NOTIFY_SERIAL_STATE: - tty = tty_port_tty_get(&acm->port); newctrl = get_unaligned_le16(data); - if (tty) { - if (!acm->clocal && - (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { - dev_dbg(&acm->control->dev, - "%s - calling hangup\n", __func__); - tty_hangup(tty); - } - tty_kref_put(tty); + if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dev_dbg(&acm->control->dev, "%s - calling hangup\n", + __func__); + tty_port_tty_hangup(&acm->port, false); } acm->ctrlin = newctrl; @@ -1498,15 +1492,9 @@ err_out: static int acm_reset_resume(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); - struct tty_struct *tty; - if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) { - tty = tty_port_tty_get(&acm->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) + tty_port_tty_hangup(&acm->port, false); return acm_resume(intf); } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 1fd1935c831..b011478d2e5 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -378,7 +378,6 @@ static void usa26_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; - struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -421,12 +420,8 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state) { - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -510,7 +505,6 @@ static void usa28_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; - struct tty_struct *tty; int old_dcd_state; int status = urb->status; @@ -551,12 +545,8 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -642,12 +632,8 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -851,7 +837,6 @@ static void usa90_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; - struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -880,12 +865,8 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -953,12 +934,8 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index f7d339d8187..602d1f389a3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1532,13 +1532,8 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (old_dcd_state && !portdata->dcd_state) { - struct tty_struct *tty = - tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state && !portdata->dcd_state) + tty_port_tty_hangup(&port->port, true); } else { dev_dbg(dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, req_pkt->bRequest); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index c13f6e74774..d66148a17fe 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -628,7 +628,6 @@ static void sierra_instat_callback(struct urb *urb) unsigned char signals = *((unsigned char *) urb->transfer_buffer + sizeof(struct usb_ctrlrequest)); - struct tty_struct *tty; dev_dbg(&port->dev, "%s: signal x%x\n", __func__, signals); @@ -639,11 +638,8 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty) && - old_dcd_state && !portdata->dcd_state) - tty_hangup(tty); - tty_kref_put(tty); + if (old_dcd_state && !portdata->dcd_state) + tty_port_tty_hangup(&port->port, true); } else { dev_dbg(&port->dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, diff --git a/include/linux/tty.h b/include/linux/tty.h index b6e890a87eb..d3548f87196 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -534,6 +534,7 @@ extern int tty_port_carrier_raised(struct tty_port *port); extern void tty_port_raise_dtr_rts(struct tty_port *port); extern void tty_port_lower_dtr_rts(struct tty_port *port); extern void tty_port_hangup(struct tty_port *port); +extern void tty_port_tty_hangup(struct tty_port *port, bool check_clocal); extern void tty_port_tty_wakeup(struct tty_port *port); extern int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp); diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index edab393e0c8..a2a508f5f26 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -997,12 +997,8 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, self->settings.dce = IRCOMM_DELTA_CD; ircomm_tty_check_modem_status(self); } else { - struct tty_struct *tty = tty_port_tty_get(&self->port); IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ ); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&self->port, false); } break; default: -- cgit v1.2.3-18-g5258 From e99c33b9d3abfbba5ff34a16579a8e0a9e3211c4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:32 +0100 Subject: TTY: serial/bfin_uart, unbreak build with KGDB enabled There are no (and never were any) kgdb fields in uart_ops. Setting them produces a build error: drivers/tty/serial/bfin_uart.c:1054:2: error: unknown field 'kgdboc_port_startup' specified in initializer drivers/tty/serial/bfin_uart.c:1054:2: warning: initialization from incompatible pointer type [enabled by default] drivers/tty/serial/bfin_uart.c:1054:2: warning: (near initialization for 'bfin_serial_pops.ioctl') [enabled by default] drivers/tty/serial/bfin_uart.c:1055:2: error: unknown field 'kgdboc_port_shutdown' specified in initializer drivers/tty/serial/bfin_uart.c:1055:2: warning: initialization from incompatible pointer type [enabled by default] drivers/tty/serial/bfin_uart.c:1055:2: warning: (near initialization for 'bfin_serial_pops.poll_init') [enabled by default] Remove them. Signed-off-by: Jiri Slaby Acked-by: Sonic Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 12dceda9db3..26a3be7ced7 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -1011,24 +1011,6 @@ static int bfin_serial_poll_get_char(struct uart_port *port) } #endif -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) -static void bfin_kgdboc_port_shutdown(struct uart_port *port) -{ - if (kgdboc_break_enabled) { - kgdboc_break_enabled = 0; - bfin_serial_shutdown(port); - } -} - -static int bfin_kgdboc_port_startup(struct uart_port *port) -{ - kgdboc_port_line = port->line; - kgdboc_break_enabled = !bfin_serial_startup(port); - return 0; -} -#endif - static struct uart_ops bfin_serial_pops = { .tx_empty = bfin_serial_tx_empty, .set_mctrl = bfin_serial_set_mctrl, @@ -1047,11 +1029,6 @@ static struct uart_ops bfin_serial_pops = { .request_port = bfin_serial_request_port, .config_port = bfin_serial_config_port, .verify_port = bfin_serial_verify_port, -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - .kgdboc_port_startup = bfin_kgdboc_port_startup, - .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown, -#endif #ifdef CONFIG_CONSOLE_POLL .poll_put_char = bfin_serial_poll_put_char, .poll_get_char = bfin_serial_poll_get_char, -- cgit v1.2.3-18-g5258 From 4d29994ddb4cc97e19a533834df2e0fdb1c1d8a8 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:33 +0100 Subject: TTY: serial/msm_serial_hs, remove unused tty Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial_hs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 4a942c78347..4ca2f64861e 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -907,7 +907,6 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, unsigned int error_f = 0; unsigned long flags; unsigned int flush; - struct tty_struct *tty; struct tty_port *port; struct uart_port *uport; struct msm_hs_port *msm_uport; @@ -919,7 +918,6 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, clk_enable(msm_uport->clk); port = &uport->state->port; - tty = port->tty; msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); -- cgit v1.2.3-18-g5258 From ee7970690568b0c875467f475d9c957ec0d9e099 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:34 +0100 Subject: TTY: cleanup tty->hw_stopped uses tty->hw_stopped is set only by drivers to remember HW state. If it is never set to 1 in a particular driver, there is no need to check it in the driver at all. Remove such checks. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 16 +++------------- drivers/isdn/i4l/isdn_tty.c | 3 --- drivers/net/caif/caif_serial.c | 1 - drivers/tty/rocket.c | 19 ++++++++----------- drivers/tty/serial/68328serial.c | 9 +++------ drivers/tty/serial/arc_uart.c | 2 +- drivers/tty/serial/crisv10.c | 12 ++---------- 7 files changed, 17 insertions(+), 45 deletions(-) diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index da2f319fb71..e70cadec7ce 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -142,8 +142,7 @@ static void transmit_chars(struct tty_struct *tty, struct serial_state *info, goto out; } - if (info->xmit.head == info->xmit.tail || tty->stopped || - tty->hw_stopped) { + if (info->xmit.head == info->xmit.tail || tty->stopped) { #ifdef SIMSERIAL_DEBUG printk("transmit_chars: head=%d, tail=%d, stopped=%d\n", info->xmit.head, info->xmit.tail, tty->stopped); @@ -181,7 +180,7 @@ static void rs_flush_chars(struct tty_struct *tty) struct serial_state *info = tty->driver_data; if (info->xmit.head == info->xmit.tail || tty->stopped || - tty->hw_stopped || !info->xmit.buf) + !info->xmit.buf) return; transmit_chars(tty, info, NULL); @@ -217,7 +216,7 @@ static int rs_write(struct tty_struct * tty, * Hey, we transmit directly from here in our case */ if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) && - !tty->stopped && !tty->hw_stopped) + !tty->stopped) transmit_chars(tty, info, NULL); return ret; @@ -325,14 +324,6 @@ static int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) -static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - } -} /* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. @@ -481,7 +472,6 @@ static const struct tty_operations hp_ops = { .throttle = rs_throttle, .unthrottle = rs_unthrottle, .send_xchar = rs_send_xchar, - .set_termios = rs_set_termios, .hangup = rs_hangup, .proc_fops = &rs_proc_fops, }; diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index d8a7d832341..22107662858 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1470,9 +1470,6 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) tty->termios.c_ospeed == old_termios->c_ospeed) return; isdn_tty_change_speed(info); - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) - tty->hw_stopped = 0; } } diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 666891a9a24..d1bf0ff93ae 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -88,7 +88,6 @@ static inline void update_tty_status(struct ser_device *ser) { ser->tty_status = ser->tty->stopped << 5 | - ser->tty->hw_stopped << 4 | ser->tty->flow_stopped << 3 | ser->tty->packet << 2 | ser->tty->port->low_latency << 1 | diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index bbffd7a431e..f5abc288882 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -449,7 +449,7 @@ static void rp_do_transmit(struct r_port *info) /* Loop sending data to FIFO until done or FIFO full */ while (1) { - if (tty->stopped || tty->hw_stopped) + if (tty->stopped) break; c = min(info->xmit_fifo_room, info->xmit_cnt); c = min(c, XMIT_BUF_SIZE - info->xmit_tail); @@ -1106,15 +1106,12 @@ static void rp_set_termios(struct tty_struct *tty, /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (tty->termios.c_cflag & CBAUD)) { - if (!tty->hw_stopped || !(tty->termios.c_cflag & CRTSCTS)) - sSetRTS(cp); + sSetRTS(cp); sSetDTR(cp); } - if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) rp_start(tty); - } } static int rp_break(struct tty_struct *tty, int break_state) @@ -1570,10 +1567,10 @@ static int rp_put_char(struct tty_struct *tty, unsigned char ch) spin_lock_irqsave(&info->slock, flags); cp = &info->channel; - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0) + if (!tty->stopped && info->xmit_fifo_room == 0) info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { + if (tty->stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= XMIT_BUF_SIZE - 1; info->xmit_cnt++; @@ -1614,14 +1611,14 @@ static int rp_write(struct tty_struct *tty, #endif cp = &info->channel; - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count) + if (!tty->stopped && info->xmit_fifo_room < count) info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); /* * If the write queue for the port is empty, and there is FIFO space, stuff bytes * into FIFO. Use the write queue for temp storage. */ - if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { + if (!tty->stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { c = min(count, info->xmit_fifo_room); b = buf; @@ -1669,7 +1666,7 @@ static int rp_write(struct tty_struct *tty, retval += c; } - if ((retval > 0) && !tty->stopped && !tty->hw_stopped) + if ((retval > 0) && !tty->stopped) set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); end: diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 49399470794..ef2e08e9b59 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -630,8 +630,7 @@ static void rs_flush_chars(struct tty_struct *tty) /* Enable transmitter */ local_irq_save(flags); - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) { + if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) { local_irq_restore(flags); return; } @@ -697,7 +696,7 @@ static int rs_write(struct tty_struct * tty, total += c; } - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + if (info->xmit_cnt && !tty->stopped) { /* Enable transmitter */ local_irq_disable(); #ifndef USE_INTS @@ -978,10 +977,8 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_speed(info, tty); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + !(tty->termios.c_cflag & CRTSCTS)) rs_start(tty); - } } diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index d97e194b6bc..cbf1d155b7b 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -162,7 +162,7 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port) /* * Driver internal routine, used by both tty(serial core) as well as tx-isr * -Called under spinlock in either cases - * -also tty->stopped / tty->hw_stopped has already been checked + * -also tty->stopped has already been checked * = by uart_start( ) before calling us * = tx_ist checks that too before calling */ diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 5f37c31e32b..50f56f39d43 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -2534,8 +2534,7 @@ static void handle_ser_tx_interrupt(struct e100_serial *info) } /* Normal char-by-char interrupt */ if (info->xmit.head == info->xmit.tail - || info->port.tty->stopped - || info->port.tty->hw_stopped) { + || info->port.tty->stopped) { DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->port.tty->stopped)); e100_disable_serial_tx_ready_irq(info); @@ -3098,7 +3097,6 @@ rs_flush_chars(struct tty_struct *tty) if (info->tr_running || info->xmit.head == info->xmit.tail || tty->stopped || - tty->hw_stopped || !info->xmit.buf) return; @@ -3176,7 +3174,6 @@ static int rs_raw_write(struct tty_struct *tty, if (info->xmit.head != info->xmit.tail && !tty->stopped && - !tty->hw_stopped && !info->tr_running) { start_transmit(info); } @@ -3733,10 +3730,8 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + !(tty->termios.c_cflag & CRTSCTS)) rs_start(tty); - } } @@ -4256,9 +4251,6 @@ static void seq_line_info(struct seq_file *m, struct e100_serial *info) if (info->port.tty->stopped) seq_printf(m, " stopped:%i", (int)info->port.tty->stopped); - if (info->port.tty->hw_stopped) - seq_printf(m, " hw_stopped:%i", - (int)info->port.tty->hw_stopped); } { -- cgit v1.2.3-18-g5258 From b1d984cf7d6b7d4e395f1aa6a0a4d3d1ecf21495 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:36 +0100 Subject: crisv10: use flags from tty_port First, remove STD_FLAGS as the value, or its subvalues (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) is not tested anywhere -- there is no point to initialize flags to that. Second, use flags member from tty_port when we have it now. So that we do not waste space. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 64 ++++++++++++++++++++------------------------ drivers/tty/serial/crisv10.h | 2 -- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 50f56f39d43..2ae378cc65b 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -169,7 +169,6 @@ static int get_lsr_info(struct e100_serial *info, unsigned int *value); #define DEF_BAUD 115200 /* 115.2 kbit/s */ -#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) #define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */ /* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */ #define DEF_TX 0x80 /* or SERIAL_CTRL_B */ @@ -246,7 +245,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH7_FIRST, .icmdadr = R_DMA_CH7_CMD, .idescradr = R_DMA_CH7_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 2, @@ -300,7 +298,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH9_FIRST, .icmdadr = R_DMA_CH9_CMD, .idescradr = R_DMA_CH9_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 3, @@ -356,7 +353,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH3_FIRST, .icmdadr = R_DMA_CH3_CMD, .idescradr = R_DMA_CH3_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 0, @@ -410,7 +406,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH5_FIRST, .icmdadr = R_DMA_CH5_CMD, .idescradr = R_DMA_CH5_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 1, @@ -2721,7 +2716,7 @@ startup(struct e100_serial * info) /* if it was already initialized, skip this */ - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { local_irq_restore(flags); free_page(xmit_page); return 0; @@ -2846,7 +2841,7 @@ startup(struct e100_serial * info) #endif /* CONFIG_SVINTO_SIM */ - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; @@ -2891,7 +2886,7 @@ shutdown(struct e100_serial * info) #endif /* CONFIG_SVINTO_SIM */ - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; #ifdef SERIAL_DEBUG_OPEN @@ -2922,7 +2917,7 @@ shutdown(struct e100_serial * info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->port.flags &= ~ASYNC_INITIALIZED; local_irq_restore(flags); } @@ -2947,7 +2942,7 @@ change_speed(struct e100_serial *info) /* possibly, the tx/rx should be disabled first to do this safely */ /* change baud-rate and write it to the hardware */ - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { /* Special baudrate */ u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ unsigned long alt_source = @@ -3397,7 +3392,7 @@ get_serial_info(struct e100_serial * info, tmp.line = info->line; tmp.port = (int)info->ioport; tmp.irq = info->irq; - tmp.flags = info->flags; + tmp.flags = info->port.flags; tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; @@ -3424,9 +3419,9 @@ set_serial_info(struct e100_serial *info, if ((new_serial.type != info->type) || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) + (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | + info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); goto check_and_exit; } @@ -3440,16 +3435,16 @@ set_serial_info(struct e100_serial *info, */ info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~ASYNC_FLAGS) | + info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait; - info->port.low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { change_speed(info); } else retval = startup(info); @@ -3789,12 +3784,12 @@ rs_close(struct tty_struct *tty, struct file * filp) local_irq_restore(flags); return; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ - if (info->flags & ASYNC_NORMAL_ACTIVE) + if (info->port.flags & ASYNC_NORMAL_ACTIVE) info->normal_termios = tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify @@ -3815,7 +3810,7 @@ rs_close(struct tty_struct *tty, struct file * filp) e100_disable_rx(info); e100_disable_rx_irq(info); - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -3836,7 +3831,7 @@ rs_close(struct tty_struct *tty, struct file * filp) schedule_timeout_interruptible(info->close_delay); wake_up_interruptible(&info->open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); local_irq_restore(flags); @@ -3931,7 +3926,7 @@ rs_hangup(struct tty_struct *tty) shutdown(info); info->event = 0; info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; wake_up_interruptible(&info->open_wait); } @@ -3955,11 +3950,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { + (info->port.flags & ASYNC_CLOSING)) { wait_event_interruptible_tty(tty, info->close_wait, - !(info->flags & ASYNC_CLOSING)); + !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -3974,7 +3969,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -4010,9 +4005,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { + !(info->port.flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -4021,7 +4016,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } - if (!(info->flags & ASYNC_CLOSING) && do_clocal) + if (!(info->port.flags & ASYNC_CLOSING) && do_clocal) /* && (do_clocal || DCD_IS_ASSERTED) */ break; if (signal_pending(current)) { @@ -4047,7 +4042,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -4088,17 +4083,17 @@ rs_open(struct tty_struct *tty, struct file * filp) tty->driver_data = info; info->port.tty = tty; - info->port.low_latency = !!(info->flags & ASYNC_LOW_LATENCY); + info->port.low_latency = !!(info->port.flags & ASYNC_LOW_LATENCY); /* * If the port is in the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { + (info->port.flags & ASYNC_CLOSING)) { wait_event_interruptible_tty(tty, info->close_wait, - !(info->flags & ASYNC_CLOSING)); + !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? + return ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -4198,7 +4193,7 @@ rs_open(struct tty_struct *tty, struct file * filp) return retval; } - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if ((info->count == 1) && (info->port.flags & ASYNC_SPLIT_TERMIOS)) { tty->termios = info->normal_termios; change_speed(info); } @@ -4447,7 +4442,6 @@ static int __init rs_init(void) info->forced_eop = 0; info->baud_base = DEF_BAUD_BASE; info->custom_divisor = 0; - info->flags = 0; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; info->x_char = 0; diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index ea0beb46a10..7146ed29d50 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -53,8 +53,6 @@ struct e100_serial { volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */ volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */ - int flags; /* defined in tty.h */ - u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ u8 iseteop; /* bit number for R_SET_EOP for the input dma */ -- cgit v1.2.3-18-g5258 From 12aad550f005fef2dd0ad40e4978549f64f8f296 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:35 +0100 Subject: crisv10: stop returning info from handle_ser_rx_interrupt The return value is not used anywhere, so no need to return anything. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 2ae378cc65b..ec2dcc72638 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -2258,8 +2258,7 @@ TODO: The break will be delayed until an F or V character is received. */ -static -struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info) +static void handle_ser_rx_interrupt_no_dma(struct e100_serial *info) { unsigned long data_read; @@ -2365,10 +2364,9 @@ more_data: } tty_flip_buffer_push(&info->port); - return info; } -static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) +static void handle_ser_rx_interrupt(struct e100_serial *info) { unsigned char rstat; @@ -2377,7 +2375,8 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) #endif /* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ if (!info->uses_dma_in) { - return handle_ser_rx_interrupt_no_dma(info); + handle_ser_rx_interrupt_no_dma(info); + return; } /* DMA is used */ rstat = info->ioport[REG_STATUS]; @@ -2484,7 +2483,6 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); START_FLUSH_FAST_TIMER(info, "ser_int"); - return info; } /* handle_ser_rx_interrupt */ static void handle_ser_tx_interrupt(struct e100_serial *info) -- cgit v1.2.3-18-g5258 From 82c3b87b7e0153c5a05ac994cd5586df41264cb6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:37 +0100 Subject: crisv10: remove unused members Well, all those are unused. They were perhaps copied from generic serial structure ages ago. Remove them for good. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index 7146ed29d50..59f70c4cc86 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -86,15 +86,10 @@ struct e100_serial { volatile int tr_running; /* 1 if output is running */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; int x_char; /* xon/xoff character */ int close_delay; unsigned short closing_wait; - unsigned short closing_wait2; unsigned long event; - unsigned long last_active; int line; int type; /* PORT_ETRAX */ int count; /* # of fd on device */ @@ -108,7 +103,6 @@ struct e100_serial { struct work_struct work; struct async_icount icount; /* error-statistics etc.*/ struct ktermios normal_termios; - struct ktermios callout_termios; wait_queue_head_t open_wait; wait_queue_head_t close_wait; -- cgit v1.2.3-18-g5258 From 892c7cfc16f4379e04983f2b58cdfc35f486d269 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:38 +0100 Subject: crisv10: use close delays from tty_port The same as flags, convert to using close delays from tty_port. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 20 +++++++++----------- drivers/tty/serial/crisv10.h | 2 -- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index ec2dcc72638..0e75ec331c0 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3392,8 +3392,8 @@ get_serial_info(struct e100_serial * info, tmp.irq = info->irq; tmp.flags = info->port.flags; tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; tmp.custom_divisor = info->custom_divisor; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -3415,7 +3415,7 @@ set_serial_info(struct e100_serial *info, if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || + (new_serial.close_delay != info->port.close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; @@ -3437,8 +3437,8 @@ set_serial_info(struct e100_serial *info, (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; + info->port.close_delay = new_serial.close_delay; + info->port.closing_wait = new_serial.closing_wait; info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: @@ -3794,8 +3794,8 @@ rs_close(struct tty_struct *tty, struct file * filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the serial receiver and the DMA receive interrupt. @@ -3825,8 +3825,8 @@ rs_close(struct tty_struct *tty, struct file * filp) info->event = 0; info->port.tty = NULL; if (info->blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); + if (info->port.close_delay) + schedule_timeout_interruptible(info->port.close_delay); wake_up_interruptible(&info->open_wait); } info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -4440,8 +4440,6 @@ static int __init rs_init(void) info->forced_eop = 0; info->baud_base = DEF_BAUD_BASE; info->custom_divisor = 0; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; info->x_char = 0; info->event = 0; info->count = 0; diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index 59f70c4cc86..a05c36b1a2a 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -87,8 +87,6 @@ struct e100_serial { volatile int tr_running; /* 1 if output is running */ int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; unsigned long event; int line; int type; /* PORT_ETRAX */ -- cgit v1.2.3-18-g5258 From 4aeaeb0c39c6d0cca78d829c4fe2042e0d8d595d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:39 +0100 Subject: crisv10: use *_wait from tty_port The same as flags, convert to using *_wait queues from tty_port. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 16 +++++++--------- drivers/tty/serial/crisv10.h | 2 -- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 0e75ec331c0..ef5bca11842 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3827,10 +3827,10 @@ rs_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->port.close_delay) schedule_timeout_interruptible(info->port.close_delay); - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + wake_up_interruptible(&info->port.close_wait); local_irq_restore(flags); /* port closed */ @@ -3926,7 +3926,7 @@ rs_hangup(struct tty_struct *tty) info->count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } /* @@ -3949,7 +3949,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->port.flags & ASYNC_HUP_NOTIFY) @@ -3983,7 +3983,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttyS%d, count = %d\n", info->line, info->count); @@ -4030,7 +4030,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, tty_lock(tty); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (extra_count) info->count++; info->blocked_open--; @@ -4088,7 +4088,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->port.flags & ASYNC_HUP_NOTIFY) ? @@ -4445,8 +4445,6 @@ static int __init rs_init(void) info->count = 0; info->blocked_open = 0; info->normal_termios = driver->init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); info->xmit.buf = NULL; info->xmit.tail = info->xmit.head = 0; info->first_recv_buffer = info->last_recv_buffer = NULL; diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index a05c36b1a2a..1cd22998e6b 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -101,8 +101,6 @@ struct e100_serial { struct work_struct work; struct async_icount icount; /* error-statistics etc.*/ struct ktermios normal_termios; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; unsigned long char_time_usec; /* The time for 1 char, in usecs */ unsigned long flush_time_usec; /* How often we should flush */ -- cgit v1.2.3-18-g5258 From b12d8dc2dbe2d2d1d6eec314d586b1eed75756dc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:40 +0100 Subject: crisv10: use counts from tty_port The same as flags, convert to using open/close counts from tty_port. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 48 +++++++++++++++++++++----------------------- drivers/tty/serial/crisv10.h | 2 -- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index ef5bca11842..477f22f773f 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3424,7 +3424,7 @@ set_serial_info(struct e100_serial *info, goto check_and_exit; } - if (info->count > 1) + if (info->port.count > 1) return -EBUSY; /* @@ -3760,7 +3760,7 @@ rs_close(struct tty_struct *tty, struct file * filp) printk("[%d] rs_close ttyS%d, count = %d\n", current->pid, info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (info->port.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -3770,15 +3770,15 @@ rs_close(struct tty_struct *tty, struct file * filp) */ printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; + "info->count is %d\n", info->port.count); + info->port.count = 1; } - if (--info->count < 0) { + if (--info->port.count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, info->port.count); + info->port.count = 0; } - if (info->count) { + if (info->port.count) { local_irq_restore(flags); return; } @@ -3824,7 +3824,7 @@ rs_close(struct tty_struct *tty, struct file * filp) tty->closing = 0; info->event = 0; info->port.tty = NULL; - if (info->blocked_open) { + if (info->port.blocked_open) { if (info->port.close_delay) schedule_timeout_interruptible(info->port.close_delay); wake_up_interruptible(&info->port.open_wait); @@ -3923,7 +3923,7 @@ rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(info); info->event = 0; - info->count = 0; + info->port.count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; wake_up_interruptible(&info->port.open_wait); @@ -3978,7 +3978,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->port.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ @@ -3986,15 +3986,15 @@ block_til_ready(struct tty_struct *tty, struct file * filp, add_wait_queue(&info->port.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif local_irq_save(flags); if (!tty_hung_up_p(filp)) { extra_count++; - info->count--; + info->port.count--; } local_irq_restore(flags); - info->blocked_open++; + info->port.blocked_open++; while (1) { local_irq_save(flags); /* assert RTS and DTR */ @@ -4023,7 +4023,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif tty_unlock(tty); schedule(); @@ -4032,11 +4032,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp, set_current_state(TASK_RUNNING); remove_wait_queue(&info->port.open_wait, &wait); if (extra_count) - info->count++; - info->blocked_open--; + info->port.count++; + info->port.blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif if (retval) return retval; @@ -4074,10 +4074,10 @@ rs_open(struct tty_struct *tty, struct file * filp) #ifdef SERIAL_DEBUG_OPEN printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name, - info->count); + info->port.count); #endif - info->count++; + info->port.count++; tty->driver_data = info; info->port.tty = tty; @@ -4101,7 +4101,7 @@ rs_open(struct tty_struct *tty, struct file * filp) /* * If DMA is enabled try to allocate the irq's. */ - if (info->count == 1) { + if (info->port.count == 1) { allocated_resources = 1; if (info->dma_in_enabled) { if (request_irq(info->dma_in_irq_nbr, @@ -4174,7 +4174,7 @@ rs_open(struct tty_struct *tty, struct file * filp) if (allocated_resources) deinit_port(info); - /* FIXME Decrease count info->count here too? */ + /* FIXME Decrease count info->port.count here too? */ return retval; } @@ -4191,7 +4191,7 @@ rs_open(struct tty_struct *tty, struct file * filp) return retval; } - if ((info->count == 1) && (info->port.flags & ASYNC_SPLIT_TERMIOS)) { + if ((info->port.count == 1) && (info->port.flags & ASYNC_SPLIT_TERMIOS)) { tty->termios = info->normal_termios; change_speed(info); } @@ -4442,8 +4442,6 @@ static int __init rs_init(void) info->custom_divisor = 0; info->x_char = 0; info->event = 0; - info->count = 0; - info->blocked_open = 0; info->normal_termios = driver->init_termios; info->xmit.buf = NULL; info->xmit.tail = info->xmit.head = 0; diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index 1cd22998e6b..7599014ae03 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -90,8 +90,6 @@ struct e100_serial { unsigned long event; int line; int type; /* PORT_ETRAX */ - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ struct circ_buf xmit; struct etrax_recv_buffer *first_recv_buffer; struct etrax_recv_buffer *last_recv_buffer; -- cgit v1.2.3-18-g5258 From 8bde9658a0e6a7098dcda1ce6ea6b278029644b4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Mar 2013 15:55:48 +0100 Subject: TTY: clean up port shutdown Untangle port-shutdown logic and make sure the initialised flag is always cleared for non-console ports. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 7f38eeaafac..2aea2f91e27 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -199,9 +199,14 @@ EXPORT_SYMBOL(tty_port_tty_set); static void tty_port_shutdown(struct tty_port *port) { mutex_lock(&port->mutex); - if (port->ops->shutdown && !port->console && - test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) + if (port->console) + goto out; + + if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (port->ops->shutdown) port->ops->shutdown(port); + } +out: mutex_unlock(&port->mutex); } -- cgit v1.2.3-18-g5258 From 31ca020b57f7c08da187613cf20becd664c467fa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Mar 2013 15:55:49 +0100 Subject: TTY: wake up processes last at hangup Move wake up of processes on blocked-open and modem-status wait queues to after port shutdown at hangup. This way the woken up processes can use the ASYNC_INITIALIZED flag to detect port shutdown. Note that this is the order currently used by serial-core. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 2aea2f91e27..73bc1d989d3 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -231,9 +231,9 @@ void tty_port_hangup(struct tty_port *port) } port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); + tty_port_shutdown(port); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); - tty_port_shutdown(port); } EXPORT_SYMBOL(tty_port_hangup); -- cgit v1.2.3-18-g5258 From e584a02cf517537a3d13c7f85ced45fd07ab85cd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Mar 2013 15:55:50 +0100 Subject: TTY: fix DTR being raised on hang up Make sure to check ASYNC_INITIALISED before raising DTR when waking up from blocked open in tty_port_block_til_ready. Currently DTR could get raised at hang up as a blocked process would raise DTR unconditionally before checking for hang up and returning. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 73bc1d989d3..7e3eaf4eb9f 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -388,7 +388,7 @@ int tty_port_block_til_ready(struct tty_port *port, while (1) { /* Indicate we are open */ - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); -- cgit v1.2.3-18-g5258 From 957dacaee56d18e5c2cbe722429de5a746a3cf44 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Mar 2013 15:55:51 +0100 Subject: TTY: fix DTR not being dropped on hang up Move HUPCL handling to port shutdown so that DTR is dropped also on hang up (tty_port_close is a noop for hung-up ports). Also do not try to drop DTR for uninitialised ports where it has never been raised (e.g. after a failed open). Note that this is also the current behaviour of serial-core. Nine drivers currently call tty_port_close_start directly (rather than through tty_port_close) and seven of them lower DTR as part of their close (if the port has been initialised). Fixup the remaining two drivers so that it continues to be lowered also on normal (non-HUP) close. [ Note that most of those other seven drivers did not expect DTR to have been dropped by tty_port_close_start in the first place. ] Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/mxser.c | 4 ++++ drivers/tty/n_gsm.c | 4 ++++ drivers/tty/tty_port.c | 27 +++++++++++++++------------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 484b6a3c9b0..d996038eacf 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -1084,6 +1084,10 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) mutex_lock(&port->mutex); mxser_close_port(port); mxser_flush_buffer(tty); + if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (C_HUPCL(tty)) + tty_port_lower_dtr_rts(port); + } mxser_shutdown_port(port); clear_bit(ASYNCB_INITIALIZED, &port->flags); mutex_unlock(&port->mutex); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 74d9a0258d7..642239015b4 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2964,6 +2964,10 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) if (tty_port_close_start(&dlci->port, tty, filp) == 0) goto out; gsm_dlci_begin_close(dlci); + if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { + if (C_HUPCL(tty)) + tty_port_lower_dtr_rts(&dlci->port); + } tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); out: diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 7e3eaf4eb9f..0af8d9aa5b0 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -196,13 +196,20 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); -static void tty_port_shutdown(struct tty_port *port) +static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) { mutex_lock(&port->mutex); if (port->console) goto out; if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + /* + * Drop DTR/RTS if HUPCL is set. This causes any attached + * modem to hang up the line. + */ + if (tty && C_HUPCL(tty)) + tty_port_lower_dtr_rts(port); + if (port->ops->shutdown) port->ops->shutdown(port); } @@ -220,18 +227,19 @@ out: void tty_port_hangup(struct tty_port *port) { + struct tty_struct *tty; unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->count = 0; port->flags &= ~ASYNC_NORMAL_ACTIVE; - if (port->tty) { - set_bit(TTY_IO_ERROR, &port->tty->flags); - tty_kref_put(port->tty); - } + tty = port->tty; + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); - tty_port_shutdown(port); + tty_port_shutdown(port, tty); + tty_kref_put(tty); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); } @@ -485,11 +493,6 @@ int tty_port_close_start(struct tty_port *port, /* Flush the ldisc buffering */ tty_ldisc_flush(tty); - /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to - hang up the line */ - if (tty->termios.c_cflag & HUPCL) - tty_port_lower_dtr_rts(port); - /* Don't call port->drop for the last reference. Callers will want to drop the last active reference in ->shutdown() or the tty shutdown path */ @@ -524,7 +527,7 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, { if (tty_port_close_start(port, tty, filp) == 0) return; - tty_port_shutdown(port); + tty_port_shutdown(port, tty); set_bit(TTY_IO_ERROR, &tty->flags); tty_port_close_end(port, tty); tty_port_tty_set(port, NULL); -- cgit v1.2.3-18-g5258 From b74414f5f3227d9db309bfaaea3ae889af01430a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Mar 2013 15:55:52 +0100 Subject: TTY: clean up port drain-delay handling Move port drain-delay handling to a separate function. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 0af8d9aa5b0..20426ccbd58 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -441,6 +441,20 @@ int tty_port_block_til_ready(struct tty_port *port, } EXPORT_SYMBOL(tty_port_block_til_ready); +static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) +{ + unsigned int bps = tty_get_baud_rate(tty); + long timeout; + + if (bps > 1200) { + timeout = (HZ * 10 * port->drain_delay) / bps; + timeout = max_t(long, timeout, HZ / 10); + } else { + timeout = 2 * HZ; + } + schedule_timeout_interruptible(timeout); +} + int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) { @@ -479,17 +493,8 @@ int tty_port_close_start(struct tty_port *port, if (test_bit(ASYNCB_INITIALIZED, &port->flags) && port->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent_from_close(tty, port->closing_wait); - if (port->drain_delay) { - unsigned int bps = tty_get_baud_rate(tty); - long timeout; - - if (bps > 1200) - timeout = max_t(long, - (HZ * 10 * port->drain_delay) / bps, HZ / 10); - else - timeout = 2 * HZ; - schedule_timeout_interruptible(timeout); - } + if (port->drain_delay) + tty_port_drain_delay(port, tty); /* Flush the ldisc buffering */ tty_ldisc_flush(tty); -- cgit v1.2.3-18-g5258 From 0b2588cadf9f131614cb251e34f7be1f4e1a2e08 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Mar 2013 15:55:53 +0100 Subject: TTY: fix close of uninitialised ports Make sure we do not make tty-driver callbacks or wait for port to drain on uninitialised ports (e.g. when open failed) in tty_port_close_start(). No callback, such as flush_buffer or wait_until_sent, needs to be made on a port that has never been opened. Neither does it make much sense to add drain delay for an uninitialised port. Currently a drain delay of up to two seconds could be added when a tty fails to open. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 20426ccbd58..969c3e675a7 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -487,14 +487,16 @@ int tty_port_close_start(struct tty_port *port, set_bit(ASYNCB_CLOSING, &port->flags); tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); - /* Don't block on a stalled port, just pull the chain */ - if (tty->flow_stopped) - tty_driver_flush_buffer(tty); - if (test_bit(ASYNCB_INITIALIZED, &port->flags) && - port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent_from_close(tty, port->closing_wait); - if (port->drain_delay) - tty_port_drain_delay(port, tty); + + if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + /* Don't block on a stalled port, just pull the chain */ + if (tty->flow_stopped) + tty_driver_flush_buffer(tty); + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent_from_close(tty, port->closing_wait); + if (port->drain_delay) + tty_port_drain_delay(port, tty); + } /* Flush the ldisc buffering */ tty_ldisc_flush(tty); -- cgit v1.2.3-18-g5258 From c47ddc26db389e046c1414c78ac4a6016c7df500 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Mar 2013 18:44:49 +0100 Subject: tty: max3100: Use dev_pm_ops Use dev_pm_ops instead of the deprecated legacy suspend/resume for the max3100 driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 32517d4bcea..57da9bbaaab 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -849,11 +849,11 @@ static int max3100_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int max3100_suspend(struct spi_device *spi, pm_message_t state) +static int max3100_suspend(struct device *dev) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = dev_get_drvdata(dev); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -874,9 +874,9 @@ static int max3100_suspend(struct spi_device *spi, pm_message_t state) return 0; } -static int max3100_resume(struct spi_device *spi) +static int max3100_resume(struct device *dev) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = dev_get_drvdata(dev); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -894,21 +894,21 @@ static int max3100_resume(struct spi_device *spi) return 0; } +static SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); +#define MAX3100_PM_OPS (&max3100_pm_ops) + #else -#define max3100_suspend NULL -#define max3100_resume NULL +#define MAX3100_PM_OPS NULL #endif static struct spi_driver max3100_driver = { .driver = { .name = "max3100", .owner = THIS_MODULE, + .pm = MAX3100_PM_OPS, }, - .probe = max3100_probe, .remove = max3100_remove, - .suspend = max3100_suspend, - .resume = max3100_resume, }; module_spi_driver(max3100_driver); -- cgit v1.2.3-18-g5258 From b7df719db7326d51a3145bb0cfc277aeb50763c3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Mar 2013 18:44:50 +0100 Subject: tty: max310x: Use dev_pm_ops Use dev_pm_ops instead of the deprecated legacy suspend/resume for the max310x driver. Cc: Alexander Shiyan Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 0c2422cb04e..8941e641894 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -881,12 +881,14 @@ static struct uart_ops max310x_ops = { .verify_port = max310x_verify_port, }; -static int max310x_suspend(struct spi_device *spi, pm_message_t state) +#ifdef CONFIG_PM_SLEEP + +static int max310x_suspend(struct device *dev) { int ret; - struct max310x_port *s = dev_get_drvdata(&spi->dev); + struct max310x_port *s = dev_get_drvdata(dev); - dev_dbg(&spi->dev, "Suspend\n"); + dev_dbg(dev, "Suspend\n"); ret = uart_suspend_port(&s->uart, &s->port); @@ -905,11 +907,11 @@ static int max310x_suspend(struct spi_device *spi, pm_message_t state) return ret; } -static int max310x_resume(struct spi_device *spi) +static int max310x_resume(struct device *dev) { - struct max310x_port *s = dev_get_drvdata(&spi->dev); + struct max310x_port *s = dev_get_drvdata(dev); - dev_dbg(&spi->dev, "Resume\n"); + dev_dbg(dev, "Resume\n"); if (s->pdata->suspend) s->pdata->suspend(0); @@ -928,6 +930,13 @@ static int max310x_resume(struct spi_device *spi) return uart_resume_port(&s->uart, &s->port); } +static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); +#define MAX310X_PM_OPS (&max310x_pm_ops) + +#else +#define MAX310X_PM_OPS NULL +#endif + #ifdef CONFIG_GPIOLIB static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) { @@ -1242,11 +1251,10 @@ static struct spi_driver max310x_driver = { .driver = { .name = "max310x", .owner = THIS_MODULE, + .pm = MAX310X_PM_OPS, }, .probe = max310x_probe, .remove = max310x_remove, - .suspend = max310x_suspend, - .resume = max310x_resume, .id_table = max310x_id_table, }; module_spi_driver(max310x_driver); -- cgit v1.2.3-18-g5258 From c2ee91bd6644324160ba9e090e3a017d62abc0b4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Mar 2013 18:44:51 +0100 Subject: tty: mrst_max3110: Use dev_pm_ops Use dev_pm_ops instead of the deprecated legacy suspend/resume for the mrst_max3110 driver. Cc: Alan Cox Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index f641c232bec..9b6ef20420c 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -743,9 +743,10 @@ static struct uart_driver serial_m3110_reg = { .cons = &serial_m3110_console, }; -#ifdef CONFIG_PM -static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int serial_m3110_suspend(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct uart_max3110 *max = spi_get_drvdata(spi); disable_irq(max->irq); @@ -754,8 +755,9 @@ static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) return 0; } -static int serial_m3110_resume(struct spi_device *spi) +static int serial_m3110_resume(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct uart_max3110 *max = spi_get_drvdata(spi); max3110_out(max, max->cur_conf); @@ -763,9 +765,13 @@ static int serial_m3110_resume(struct spi_device *spi) enable_irq(max->irq); return 0; } + +static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend, + serial_m3110_resume); +#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops) + #else -#define serial_m3110_suspend NULL -#define serial_m3110_resume NULL +#define SERIAL_M3110_PM_OPS NULL #endif static int serial_m3110_probe(struct spi_device *spi) @@ -872,11 +878,10 @@ static struct spi_driver uart_max3110_driver = { .driver = { .name = "spi_max3111", .owner = THIS_MODULE, + .pm = SERIAL_M3110_PM_OPS, }, .probe = serial_m3110_probe, .remove = serial_m3110_remove, - .suspend = serial_m3110_suspend, - .resume = serial_m3110_resume, }; static int __init serial_m3110_init(void) -- cgit v1.2.3-18-g5258 From 91debb0383d6564e0dc8ae76181f6daf8e24717a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Mar 2013 18:44:52 +0100 Subject: tty: ifx6x60: Remove unused suspend/resume callbacks The ifx6x60 driver implements both legacy suspend/resume callbacks and dev_pm_ops. The SPI core is going to ignore legacy suspend/resume callbacks if a driver implements dev_pm_ops. Since the legacy suspend/resume callbacks are empty in this case it is safe to just remove them. Cc: Bi Chao Cc: Chen Jun Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 2c77fed31a7..8b1534c424a 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1278,30 +1278,6 @@ static void ifx_spi_spi_shutdown(struct spi_device *spi) * no hardware to save state for */ -/** - * ifx_spi_spi_suspend - suspend SPI on system suspend - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return 0; -} - -/** - * ifx_spi_spi_resume - resume SPI side on system resume - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_resume(struct spi_device *spi) -{ - return 0; -} - /** * ifx_spi_pm_suspend - suspend modem on system suspend * @dev: device being suspended @@ -1391,8 +1367,6 @@ static struct spi_driver ifx_spi_driver = { .probe = ifx_spi_spi_probe, .shutdown = ifx_spi_spi_shutdown, .remove = ifx_spi_spi_remove, - .suspend = ifx_spi_spi_suspend, - .resume = ifx_spi_spi_resume, .id_table = ifx_id_table }; -- cgit v1.2.3-18-g5258 From 21622939fc452c7fb739464b8e49368c3ceaa0ee Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:21 -0400 Subject: tty: Add diagnostic for halted line discipline Flip buffer work must not be scheduled by the line discipline after the line discipline has been halted; issue warning. Note: drivers can still schedule flip buffer work. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 8 ++++++++ drivers/tty/tty_ldisc.c | 7 ++++++- include/linux/tty.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 68865d9af8a..16793eccc6a 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -153,6 +153,12 @@ static void n_tty_set_room(struct tty_struct *tty) if (left && !old_left) { WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); + /* see if ldisc has been killed - if so, this means that + * even though the ldisc has been halted and ->buf.work + * cancelled, ->buf.work is about to be rescheduled + */ + WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), + "scheduling buffer work for halted ldisc\n"); schedule_work(&tty->port->buf.work); } } @@ -1624,6 +1630,8 @@ static int n_tty_open(struct tty_struct *tty) goto err_free_bufs; tty->disc_data = ldata; + /* indicate buffer work may resume */ + clear_bit(TTY_LDISC_HALTED, &tty->flags); reset_buffer_flags(tty); tty_unthrottle(tty); ldata->column = 0; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index d794087c327..c641321b940 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -375,6 +375,7 @@ static inline void tty_ldisc_put(struct tty_ldisc *ld) void tty_ldisc_enable(struct tty_struct *tty) { + clear_bit(TTY_LDISC_HALTED, &tty->flags); set_bit(TTY_LDISC, &tty->flags); clear_bit(TTY_LDISC_CHANGING, &tty->flags); wake_up(&tty_ldisc_wait); @@ -513,8 +514,11 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) static int tty_ldisc_halt(struct tty_struct *tty) { + int scheduled; clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->port->buf.work); + scheduled = cancel_work_sync(&tty->port->buf.work); + set_bit(TTY_LDISC_HALTED, &tty->flags); + return scheduled; } /** @@ -820,6 +824,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) clear_bit(TTY_LDISC, &tty->flags); tty_unlock(tty); cancel_work_sync(&tty->port->buf.work); + set_bit(TTY_LDISC_HALTED, &tty->flags); mutex_unlock(&tty->ldisc_mutex); retry: tty_lock(tty); diff --git a/include/linux/tty.h b/include/linux/tty.h index d3548f87196..66ae020e8a9 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -315,6 +315,7 @@ struct tty_file_private { #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ #define TTY_HUPPED 18 /* Post driver->hangup() */ #define TTY_HUPPING 21 /* ->hangup() in progress */ +#define TTY_LDISC_HALTED 22 /* Line discipline is halted */ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) -- cgit v1.2.3-18-g5258 From a30737ab7d99f27810b254787e5e62a6c92cb355 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:22 -0400 Subject: n_tty: Factor packet mode status change for reuse Factor the packet mode status change from n_tty_flush_buffer for use by follow-on patch. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 16793eccc6a..7e7f6514fb5 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -223,6 +223,18 @@ static void reset_buffer_flags(struct tty_struct *tty) n_tty_set_room(tty); } +static void n_tty_packet_mode_flush(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->link->packet) { + tty->ctrl_status |= TIOCPKT_FLUSHREAD; + wake_up_interruptible(&tty->link->read_wait); + } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); +} + /** * n_tty_flush_buffer - clean input queue * @tty: terminal device @@ -237,19 +249,11 @@ static void reset_buffer_flags(struct tty_struct *tty) static void n_tty_flush_buffer(struct tty_struct *tty) { - unsigned long flags; /* clear everything and unthrottle the driver */ reset_buffer_flags(tty); - if (!tty->link) - return; - - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->link->packet) { - tty->ctrl_status |= TIOCPKT_FLUSHREAD; - wake_up_interruptible(&tty->link->read_wait); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (tty->link) + n_tty_packet_mode_flush(tty); } /** -- cgit v1.2.3-18-g5258 From 79901317ce80c43a0249ccc6e3fea9d0968e159e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:23 -0400 Subject: n_tty: Don't flush buffer when closing ldisc A buffer flush is both undesirable and unnecessary when the ldisc is closing. A buffer flush performs the following: 1. resets ldisc data fields to their initial state 2. resets tty->receive_room to indicate more data can be sent 3. schedules buffer work to receive more data 4. signals a buffer flush has happened to linked pty in packet mode Since the ldisc has been halted and the tty may soon be destructed, buffer work must not be scheduled as that work might access an invalid tty and ldisc state. Also, the ldisc read buffer is about to be freed, so that's pointless. Resetting the ldisc data fields is pointless as well since that structure is about to be freed. Resetting tty->receive_room is unnecessary, as it will be properly reset if a new ldisc is reopened. Besides, resetting the original receive_room value would be wrong since the read buffer will be gone. Since the packet mode flush is observable from userspace, this behavior has been preserved. The test jig originally authored by Ilya Zykov and signed off by him is included below. The test jig prompts the following warnings which this patch fixes. [ 38.051111] ------------[ cut here ]------------ [ 38.052113] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room.part.6+0x8b/0xa0() [ 38.053916] Hardware name: Bochs [ 38.054819] Modules linked in: netconsole configfs bnep rfcomm bluetooth parport_pc ppdev snd_hda_intel snd_hda_codec snd_hwdep snd_pcm snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq psmouse snd_timer serio_raw mac_hid snd_seq_device snd microcode lp parport virtio_balloon soundcore i2c_piix4 snd_page_alloc floppy 8139too 8139cp [ 38.059704] Pid: 1564, comm: pty_kill Tainted: G W 3.7.0-next-20121130+ttydebug-xeon #20121130+ttydebug [ 38.061578] Call Trace: [ 38.062491] [] warn_slowpath_common+0x7f/0xc0 [ 38.063448] [] warn_slowpath_null+0x1a/0x20 [ 38.064439] [] n_tty_set_room.part.6+0x8b/0xa0 [ 38.065381] [] n_tty_set_room+0x42/0x80 [ 38.066323] [] reset_buffer_flags+0x102/0x160 [ 38.077508] [] n_tty_flush_buffer+0x1d/0x90 [ 38.078782] [] ? default_spin_lock_flags+0x9/0x10 [ 38.079734] [] n_tty_close+0x24/0x60 [ 38.080730] [] tty_ldisc_close.isra.2+0x41/0x60 [ 38.081680] [] tty_ldisc_kill+0x3b/0x80 [ 38.082618] [] tty_ldisc_release+0x77/0xe0 [ 38.083549] [] tty_release+0x451/0x4d0 [ 38.084525] [] __fput+0xae/0x230 [ 38.085472] [] ____fput+0xe/0x10 [ 38.086401] [] task_work_run+0xc8/0xf0 [ 38.087334] [] do_exit+0x196/0x4b0 [ 38.088304] [] ? __dequeue_signal+0x6b/0xb0 [ 38.089240] [] do_group_exit+0x44/0xa0 [ 38.090182] [] get_signal_to_deliver+0x20d/0x4e0 [ 38.091125] [] do_signal+0x29/0x130 [ 38.092096] [] ? tty_ldisc_deref+0xe/0x10 [ 38.093030] [] ? tty_write+0xb7/0xf0 [ 38.093976] [] ? vfs_write+0xb3/0x180 [ 38.094904] [] do_notify_resume+0x80/0xc0 [ 38.095830] [] int_signal+0x12/0x17 [ 38.096788] ---[ end trace 5f6f7a9651cd999b ]--- [ 2730.570602] ------------[ cut here ]------------ [ 2730.572130] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0x107/0x140() [ 2730.574904] scheduling buffer work for halted ldisc [ 2730.578303] Pid: 9691, comm: trinity-child15 Tainted: G W 3.7.0-rc8-next-20121205-sasha-00023-g59f0d85 #207 [ 2730.588694] Call Trace: [ 2730.590486] [] ? n_tty_set_room+0x107/0x140 [ 2730.592559] [] warn_slowpath_common+0x87/0xb0 [ 2730.595317] [] warn_slowpath_fmt+0x41/0x50 [ 2730.599186] [] n_tty_set_room+0x107/0x140 [ 2730.603141] [] reset_buffer_flags+0x137/0x150 [ 2730.607166] [] n_tty_flush_buffer+0x18/0x90 [ 2730.610123] [] n_tty_close+0x1f/0x60 [ 2730.612068] [] tty_ldisc_close.isra.4+0x52/0x60 [ 2730.614078] [] tty_ldisc_reinit+0x3b/0x70 [ 2730.615891] [] tty_ldisc_hangup+0x102/0x1e0 [ 2730.617780] [] __tty_hangup+0x137/0x440 [ 2730.619547] [] tty_vhangup+0x9/0x10 [ 2730.621266] [] pty_close+0x14c/0x160 [ 2730.622952] [] tty_release+0xd5/0x490 [ 2730.624674] [] __fput+0x122/0x250 [ 2730.626195] [] ____fput+0x9/0x10 [ 2730.627758] [] task_work_run+0xb2/0xf0 [ 2730.629491] [] do_exit+0x36d/0x580 [ 2730.631159] [] do_group_exit+0x8a/0xc0 [ 2730.632819] [] get_signal_to_deliver+0x501/0x5b0 [ 2730.634758] [] do_signal+0x24/0x100 [ 2730.636412] [] ? user_exit+0xa5/0xd0 [ 2730.638078] [] ? trace_hardirqs_on_caller+0x118/0x140 [ 2730.640279] [] ? trace_hardirqs_on+0xd/0x10 [ 2730.642164] [] do_notify_resume+0x48/0xa0 [ 2730.643966] [] int_signal+0x12/0x17 [ 2730.645672] ---[ end trace a40d53149c07fce0 ]--- /* * pty_thrash.c * * Based on original test jig by Ilya Zykov * * Signed-off-by: Peter Hurley * Signed-off-by: Ilya Zykov */ static int fd; static void error_exit(char *f, ...) { va_list va; va_start(va, f); vprintf(f, va); printf(": %s\n", strerror(errno)); va_end(va); if (fd >= 0) close(fd); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int parent; char pts_name[24]; int ptn, unlock; while (1) { fd = open("/dev/ptmx", O_RDWR); if (fd < 0) error_exit("opening pty master"); unlock = 0; if (ioctl(fd, TIOCSPTLCK, &unlock) < 0) error_exit("unlocking pty pair"); if (ioctl(fd, TIOCGPTN, &ptn) < 0) error_exit("getting pty #"); snprintf(pts_name, sizeof(pts_name), "/dev/pts/%d", ptn); child_id = fork(); if (child_id == -1) error_exit("forking child"); if (parent) { int err, id, status; char buf[128]; int n; n = read(fd, buf, sizeof(buf)); if (n < 0) error_exit("master reading"); printf("%.*s\n", n-1, buf); close(fd); err = kill(child_id, SIGKILL); if (err < 0) error_exit("killing child"); id = waitpid(child_id, &status, 0); if (id < 0 || id != child_id) error_exit("waiting for child"); } else { /* Child */ close(fd); printf("Test cycle on slave pty %s\n", pts_name); fd = open(pts_name, O_RDWR); if (fd < 0) error_exit("opening pty slave"); while (1) { char pattern[] = "test\n"; if (write(fd, pattern, strlen(pattern)) < 0) error_exit("slave writing"); } } } /* never gets here */ return 0; } Reported-by: Sasha Levin Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 7e7f6514fb5..15f7195f4b4 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1596,7 +1596,9 @@ static void n_tty_close(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - n_tty_flush_buffer(tty); + if (tty->link) + n_tty_packet_mode_flush(tty); + kfree(ldata->read_buf); kfree(ldata->echo_buf); kfree(ldata); -- cgit v1.2.3-18-g5258 From 168942c9fa01916173a7b72ac67e1d218d571013 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:24 -0400 Subject: tty: Refactor wait for ldisc refs out of tty_ldisc_hangup() Refactor tty_ldisc_hangup() to extract standalone function, tty_ldisc_hangup_wait_idle(), to wait for ldisc references to be released. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 54 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index c641321b940..c5b848a78e4 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -550,6 +550,41 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) return ret > 0 ? 0 : -EBUSY; } +/** + * tty_ldisc_hangup_wait_idle - wait for the ldisc to become idle + * @tty: tty to wait for + * + * Wait for the line discipline to become idle. The discipline must + * have been halted for this to guarantee it remains idle. + * + * Caller must hold legacy and ->ldisc_mutex. + */ +static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty) +{ + while (tty->ldisc) { /* Not yet closed */ + if (atomic_read(&tty->ldisc->users) != 1) { + char cur_n[TASK_COMM_LEN], tty_n[64]; + long timeout = 3 * HZ; + tty_unlock(tty); + + while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { + timeout = MAX_SCHEDULE_TIMEOUT; + printk_ratelimited(KERN_WARNING + "%s: waiting (%s) for %s took too long, but we keep waiting...\n", + __func__, get_task_comm(cur_n, current), + tty_name(tty, tty_n)); + } + /* must reacquire both locks and preserve lock order */ + mutex_unlock(&tty->ldisc_mutex); + tty_lock(tty); + mutex_lock(&tty->ldisc_mutex); + continue; + } + break; + } + return !!tty->ldisc; +} + /** * tty_set_ldisc - set line discipline * @tty: the terminal to set @@ -826,7 +861,6 @@ void tty_ldisc_hangup(struct tty_struct *tty) cancel_work_sync(&tty->port->buf.work); set_bit(TTY_LDISC_HALTED, &tty->flags); mutex_unlock(&tty->ldisc_mutex); -retry: tty_lock(tty); mutex_lock(&tty->ldisc_mutex); @@ -834,23 +868,7 @@ retry: reopen it. We could defer this to the next open but it means auditing a lot of other paths so this is a FIXME */ - if (tty->ldisc) { /* Not yet closed */ - if (atomic_read(&tty->ldisc->users) != 1) { - char cur_n[TASK_COMM_LEN], tty_n[64]; - long timeout = 3 * HZ; - tty_unlock(tty); - - while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { - timeout = MAX_SCHEDULE_TIMEOUT; - printk_ratelimited(KERN_WARNING - "%s: waiting (%s) for %s took too long, but we keep waiting...\n", - __func__, get_task_comm(cur_n, current), - tty_name(tty, tty_n)); - } - mutex_unlock(&tty->ldisc_mutex); - goto retry; - } - + if (tty_ldisc_hangup_wait_idle(tty)) { if (reset == 0) { if (!tty_ldisc_reinit(tty, tty->termios.c_line)) -- cgit v1.2.3-18-g5258 From 2276ad9765b395577442d6ddf748f72c329234ae Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:25 -0400 Subject: tty: Remove unnecessary re-test of ldisc ref count Since the tty->ldisc is prevented from being changed by tty_set_ldisc() when a tty is being hung up, re-testing the ldisc user count is unnecessary -- ie, it cannot be a different ldisc and the user count cannot have increased (assuming the caller meets the precondition that TTY_LDISC flag is cleared) Removal of the 'early-out' locking optimization is necessary for the subsequent patch 'tty: Fix ldisc halt sequence on hangup'. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index c5b848a78e4..fa0170e1b08 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -558,29 +558,29 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) * have been halted for this to guarantee it remains idle. * * Caller must hold legacy and ->ldisc_mutex. + * + * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently + * with this function by checking the TTY_HUPPING flag. */ static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty) { - while (tty->ldisc) { /* Not yet closed */ - if (atomic_read(&tty->ldisc->users) != 1) { - char cur_n[TASK_COMM_LEN], tty_n[64]; - long timeout = 3 * HZ; - tty_unlock(tty); - - while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { - timeout = MAX_SCHEDULE_TIMEOUT; - printk_ratelimited(KERN_WARNING - "%s: waiting (%s) for %s took too long, but we keep waiting...\n", - __func__, get_task_comm(cur_n, current), - tty_name(tty, tty_n)); - } - /* must reacquire both locks and preserve lock order */ - mutex_unlock(&tty->ldisc_mutex); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - continue; + char cur_n[TASK_COMM_LEN], tty_n[64]; + long timeout = 3 * HZ; + + if (tty->ldisc) { /* Not yet closed */ + tty_unlock(tty); + + while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { + timeout = MAX_SCHEDULE_TIMEOUT; + printk_ratelimited(KERN_WARNING + "%s: waiting (%s) for %s took too long, but we keep waiting...\n", + __func__, get_task_comm(cur_n, current), + tty_name(tty, tty_n)); } - break; + /* must reacquire both locks and preserve lock order */ + mutex_unlock(&tty->ldisc_mutex); + tty_lock(tty); + mutex_lock(&tty->ldisc_mutex); } return !!tty->ldisc; } -- cgit v1.2.3-18-g5258 From 76bc35e78fdf1065ffa2bb62fabe3e8423d378c8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:26 -0400 Subject: tty: Fix ldisc halt sequence on hangup Flip buffer work cannot be cancelled until all outstanding ldisc references have been released. Convert the ldisc ref wait into a full ldisc halt with buffer work cancellation. Note that the legacy mutex is not held while cancelling. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index fa0170e1b08..15667c0fd64 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -551,22 +551,30 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) } /** - * tty_ldisc_hangup_wait_idle - wait for the ldisc to become idle - * @tty: tty to wait for - * - * Wait for the line discipline to become idle. The discipline must - * have been halted for this to guarantee it remains idle. + * tty_ldisc_hangup_halt - halt the line discipline for hangup + * @tty: tty being hung up * + * Shut down the line discipline and work queue for the tty device + * being hungup. Clear the TTY_LDISC flag to ensure no further + * references can be obtained, wait for remaining references to be + * released, and cancel pending buffer work to ensure no more + * data is fed to this ldisc. * Caller must hold legacy and ->ldisc_mutex. * * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently * with this function by checking the TTY_HUPPING flag. + * + * NB: if tty->ldisc is NULL then buffer work does not need to be + * cancelled because it must already have done as a precondition + * of closing the ldisc and setting tty->ldisc to NULL */ -static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty) +static bool tty_ldisc_hangup_halt(struct tty_struct *tty) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; + clear_bit(TTY_LDISC, &tty->flags); + if (tty->ldisc) { /* Not yet closed */ tty_unlock(tty); @@ -577,6 +585,10 @@ static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty) __func__, get_task_comm(cur_n, current), tty_name(tty, tty_n)); } + + cancel_work_sync(&tty->port->buf.work); + set_bit(TTY_LDISC_HALTED, &tty->flags); + /* must reacquire both locks and preserve lock order */ mutex_unlock(&tty->ldisc_mutex); tty_lock(tty); @@ -851,24 +863,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ mutex_lock(&tty->ldisc_mutex); - /* - * this is like tty_ldisc_halt, but we need to give up - * the BTM before calling cancel_work_sync, which may - * need to wait for another function taking the BTM - */ - clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(tty); - cancel_work_sync(&tty->port->buf.work); - set_bit(TTY_LDISC_HALTED, &tty->flags); - mutex_unlock(&tty->ldisc_mutex); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - /* At this point we have a closed ldisc and we want to reopen it. We could defer this to the next open but it means auditing a lot of other paths so this is a FIXME */ - if (tty_ldisc_hangup_wait_idle(tty)) { + if (tty_ldisc_hangup_halt(tty)) { if (reset == 0) { if (!tty_ldisc_reinit(tty, tty->termios.c_line)) -- cgit v1.2.3-18-g5258 From 11cf48eab21887a120f7f47c67b44a829e3c371d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:27 -0400 Subject: tty: Relocate tty_ldisc_halt() to avoid forward declaration tty_ldisc_halt() will use the file-scoped function, tty_ldisc_wait_idle(), in the following patch. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 15667c0fd64..f691c7604d9 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -498,29 +498,6 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) } } -/** - * tty_ldisc_halt - shut down the line discipline - * @tty: tty device - * - * Shut down the line discipline and work queue for this tty device. - * The TTY_LDISC flag being cleared ensures no further references can - * be obtained while the delayed work queue halt ensures that no more - * data is fed to the ldisc. - * - * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) - * in order to make sure any currently executing ldisc work is also - * flushed. - */ - -static int tty_ldisc_halt(struct tty_struct *tty) -{ - int scheduled; - clear_bit(TTY_LDISC, &tty->flags); - scheduled = cancel_work_sync(&tty->port->buf.work); - set_bit(TTY_LDISC_HALTED, &tty->flags); - return scheduled; -} - /** * tty_ldisc_flush_works - flush all works of a tty * @tty: tty device to flush works for @@ -550,6 +527,29 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) return ret > 0 ? 0 : -EBUSY; } +/** + * tty_ldisc_halt - shut down the line discipline + * @tty: tty device + * + * Shut down the line discipline and work queue for this tty device. + * The TTY_LDISC flag being cleared ensures no further references can + * be obtained while the delayed work queue halt ensures that no more + * data is fed to the ldisc. + * + * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) + * in order to make sure any currently executing ldisc work is also + * flushed. + */ + +static int tty_ldisc_halt(struct tty_struct *tty) +{ + int scheduled; + clear_bit(TTY_LDISC, &tty->flags); + scheduled = cancel_work_sync(&tty->port->buf.work); + set_bit(TTY_LDISC_HALTED, &tty->flags); + return scheduled; +} + /** * tty_ldisc_hangup_halt - halt the line discipline for hangup * @tty: tty being hung up -- cgit v1.2.3-18-g5258 From cf5284765862ac65e4a3e5b34652e593ffda2bdf Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:28 -0400 Subject: tty: Strengthen no-subsequent-use guarantee of tty_ldisc_halt() In preparation for destructing and freeing the tty, the line discipline must first be brought to an inactive state before it can be destructed. This line discipline shutdown must: - disallow new users of the ldisc - wait for existing ldisc users to finish - only then, cancel/flush their pending/running work Factor tty_ldisc_wait_idle() from tty_set_ldisc() and tty_ldisc_kill() to ensure this shutdown order. Failure to provide this guarantee can result in scheduled work running after the tty has already been freed, as indicated in the following log message: [ 88.331234] WARNING: at drivers/tty/tty_buffer.c:435 flush_to_ldisc+0x194/0x1d0() [ 88.334505] Hardware name: Bochs [ 88.335618] tty is bad=-1 [ 88.335703] Modules linked in: netconsole configfs bnep rfcomm bluetooth ...... [ 88.345272] Pid: 39, comm: kworker/1:1 Tainted: G W 3.7.0-next-20121129+ttydebug-xeon #20121129+ttydebug [ 88.347736] Call Trace: [ 88.349024] [] warn_slowpath_common+0x7f/0xc0 [ 88.350383] [] warn_slowpath_fmt+0x46/0x50 [ 88.351745] [] flush_to_ldisc+0x194/0x1d0 [ 88.353047] [] ? _raw_spin_unlock_irq+0x21/0x50 [ 88.354190] [] ? finish_task_switch+0x49/0xe0 [ 88.355436] [] process_one_work+0x121/0x490 [ 88.357674] [] ? __tty_buffer_flush+0x90/0x90 [ 88.358954] [] worker_thread+0x164/0x3e0 [ 88.360247] [] ? manage_workers+0x120/0x120 [ 88.361282] [] kthread+0xc0/0xd0 [ 88.362284] [] ? cmos_do_probe+0x2eb/0x3bf [ 88.363391] [] ? flush_kthread_worker+0xb0/0xb0 [ 88.364797] [] ret_from_fork+0x7c/0xb0 [ 88.366087] [] ? flush_kthread_worker+0xb0/0xb0 [ 88.367266] ---[ end trace 453a7c9f38fbfec0 ]--- Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index f691c7604d9..525ee535c10 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -530,24 +530,38 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) /** * tty_ldisc_halt - shut down the line discipline * @tty: tty device + * @pending: returns true if work was scheduled when cancelled + * (can be set to NULL) + * @timeout: # of jiffies to wait for ldisc refs to be released * * Shut down the line discipline and work queue for this tty device. * The TTY_LDISC flag being cleared ensures no further references can * be obtained while the delayed work queue halt ensures that no more * data is fed to the ldisc. * + * Furthermore, guarantee that existing ldisc references have been + * released, which in turn, guarantees that no future buffer work + * can be rescheduled. + * * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) * in order to make sure any currently executing ldisc work is also * flushed. */ -static int tty_ldisc_halt(struct tty_struct *tty) +static int tty_ldisc_halt(struct tty_struct *tty, int *pending, long timeout) { - int scheduled; + int scheduled, retval; + clear_bit(TTY_LDISC, &tty->flags); + retval = tty_ldisc_wait_idle(tty, timeout); + if (retval) + return retval; + scheduled = cancel_work_sync(&tty->port->buf.work); set_bit(TTY_LDISC_HALTED, &tty->flags); - return scheduled; + if (pending) + *pending = scheduled; + return 0; } /** @@ -688,9 +702,9 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) * parallel to the change and re-referencing the tty. */ - work = tty_ldisc_halt(tty); - if (o_tty) - o_work = tty_ldisc_halt(o_tty); + retval = tty_ldisc_halt(tty, &work, 5 * HZ); + if (!retval && o_tty) + retval = tty_ldisc_halt(o_tty, &o_work, 5 * HZ); /* * Wait for ->hangup_work and ->buf.work handlers to terminate. @@ -701,8 +715,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_ldisc_flush_works(tty); - retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(tty); mutex_lock(&tty->ldisc_mutex); @@ -921,11 +933,6 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) static void tty_ldisc_kill(struct tty_struct *tty) { - /* There cannot be users from userspace now. But there still might be - * drivers holding a reference via tty_ldisc_ref. Do not steal them the - * ldisc until they are done. */ - tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT); - mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc @@ -958,13 +965,12 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_ldisc_halt(tty); - if (o_tty) - tty_ldisc_halt(o_tty); - + tty_ldisc_halt(tty, NULL, MAX_SCHEDULE_TIMEOUT); tty_ldisc_flush_works(tty); - if (o_tty) + if (o_tty) { + tty_ldisc_halt(o_tty, NULL, MAX_SCHEDULE_TIMEOUT); tty_ldisc_flush_works(o_tty); + } tty_lock_pair(tty, o_tty); /* This will need doing differently if we need to lock */ -- cgit v1.2.3-18-g5258 From f4cf7a384587c16c59565dd6930dd763f243dba4 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:29 -0400 Subject: tty: Halt both ldiscs concurrently The pty driver does not obtain an ldisc reference to the linked tty when writing. When the ldiscs are sequentially halted, it is possible for one ldisc to be halted, and before the second ldisc can be halted, a concurrent write schedules buffer work on the first ldisc. This can lead to an access-after-free error when the scheduled buffer work starts on the closed ldisc. Prevent subsequent use after halt by performing each stage of the halt on both ttys. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 525ee535c10..77120911e01 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -530,14 +530,17 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) /** * tty_ldisc_halt - shut down the line discipline * @tty: tty device + * @o_tty: paired pty device (can be NULL) * @pending: returns true if work was scheduled when cancelled * (can be set to NULL) + * @o_pending: returns true if work was scheduled when cancelled + * (can be set to NULL) * @timeout: # of jiffies to wait for ldisc refs to be released * - * Shut down the line discipline and work queue for this tty device. - * The TTY_LDISC flag being cleared ensures no further references can - * be obtained while the delayed work queue halt ensures that no more - * data is fed to the ldisc. + * Shut down the line discipline and work queue for this tty device and + * its paired pty (if exists). Clearing the TTY_LDISC flag ensures + * no further references can be obtained while the work queue halt + * ensures that no more data is fed to the ldisc. * * Furthermore, guarantee that existing ldisc references have been * released, which in turn, guarantees that no future buffer work @@ -548,19 +551,32 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) * flushed. */ -static int tty_ldisc_halt(struct tty_struct *tty, int *pending, long timeout) +static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty, + int *pending, int *o_pending, long timeout) { - int scheduled, retval; + int scheduled, o_scheduled, retval; clear_bit(TTY_LDISC, &tty->flags); + if (o_tty) + clear_bit(TTY_LDISC, &o_tty->flags); + retval = tty_ldisc_wait_idle(tty, timeout); + if (!retval && o_tty) + retval = tty_ldisc_wait_idle(o_tty, timeout); if (retval) return retval; scheduled = cancel_work_sync(&tty->port->buf.work); set_bit(TTY_LDISC_HALTED, &tty->flags); + if (o_tty) { + o_scheduled = cancel_work_sync(&o_tty->port->buf.work); + set_bit(TTY_LDISC_HALTED, &o_tty->flags); + } + if (pending) *pending = scheduled; + if (o_tty && o_pending) + *o_pending = o_scheduled; return 0; } @@ -702,9 +718,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) * parallel to the change and re-referencing the tty. */ - retval = tty_ldisc_halt(tty, &work, 5 * HZ); - if (!retval && o_tty) - retval = tty_ldisc_halt(o_tty, &o_work, 5 * HZ); + retval = tty_ldisc_halt(tty, o_tty, &work, &o_work, 5 * HZ); /* * Wait for ->hangup_work and ->buf.work handlers to terminate. @@ -965,12 +979,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_ldisc_halt(tty, NULL, MAX_SCHEDULE_TIMEOUT); + tty_ldisc_halt(tty, o_tty, NULL, NULL, MAX_SCHEDULE_TIMEOUT); tty_ldisc_flush_works(tty); - if (o_tty) { - tty_ldisc_halt(o_tty, NULL, MAX_SCHEDULE_TIMEOUT); + if (o_tty) tty_ldisc_flush_works(o_tty); - } tty_lock_pair(tty, o_tty); /* This will need doing differently if we need to lock */ -- cgit v1.2.3-18-g5258 From 977066e7587b1b57023a048c0ba754955ea3e7bc Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:30 -0400 Subject: tty: Wait for SAK work before waiting for hangup work SAK work may schedule hangup work (if TTY_SOFT_SAK is defined), thus SAK work must be flushed before hangup work. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 77120911e01..37671fcc7e4 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -506,8 +506,8 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) */ static void tty_ldisc_flush_works(struct tty_struct *tty) { - flush_work(&tty->hangup_work); flush_work(&tty->SAK_work); + flush_work(&tty->hangup_work); flush_work(&tty->port->buf.work); } -- cgit v1.2.3-18-g5258 From 25518c68b334aa977d857dd71dd53f694ffb11e8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:31 -0400 Subject: n_tty: Correct unthrottle-with-buffer-flush comments The driver is no longer unthrottled on buffer reset, so remove comments that claim it is. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 15f7195f4b4..0d3f715de7d 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -198,9 +198,8 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) * reset_buffer_flags - reset buffer state * @tty: terminal to reset * - * Reset the read buffer counters, clear the flags, - * and make sure the driver is unthrottled. Called - * from n_tty_open() and n_tty_flush_buffer(). + * Reset the read buffer counters and clear the flags. + * Called from n_tty_open() and n_tty_flush_buffer(). * * Locking: tty_read_lock for read fields. */ @@ -239,17 +238,15 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty) * n_tty_flush_buffer - clean input queue * @tty: terminal device * - * Flush the input buffer. Called when the line discipline is - * being closed, when the tty layer wants the buffer flushed (eg - * at hangup) or when the N_TTY line discipline internally has to - * clean the pending queue (for example some signals). + * Flush the input buffer. Called when the tty layer wants the + * buffer flushed (eg at hangup) or when the N_TTY line discipline + * internally has to clean the pending queue (for example some signals). * * Locking: ctrl_lock, read_lock. */ static void n_tty_flush_buffer(struct tty_struct *tty) { - /* clear everything and unthrottle the driver */ reset_buffer_flags(tty); if (tty->link) -- cgit v1.2.3-18-g5258 From b66f4fa509153ca9d313075806d5c6425dfa43c5 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:32 -0400 Subject: n_tty: Fully initialize ldisc before restarting buffer work Buffer work may already be pending when the n_tty ldisc is re-opened, eg., when setting the ldisc (via TIOCSETD ioctl) and when hanging up the tty. Since n_tty_set_room() may restart buffer work, first ensure the ldisc is completely initialized. Factor n_tty_set_room() out of reset_buffer_flags() (only 2 callers) and reorganize n_tty_open() to set termios last; buffer work will be restarted there if necessary, after the char_map is properly initialized. Fixes this WARNING: [ 549.561769] ------------[ cut here ]------------ [ 549.598755] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xff/0x130() [ 549.604058] scheduling buffer work for halted ldisc [ 549.607741] Pid: 9417, comm: trinity-child28 Tainted: G D W 3.7.0-next-20121217-sasha-00023-g8689ef9 #219 [ 549.652580] Call Trace: [ 549.662754] [] ? n_tty_set_room+0xff/0x130 [ 549.665458] [] warn_slowpath_common+0x87/0xb0 [ 549.668257] [] warn_slowpath_fmt+0x41/0x50 [ 549.671007] [] n_tty_set_room+0xff/0x130 [ 549.673268] [] reset_buffer_flags+0x137/0x150 [ 549.675607] [] n_tty_open+0x131/0x1c0 [ 549.677699] [] tty_ldisc_open.isra.5+0x54/0x70 [ 549.680147] [] tty_ldisc_hangup+0x11f/0x1e0 [ 549.682409] [] __tty_hangup+0x137/0x440 [ 549.684634] [] tty_vhangup+0x9/0x10 [ 549.686443] [] pty_close+0x14c/0x160 [ 549.688446] [] tty_release+0xd5/0x490 [ 549.690460] [] __fput+0x122/0x250 [ 549.692577] [] ____fput+0x9/0x10 [ 549.694534] [] task_work_run+0xb2/0xf0 [ 549.696349] [] do_exit+0x36d/0x580 [ 549.698286] [] ? syscall_trace_enter+0x24/0x2e0 [ 549.702729] [] do_group_exit+0x8a/0xc0 [ 549.706775] [] sys_exit_group+0x12/0x20 [ 549.711088] [] tracesys+0xe1/0xe6 [ 549.728001] ---[ end trace 73eb41728f11f87e ]--- Reported-by: Sasha Levin Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 0d3f715de7d..d655416087b 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -204,9 +204,8 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) * Locking: tty_read_lock for read fields. */ -static void reset_buffer_flags(struct tty_struct *tty) +static void reset_buffer_flags(struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; unsigned long flags; raw_spin_lock_irqsave(&ldata->read_lock, flags); @@ -219,7 +218,6 @@ static void reset_buffer_flags(struct tty_struct *tty) ldata->canon_head = ldata->canon_data = ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - n_tty_set_room(tty); } static void n_tty_packet_mode_flush(struct tty_struct *tty) @@ -247,7 +245,8 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty) static void n_tty_flush_buffer(struct tty_struct *tty) { - reset_buffer_flags(tty); + reset_buffer_flags(tty->disc_data); + n_tty_set_room(tty); if (tty->link) n_tty_packet_mode_flush(tty); @@ -1633,14 +1632,14 @@ static int n_tty_open(struct tty_struct *tty) goto err_free_bufs; tty->disc_data = ldata; - /* indicate buffer work may resume */ - clear_bit(TTY_LDISC_HALTED, &tty->flags); - reset_buffer_flags(tty); - tty_unthrottle(tty); + reset_buffer_flags(tty->disc_data); ldata->column = 0; - n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; tty->closing = 0; + /* indicate buffer work may resume */ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + n_tty_set_termios(tty, NULL); + tty_unthrottle(tty); return 0; err_free_bufs: -- cgit v1.2.3-18-g5258 From d912156605b0eb3b3070dc7eabc43db6379aa43b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:33 -0400 Subject: tty: Don't reenable already enabled ldisc tty_ldisc_hangup() guarantees the ldisc is enabled (or that there is no ldisc). Since __tty_hangup() was the only user, re-define tty_ldisc_enable() in file-scope. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 1 - drivers/tty/tty_ldisc.c | 2 +- include/linux/tty.h | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d3ddb31e363..e6ee0f459a2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -693,7 +693,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) */ set_bit(TTY_HUPPED, &tty->flags); clear_bit(TTY_HUPPING, &tty->flags); - tty_ldisc_enable(tty); tty_unlock(tty); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 37671fcc7e4..9c727da59fa 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -373,7 +373,7 @@ static inline void tty_ldisc_put(struct tty_ldisc *ld) * Clearing directly is allowed. */ -void tty_ldisc_enable(struct tty_struct *tty) +static void tty_ldisc_enable(struct tty_struct *tty) { clear_bit(TTY_LDISC_HALTED, &tty->flags); set_bit(TTY_LDISC, &tty->flags); diff --git a/include/linux/tty.h b/include/linux/tty.h index 66ae020e8a9..367a9dfc4ea 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -561,8 +561,6 @@ extern void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty); extern void tty_ldisc_init(struct tty_struct *tty); extern void tty_ldisc_deinit(struct tty_struct *tty); extern void tty_ldisc_begin(void); -/* This last one is just for the tty layer internals and shouldn't be used elsewhere */ -extern void tty_ldisc_enable(struct tty_struct *tty); /* n_tty.c */ -- cgit v1.2.3-18-g5258 From 4f98d4675166fc1991dbad7dd2af634df7c14061 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:34 -0400 Subject: tty: Complete ownership transfer of flip buffers Waiting for buffer work to complete is not required for safely performing changes to the line discipline, once the line discipline is halted. The buffer work routine, flush_to_ldisc(), will be unable to acquire an ldisc ref and all existing references were waited until released (so it can't already have one). Ensure running buffer work which may reference the soon-to-be-gone tty completes and any buffer work running after this point retrieves a NULL tty. Also, ensure all buffer work is cancelled on port destruction. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 1 + drivers/tty/tty_ldisc.c | 47 ++++++++++++----------------------------------- drivers/tty/tty_port.c | 1 + 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e6ee0f459a2..45876341870 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1595,6 +1595,7 @@ static void release_tty(struct tty_struct *tty, int idx) tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); tty->port->itty = NULL; + cancel_work_sync(&tty->port->buf.work); if (tty->link) tty_kref_put(tty->link); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 9c727da59fa..cbb945b03cd 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -508,7 +508,6 @@ static void tty_ldisc_flush_works(struct tty_struct *tty) { flush_work(&tty->SAK_work); flush_work(&tty->hangup_work); - flush_work(&tty->port->buf.work); } /** @@ -531,20 +530,12 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) * tty_ldisc_halt - shut down the line discipline * @tty: tty device * @o_tty: paired pty device (can be NULL) - * @pending: returns true if work was scheduled when cancelled - * (can be set to NULL) - * @o_pending: returns true if work was scheduled when cancelled - * (can be set to NULL) * @timeout: # of jiffies to wait for ldisc refs to be released * * Shut down the line discipline and work queue for this tty device and * its paired pty (if exists). Clearing the TTY_LDISC flag ensures - * no further references can be obtained while the work queue halt - * ensures that no more data is fed to the ldisc. - * - * Furthermore, guarantee that existing ldisc references have been - * released, which in turn, guarantees that no future buffer work - * can be rescheduled. + * no further references can be obtained, while waiting for existing + * references to be released ensures no more data is fed to the ldisc. * * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) * in order to make sure any currently executing ldisc work is also @@ -552,9 +543,9 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) */ static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty, - int *pending, int *o_pending, long timeout) + long timeout) { - int scheduled, o_scheduled, retval; + int retval; clear_bit(TTY_LDISC, &tty->flags); if (o_tty) @@ -566,17 +557,10 @@ static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty, if (retval) return retval; - scheduled = cancel_work_sync(&tty->port->buf.work); set_bit(TTY_LDISC_HALTED, &tty->flags); - if (o_tty) { - o_scheduled = cancel_work_sync(&o_tty->port->buf.work); + if (o_tty) set_bit(TTY_LDISC_HALTED, &o_tty->flags); - } - if (pending) - *pending = scheduled; - if (o_tty && o_pending) - *o_pending = o_scheduled; return 0; } @@ -586,17 +570,12 @@ static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty, * * Shut down the line discipline and work queue for the tty device * being hungup. Clear the TTY_LDISC flag to ensure no further - * references can be obtained, wait for remaining references to be - * released, and cancel pending buffer work to ensure no more - * data is fed to this ldisc. + * references can be obtained and wait for remaining references to be + * released to ensure no more data is fed to this ldisc. * Caller must hold legacy and ->ldisc_mutex. * * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently * with this function by checking the TTY_HUPPING flag. - * - * NB: if tty->ldisc is NULL then buffer work does not need to be - * cancelled because it must already have done as a precondition - * of closing the ldisc and setting tty->ldisc to NULL */ static bool tty_ldisc_hangup_halt(struct tty_struct *tty) { @@ -616,7 +595,6 @@ static bool tty_ldisc_hangup_halt(struct tty_struct *tty) tty_name(tty, tty_n)); } - cancel_work_sync(&tty->port->buf.work); set_bit(TTY_LDISC_HALTED, &tty->flags); /* must reacquire both locks and preserve lock order */ @@ -644,7 +622,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval; struct tty_ldisc *o_ldisc, *new_ldisc; - int work, o_work = 0; struct tty_struct *o_tty; new_ldisc = tty_ldisc_get(ldisc); @@ -718,7 +695,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) * parallel to the change and re-referencing the tty. */ - retval = tty_ldisc_halt(tty, o_tty, &work, &o_work, 5 * HZ); + retval = tty_ldisc_halt(tty, o_tty, 5 * HZ); /* * Wait for ->hangup_work and ->buf.work handlers to terminate. @@ -782,10 +759,10 @@ enable: /* Restart the work queue in case no characters kick it off. Safe if already running */ - if (work) - schedule_work(&tty->port->buf.work); - if (o_work) + schedule_work(&tty->port->buf.work); + if (o_tty) schedule_work(&o_tty->port->buf.work); + mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; @@ -979,7 +956,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_ldisc_halt(tty, o_tty, NULL, NULL, MAX_SCHEDULE_TIMEOUT); + tty_ldisc_halt(tty, o_tty, MAX_SCHEDULE_TIMEOUT); tty_ldisc_flush_works(tty); if (o_tty) tty_ldisc_flush_works(o_tty); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 969c3e675a7..121aeb9393e 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -132,6 +132,7 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf); */ void tty_port_destroy(struct tty_port *port) { + cancel_work_sync(&port->buf.work); tty_buffer_free_all(port); } EXPORT_SYMBOL(tty_port_destroy); -- cgit v1.2.3-18-g5258 From a2965b7bee00a01731ae79de34c26e146cbd08cf Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:35 -0400 Subject: tty: Make core responsible for synchronizing its work The tty core relies on the ldisc layer for synchronizing destruction of the tty. Instead, the final tty release must wait for any pending tty work to complete prior to tty destruction. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 17 +++++++++++++++++ drivers/tty/tty_ldisc.c | 24 ++++-------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 45876341870..95e97128e2e 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1510,6 +1510,17 @@ void tty_free_termios(struct tty_struct *tty) } EXPORT_SYMBOL(tty_free_termios); +/** + * tty_flush_works - flush all works of a tty + * @tty: tty device to flush works for + * + * Sync flush all works belonging to @tty. + */ +static void tty_flush_works(struct tty_struct *tty) +{ + flush_work(&tty->SAK_work); + flush_work(&tty->hangup_work); +} /** * release_one_tty - release tty structure memory @@ -1831,6 +1842,12 @@ int tty_release(struct inode *inode, struct file *filp) * Ask the line discipline code to release its structures */ tty_ldisc_release(tty, o_tty); + + /* Wait for pending work before tty destruction commmences */ + tty_flush_works(tty); + if (o_tty) + tty_flush_works(o_tty); + /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. The tty_unlock_pair diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index cbb945b03cd..7f7e1a3d382 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -498,18 +498,6 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) } } -/** - * tty_ldisc_flush_works - flush all works of a tty - * @tty: tty device to flush works for - * - * Sync flush all works belonging to @tty. - */ -static void tty_ldisc_flush_works(struct tty_struct *tty) -{ - flush_work(&tty->SAK_work); - flush_work(&tty->hangup_work); -} - /** * tty_ldisc_wait_idle - wait for the ldisc to become idle * @tty: tty to wait for @@ -698,13 +686,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_halt(tty, o_tty, 5 * HZ); /* - * Wait for ->hangup_work and ->buf.work handlers to terminate. + * Wait for hangup to complete, if pending. * We must drop the mutex here in case a hangup is also in process. */ mutex_unlock(&tty->ldisc_mutex); - tty_ldisc_flush_works(tty); + flush_work(&tty->hangup_work); tty_lock(tty); mutex_lock(&tty->ldisc_mutex); @@ -951,15 +939,11 @@ static void tty_ldisc_kill(struct tty_struct *tty) void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) { /* - * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. As this is the final close it does not - * race with the set_ldisc code path. + * Shutdown this line discipline. As this is the final close, + * it does not race with the set_ldisc code path. */ tty_ldisc_halt(tty, o_tty, MAX_SCHEDULE_TIMEOUT); - tty_ldisc_flush_works(tty); - if (o_tty) - tty_ldisc_flush_works(o_tty); tty_lock_pair(tty, o_tty); /* This will need doing differently if we need to lock */ -- cgit v1.2.3-18-g5258 From c8785241741d4bc3ee6da2ff415a9a1b3df2b4cb Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:36 -0400 Subject: tty: Fix 'deferred reopen' ldisc comment This comment is a victim of code migration from "tty: Fix the ldisc hangup race"; re-parent it. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 7f7e1a3d382..0030d556b9b 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -854,11 +854,12 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ mutex_lock(&tty->ldisc_mutex); - /* At this point we have a closed ldisc and we want to - reopen it. We could defer this to the next open but - it means auditing a lot of other paths so this is - a FIXME */ if (tty_ldisc_hangup_halt(tty)) { + + /* At this point we have a halted ldisc; we want to close it and + reopen a new ldisc. We could defer the reopen to the next + open but it means auditing a lot of other paths so this is + a FIXME */ if (reset == 0) { if (!tty_ldisc_reinit(tty, tty->termios.c_line)) -- cgit v1.2.3-18-g5258 From 96433d104a4b39c43dd6f57776f9fcb765111a56 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:37 -0400 Subject: tty: Bracket ldisc release with TTY_DEBUG_HANGUP messages Expected typical log output: [ 2.437211] tty_open: opening pts1... [ 2.443376] tty_open: opening pts5... [ 2.447830] tty_release: ptm0 (tty count=1)... [ 2.447849] pts0 vhangup... [ 2.447865] tty_release: ptm0: final close [ 2.447876] tty_release: ptm0: freeing structure... [ 2.451634] tty_release: tty1 (tty count=1)... [ 2.451638] tty_release: tty1: final close [ 2.451654] tty_release: tty1: freeing structure... [ 2.452505] tty_release: pts5 (tty count=2)... [ 2.453029] tty_open: opening pts0... Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 95e97128e2e..f6ce2c5fbe5 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1836,7 +1836,7 @@ int tty_release(struct inode *inode, struct file *filp) return 0; #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__); + printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf)); #endif /* * Ask the line discipline code to release its structures @@ -1848,6 +1848,9 @@ int tty_release(struct inode *inode, struct file *filp) if (o_tty) tty_flush_works(o_tty); +#ifdef TTY_DEBUG_HANGUP + printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf)); +#endif /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. The tty_unlock_pair -- cgit v1.2.3-18-g5258 From fc575ee6eadbcac757e3216e230b6fab1ba5b140 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:38 -0400 Subject: tty: Add ldisc hangup debug messages Expected typical debug log: [ 582.721965] tty_open: opening pts3... [ 582.721970] tty_open: opening pts3... [ 582.721977] tty_release: pts3 (tty count=3)... [ 582.721980] tty_release: ptm3 (tty count=1)... [ 582.722015] pts3 vhangup... [ 582.722020] tty_ldisc_hangup: pts3: closing ldisc: ffff88007a920540 [ 582.724128] tty_release: pts3 (tty count=2)... [ 582.724217] tty_ldisc_hangup: pts3: re-opened ldisc: ffff88007a920580 [ 582.724221] tty_release: ptm3: final close [ 582.724234] tty_ldisc_release: ptm3: closing ldisc: ffff88007a920a80 [ 582.724238] tty_ldisc_release: ptm3: ldisc closed [ 582.724241] tty_release: ptm3: freeing structure... [ 582.724741] tty_open: opening pts3... Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 0030d556b9b..328ff5b544a 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -20,6 +20,17 @@ #include #include +#undef LDISC_DEBUG_HANGUP + +#ifdef LDISC_DEBUG_HANGUP +#define tty_ldisc_debug(tty, f, args...) ({ \ + char __b[64]; \ + printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty, __b), ##args); \ +}) +#else +#define tty_ldisc_debug(tty, f, args...) +#endif + /* * This guards the refcounted line discipline lists. The lock * must be taken with irqs off because there are hangup path @@ -822,6 +833,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; int err = 0; + tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); + /* * FIXME! What are the locking issues here? This may me overdoing * things... This question is especially important now that we've @@ -878,6 +891,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) mutex_unlock(&tty->ldisc_mutex); if (reset) tty_reset_termios(tty); + + tty_ldisc_debug(tty, "re-opened ldisc: %p\n", tty->ldisc); } /** @@ -944,6 +959,8 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * it does not race with the set_ldisc code path. */ + tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); + tty_ldisc_halt(tty, o_tty, MAX_SCHEDULE_TIMEOUT); tty_lock_pair(tty, o_tty); @@ -955,6 +972,8 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) tty_unlock_pair(tty, o_tty); /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ + + tty_ldisc_debug(tty, "ldisc closed\n"); } /** -- cgit v1.2.3-18-g5258 From 8842dda2366d3d0c97646102768831f9b0ffd712 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:39 -0400 Subject: tty: Don't protect atomic operation with mutex test_bit() is already atomic; drop mutex lock/unlock. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index f6ce2c5fbe5..6afca98fae2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1391,9 +1391,7 @@ static int tty_reopen(struct tty_struct *tty) } tty->count++; - mutex_lock(&tty->ldisc_mutex); WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); - mutex_unlock(&tty->ldisc_mutex); return 0; } -- cgit v1.2.3-18-g5258 From ebc9baed42e42f9b51cf61672b7afb72f068d523 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:40 -0400 Subject: tty: Separate release semantics of ldisc reference tty_ldisc_ref()/tty_ldisc_unref() have usage semantics equivalent to down_read_trylock()/up_read(). Only callers of tty_ldisc_put() are performing the additional operations necessary for proper ldisc teardown, and then only after ensuring no outstanding 'read lock' remains. Thus, tty_ldisc_unref() should never be the last reference; WARN if it is. Conversely, tty_ldisc_put() should never be destructing if the use count != 1. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 69 +++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 328ff5b544a..9362a1030c9 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -49,37 +49,6 @@ static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld) return ld; } -static void put_ldisc(struct tty_ldisc *ld) -{ - unsigned long flags; - - if (WARN_ON_ONCE(!ld)) - return; - - /* - * If this is the last user, free the ldisc, and - * release the ldisc ops. - * - * We really want an "atomic_dec_and_raw_lock_irqsave()", - * but we don't have it, so this does it by hand. - */ - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - if (atomic_dec_and_test(&ld->users)) { - struct tty_ldisc_ops *ldo = ld->ops; - - ldo->refcount--; - module_put(ldo->owner); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - kfree(ld); - return; - } - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - if (waitqueue_active(&ld->wq_idle)) - wake_up(&ld->wq_idle); -} - /** * tty_register_ldisc - install a line discipline * @disc: ldisc number @@ -363,13 +332,45 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); void tty_ldisc_deref(struct tty_ldisc *ld) { - put_ldisc(ld); + unsigned long flags; + + if (WARN_ON_ONCE(!ld)) + return; + + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + /* + * WARNs if one-too-many reader references were released + * - the last reference must be released with tty_ldisc_put + */ + WARN_ON(atomic_dec_and_test(&ld->users)); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + if (waitqueue_active(&ld->wq_idle)) + wake_up(&ld->wq_idle); } EXPORT_SYMBOL_GPL(tty_ldisc_deref); +/** + * tty_ldisc_put - release the ldisc + * + * Complement of tty_ldisc_get(). + */ static inline void tty_ldisc_put(struct tty_ldisc *ld) { - put_ldisc(ld); + unsigned long flags; + + if (WARN_ON_ONCE(!ld)) + return; + + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + + /* unreleased reader reference(s) will cause this WARN */ + WARN_ON(!atomic_dec_and_test(&ld->users)); + + ld->ops->refcount--; + module_put(ld->ops->owner); + kfree(ld); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); } /** @@ -1001,7 +1002,7 @@ void tty_ldisc_init(struct tty_struct *tty) */ void tty_ldisc_deinit(struct tty_struct *tty) { - put_ldisc(tty->ldisc); + tty_ldisc_put(tty->ldisc); tty_ldisc_assign(tty, NULL); } -- cgit v1.2.3-18-g5258 From 16759f6cd8c590fa23cb2956fdf32fe23a67e482 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:41 -0400 Subject: tty: Document unsafe ldisc reference acquire Merge get_ldisc() into its only call site. Note how, after merging, the unsafe acquire of an ldisc reference is obvious. CPU 0 in tty_ldisc_try() | CPU 1 in tty_ldisc_halt() | test_bit(TTY_LDISC, &tty_flags) | if (true) | clear_bit(TTY_LDISC, &tty_flags) tty->ldisc != 0? | atomic_read(&tty->ldisc->users) if (true) | ret_val == 1? atomic_inc(&tty->ldisc->users) | if (false) | wait | | | The spin lock in tty_ldisc_try() does nothing wrt synchronizing the ldisc halt since it's not acquired as part of halting. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 9362a1030c9..5ee0b2be7d7 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -42,13 +42,6 @@ static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; -static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld) -{ - if (ld) - atomic_inc(&ld->users); - return ld; -} - /** * tty_register_ldisc - install a line discipline * @disc: ldisc number @@ -269,10 +262,13 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) unsigned long flags; struct tty_ldisc *ld; + /* FIXME: this allows reference acquire after TTY_LDISC is cleared */ raw_spin_lock_irqsave(&tty_ldisc_lock, flags); ld = NULL; - if (test_bit(TTY_LDISC, &tty->flags)) - ld = get_ldisc(tty->ldisc); + if (test_bit(TTY_LDISC, &tty->flags) && tty->ldisc) { + ld = tty->ldisc; + atomic_inc(&ld->users); + } raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ld; } -- cgit v1.2.3-18-g5258 From f48070457c728a1ff8f327240e70483cebabf83b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:42 -0400 Subject: tty: Fold one-line assign function into callers Now that tty_ldisc_assign() is a one-line file-scoped function, remove it and perform the simple assignment at its call sites. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 5ee0b2be7d7..f26ef1ace4f 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -227,24 +227,6 @@ const struct file_operations tty_ldiscs_proc_fops = { .release = seq_release, }; -/** - * tty_ldisc_assign - set ldisc on a tty - * @tty: tty to assign - * @ld: line discipline - * - * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains. - * The tty instance refcount starts at zero. - * - * Locking: - * Caller must hold references - */ - -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) -{ - tty->ldisc = ld; -} - /** * tty_ldisc_try - internal helper * @tty: the tty @@ -488,7 +470,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) /* There is an outstanding reference here so this is safe */ old = tty_ldisc_get(old->ops->num); WARN_ON(IS_ERR(old)); - tty_ldisc_assign(tty, old); + tty->ldisc = old; tty_set_termios_ldisc(tty, old->ops->num); if (tty_ldisc_open(tty, old) < 0) { tty_ldisc_put(old); @@ -496,7 +478,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) new_ldisc = tty_ldisc_get(N_TTY); if (IS_ERR(new_ldisc)) panic("n_tty: get"); - tty_ldisc_assign(tty, new_ldisc); + tty->ldisc = new_ldisc; tty_set_termios_ldisc(tty, N_TTY); r = tty_ldisc_open(tty, new_ldisc); if (r < 0) @@ -725,7 +707,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_ldisc_close(tty, o_ldisc); /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, new_ldisc); + tty->ldisc = new_ldisc; tty_set_termios_ldisc(tty, ldisc); retval = tty_ldisc_open(tty, new_ldisc); @@ -799,11 +781,10 @@ static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) tty_ldisc_close(tty, tty->ldisc); tty_ldisc_put(tty->ldisc); - tty->ldisc = NULL; /* * Switch the line discipline back */ - tty_ldisc_assign(tty, ld); + tty->ldisc = ld; tty_set_termios_ldisc(tty, ldisc); return 0; @@ -986,7 +967,7 @@ void tty_ldisc_init(struct tty_struct *tty) struct tty_ldisc *ld = tty_ldisc_get(N_TTY); if (IS_ERR(ld)) panic("n_tty: init_tty"); - tty_ldisc_assign(tty, ld); + tty->ldisc = ld; } /** @@ -999,7 +980,7 @@ void tty_ldisc_init(struct tty_struct *tty) void tty_ldisc_deinit(struct tty_struct *tty) { tty_ldisc_put(tty->ldisc); - tty_ldisc_assign(tty, NULL); + tty->ldisc = NULL; } void tty_ldisc_begin(void) -- cgit v1.2.3-18-g5258 From 734de249fbe2fbf594c30202a343f0772b6d18fe Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:43 -0400 Subject: tty: Locate get/put ldisc functions together Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index f26ef1ace4f..4e46c1721b9 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -179,6 +179,29 @@ static struct tty_ldisc *tty_ldisc_get(int disc) return ld; } +/** + * tty_ldisc_put - release the ldisc + * + * Complement of tty_ldisc_get(). + */ +static inline void tty_ldisc_put(struct tty_ldisc *ld) +{ + unsigned long flags; + + if (WARN_ON_ONCE(!ld)) + return; + + raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + + /* unreleased reader reference(s) will cause this WARN */ + WARN_ON(!atomic_dec_and_test(&ld->users)); + + ld->ops->refcount--; + module_put(ld->ops->owner); + kfree(ld); + raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) { return (*pos < NR_LDISCS) ? pos : NULL; @@ -328,29 +351,6 @@ void tty_ldisc_deref(struct tty_ldisc *ld) } EXPORT_SYMBOL_GPL(tty_ldisc_deref); -/** - * tty_ldisc_put - release the ldisc - * - * Complement of tty_ldisc_get(). - */ -static inline void tty_ldisc_put(struct tty_ldisc *ld) -{ - unsigned long flags; - - if (WARN_ON_ONCE(!ld)) - return; - - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - - /* unreleased reader reference(s) will cause this WARN */ - WARN_ON(!atomic_dec_and_test(&ld->users)); - - ld->ops->refcount--; - module_put(ld->ops->owner); - kfree(ld); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - /** * tty_ldisc_enable - allow ldisc use * @tty: terminal to activate ldisc on -- cgit v1.2.3-18-g5258 From be3971166d93a401105952672dab2eac6542cb57 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:44 -0400 Subject: tty: Remove redundant tty_wait_until_sent() tty_ioctl() already waits until sent. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 4e46c1721b9..1afe192bef6 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -625,15 +625,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) return 0; } - tty_unlock(tty); - /* - * Problem: What do we do if this blocks ? - * We could deadlock here - */ - - tty_wait_until_sent(tty, 0); - - tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* -- cgit v1.2.3-18-g5258 From e7f3880cd9b98c5bf9391ae7acdec82b75403776 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:45 -0400 Subject: tty: Fix recursive deadlock in tty_perform_flush() tty_perform_flush() can deadlock when called while holding a line discipline reference. By definition, all ldisc drivers hold a ldisc reference, so calls originating from ldisc drivers must not block for a ldisc reference. The deadlock can occur when: CPU 0 | CPU 1 | tty_ldisc_ref(tty) | .... | tty_ldisc_ref_wait(tty) | | CPU 0 cannot progess because it cannot obtain an ldisc reference with the line discipline has been halted (thus no new references are granted). CPU 1 cannot progress because an outstanding ldisc reference has not been released. An in-tree call-tree audit of tty_perform_flush() [1] shows 5 ldisc drivers calling tty_perform_flush() indirectly via n_tty_ioctl_helper() and 2 ldisc drivers calling directly. A single tty driver safely uses the function. [1] Recursive usage: /* These functions are line discipline ioctls and thus * recursive wrt line discipline references */ tty_perform_flush() - ./drivers/tty/tty_ioctl.c n_tty_ioctl_helper() hci_uart_tty_ioctl(default) - drivers/bluetooth/hci_ldisc.c (N_HCI) n_hdlc_tty_ioctl(default) - drivers/tty/n_hdlc.c (N_HDLC) gsmld_ioctl(default) - drivers/tty/n_gsm.c (N_GSM0710) n_tty_ioctl(default) - drivers/tty/n_tty.c (N_TTY) gigaset_tty_ioctl(default) - drivers/isdn/gigaset/ser-gigaset.c (N_GIGASET_M101) ppp_synctty_ioctl(TCFLSH) - drivers/net/ppp/pps_synctty.c ppp_asynctty_ioctl(TCFLSH) - drivers/net/ppp/ppp_async.c Non-recursive use: tty_perform_flush() - drivers/tty/tty_ioctl.c ipw_ioctl(TCFLSH) - drivers/tty/ipwireless/tty.c /* This function is a tty i/o ioctl method, which * is invoked by tty_ioctl() */ Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/net/ppp/ppp_async.c | 2 +- drivers/net/ppp/ppp_synctty.c | 2 +- drivers/tty/tty_ioctl.c | 28 +++++++++++++++++++--------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index a031f6b456b..9c889e0303d 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -314,7 +314,7 @@ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file, /* flush our buffers and the serial port's buffer */ if (arg == TCIOFLUSH || arg == TCOFLUSH) ppp_async_flush_output(ap); - err = tty_perform_flush(tty, arg); + err = n_tty_ioctl_helper(tty, file, cmd, arg); break; case FIONREAD: diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index 1a12033d2ef..bdf3b13a71a 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -355,7 +355,7 @@ ppp_synctty_ioctl(struct tty_struct *tty, struct file *file, /* flush our buffers and the serial port's buffer */ if (arg == TCIOFLUSH || arg == TCOFLUSH) ppp_sync_flush_output(ap); - err = tty_perform_flush(tty, arg); + err = n_tty_ioctl_helper(tty, file, cmd, arg); break; case FIONREAD: diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 28715e48b2f..d119034877d 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1122,14 +1122,12 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, } EXPORT_SYMBOL_GPL(tty_mode_ioctl); -int tty_perform_flush(struct tty_struct *tty, unsigned long arg) + +/* Caller guarantees ldisc reference is held */ +static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg) { - struct tty_ldisc *ld; - int retval = tty_check_change(tty); - if (retval) - return retval; + struct tty_ldisc *ld = tty->ldisc; - ld = tty_ldisc_ref_wait(tty); switch (arg) { case TCIFLUSH: if (ld && ld->ops->flush_buffer) { @@ -1147,12 +1145,24 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) tty_driver_flush_buffer(tty); break; default: - tty_ldisc_deref(ld); return -EINVAL; } - tty_ldisc_deref(ld); return 0; } + +int tty_perform_flush(struct tty_struct *tty, unsigned long arg) +{ + struct tty_ldisc *ld; + int retval = tty_check_change(tty); + if (retval) + return retval; + + ld = tty_ldisc_ref_wait(tty); + retval = __tty_perform_flush(tty, arg); + if (ld) + tty_ldisc_deref(ld); + return retval; +} EXPORT_SYMBOL_GPL(tty_perform_flush); int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, @@ -1191,7 +1201,7 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, } return 0; case TCFLSH: - return tty_perform_flush(tty, arg); + return __tty_perform_flush(tty, arg); default: /* Try the mode commands */ return tty_mode_ioctl(tty, file, cmd, arg); -- cgit v1.2.3-18-g5258 From 7bbe08d6b89fce09ae4e6a7ce62ccd3c279a31ce Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 14 Mar 2013 00:30:34 +0100 Subject: TTY: serial, stop accessing potential NULLs The following commits: * 6732c8bb8671acbdac6cdc93dd72ddd581dd5e25 (TTY: switch tty_schedule_flip) * 2e124b4a390ca85325fae75764bef92f0547fa25 (TTY: switch tty_flip_buffer_push) * 05c7cd39907184328f48d3e7899f9cdd653ad336 (TTY: switch tty_insert_flip_string) * 92a19f9cec9a80ad93c06e115822deb729e2c6ad (TTY: switch tty_insert_flip_char) * 227434f8986c3827a1faedd1feb437acd6285315 (TTY: switch tty_buffer_request_room to tty_port) introduced a potential NULL dereference to some drivers. In particular, when the device is used as a console, incoming bytes can kill the box. This is caused by removed checks for TTY against NULL. It happened because it was unclear to me why the checks were there. I assumed them superfluous because the interrupts were unbound or otherwise stopped. But this is not the case for consoles for these drivers, as was pointed out by David Miller. Now, this patch re-introduces the checks (at this point we check port->state, not the tty proper, as we do not care about tty pointers anymore). For both of the drivers, we place the check below the handling of break signal so that sysrq can actually work. (One needs to issue a break and then sysrq key within the following 5 seconds.) We do not change sc26xx, sunhv, and sunsu here because they behave the same as before. People having that hardware should fix the driver eventually, however. They always could unconditionally dereference tty in receive_chars, port->state in uart_handle_dcd_change, and up->port.state->port.tty. There is perhaps more to fix in all those drivers, but they are at least in a state they were before. Signed-off-by: Jiri Slaby Cc: "David S. Miller" Cc: Grant Likely Cc: Rob Herring Cc: sparclinux@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sunsab.c | 2 +- drivers/tty/serial/sunzilog.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 8de2213664e..a422c8b55a4 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -203,7 +203,7 @@ receive_chars(struct uart_sunsab_port *up, flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_handle_sysrq_char(&up->port, ch) || !port) continue; if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 && diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 27669ff3d44..813ef8eb8ef 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -388,7 +388,7 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up, else if (r1 & CRC_ERR) flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_handle_sysrq_char(&up->port, ch) || !port) continue; if (up->port.ignore_status_mask == 0xff || -- cgit v1.2.3-18-g5258 From b9a129f4813ef5dea8da4670e100f8ba89abebea Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Tue, 12 Mar 2013 13:27:29 +0800 Subject: driver: tty: serial: remove cast for kzalloc return value remove cast for kzalloc return value. Signed-off-by: Zhang Yanfei Cc: Jiri Slaby Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/icom.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index bc9e6b017b0..18ed5aebb16 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1415,8 +1415,7 @@ static int icom_alloc_adapter(struct icom_adapter struct icom_adapter *cur_adapter_entry; struct list_head *tmp; - icom_adapter = (struct icom_adapter *) - kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); + icom_adapter = kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); if (!icom_adapter) { return -ENOMEM; -- cgit v1.2.3-18-g5258 From 8358f6242dd447a4f694c7bc949bbfc842ca5db1 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Tue, 12 Mar 2013 13:29:32 +0800 Subject: driver: tty: vt: remove cast for kmalloc return value remove cast for kmalloc return value. Signed-off-by: Zhang Yanfei Cc: Jiri Slaby Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 248381b3072..2978ca596a7 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -194,8 +194,7 @@ static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int q = p->inverse_translations[i]; if (!q) { - q = p->inverse_translations[i] = (unsigned char *) - kmalloc(MAX_GLYPH, GFP_KERNEL); + q = p->inverse_translations[i] = kmalloc(MAX_GLYPH, GFP_KERNEL); if (!q) return; } memset(q, 0, MAX_GLYPH); -- cgit v1.2.3-18-g5258 From 2be818a116b22dff518f0d7112e6bed39f2034a1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 21 Mar 2013 16:07:57 -0700 Subject: Revert "USB: quatech2: only write to the tty if the port is open." This reverts commit 27b351c5546008c640b3e65152f60ca74b3706f1. It caused problems with the build. Reported-by: Stephen Rothwell Cc: Bill Pemberton Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 4a4a5eb00aa..d8531047b41 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -657,9 +657,7 @@ void qt2_process_read_urb(struct urb *urb) __func__); break; } - - if (port_priv->is_open) - tty_flip_buffer_push(&port->port); + tty_flip_buffer_push(&port->port); newport = *(ch + 3); @@ -702,8 +700,7 @@ void qt2_process_read_urb(struct urb *urb) tty_insert_flip_string(&port->port, ch, 1); } - if (port_priv->is_open) - tty_flip_buffer_push(&port->port); + tty_flip_buffer_push(&port->port); } static void qt2_write_bulk_callback(struct urb *urb) -- cgit v1.2.3-18-g5258 From 6a3f45dccfa3db4de43aabc2f75a337e878f9b4b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Mar 2013 13:34:44 +0200 Subject: serial: 8250: Allow probe drivers to ignore tx_loadsz In most cases the tx_loadsz is the same as fifosize. This will store the fifosize in it if it was not separately delivered from the driver. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index cf6a5383748..19ebbdf4310 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -3247,6 +3247,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->tx_loadsz = up->tx_loadsz; uart->capabilities = up->capabilities; + /* Take tx_loadsz from fifosize if it wasn't set separately */ + if (uart->port.fifosize && !uart->tx_loadsz) + uart->tx_loadsz = uart->port.fifosize; + if (up->port.dev) uart->port.dev = up->port.dev; -- cgit v1.2.3-18-g5258 From 9f1ca068ea9968e2bde4a2418d97fcd89005f4bf Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Mar 2013 13:34:45 +0200 Subject: serial: of_serial: Handle fifo-size property This will reduce the need for extra types in 8250.c just in case the fifo size differs from the standard. Signed-off-by: Heikki Krogerus Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 1 + drivers/tty/serial/of_serial.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index 8f01cb190f2..c13f0ce74da 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -33,6 +33,7 @@ Optional properties: RTAS and should not be registered. - no-loopback-test: set to indicate that the port does not implements loopback test mode +- fifo-size: the fifo size of the UART. Example: diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index b025d543827..267711b5cb4 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -97,6 +97,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_property_read_u32(np, "reg-shift", &prop) == 0) port->regshift = prop; + /* Check for fifo size */ + if (of_property_read_u32(np, "fifo-size", &prop) == 0) + port->fifosize = prop; + port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { -- cgit v1.2.3-18-g5258 From b250a6b0015753362eeb63c3a0dd985848ed772d Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 25 Mar 2013 11:50:28 +0100 Subject: serial: sh-sci: remove obsolete Kconfig macros Support for SH7367 and SH7377 got removed in v3.8. Now remove their last Kconfig macros. Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 4c22a1529aa..5aca7364634 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -15,8 +15,6 @@ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ defined(CONFIG_ARCH_SH7372) || \ defined(CONFIG_ARCH_R8A7740) -- cgit v1.2.3-18-g5258 From 30215c3b8f36cc532267615ceade3657a9bd9563 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Fri, 22 Mar 2013 18:50:02 +0100 Subject: serial: xilinx_uartps: remove superfluous IDR write The datesheet clearly states, that writing low bits to the XUARTPS_IDR register have no effect. Remove the write. Signed-off-by: Steffen Trumtrar Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index ba451c7209f..974c361a91f 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -583,9 +583,6 @@ static int xuartps_startup(struct uart_port *port) xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); - xuartps_writel(~(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | - XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT), XUARTPS_IDR_OFFSET); return retval; } -- cgit v1.2.3-18-g5258 From 0d3b88d187d02c910fce62ee4d14fb29617f21ba Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 20 Mar 2013 15:30:57 +0100 Subject: CAIF: fix tty->warned build error After commit 6865ff222cca ("TTY: do not warn about setting speed via SPD_*"), we are seeing a build error when DEBUG_FS is enabled: drivers/net/caif/caif_serial.c: In function 'update_tty_status': drivers/net/caif/caif_serial.c:94:11: error: 'struct tty_struct' has no member named 'warned' Fix the error by removing the reference to the variable as it was not set in the last decade (as documented in the changelog of the commit above). Signed-off-by: Jiri Slaby Reported-by: Stephen Rothwell Cc: Sjur Braendeland Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/net/caif/caif_serial.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index d1bf0ff93ae..be90debc7cd 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -90,8 +90,7 @@ static inline void update_tty_status(struct ser_device *ser) ser->tty->stopped << 5 | ser->tty->flow_stopped << 3 | ser->tty->packet << 2 | - ser->tty->port->low_latency << 1 | - ser->tty->warned; + ser->tty->port->low_latency << 1; } static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) { -- cgit v1.2.3-18-g5258 From 44a459fd83376d08b0dea776354761ebb1bacde1 Mon Sep 17 00:00:00 2001 From: Changlong Xie Date: Wed, 20 Mar 2013 08:42:04 +0800 Subject: tty: tty_vhangup_session can be static Reported-by: Fengguang Wu Acked-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6afca98fae2..cbf5a504090 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -777,7 +777,7 @@ void tty_vhangup_self(void) * is complete. That guarantee is necessary for security reasons. */ -void tty_vhangup_session(struct tty_struct *tty) +static void tty_vhangup_session(struct tty_struct *tty) { #ifdef TTY_DEBUG_HANGUP char buf[64]; -- cgit v1.2.3-18-g5258 From b0b8c84cf58d2486d48f486b5c47af7a7a33a497 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Mar 2013 15:51:15 +0200 Subject: serial: of_serial: Handle auto-flow-control property Automatic Flow Control capability is not tied to this property. This is only one way of detecting it. The property is limited to be used only with 8250 driver. Signed-off-by: Heikki Krogerus Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 3 +++ drivers/tty/serial/of_serial.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index c13f0ce74da..1928a3e83cd 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -34,6 +34,9 @@ Optional properties: - no-loopback-test: set to indicate that the port does not implements loopback test mode - fifo-size: the fifo size of the UART. +- auto-flow-control: one way to enable automatic flow control support. The + driver is allowed to detect support for the capability even without this + property. Example: diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 267711b5cb4..39c7ea4cb14 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,8 @@ #include #include +#include "8250/8250.h" + struct of_serial_info { struct clk *clk; int type; @@ -171,11 +172,17 @@ static int of_platform_serial_probe(struct platform_device *ofdev) #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: { - /* For now the of bindings don't support the extra - 8250 specific bits */ struct uart_8250_port port8250; memset(&port8250, 0, sizeof(port8250)); port8250.port = port; + + if (port.fifosize) + port8250.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(ofdev->dev.of_node, + "auto-flow-control")) + port8250.capabilities |= UART_CAP_AFE; + ret = serial8250_register_8250_port(&port8250); break; } -- cgit v1.2.3-18-g5258 From 65c1b12b453ec726fe0692707a8a2493502bd6a2 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 25 Mar 2013 23:01:21 +0100 Subject: serial: 8250: remove a few lines of dead code Support for the WindRiver SBC8560 board was removed in v3.6. But there are still a few lines depending on its obsolete Kconfig macro. Remove these now. Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 34eb676916f..1ebf8538b4f 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -117,13 +117,6 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) * is cleared, the machine locks up with endless interrupts. */ #define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) -#elif defined(CONFIG_SBC8560) -/* - * WindRiver did something similarly broken on their SBC8560 board. The - * UART tristates its IRQ output while OUT2 is clear, but they pulled - * the interrupt line _up_ instead of down, so if we register the IRQ - * while the UART is in that state, we die in an IRQ storm. */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) #else #define ALPHA_KLUDGE_MCR 0 #endif -- cgit v1.2.3-18-g5258 From cfcec52e9781f08948c6eb98198d65c45be75a70 Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Thu, 28 Mar 2013 17:33:20 -0500 Subject: serial: 8250: Make SERIAL_8250_RUNTIME_UARTS work correctly Consider a situation where I have an ARM based system and therefore no legacy ports. Say, I have two memory-mapped ports. I use device tree to describe the ports. What would be the config options I set so that I get only the two ports in my system? I do not want legacy ports being created automatically and I want it to be flexible enough that it creates the devices based only on the device tree. I expected setting SERIAL_8250_RUNTIME_UARTS = 0 to work because the description said, "Set this to the maximum number of serial ports you want the kernel to register at boot time." Unfortunately, even though SERIAL_8250_NR_UARTS was set to the default value of 4, I did not get any device nodes (because SERIAL_8250_RUNTIME_UARTS was 0). This is what this change is addressing. SERIAL_8250_NR_UARTS controls the maximum number of ports you can support. SERIAL_8250_RUNTIME_UARTS specifies the number of ports you want to create automatically for legacy ports at boot time. All other ports will be created when serial8250_register_port is called (and if does not exceed the total number of supported ports as specified by SERIAL_8250_NR_UARTS). Signed-off-by: Karthik Manamcheri Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 19ebbdf4310..e5ddfd6fc46 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -2755,7 +2755,7 @@ static void __init serial8250_isa_init_ports(void) if (nr_uarts > UART_NR) nr_uarts = UART_NR; - for (i = 0; i < nr_uarts; i++) { + for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; struct uart_port *port = &up->port; @@ -2916,7 +2916,7 @@ static int __init serial8250_console_setup(struct console *co, char *options) * if so, search for the first available port that does have * console support. */ - if (co->index >= nr_uarts) + if (co->index >= UART_NR) co->index = 0; port = &serial8250_ports[co->index].port; if (!port->iobase && !port->membase) @@ -2957,7 +2957,7 @@ int serial8250_find_port(struct uart_port *p) int line; struct uart_port *port; - for (line = 0; line < nr_uarts; line++) { + for (line = 0; line < UART_NR; line++) { port = &serial8250_ports[line].port; if (uart_match_port(p, port)) return line; @@ -3110,7 +3110,7 @@ static int serial8250_remove(struct platform_device *dev) { int i; - for (i = 0; i < nr_uarts; i++) { + for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; if (up->port.dev == &dev->dev) @@ -3178,7 +3178,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * /* * First, find a port entry which matches. */ - for (i = 0; i < nr_uarts; i++) + for (i = 0; i < UART_NR; i++) if (uart_match_port(&serial8250_ports[i].port, port)) return &serial8250_ports[i]; @@ -3187,7 +3187,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * * free entry. We look for one which hasn't been previously * used (indicated by zero iobase). */ - for (i = 0; i < nr_uarts; i++) + for (i = 0; i < UART_NR; i++) if (serial8250_ports[i].port.type == PORT_UNKNOWN && serial8250_ports[i].port.iobase == 0) return &serial8250_ports[i]; @@ -3196,7 +3196,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * * That also failed. Last resort is to find any entry which * doesn't have a real port associated with it. */ - for (i = 0; i < nr_uarts; i++) + for (i = 0; i < UART_NR; i++) if (serial8250_ports[i].port.type == PORT_UNKNOWN) return &serial8250_ports[i]; -- cgit v1.2.3-18-g5258 From b6ad29355560beef6d127c6d33fb10ec0113bd85 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 26 Mar 2013 15:57:35 +0100 Subject: tty: serial: samsung: Disable interrupts in a suspend-friendly way Since the interrupt mask register is not preserved across system suspend and it defaults to all interrupts enabled, it is not enough to disable UART interrupt. This patch adds free_irq to port shutdown and mask setting to port startup to handle IRQ disabling in a suspend-friendly way. In addition, a bug caused by multiple request_irq calls in port startup callback is fixed. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 2769a38d15b..b386fc8a5ee 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -446,6 +446,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) /* Clear pending interrupts and mask all interrupts */ if (s3c24xx_serial_has_interrupt_mask(port)) { + free_irq(port->irq, ourport); + wr_regl(port, S3C64XX_UINTP, 0xf); wr_regl(port, S3C64XX_UINTM, 0xf); } @@ -505,6 +507,8 @@ static int s3c64xx_serial_startup(struct uart_port *port) dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n", port->mapbase, port->membase); + wr_regl(port, S3C64XX_UINTM, 0xf); + ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED, s3c24xx_serial_portname(port), ourport); if (ret) { -- cgit v1.2.3-18-g5258 From d09a7308e9887a83abfd3176f4416d4142a265dc Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Wed, 27 Mar 2013 19:34:24 -0400 Subject: serial: samsung: Restore IRQ mask during noirq resume This closes a window where the system may hang in resume as soon as the UART interrupt is enabled and before the mask is restored. The hang occurs because the driver can't handle IRQs it thinks are masked. Signed-off-by: Michael Spang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index b386fc8a5ee..78d9c55a9f8 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1311,9 +1311,29 @@ static int s3c24xx_serial_resume(struct device *dev) return 0; } +static int s3c24xx_serial_resume_noirq(struct device *dev) +{ + struct uart_port *port = s3c24xx_dev_to_port(dev); + + if (port) { + /* restore IRQ mask */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + unsigned int uintm = 0xf; + if (tx_enabled(port)) + uintm &= ~S3C64XX_UINTM_TXD_MSK; + if (rx_enabled(port)) + uintm &= ~S3C64XX_UINTM_RXD_MSK; + wr_regl(port, S3C64XX_UINTM, uintm); + } + } + + return 0; +} + static const struct dev_pm_ops s3c24xx_serial_pm_ops = { .suspend = s3c24xx_serial_suspend, .resume = s3c24xx_serial_resume, + .resume_noirq = s3c24xx_serial_resume_noirq, }; #define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops) -- cgit v1.2.3-18-g5258 From 38adbc54cea90e220c9212f961a621b2c6af9ae0 Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Wed, 27 Mar 2013 19:34:25 -0400 Subject: serial: samsung: Avoid waiting forever for TX ready The no_console_suspend option allows the console to write to the UART during early resume, before the serial port itself is resumed. Such writes hang indefinitely waiting for TX ready. This adds a check to the console putchar function to drop characters instead of hanging if the port hasn't been initialized. That way, we can use no_console_suspend without risk of hanging. Signed-off-by: Michael Spang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 78d9c55a9f8..7883f11b8c5 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1367,6 +1367,13 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; } +static bool +s3c24xx_port_configured(unsigned int ucon) +{ + /* consider the serial port configured if the tx/rx mode set */ + return (ucon & 0xf) != 0; +} + #ifdef CONFIG_CONSOLE_POLL /* * Console polling routines for writing and reading from the uart while @@ -1389,6 +1396,11 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c) { unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON); + + /* not possible to xmit on unconfigured port */ + if (!s3c24xx_port_configured(ucon)) + return; while (!s3c24xx_serial_console_txrdy(port, ufcon)) cpu_relax(); @@ -1401,6 +1413,12 @@ static void s3c24xx_serial_console_putchar(struct uart_port *port, int ch) { unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON); + + /* not possible to xmit on unconfigured port */ + if (!s3c24xx_port_configured(ucon)) + return; + while (!s3c24xx_serial_console_txrdy(port, ufcon)) barrier(); wr_regb(cons_uart, S3C2410_UTXH, ch); @@ -1433,9 +1451,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n", port, ulcon, ucon, ubrdiv); - if ((ucon & 0xf) != 0) { - /* consider the serial port configured if the tx/rx mode set */ - + if (s3c24xx_port_configured(ucon)) { switch (ulcon & S3C2410_LCON_CSMASK) { case S3C2410_LCON_CS5: *bits = 5; -- cgit v1.2.3-18-g5258 From e302cd932094fa59463b5891814d2a5ace56cfc1 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 29 Mar 2013 00:15:49 +0100 Subject: serial: 8250_dw: add support for clk api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements support for using the clk api; this lets us use the "clocks" property with device tree, instead of having to use clock-frequency. Signed-off-by: Emilio López Signed-off-by: Maxime Ripard Reviewed-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index db0e66f6dd0..3dedd2470db 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "8250.h" @@ -55,8 +56,9 @@ struct dw8250_data { - int last_lcr; - int line; + int last_lcr; + int line; + struct clk *clk; }; static void dw8250_serial_out(struct uart_port *p, int offset, int value) @@ -136,8 +138,13 @@ static int dw8250_probe_of(struct uart_port *p) if (!of_property_read_u32(np, "reg-shift", &val)) p->regshift = val; + /* clock got configured through clk api, all done */ + if (p->uartclk) + return 0; + + /* try to find out clock frequency from DT as fallback */ if (of_property_read_u32(np, "clock-frequency", &val)) { - dev_err(p->dev, "no clock-frequency property set\n"); + dev_err(p->dev, "clk or clock-frequency not defined\n"); return -EINVAL; } p->uartclk = val; @@ -294,9 +301,20 @@ static int dw8250_probe(struct platform_device *pdev) if (!uart.port.membase) return -ENOMEM; + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(data->clk)) { + clk_prepare_enable(data->clk); + uart.port.uartclk = clk_get_rate(data->clk); + } + uart.port.iotype = UPIO_MEM; uart.port.serial_in = dw8250_serial_in; uart.port.serial_out = dw8250_serial_out; + uart.port.private_data = data; dw8250_setup_port(&uart); @@ -312,12 +330,6 @@ static int dw8250_probe(struct platform_device *pdev) return -ENODEV; } - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - uart.port.private_data = data; - data->line = serial8250_register_8250_port(&uart); if (data->line < 0) return data->line; @@ -333,6 +345,9 @@ static int dw8250_remove(struct platform_device *pdev) serial8250_unregister_port(data->line); + if (!IS_ERR(data->clk)) + clk_disable_unprepare(data->clk); + return 0; } -- cgit v1.2.3-18-g5258 From cb06ff102e2d79a82cf780aa5e6947b2e0529ac0 Mon Sep 17 00:00:00 2001 From: Chanho Min Date: Wed, 27 Mar 2013 18:38:11 +0900 Subject: ARM: PL011: Add support for Rx DMA buffer polling. In DMA support, The received data is not pushed to tty until the DMA buffer is filled. But some megabyte rate chips such as BT expect fast response and data should be pushed immediately. In order to fix this issue, We suggest the use of the timer for polling DMA buffer. In our test, no data loss occurred at high-baudrate as compared with interrupt- driven (We tested with 3Mbps). We changes: - We add timer for polling. If we set poll_timer to 10, every 10ms, timer handler checks the residue in the dma buffer and transfer data to the tty. Also, last_residue is updated for the next polling. - poll_timeout is used to prevent the timer's system cost. If poll_timeout is set to 3000 and no data is received in 3 seconds, we inactivate poll timer and driver falls back to interrupt-driven. When data is received again in FIFO and UART irq is occurred, we switch back to DMA mode and start polling. - We use consistent DMA mappings to avoid from the frequent cache operation of the timer function for default. - pl011_dma_rx_chars is modified. the pending size is recalculated because data can be taken by polling. - the polling time is adjusted if dma rx poll is enabled but no rate is specified. Ideal polling interval to push 1 character at every interval is the reciprocal of 'baud rate / 10 line bits per character / 1000 ms per sec'. But It is very aggressive to system. Experimentally, '10000000 / baud' is suitable to receive dozens of characters. the poll rate can be specified statically by dma_rx_poll_rate of the platform data as well. Changes compared to v1: - Use of consistent DMA mappings. - Added dma_rx_poll_rate in platform data to specify the polling interval. - Added dma_rx_poll_timeout in platform data to specify the polling timeout. Changes compared to v2: - Use of consistent DMA mappings for default. - Added dma_rx_poll_enable in platform data to adjust the polling time according to the baud rate. - remove unnecessary lock from the polling function. Signed-off-by: Chanho Min Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 157 +++++++++++++++++++++++++++++++++++----- include/linux/amba/serial.h | 3 + 2 files changed, 141 insertions(+), 19 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3ea5408fcbe..b031abf43a7 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -29,6 +29,7 @@ * and hooked into this driver. */ + #if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif @@ -117,6 +118,12 @@ struct pl011_dmarx_data { struct pl011_sgbuf sgbuf_b; dma_cookie_t cookie; bool running; + struct timer_list timer; + unsigned int last_residue; + unsigned long last_jiffies; + bool auto_poll_rate; + unsigned int poll_rate; + unsigned int poll_timeout; }; struct pl011_dmatx_data { @@ -223,16 +230,18 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) static int pl011_sgbuf_init(struct dma_chan *chan, struct pl011_sgbuf *sg, enum dma_data_direction dir) { - sg->buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL); + dma_addr_t dma_addr; + + sg->buf = dma_alloc_coherent(chan->device->dev, + PL011_DMA_BUFFER_SIZE, &dma_addr, GFP_KERNEL); if (!sg->buf) return -ENOMEM; - sg_init_one(&sg->sg, sg->buf, PL011_DMA_BUFFER_SIZE); + sg_init_table(&sg->sg, 1); + sg_set_page(&sg->sg, phys_to_page(dma_addr), + PL011_DMA_BUFFER_SIZE, offset_in_page(dma_addr)); + sg_dma_address(&sg->sg) = dma_addr; - if (dma_map_sg(chan->device->dev, &sg->sg, 1, dir) != 1) { - kfree(sg->buf); - return -EINVAL; - } return 0; } @@ -240,8 +249,9 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg, enum dma_data_direction dir) { if (sg->buf) { - dma_unmap_sg(chan->device->dev, &sg->sg, 1, dir); - kfree(sg->buf); + dma_free_coherent(chan->device->dev, + PL011_DMA_BUFFER_SIZE, sg->buf, + sg_dma_address(&sg->sg)); } } @@ -300,6 +310,29 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; + if (plat->dma_rx_poll_enable) { + /* Set poll rate if specified. */ + if (plat->dma_rx_poll_rate) { + uap->dmarx.auto_poll_rate = false; + uap->dmarx.poll_rate = plat->dma_rx_poll_rate; + } else { + /* + * 100 ms defaults to poll rate if not + * specified. This will be adjusted with + * the baud rate at set_termios. + */ + uap->dmarx.auto_poll_rate = true; + uap->dmarx.poll_rate = 100; + } + /* 3 secs defaults poll_timeout if not specified. */ + if (plat->dma_rx_poll_timeout) + uap->dmarx.poll_timeout = + plat->dma_rx_poll_timeout; + else + uap->dmarx.poll_timeout = 3000; + } else + uap->dmarx.auto_poll_rate = false; + dev_info(uap->port.dev, "DMA channel RX %s\n", dma_chan_name(uap->dmarx.chan)); } @@ -701,24 +734,30 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, struct tty_port *port = &uap->port.state->port; struct pl011_sgbuf *sgbuf = use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; - struct device *dev = uap->dmarx.chan->device->dev; int dma_count = 0; u32 fifotaken = 0; /* only used for vdbg() */ - /* Pick everything from the DMA first */ + struct pl011_dmarx_data *dmarx = &uap->dmarx; + int dmataken = 0; + + if (uap->dmarx.poll_rate) { + /* The data can be taken by polling */ + dmataken = sgbuf->sg.length - dmarx->last_residue; + /* Recalculate the pending size */ + if (pending >= dmataken) + pending -= dmataken; + } + + /* Pick the remain data from the DMA */ if (pending) { - /* Sync in buffer */ - dma_sync_sg_for_cpu(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE); /* * First take all chars in the DMA pipe, then look in the FIFO. * Note that tty_insert_flip_buf() tries to take as many chars * as it can. */ - dma_count = tty_insert_flip_string(port, sgbuf->buf, pending); - - /* Return buffer to device */ - dma_sync_sg_for_device(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE); + dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken, + pending); uap->port.icount.rx += dma_count; if (dma_count < pending) @@ -726,6 +765,10 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, "couldn't insert all characters (TTY is full?)\n"); } + /* Reset the last_residue for Rx DMA poll */ + if (uap->dmarx.poll_rate) + dmarx->last_residue = sgbuf->sg.length; + /* * Only continue with trying to read the FIFO if all DMA chars have * been taken first. @@ -865,6 +908,57 @@ static inline void pl011_dma_rx_stop(struct uart_amba_port *uap) writew(uap->dmacr, uap->port.membase + UART011_DMACR); } +/* + * Timer handler for Rx DMA polling. + * Every polling, It checks the residue in the dma buffer and transfer + * data to the tty. Also, last_residue is updated for the next polling. + */ +static void pl011_dma_rx_poll(unsigned long args) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)args; + struct tty_port *port = &uap->port.state->port; + struct pl011_dmarx_data *dmarx = &uap->dmarx; + struct dma_chan *rxchan = uap->dmarx.chan; + unsigned long flags = 0; + unsigned int dmataken = 0; + unsigned int size = 0; + struct pl011_sgbuf *sgbuf; + int dma_count; + struct dma_tx_state state; + + sgbuf = dmarx->use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; + rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state); + if (likely(state.residue < dmarx->last_residue)) { + dmataken = sgbuf->sg.length - dmarx->last_residue; + size = dmarx->last_residue - state.residue; + dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken, + size); + if (dma_count == size) + dmarx->last_residue = state.residue; + dmarx->last_jiffies = jiffies; + } + tty_flip_buffer_push(port); + + /* + * If no data is received in poll_timeout, the driver will fall back + * to interrupt mode. We will retrigger DMA at the first interrupt. + */ + if (jiffies_to_msecs(jiffies - dmarx->last_jiffies) + > uap->dmarx.poll_timeout) { + + spin_lock_irqsave(&uap->port.lock, flags); + pl011_dma_rx_stop(uap); + spin_unlock_irqrestore(&uap->port.lock, flags); + + uap->dmarx.running = false; + dmaengine_terminate_all(rxchan); + del_timer(&uap->dmarx.timer); + } else { + mod_timer(&uap->dmarx.timer, + jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); + } +} + static void pl011_dma_startup(struct uart_amba_port *uap) { int ret; @@ -927,6 +1021,16 @@ skip_rx: if (pl011_dma_rx_trigger_dma(uap)) dev_dbg(uap->port.dev, "could not trigger initial " "RX DMA job, fall back to interrupt mode\n"); + if (uap->dmarx.poll_rate) { + init_timer(&(uap->dmarx.timer)); + uap->dmarx.timer.function = pl011_dma_rx_poll; + uap->dmarx.timer.data = (unsigned long)uap; + mod_timer(&uap->dmarx.timer, + jiffies + + msecs_to_jiffies(uap->dmarx.poll_rate)); + uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; + uap->dmarx.last_jiffies = jiffies; + } } } @@ -962,6 +1066,8 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) /* Clean up the RX DMA */ pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_a, DMA_FROM_DEVICE); pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_b, DMA_FROM_DEVICE); + if (uap->dmarx.poll_rate) + del_timer_sync(&uap->dmarx.timer); uap->using_rx_dma = false; } } @@ -976,7 +1082,6 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) return uap->using_rx_dma && uap->dmarx.running; } - #else /* Blank functions if the DMA engine is not available */ static inline void pl011_dma_probe(struct uart_amba_port *uap) @@ -1088,8 +1193,18 @@ static void pl011_rx_chars(struct uart_amba_port *uap) dev_dbg(uap->port.dev, "could not trigger RX DMA job " "fall back to interrupt mode again\n"); uap->im |= UART011_RXIM; - } else + } else { uap->im &= ~UART011_RXIM; + /* Start Rx DMA poll */ + if (uap->dmarx.poll_rate) { + uap->dmarx.last_jiffies = jiffies; + uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; + mod_timer(&uap->dmarx.timer, + jiffies + + msecs_to_jiffies(uap->dmarx.poll_rate)); + } + } + writew(uap->im, uap->port.membase + UART011_IMSC); } spin_lock(&uap->port.lock); @@ -1164,7 +1279,6 @@ static irqreturn_t pl011_int(int irq, void *dev_id) unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); - status = readw(uap->port.membase + UART011_MIS); if (status) { do { @@ -1551,6 +1665,11 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / clkdiv); + /* + * Adjust RX DMA polling rate with baud rate if not specified. + */ + if (uap->dmarx.auto_poll_rate) + uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); if (baud > port->uartclk/16) quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index f612c783170..62d9303c283 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -203,6 +203,9 @@ struct amba_pl011_data { bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; + bool dma_rx_poll_enable; + unsigned int dma_rx_poll_rate; + unsigned int dma_rx_poll_timeout; void (*init) (void); void (*exit) (void); }; -- cgit v1.2.3-18-g5258 From 89fa28dbe0395ee06f3aacfe27655323ebad43d1 Mon Sep 17 00:00:00 2001 From: Chanho Min Date: Wed, 3 Apr 2013 11:10:37 +0900 Subject: amba-pl011: fix build error if CONFIG_DMA_ENGINE is not enabled Following patch will fix build error if CONFIG_DMA_ENGINE is unset. Reported-by: Stephen Rothwell Signed-off-by: Chanho Min Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index b031abf43a7..6cf861efb2a 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1195,6 +1195,7 @@ static void pl011_rx_chars(struct uart_amba_port *uap) uap->im |= UART011_RXIM; } else { uap->im &= ~UART011_RXIM; +#ifdef CONFIG_DMA_ENGINE /* Start Rx DMA poll */ if (uap->dmarx.poll_rate) { uap->dmarx.last_jiffies = jiffies; @@ -1203,6 +1204,7 @@ static void pl011_rx_chars(struct uart_amba_port *uap) jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); } +#endif } writew(uap->im, uap->port.membase + UART011_IMSC); @@ -1665,11 +1667,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / clkdiv); +#ifdef CONFIG_DMA_ENGINE /* * Adjust RX DMA polling rate with baud rate if not specified. */ if (uap->dmarx.auto_poll_rate) uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); +#endif if (baud > port->uartclk/16) quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); -- cgit v1.2.3-18-g5258 From 31bdfc649f4577c9b1120c67b7b1e85f7777aad0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 5 Apr 2013 10:54:05 +0900 Subject: serial: max3100: use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max3100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 57da9bbaaab..35866d5872a 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -778,7 +778,7 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->spi = spi; max3100s[i]->irq = spi->irq; spin_lock_init(&max3100s[i]->conf_lock); - dev_set_drvdata(&spi->dev, max3100s[i]); + spi_set_drvdata(spi, max3100s[i]); pdata = spi->dev.platform_data; max3100s[i]->crystal = pdata->crystal; max3100s[i]->loopback = pdata->loopback; @@ -819,7 +819,7 @@ static int max3100_probe(struct spi_device *spi) static int max3100_remove(struct spi_device *spi) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = spi_get_drvdata(spi); int i; mutex_lock(&max3100s_lock); -- cgit v1.2.3-18-g5258 From 39f610e40eecc39e4c34e047fd6904ca6b525520 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 20 Mar 2013 13:20:43 -0400 Subject: tty: Fix race condition if flushing tty flip buffers As Ilya Zykov identified in his patch 'PROBLEM: Race condition in tty buffer's function flush_to_ldisc()', a race condition exists which allows a parallel flush_to_ldisc() to flush and free the tty flip buffers while those buffers are in-use. For example, CPU 0 | CPU 1 | CPU 2 | flush_to_ldisc() | | grab spin lock | tty_buffer_flush() | | flush_to_ldisc() wait for spin lock | | wait for spin lock | if (!test_and_set_bit(TTYP_FLUSHING)) | | while (next flip buffer) | | ... | | drop spin lock | grab spin lock | | if (test_bit(TTYP_FLUSHING)) | | set_bit(TTYP_FLUSHPENDING) | receive_buf() | drop spin lock | | | | grab spin lock | | if (!test_and_set_bit(TTYP_FLUSHING)) | | if (test_bit(TTYP_FLUSHPENDING)) | | __tty_buffer_flush() CPU 2 has just flushed and freed all tty flip buffers while CPU 1 is transferring data from the head flip buffer. The original patch was rejected under the assumption that parallel flush_to_ldisc() was not possible. Because of necessary changes to the workqueue api, work items can execute in parallel on SMP. This patch differs slightly from the original patch by testing for a pending flush _after_ each receive_buf(), since TTYP_FLUSHPENDING can only be set while the lock is dropped around receive_buf(). Reported-by: Ilya Zykov Signed-off-by: Peter Hurley Acked-by: Ilya Zykov Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 578aa7594b1..9121c1f7aee 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -449,11 +449,6 @@ static void flush_to_ldisc(struct work_struct *work) tty_buffer_free(port, head); continue; } - /* Ldisc or user is trying to flush the buffers - we are feeding to the ldisc, stop feeding the - line discipline as we want to empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) - break; if (!tty->receive_room) break; if (count > tty->receive_room) @@ -465,17 +460,20 @@ static void flush_to_ldisc(struct work_struct *work) disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&buf->lock, flags); + /* Ldisc or user is trying to flush the buffers. + We may have a deferred request to flush the + input buffer, if so pull the chain under the lock + and empty the queue */ + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { + __tty_buffer_flush(port); + clear_bit(TTYP_FLUSHPENDING, &port->iflags); + wake_up(&tty->read_wait); + break; + } } clear_bit(TTYP_FLUSHING, &port->iflags); } - /* We may have a deferred request to flush the input buffer, - if so pull the chain under the lock and empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); - wake_up(&tty->read_wait); - } spin_unlock_irqrestore(&buf->lock, flags); tty_ldisc_deref(disc); -- cgit v1.2.3-18-g5258 From 17efd2b7867b5ee1076d3c9ae5a6c937907d9198 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Apr 2013 02:04:47 +0200 Subject: tty: serial/samsung: prepare for common clock API With the common clock interface, there is no way to provide the "clock_source" sysfs attribute for the samsung serial ports. Given that this file was purely informational and had fixed contents, we have reason to believe that no user space programs were relying on it. The sysfs file is not documented in the ABI docs. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 7883f11b8c5..e91378b40d5 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1183,6 +1183,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, return 0; } +#ifdef CONFIG_SAMSUNG_CLOCK static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, struct device_attribute *attr, char *buf) @@ -1198,7 +1199,7 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, } static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); - +#endif /* Device driver serial port probe */ @@ -1256,9 +1257,11 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(pdev, &ourport->port); +#ifdef CONFIG_SAMSUNG_CLOCK ret = device_create_file(&pdev->dev, &dev_attr_clock_source); if (ret < 0) dev_err(&pdev->dev, "failed to add clock source attr.\n"); +#endif ret = s3c24xx_serial_cpufreq_register(ourport); if (ret < 0) @@ -1276,7 +1279,9 @@ static int s3c24xx_serial_remove(struct platform_device *dev) if (port) { s3c24xx_serial_cpufreq_deregister(to_ourport(port)); +#ifdef CONFIG_SAMSUNG_CLOCK device_remove_file(&dev->dev, &dev_attr_clock_source); +#endif uart_remove_one_port(&s3c24xx_uart_drv, port); } -- cgit v1.2.3-18-g5258 From 9ee51f01eee84a108510ac2794b9a9dff8be6d3f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Apr 2013 02:04:48 +0200 Subject: tty: serial/samsung: make register definitions global The registers for the Samsung S3C serial port are currently defined in the platform specific arch/arm/plat-samsung/include/plat/regs-serial.h file, which is not visible to multiplatform capable drivers. Unfortunately, it is not possible to move the file into a more local place as we should normally try to, because the same registers may be used in one of four places: * In the driver itself * In platform-independent ARM code for early debug output * In platform_data definitions * In the Samsung platform power management code I have also found no way to logically split out a platform_data file, other than possibly move everything into include/linux/platform_data, which also felt wrong. The only part of this file that makes sense to keep specific to the s3c24xx platform are the virtual and physical addresses defined here, which are needed in no other location. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-s3c24xx/clock-s3c2440.c | 5 + arch/arm/mach-s3c24xx/common.c | 5 + arch/arm/plat-samsung/include/plat/regs-serial.h | 282 +---------------------- drivers/tty/serial/samsung.c | 6 +- include/linux/serial_s3c.h | 260 +++++++++++++++++++++ 5 files changed, 274 insertions(+), 284 deletions(-) create mode 100644 include/linux/serial_s3c.h diff --git a/arch/arm/mach-s3c24xx/clock-s3c2440.c b/arch/arm/mach-s3c24xx/clock-s3c2440.c index 04b87ec9253..1069b568082 100644 --- a/arch/arm/mach-s3c24xx/clock-s3c2440.c +++ b/arch/arm/mach-s3c24xx/clock-s3c2440.c @@ -123,6 +123,11 @@ static struct clk s3c2440_clk_ac97 = { .ctrlbit = S3C2440_CLKCON_AC97, }; +#define S3C24XX_VA_UART0 (S3C_VA_UART) +#define S3C24XX_VA_UART1 (S3C_VA_UART + 0x4000 ) +#define S3C24XX_VA_UART2 (S3C_VA_UART + 0x8000 ) +#define S3C24XX_VA_UART3 (S3C_VA_UART + 0xC000 ) + static unsigned long s3c2440_fclk_n_getrate(struct clk *clk) { unsigned long ucon0, ucon1, ucon2, divisor; diff --git a/arch/arm/mach-s3c24xx/common.c b/arch/arm/mach-s3c24xx/common.c index 6bcf87f65f9..92e609440c5 100644 --- a/arch/arm/mach-s3c24xx/common.c +++ b/arch/arm/mach-s3c24xx/common.c @@ -239,6 +239,11 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) /* Serial port registrations */ +#define S3C2410_PA_UART0 (S3C24XX_PA_UART) +#define S3C2410_PA_UART1 (S3C24XX_PA_UART + 0x4000 ) +#define S3C2410_PA_UART2 (S3C24XX_PA_UART + 0x8000 ) +#define S3C2443_PA_UART3 (S3C24XX_PA_UART + 0xC000 ) + static struct resource s3c2410_uart0_resource[] = { [0] = DEFINE_RES_MEM(S3C2410_PA_UART0, SZ_16K), [1] = DEFINE_RES_NAMED(IRQ_S3CUART_RX0, \ diff --git a/arch/arm/plat-samsung/include/plat/regs-serial.h b/arch/arm/plat-samsung/include/plat/regs-serial.h index 29c26a81884..f05f2afa440 100644 --- a/arch/arm/plat-samsung/include/plat/regs-serial.h +++ b/arch/arm/plat-samsung/include/plat/regs-serial.h @@ -1,281 +1 @@ -/* arch/arm/plat-samsung/include/plat/regs-serial.h - * - * From linux/include/asm-arm/hardware/serial_s3c2410.h - * - * Internal header file for Samsung S3C2410 serial ports (UART0-2) - * - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * - * Additional defines, Copyright 2003 Simtec Electronics (linux@simtec.co.uk) - * - * Adapted from: - * - * Internal header file for MX1ADS serial ports (UART1 & 2) - * - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef __ASM_ARM_REGS_SERIAL_H -#define __ASM_ARM_REGS_SERIAL_H - -#define S3C24XX_VA_UART0 (S3C_VA_UART) -#define S3C24XX_VA_UART1 (S3C_VA_UART + 0x4000 ) -#define S3C24XX_VA_UART2 (S3C_VA_UART + 0x8000 ) -#define S3C24XX_VA_UART3 (S3C_VA_UART + 0xC000 ) - -#define S3C2410_PA_UART0 (S3C24XX_PA_UART) -#define S3C2410_PA_UART1 (S3C24XX_PA_UART + 0x4000 ) -#define S3C2410_PA_UART2 (S3C24XX_PA_UART + 0x8000 ) -#define S3C2443_PA_UART3 (S3C24XX_PA_UART + 0xC000 ) - -#define S3C2410_URXH (0x24) -#define S3C2410_UTXH (0x20) -#define S3C2410_ULCON (0x00) -#define S3C2410_UCON (0x04) -#define S3C2410_UFCON (0x08) -#define S3C2410_UMCON (0x0C) -#define S3C2410_UBRDIV (0x28) -#define S3C2410_UTRSTAT (0x10) -#define S3C2410_UERSTAT (0x14) -#define S3C2410_UFSTAT (0x18) -#define S3C2410_UMSTAT (0x1C) - -#define S3C2410_LCON_CFGMASK ((0xF<<3)|(0x3)) - -#define S3C2410_LCON_CS5 (0x0) -#define S3C2410_LCON_CS6 (0x1) -#define S3C2410_LCON_CS7 (0x2) -#define S3C2410_LCON_CS8 (0x3) -#define S3C2410_LCON_CSMASK (0x3) - -#define S3C2410_LCON_PNONE (0x0) -#define S3C2410_LCON_PEVEN (0x5 << 3) -#define S3C2410_LCON_PODD (0x4 << 3) -#define S3C2410_LCON_PMASK (0x7 << 3) - -#define S3C2410_LCON_STOPB (1<<2) -#define S3C2410_LCON_IRM (1<<6) - -#define S3C2440_UCON_CLKMASK (3<<10) -#define S3C2440_UCON_CLKSHIFT (10) -#define S3C2440_UCON_PCLK (0<<10) -#define S3C2440_UCON_UCLK (1<<10) -#define S3C2440_UCON_PCLK2 (2<<10) -#define S3C2440_UCON_FCLK (3<<10) -#define S3C2443_UCON_EPLL (3<<10) - -#define S3C6400_UCON_CLKMASK (3<<10) -#define S3C6400_UCON_CLKSHIFT (10) -#define S3C6400_UCON_PCLK (0<<10) -#define S3C6400_UCON_PCLK2 (2<<10) -#define S3C6400_UCON_UCLK0 (1<<10) -#define S3C6400_UCON_UCLK1 (3<<10) - -#define S3C2440_UCON2_FCLK_EN (1<<15) -#define S3C2440_UCON0_DIVMASK (15 << 12) -#define S3C2440_UCON1_DIVMASK (15 << 12) -#define S3C2440_UCON2_DIVMASK (7 << 12) -#define S3C2440_UCON_DIVSHIFT (12) - -#define S3C2412_UCON_CLKMASK (3<<10) -#define S3C2412_UCON_CLKSHIFT (10) -#define S3C2412_UCON_UCLK (1<<10) -#define S3C2412_UCON_USYSCLK (3<<10) -#define S3C2412_UCON_PCLK (0<<10) -#define S3C2412_UCON_PCLK2 (2<<10) - -#define S3C2410_UCON_CLKMASK (1 << 10) -#define S3C2410_UCON_CLKSHIFT (10) -#define S3C2410_UCON_UCLK (1<<10) -#define S3C2410_UCON_SBREAK (1<<4) - -#define S3C2410_UCON_TXILEVEL (1<<9) -#define S3C2410_UCON_RXILEVEL (1<<8) -#define S3C2410_UCON_TXIRQMODE (1<<2) -#define S3C2410_UCON_RXIRQMODE (1<<0) -#define S3C2410_UCON_RXFIFO_TOI (1<<7) -#define S3C2443_UCON_RXERR_IRQEN (1<<6) -#define S3C2443_UCON_LOOPBACK (1<<5) - -#define S3C2410_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ - S3C2410_UCON_RXILEVEL | \ - S3C2410_UCON_TXIRQMODE | \ - S3C2410_UCON_RXIRQMODE | \ - S3C2410_UCON_RXFIFO_TOI) - -#define S3C2410_UFCON_FIFOMODE (1<<0) -#define S3C2410_UFCON_TXTRIG0 (0<<6) -#define S3C2410_UFCON_RXTRIG8 (1<<4) -#define S3C2410_UFCON_RXTRIG12 (2<<4) - -/* S3C2440 FIFO trigger levels */ -#define S3C2440_UFCON_RXTRIG1 (0<<4) -#define S3C2440_UFCON_RXTRIG8 (1<<4) -#define S3C2440_UFCON_RXTRIG16 (2<<4) -#define S3C2440_UFCON_RXTRIG32 (3<<4) - -#define S3C2440_UFCON_TXTRIG0 (0<<6) -#define S3C2440_UFCON_TXTRIG16 (1<<6) -#define S3C2440_UFCON_TXTRIG32 (2<<6) -#define S3C2440_UFCON_TXTRIG48 (3<<6) - -#define S3C2410_UFCON_RESETBOTH (3<<1) -#define S3C2410_UFCON_RESETTX (1<<2) -#define S3C2410_UFCON_RESETRX (1<<1) - -#define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ - S3C2410_UFCON_TXTRIG0 | \ - S3C2410_UFCON_RXTRIG8 ) - -#define S3C2410_UMCOM_AFC (1<<4) -#define S3C2410_UMCOM_RTS_LOW (1<<0) - -#define S3C2412_UMCON_AFC_63 (0<<5) /* same as s3c2443 */ -#define S3C2412_UMCON_AFC_56 (1<<5) -#define S3C2412_UMCON_AFC_48 (2<<5) -#define S3C2412_UMCON_AFC_40 (3<<5) -#define S3C2412_UMCON_AFC_32 (4<<5) -#define S3C2412_UMCON_AFC_24 (5<<5) -#define S3C2412_UMCON_AFC_16 (6<<5) -#define S3C2412_UMCON_AFC_8 (7<<5) - -#define S3C2410_UFSTAT_TXFULL (1<<9) -#define S3C2410_UFSTAT_RXFULL (1<<8) -#define S3C2410_UFSTAT_TXMASK (15<<4) -#define S3C2410_UFSTAT_TXSHIFT (4) -#define S3C2410_UFSTAT_RXMASK (15<<0) -#define S3C2410_UFSTAT_RXSHIFT (0) - -/* UFSTAT S3C2443 same as S3C2440 */ -#define S3C2440_UFSTAT_TXFULL (1<<14) -#define S3C2440_UFSTAT_RXFULL (1<<6) -#define S3C2440_UFSTAT_TXSHIFT (8) -#define S3C2440_UFSTAT_RXSHIFT (0) -#define S3C2440_UFSTAT_TXMASK (63<<8) -#define S3C2440_UFSTAT_RXMASK (63) - -#define S3C2410_UTRSTAT_TXE (1<<2) -#define S3C2410_UTRSTAT_TXFE (1<<1) -#define S3C2410_UTRSTAT_RXDR (1<<0) - -#define S3C2410_UERSTAT_OVERRUN (1<<0) -#define S3C2410_UERSTAT_FRAME (1<<2) -#define S3C2410_UERSTAT_BREAK (1<<3) -#define S3C2443_UERSTAT_PARITY (1<<1) - -#define S3C2410_UERSTAT_ANY (S3C2410_UERSTAT_OVERRUN | \ - S3C2410_UERSTAT_FRAME | \ - S3C2410_UERSTAT_BREAK) - -#define S3C2410_UMSTAT_CTS (1<<0) -#define S3C2410_UMSTAT_DeltaCTS (1<<2) - -#define S3C2443_DIVSLOT (0x2C) - -/* S3C64XX interrupt registers. */ -#define S3C64XX_UINTP 0x30 -#define S3C64XX_UINTSP 0x34 -#define S3C64XX_UINTM 0x38 - -#define S3C64XX_UINTM_RXD (0) -#define S3C64XX_UINTM_TXD (2) -#define S3C64XX_UINTM_RXD_MSK (1 << S3C64XX_UINTM_RXD) -#define S3C64XX_UINTM_TXD_MSK (1 << S3C64XX_UINTM_TXD) - -/* Following are specific to S5PV210 */ -#define S5PV210_UCON_CLKMASK (1<<10) -#define S5PV210_UCON_CLKSHIFT (10) -#define S5PV210_UCON_PCLK (0<<10) -#define S5PV210_UCON_UCLK (1<<10) - -#define S5PV210_UFCON_TXTRIG0 (0<<8) -#define S5PV210_UFCON_TXTRIG4 (1<<8) -#define S5PV210_UFCON_TXTRIG8 (2<<8) -#define S5PV210_UFCON_TXTRIG16 (3<<8) -#define S5PV210_UFCON_TXTRIG32 (4<<8) -#define S5PV210_UFCON_TXTRIG64 (5<<8) -#define S5PV210_UFCON_TXTRIG128 (6<<8) -#define S5PV210_UFCON_TXTRIG256 (7<<8) - -#define S5PV210_UFCON_RXTRIG1 (0<<4) -#define S5PV210_UFCON_RXTRIG4 (1<<4) -#define S5PV210_UFCON_RXTRIG8 (2<<4) -#define S5PV210_UFCON_RXTRIG16 (3<<4) -#define S5PV210_UFCON_RXTRIG32 (4<<4) -#define S5PV210_UFCON_RXTRIG64 (5<<4) -#define S5PV210_UFCON_RXTRIG128 (6<<4) -#define S5PV210_UFCON_RXTRIG256 (7<<4) - -#define S5PV210_UFSTAT_TXFULL (1<<24) -#define S5PV210_UFSTAT_RXFULL (1<<8) -#define S5PV210_UFSTAT_TXMASK (255<<16) -#define S5PV210_UFSTAT_TXSHIFT (16) -#define S5PV210_UFSTAT_RXMASK (255<<0) -#define S5PV210_UFSTAT_RXSHIFT (0) - -#define S3C2410_UCON_CLKSEL0 (1 << 0) -#define S3C2410_UCON_CLKSEL1 (1 << 1) -#define S3C2410_UCON_CLKSEL2 (1 << 2) -#define S3C2410_UCON_CLKSEL3 (1 << 3) - -/* Default values for s5pv210 UCON and UFCON uart registers */ -#define S5PV210_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ - S3C2410_UCON_RXILEVEL | \ - S3C2410_UCON_TXIRQMODE | \ - S3C2410_UCON_RXIRQMODE | \ - S3C2410_UCON_RXFIFO_TOI | \ - S3C2443_UCON_RXERR_IRQEN) - -#define S5PV210_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ - S5PV210_UFCON_TXTRIG4 | \ - S5PV210_UFCON_RXTRIG4) - -#ifndef __ASSEMBLY__ - -/* configuration structure for per-machine configurations for the - * serial port - * - * the pointer is setup by the machine specific initialisation from the - * arch/arm/mach-s3c2410/ directory. -*/ - -struct s3c2410_uartcfg { - unsigned char hwport; /* hardware port number */ - unsigned char unused; - unsigned short flags; - upf_t uart_flags; /* default uart flags */ - unsigned int clk_sel; - - unsigned int has_fracval; - - unsigned long ucon; /* value of ucon for port */ - unsigned long ulcon; /* value of ulcon for port */ - unsigned long ufcon; /* value of ufcon for port */ -}; - -/* s3c24xx_uart_devs - * - * this is exported from the core as we cannot use driver_register(), - * or platform_add_device() before the console_initcall() -*/ - -extern struct platform_device *s3c24xx_uart_devs[4]; - -#endif /* __ASSEMBLY__ */ - -#endif /* __ASM_ARM_REGS_SERIAL_H */ - +#include diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index e91378b40d5..6a3695681fa 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -46,10 +47,9 @@ #include -#include - -#include +#ifdef CONFIG_SAMSUNG_CLOCK #include +#endif #include "samsung.h" diff --git a/include/linux/serial_s3c.h b/include/linux/serial_s3c.h new file mode 100644 index 00000000000..907d9d1d56c --- /dev/null +++ b/include/linux/serial_s3c.h @@ -0,0 +1,260 @@ +/* + * Internal header file for Samsung S3C2410 serial ports (UART0-2) + * + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * + * Additional defines, Copyright 2003 Simtec Electronics (linux@simtec.co.uk) + * + * Adapted from: + * + * Internal header file for MX1ADS serial ports (UART1 & 2) + * + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __ASM_ARM_REGS_SERIAL_H +#define __ASM_ARM_REGS_SERIAL_H + +#define S3C2410_URXH (0x24) +#define S3C2410_UTXH (0x20) +#define S3C2410_ULCON (0x00) +#define S3C2410_UCON (0x04) +#define S3C2410_UFCON (0x08) +#define S3C2410_UMCON (0x0C) +#define S3C2410_UBRDIV (0x28) +#define S3C2410_UTRSTAT (0x10) +#define S3C2410_UERSTAT (0x14) +#define S3C2410_UFSTAT (0x18) +#define S3C2410_UMSTAT (0x1C) + +#define S3C2410_LCON_CFGMASK ((0xF<<3)|(0x3)) + +#define S3C2410_LCON_CS5 (0x0) +#define S3C2410_LCON_CS6 (0x1) +#define S3C2410_LCON_CS7 (0x2) +#define S3C2410_LCON_CS8 (0x3) +#define S3C2410_LCON_CSMASK (0x3) + +#define S3C2410_LCON_PNONE (0x0) +#define S3C2410_LCON_PEVEN (0x5 << 3) +#define S3C2410_LCON_PODD (0x4 << 3) +#define S3C2410_LCON_PMASK (0x7 << 3) + +#define S3C2410_LCON_STOPB (1<<2) +#define S3C2410_LCON_IRM (1<<6) + +#define S3C2440_UCON_CLKMASK (3<<10) +#define S3C2440_UCON_CLKSHIFT (10) +#define S3C2440_UCON_PCLK (0<<10) +#define S3C2440_UCON_UCLK (1<<10) +#define S3C2440_UCON_PCLK2 (2<<10) +#define S3C2440_UCON_FCLK (3<<10) +#define S3C2443_UCON_EPLL (3<<10) + +#define S3C6400_UCON_CLKMASK (3<<10) +#define S3C6400_UCON_CLKSHIFT (10) +#define S3C6400_UCON_PCLK (0<<10) +#define S3C6400_UCON_PCLK2 (2<<10) +#define S3C6400_UCON_UCLK0 (1<<10) +#define S3C6400_UCON_UCLK1 (3<<10) + +#define S3C2440_UCON2_FCLK_EN (1<<15) +#define S3C2440_UCON0_DIVMASK (15 << 12) +#define S3C2440_UCON1_DIVMASK (15 << 12) +#define S3C2440_UCON2_DIVMASK (7 << 12) +#define S3C2440_UCON_DIVSHIFT (12) + +#define S3C2412_UCON_CLKMASK (3<<10) +#define S3C2412_UCON_CLKSHIFT (10) +#define S3C2412_UCON_UCLK (1<<10) +#define S3C2412_UCON_USYSCLK (3<<10) +#define S3C2412_UCON_PCLK (0<<10) +#define S3C2412_UCON_PCLK2 (2<<10) + +#define S3C2410_UCON_CLKMASK (1 << 10) +#define S3C2410_UCON_CLKSHIFT (10) +#define S3C2410_UCON_UCLK (1<<10) +#define S3C2410_UCON_SBREAK (1<<4) + +#define S3C2410_UCON_TXILEVEL (1<<9) +#define S3C2410_UCON_RXILEVEL (1<<8) +#define S3C2410_UCON_TXIRQMODE (1<<2) +#define S3C2410_UCON_RXIRQMODE (1<<0) +#define S3C2410_UCON_RXFIFO_TOI (1<<7) +#define S3C2443_UCON_RXERR_IRQEN (1<<6) +#define S3C2443_UCON_LOOPBACK (1<<5) + +#define S3C2410_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ + S3C2410_UCON_RXILEVEL | \ + S3C2410_UCON_TXIRQMODE | \ + S3C2410_UCON_RXIRQMODE | \ + S3C2410_UCON_RXFIFO_TOI) + +#define S3C2410_UFCON_FIFOMODE (1<<0) +#define S3C2410_UFCON_TXTRIG0 (0<<6) +#define S3C2410_UFCON_RXTRIG8 (1<<4) +#define S3C2410_UFCON_RXTRIG12 (2<<4) + +/* S3C2440 FIFO trigger levels */ +#define S3C2440_UFCON_RXTRIG1 (0<<4) +#define S3C2440_UFCON_RXTRIG8 (1<<4) +#define S3C2440_UFCON_RXTRIG16 (2<<4) +#define S3C2440_UFCON_RXTRIG32 (3<<4) + +#define S3C2440_UFCON_TXTRIG0 (0<<6) +#define S3C2440_UFCON_TXTRIG16 (1<<6) +#define S3C2440_UFCON_TXTRIG32 (2<<6) +#define S3C2440_UFCON_TXTRIG48 (3<<6) + +#define S3C2410_UFCON_RESETBOTH (3<<1) +#define S3C2410_UFCON_RESETTX (1<<2) +#define S3C2410_UFCON_RESETRX (1<<1) + +#define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ + S3C2410_UFCON_TXTRIG0 | \ + S3C2410_UFCON_RXTRIG8 ) + +#define S3C2410_UMCOM_AFC (1<<4) +#define S3C2410_UMCOM_RTS_LOW (1<<0) + +#define S3C2412_UMCON_AFC_63 (0<<5) /* same as s3c2443 */ +#define S3C2412_UMCON_AFC_56 (1<<5) +#define S3C2412_UMCON_AFC_48 (2<<5) +#define S3C2412_UMCON_AFC_40 (3<<5) +#define S3C2412_UMCON_AFC_32 (4<<5) +#define S3C2412_UMCON_AFC_24 (5<<5) +#define S3C2412_UMCON_AFC_16 (6<<5) +#define S3C2412_UMCON_AFC_8 (7<<5) + +#define S3C2410_UFSTAT_TXFULL (1<<9) +#define S3C2410_UFSTAT_RXFULL (1<<8) +#define S3C2410_UFSTAT_TXMASK (15<<4) +#define S3C2410_UFSTAT_TXSHIFT (4) +#define S3C2410_UFSTAT_RXMASK (15<<0) +#define S3C2410_UFSTAT_RXSHIFT (0) + +/* UFSTAT S3C2443 same as S3C2440 */ +#define S3C2440_UFSTAT_TXFULL (1<<14) +#define S3C2440_UFSTAT_RXFULL (1<<6) +#define S3C2440_UFSTAT_TXSHIFT (8) +#define S3C2440_UFSTAT_RXSHIFT (0) +#define S3C2440_UFSTAT_TXMASK (63<<8) +#define S3C2440_UFSTAT_RXMASK (63) + +#define S3C2410_UTRSTAT_TXE (1<<2) +#define S3C2410_UTRSTAT_TXFE (1<<1) +#define S3C2410_UTRSTAT_RXDR (1<<0) + +#define S3C2410_UERSTAT_OVERRUN (1<<0) +#define S3C2410_UERSTAT_FRAME (1<<2) +#define S3C2410_UERSTAT_BREAK (1<<3) +#define S3C2443_UERSTAT_PARITY (1<<1) + +#define S3C2410_UERSTAT_ANY (S3C2410_UERSTAT_OVERRUN | \ + S3C2410_UERSTAT_FRAME | \ + S3C2410_UERSTAT_BREAK) + +#define S3C2410_UMSTAT_CTS (1<<0) +#define S3C2410_UMSTAT_DeltaCTS (1<<2) + +#define S3C2443_DIVSLOT (0x2C) + +/* S3C64XX interrupt registers. */ +#define S3C64XX_UINTP 0x30 +#define S3C64XX_UINTSP 0x34 +#define S3C64XX_UINTM 0x38 + +#define S3C64XX_UINTM_RXD (0) +#define S3C64XX_UINTM_TXD (2) +#define S3C64XX_UINTM_RXD_MSK (1 << S3C64XX_UINTM_RXD) +#define S3C64XX_UINTM_TXD_MSK (1 << S3C64XX_UINTM_TXD) + +/* Following are specific to S5PV210 */ +#define S5PV210_UCON_CLKMASK (1<<10) +#define S5PV210_UCON_CLKSHIFT (10) +#define S5PV210_UCON_PCLK (0<<10) +#define S5PV210_UCON_UCLK (1<<10) + +#define S5PV210_UFCON_TXTRIG0 (0<<8) +#define S5PV210_UFCON_TXTRIG4 (1<<8) +#define S5PV210_UFCON_TXTRIG8 (2<<8) +#define S5PV210_UFCON_TXTRIG16 (3<<8) +#define S5PV210_UFCON_TXTRIG32 (4<<8) +#define S5PV210_UFCON_TXTRIG64 (5<<8) +#define S5PV210_UFCON_TXTRIG128 (6<<8) +#define S5PV210_UFCON_TXTRIG256 (7<<8) + +#define S5PV210_UFCON_RXTRIG1 (0<<4) +#define S5PV210_UFCON_RXTRIG4 (1<<4) +#define S5PV210_UFCON_RXTRIG8 (2<<4) +#define S5PV210_UFCON_RXTRIG16 (3<<4) +#define S5PV210_UFCON_RXTRIG32 (4<<4) +#define S5PV210_UFCON_RXTRIG64 (5<<4) +#define S5PV210_UFCON_RXTRIG128 (6<<4) +#define S5PV210_UFCON_RXTRIG256 (7<<4) + +#define S5PV210_UFSTAT_TXFULL (1<<24) +#define S5PV210_UFSTAT_RXFULL (1<<8) +#define S5PV210_UFSTAT_TXMASK (255<<16) +#define S5PV210_UFSTAT_TXSHIFT (16) +#define S5PV210_UFSTAT_RXMASK (255<<0) +#define S5PV210_UFSTAT_RXSHIFT (0) + +#define S3C2410_UCON_CLKSEL0 (1 << 0) +#define S3C2410_UCON_CLKSEL1 (1 << 1) +#define S3C2410_UCON_CLKSEL2 (1 << 2) +#define S3C2410_UCON_CLKSEL3 (1 << 3) + +/* Default values for s5pv210 UCON and UFCON uart registers */ +#define S5PV210_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ + S3C2410_UCON_RXILEVEL | \ + S3C2410_UCON_TXIRQMODE | \ + S3C2410_UCON_RXIRQMODE | \ + S3C2410_UCON_RXFIFO_TOI | \ + S3C2443_UCON_RXERR_IRQEN) + +#define S5PV210_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ + S5PV210_UFCON_TXTRIG4 | \ + S5PV210_UFCON_RXTRIG4) + +#ifndef __ASSEMBLY__ + +/* configuration structure for per-machine configurations for the + * serial port + * + * the pointer is setup by the machine specific initialisation from the + * arch/arm/mach-s3c2410/ directory. +*/ + +struct s3c2410_uartcfg { + unsigned char hwport; /* hardware port number */ + unsigned char unused; + unsigned short flags; + upf_t uart_flags; /* default uart flags */ + unsigned int clk_sel; + + unsigned int has_fracval; + + unsigned long ucon; /* value of ucon for port */ + unsigned long ulcon; /* value of ulcon for port */ + unsigned long ufcon; /* value of ufcon for port */ +}; + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_ARM_REGS_SERIAL_H */ + -- cgit v1.2.3-18-g5258 From 84f57d9e3685500a7447fc09a6891ce218869174 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Apr 2013 02:04:49 +0200 Subject: tty: serial/samsung: fix modular build There are a few bugs in the samsung serial driver when built as a loadable module, which makes the console code unavailable, as well as giving no access to the 'printascii' early debug function. This adds the appropriate compile time conditionals. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 4 ++-- drivers/tty/serial/samsung.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 6a3695681fa..074b9194144 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -898,7 +898,7 @@ console_initcall(s3c24xx_serial_console_init); #define S3C24XX_SERIAL_CONSOLE NULL #endif -#ifdef CONFIG_CONSOLE_POLL +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) static int s3c24xx_serial_get_poll_char(struct uart_port *port); static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c); @@ -922,7 +922,7 @@ static struct uart_ops s3c24xx_serial_ops = { .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, -#ifdef CONFIG_CONSOLE_POLL +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) .poll_get_char = s3c24xx_serial_get_poll_char, .poll_put_char = s3c24xx_serial_put_poll_char, #endif diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 1a4bca3e417..00a499ecd38 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -76,7 +76,9 @@ struct s3c24xx_uart_port { #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) -#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG +#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \ + defined(CONFIG_DEBUG_LL) && \ + !defined(MODULE) extern void printascii(const char *); -- cgit v1.2.3-18-g5258 From 5ea5b24da80322b8136cb000a7340cdc29a3d6dc Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:24 +0300 Subject: serial: 8250_dma: TX cleanup Removing one unneeded uart_write_wakeup(). There is no need to start PIO transfer unless DMA fails, so this also changes serial8250_tx_dma() to return 0 unless that is the case. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index b9f7fd28112..ce2518d5dfd 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -33,10 +33,8 @@ static void __dma_tx_complete(void *param) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&p->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) serial8250_tx_dma(p); - uart_write_wakeup(&p->port); - } } static void __dma_rx_complete(void *param) @@ -67,12 +65,11 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct circ_buf *xmit = &p->port.state->xmit; struct dma_async_tx_descriptor *desc; - if (dma->tx_running) - return -EBUSY; + if (uart_tx_stopped(&p->port) || dma->tx_running || + uart_circ_empty(xmit)) + return 0; dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - if (!dma->tx_size) - return -EINVAL; desc = dmaengine_prep_slave_single(dma->txchan, dma->tx_addr + xmit->tail, -- cgit v1.2.3-18-g5258 From 75df022b5f8982a375adb04e9e4c0a34a9689ed9 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:25 +0300 Subject: serial: 8250_dma: Fix RX handling Overrun, parity and framing errors should be handled in 8250_core. This also adds check for the dma_status and exits if the channel is not idle. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index ce2518d5dfd..66430614510 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -101,20 +101,29 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) struct dma_tx_state state; int dma_status; - /* - * If RCVR FIFO trigger level was not reached, complete the transfer and - * let 8250.c copy the remaining data. - */ - if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { - dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, - &state); + dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + + switch (iir & 0x3f) { + case UART_IIR_RLSI: + /* 8250_core handles errors and break interrupts */ + return -EIO; + case UART_IIR_RX_TIMEOUT: + /* + * If RCVR FIFO trigger level was not reached, complete the + * transfer and let 8250_core copy the remaining data. + */ if (dma_status == DMA_IN_PROGRESS) { dmaengine_pause(dma->rxchan); __dma_rx_complete(p); } return -ETIMEDOUT; + default: + break; } + if (dma_status) + return 0; + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -- cgit v1.2.3-18-g5258 From e4fb3b88ebf21e6127ed0e91336752352ba0931e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:26 +0300 Subject: serial: 8250_dma: Use dmaengine helpers to get the slave channels The helper functions in dmaengine API allow the drivers to request slave channels without the filter parameters. They will attempt to extract the needed DMA client information from DT or ACPI, but if such information is not available the filter parameters can still be used. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 66430614510..fdb6139e8c1 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -156,14 +156,18 @@ int serial8250_request_dma(struct uart_8250_port *p) dma_cap_set(DMA_SLAVE, mask); /* Get a channel for RX */ - dma->rxchan = dma_request_channel(mask, dma->fn, dma->rx_param); + dma->rxchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->rx_param, + p->port.dev, "rx"); if (!dma->rxchan) return -ENODEV; dmaengine_slave_config(dma->rxchan, &dma->rxconf); /* Get a channel for TX */ - dma->txchan = dma_request_channel(mask, dma->fn, dma->tx_param); + dma->txchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->tx_param, + p->port.dev, "tx"); if (!dma->txchan) { dma_release_channel(dma->rxchan); return -ENODEV; -- cgit v1.2.3-18-g5258 From ab19479aa1b9708110bc90ba5b6c03b013feaa46 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:27 +0300 Subject: serial: 8250_dma: Provide default slave configuration parameters Some slave channel parameters will be always the same. For example, direction for the Rx channel will always be DMA_DEV_TO_MEM and DMA_MEM_TO_DEV for Tx channel. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index fdb6139e8c1..7046769608d 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -149,8 +149,14 @@ int serial8250_request_dma(struct uart_8250_port *p) struct uart_8250_dma *dma = p->dma; dma_cap_mask_t mask; - dma->rxconf.src_addr = p->port.mapbase + UART_RX; - dma->txconf.dst_addr = p->port.mapbase + UART_TX; + /* Default slave configuration parameters */ + dma->rxconf.direction = DMA_DEV_TO_MEM; + dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->rxconf.src_addr = p->port.mapbase + UART_RX; + + dma->txconf.direction = DMA_MEM_TO_DEV; + dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->txconf.dst_addr = p->port.mapbase + UART_TX; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); -- cgit v1.2.3-18-g5258 From ffc3ae6dd925b6a89b2f4f993a732e3b866e6aa0 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:28 +0300 Subject: serial: 8250_dw: Enable runtime PM This allows ACPI to put the device to D3 when it's not used. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 61 +++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 3dedd2470db..d6aea85a9b1 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "8250.h" @@ -115,6 +116,18 @@ static int dw8250_handle_irq(struct uart_port *p) return 0; } +static void +dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + if (!state) + pm_runtime_get_sync(port->dev); + + serial8250_do_pm(port, state, old); + + if (state) + pm_runtime_put_sync_suspend(port->dev); +} + static int dw8250_probe_of(struct uart_port *p) { struct device_node *np = p->dev->of_node; @@ -293,6 +306,7 @@ static int dw8250_probe(struct platform_device *pdev) uart.port.mapbase = regs->start; uart.port.irq = irq->start; uart.port.handle_irq = dw8250_handle_irq; + uart.port.pm = dw8250_do_pm; uart.port.type = PORT_8250; uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; uart.port.dev = &pdev->dev; @@ -336,6 +350,9 @@ static int dw8250_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; } @@ -343,37 +360,64 @@ static int dw8250_remove(struct platform_device *pdev) { struct dw8250_data *data = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + serial8250_unregister_port(data->line); if (!IS_ERR(data->clk)) clk_disable_unprepare(data->clk); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return 0; } #ifdef CONFIG_PM -static int dw8250_suspend(struct platform_device *pdev, pm_message_t state) +static int dw8250_suspend(struct device *dev) { - struct dw8250_data *data = platform_get_drvdata(pdev); + struct dw8250_data *data = dev_get_drvdata(dev); serial8250_suspend_port(data->line); return 0; } -static int dw8250_resume(struct platform_device *pdev) +static int dw8250_resume(struct device *dev) { - struct dw8250_data *data = platform_get_drvdata(pdev); + struct dw8250_data *data = dev_get_drvdata(dev); serial8250_resume_port(data->line); return 0; } -#else -#define dw8250_suspend NULL -#define dw8250_resume NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_PM_RUNTIME +static int dw8250_runtime_suspend(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + clk_disable_unprepare(data->clk); + + return 0; +} + +static int dw8250_runtime_resume(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + clk_prepare_enable(data->clk); + + return 0; +} +#endif + +static const struct dev_pm_ops dw8250_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume) + SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) +}; + static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, { /* Sentinel */ } @@ -391,13 +435,12 @@ static struct platform_driver dw8250_platform_driver = { .driver = { .name = "dw-apb-uart", .owner = THIS_MODULE, + .pm = &dw8250_pm_ops, .of_match_table = dw8250_of_match, .acpi_match_table = ACPI_PTR(dw8250_acpi_match), }, .probe = dw8250_probe, .remove = dw8250_remove, - .suspend = dw8250_suspend, - .resume = dw8250_resume, }; module_platform_driver(dw8250_platform_driver); -- cgit v1.2.3-18-g5258 From aea02e87f6076f2a29d537829503e1344e92a3b8 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:29 +0300 Subject: serial: 8250_dw: Support clk framework also with ACPI The Lynxpoint LPSS peripheral clocks are now handled in clk framework so the drivers do not need to take care of them manually. In dw8250_probe_acpi(), the uartclk is now taken from the driver_data only if it was not already set. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index d6aea85a9b1..de7a1869c82 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -36,9 +36,6 @@ #define DW_UART_CPR 0xf4 /* Component Parameter Register */ #define DW_UART_UCV 0xf8 /* UART Component Version */ -/* Intel Low Power Subsystem specific */ -#define LPSS_PRV_CLOCK_PARAMS 0x800 - /* Component Parameter Register bits */ #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) #define DW_UART_CPR_AFCE_MODE (1 << 4) @@ -226,7 +223,6 @@ static int dw8250_probe_acpi(struct uart_port *p) { const struct acpi_device_id *id; acpi_status status; - u32 reg; id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); if (!id) @@ -236,7 +232,9 @@ static int dw8250_probe_acpi(struct uart_port *p) p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; p->regshift = 2; - p->uartclk = (unsigned int)id->driver_data; + + if (!p->uartclk) + p->uartclk = (unsigned int)id->driver_data; status = acpi_walk_resources(ACPI_HANDLE(p->dev), METHOD_NAME__CRS, dw8250_acpi_walk_resource, p); @@ -246,12 +244,6 @@ static int dw8250_probe_acpi(struct uart_port *p) return -ENODEV; } - /* Fix Haswell issue where the clocks do not get enabled */ - if (!strcmp(id->id, "INT33C4") || !strcmp(id->id, "INT33C5")) { - reg = readl(p->membase + LPSS_PRV_CLOCK_PARAMS); - writel(reg | 1, p->membase + LPSS_PRV_CLOCK_PARAMS); - } - return 0; } #else @@ -425,8 +417,8 @@ static const struct of_device_id dw8250_of_match[] = { MODULE_DEVICE_TABLE(of, dw8250_of_match); static const struct acpi_device_id dw8250_acpi_match[] = { - { "INT33C4", 100000000 }, - { "INT33C5", 100000000 }, + { "INT33C4", 0 }, + { "INT33C5", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); -- cgit v1.2.3-18-g5258 From 94b2b47cf68ce4633a1dbe9d221617404b5de03b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:30 +0300 Subject: serial: 8250_dw: Let ACPI code extract the DMA client info The new ACPI DMA helpers in dmaengine API can take care of extracting all the necessary information regarding DMA. The driver does not need to do this separately any more. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 75 +++++---------------------------------- 1 file changed, 9 insertions(+), 66 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index de7a1869c82..09aaea2d3a6 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -163,66 +163,10 @@ static int dw8250_probe_of(struct uart_port *p) } #ifdef CONFIG_ACPI -static bool dw8250_acpi_dma_filter(struct dma_chan *chan, void *parm) -{ - return chan->chan_id == *(int *)parm; -} - -static acpi_status -dw8250_acpi_walk_resource(struct acpi_resource *res, void *data) -{ - struct uart_port *p = data; - struct uart_8250_port *port; - struct uart_8250_dma *dma; - struct acpi_resource_fixed_dma *fixed_dma; - struct dma_slave_config *slave; - - port = container_of(p, struct uart_8250_port, port); - - switch (res->type) { - case ACPI_RESOURCE_TYPE_FIXED_DMA: - fixed_dma = &res->data.fixed_dma; - - /* TX comes first */ - if (!port->dma) { - dma = devm_kzalloc(p->dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return AE_NO_MEMORY; - - port->dma = dma; - slave = &dma->txconf; - - slave->direction = DMA_MEM_TO_DEV; - slave->dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave->slave_id = fixed_dma->request_lines; - slave->dst_maxburst = port->tx_loadsz / 4; - - dma->tx_chan_id = fixed_dma->channels; - dma->tx_param = &dma->tx_chan_id; - dma->fn = dw8250_acpi_dma_filter; - } else { - dma = port->dma; - slave = &dma->rxconf; - - slave->direction = DMA_DEV_TO_MEM; - slave->src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave->slave_id = fixed_dma->request_lines; - slave->src_maxburst = p->fifosize / 4; - - dma->rx_chan_id = fixed_dma->channels; - dma->rx_param = &dma->rx_chan_id; - } - - break; - } - - return AE_OK; -} - -static int dw8250_probe_acpi(struct uart_port *p) +static int dw8250_probe_acpi(struct uart_8250_port *up) { const struct acpi_device_id *id; - acpi_status status; + struct uart_port *p = &up->port; id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); if (!id) @@ -236,13 +180,12 @@ static int dw8250_probe_acpi(struct uart_port *p) if (!p->uartclk) p->uartclk = (unsigned int)id->driver_data; - status = acpi_walk_resources(ACPI_HANDLE(p->dev), METHOD_NAME__CRS, - dw8250_acpi_walk_resource, p); - if (ACPI_FAILURE(status)) { - dev_err_ratelimited(p->dev, "%s failed \"%s\"\n", __func__, - acpi_format_exception(status)); - return -ENODEV; - } + up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL); + if (!up->dma) + return -ENOMEM; + + up->dma->rxconf.src_maxburst = p->fifosize / 4; + up->dma->txconf.dst_maxburst = p->fifosize / 4; return 0; } @@ -329,7 +272,7 @@ static int dw8250_probe(struct platform_device *pdev) if (err) return err; } else if (ACPI_HANDLE(&pdev->dev)) { - err = dw8250_probe_acpi(&uart.port); + err = dw8250_probe_acpi(&uart); if (err) return err; } else { -- cgit v1.2.3-18-g5258 From 2920adb6dfeded0b835a809a1a2c5772d97e5965 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 10 Apr 2013 16:58:31 +0300 Subject: serial: 8250_dw: Set port capabilities based on CPR register The Designware UART has an optional support for 16750 compatible Auto Flow Control. This will enable it based on the AFCE bit in Component Parameter Register. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 09aaea2d3a6..b7c7d6a6cad 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -221,7 +221,11 @@ static void dw8250_setup_port(struct uart_8250_port *up) p->flags |= UPF_FIXED_TYPE; p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); up->tx_loadsz = p->fifosize; + up->capabilities = UART_CAP_FIFO; } + + if (reg & DW_UART_CPR_AFCE_MODE) + up->capabilities |= UART_CAP_AFE; } static int dw8250_probe(struct platform_device *pdev) -- cgit v1.2.3-18-g5258 From b88d0826ecb949f62597d4733e50e589058c20b4 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 11 Apr 2013 15:43:21 +0300 Subject: serial: 8250_dw: Convert to devm_ioremap() Use resource managed ioremap. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index b7c7d6a6cad..39b7ae465b5 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -250,7 +250,8 @@ static int dw8250_probe(struct platform_device *pdev) uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; uart.port.dev = &pdev->dev; - uart.port.membase = ioremap(regs->start, resource_size(regs)); + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); if (!uart.port.membase) return -ENOMEM; -- cgit v1.2.3-18-g5258 From 3ec857ffe3ba6e9f567a12d3575dd09d2d41a208 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 12 Apr 2013 12:44:27 +0300 Subject: serial: 8250_dw: Fix the stub for dw8250_probe_acpi() This fixes the stub for dw8250_probe_acpi() that is used when compiling without ACPI enabled. The argument type was wrong. Reported-by: kbuild test robot Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 39b7ae465b5..beaa283f5cc 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -190,7 +190,7 @@ static int dw8250_probe_acpi(struct uart_8250_port *up) return 0; } #else -static inline int dw8250_probe_acpi(struct uart_port *p) +static inline int dw8250_probe_acpi(struct uart_8250_port *up) { return -ENODEV; } -- cgit v1.2.3-18-g5258 From 78c6cccadb6ead2dab503f95aefdbfb2ae409a32 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 12 Apr 2013 10:32:28 +0200 Subject: TTY: synclink: fix DTR being raised on hang up Make sure to check ASYNC_INITIALISED before raising DTR when waking up from blocked open in block_til_ready. Currently DTR could get raised at hang up as a blocked process would raise DTR unconditionally before checking for hang up and returning. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 72d607101f9..8eaf1ab8add 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3308,7 +3308,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, port->blocked_open++; while (1) { - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); -- cgit v1.2.3-18-g5258 From 5d8be15a621d739c376572607bab1ac0449947a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 12 Apr 2013 10:32:29 +0200 Subject: TTY: synclink_gt: fix DTR being raised on hang up Make sure to check ASYNC_INITIALISED before raising DTR when waking up from blocked open in block_til_ready. Currently DTR could get raised at hang up as a blocked process would raise DTR unconditionally before checking for hang up and returning. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclink_gt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aa9eece35c3..1abf946463f 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3308,7 +3308,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if ((tty->termios.c_cflag & CBAUD)) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); -- cgit v1.2.3-18-g5258 From 53a72d57515b2c94af616bceab79751f894eca56 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 12 Apr 2013 10:32:30 +0200 Subject: TTY: synclinkmp: fix DTR being raised on hang up Make sure to check ASYNC_INITIALISED before raising DTR when waking up from blocked open in block_til_ready. Currently DTR could get raised at hang up as a blocked process would raise DTR unconditionally before checking for hang up and returning. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclinkmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 6d5780cf1d5..ff171384ea5 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3329,7 +3329,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); -- cgit v1.2.3-18-g5258 From e1c969f7814dcd23bbfe8d777544321a492b6fee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 12 Apr 2013 10:32:31 +0200 Subject: TTY: ircomm: fix DTR being raised on hang up Make sure to check ASYNC_INITIALISED before raising DTR when waking up from blocked open in ircomm_tty_block_til_ready. Currently DTR could get raised at hang up as a blocked process would raise DTR unconditionally before checking for hang up and returning. Signed-off-by: Johan Hovold Acked-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/irda/ircomm/ircomm_tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 362ba47968e..41ac7938268 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -328,7 +328,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, spin_unlock_irqrestore(&port->lock, flags); while (1) { - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); -- cgit v1.2.3-18-g5258 From f678c440022eb6274840cd11edd52a5dcc7bf1b3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 15 Apr 2013 07:52:06 +0200 Subject: TTY: rocket, fix compilation warning When CONFIG_PCI is unset, we see these warnings in rocket: drivers/tty/rocket.c:3140:13: warning: 'rmSpeakerReset' defined but not used drivers/tty/rocket.c:2599:12: warning: 'sPCIInitController' defined but not used Fix those by moving the functions to one place and make them depend on CONFIG_PCI. Signed-off-by: Jiri Slaby Reported-by: Toralf Foerster Signed-off-by: Greg Kroah-Hartman --- drivers/tty/rocket.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index f5abc288882..82d35c5a58f 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -2519,6 +2519,7 @@ static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, return (CtlP->NumAiop); } +#ifdef CONFIG_PCI /*************************************************************************** Function: sPCIInitController Purpose: Initialization of controller global registers and controller @@ -2639,6 +2640,26 @@ static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, return (CtlP->NumAiop); } +/* Resets the speaker controller on RocketModem II and III devices */ +static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) +{ + ByteIO_t addr; + + /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ + if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { + addr = CtlP->AiopIO[0] + 0x4F; + sOutB(addr, 0); + } + + /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ + if ((model == MODEL_UPCI_RM3_8PORT) + || (model == MODEL_UPCI_RM3_4PORT)) { + addr = CtlP->AiopIO[0] + 0x88; + sOutB(addr, 0); + } +} +#endif + /*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. @@ -3128,25 +3149,6 @@ static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) sOutB(addr + chan, 0); /* apply or remove reset */ } -/* Resets the speaker controller on RocketModem II and III devices */ -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) -{ - ByteIO_t addr; - - /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ - if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { - addr = CtlP->AiopIO[0] + 0x4F; - sOutB(addr, 0); - } - - /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ - if ((model == MODEL_UPCI_RM3_8PORT) - || (model == MODEL_UPCI_RM3_4PORT)) { - addr = CtlP->AiopIO[0] + 0x88; - sOutB(addr, 0); - } -} - /* Returns the line number given the controller (board), aiop and channel number */ static unsigned char GetLineNumber(int ctrl, int aiop, int ch) { -- cgit v1.2.3-18-g5258 From f4b208eb91776b0522b41c78fac6cf08134db78f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 15 Apr 2013 07:52:05 +0200 Subject: TTY: pty, fix compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CONFIG_UNIX98_PTYS is unset, we see this warning in pty: drivers/tty/pty.c:409:13: warning: ‘pty_unix98_shutdown’ defined but not used Fix that by moving the function to a section which depends on that config. Signed-off-by: Jiri Slaby Reported-by: Toralf Foerster Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 71e456aa636..a62798fcc01 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -405,12 +405,6 @@ err: return retval; } -/* this is called once with whichever end is closed last */ -static void pty_unix98_shutdown(struct tty_struct *tty) -{ - devpts_kill_index(tty->driver_data, tty->index); -} - static void pty_cleanup(struct tty_struct *tty) { tty_port_put(tty->port); @@ -626,6 +620,12 @@ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { } +/* this is called once with whichever end is closed last */ +static void pty_unix98_shutdown(struct tty_struct *tty) +{ + devpts_kill_index(tty->driver_data, tty->index); +} + static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, .install = pty_unix98_install, -- cgit v1.2.3-18-g5258 From 289b8dd695836fc295cf3cb4e1e520322495556c Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 13 Apr 2013 08:46:57 +0400 Subject: serial: sccnxp: Do not override device name Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 08dbfb88d42..b1d04aabd50 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -789,8 +789,6 @@ static int sccnxp_probe(struct platform_device *pdev) return -EADDRNOTAVAIL; } - dev_set_name(&pdev->dev, SCCNXP_NAME); - s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL); if (!s) { dev_err(&pdev->dev, "Error allocating port structure\n"); -- cgit v1.2.3-18-g5258 From 31815c08fc90f44d6165034fd473f23df5d31449 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 13 Apr 2013 08:46:58 +0400 Subject: serial: sccnxp: Replace pdata.init/exit with regulator API Typical usage of pdata.init/exit is enable/disable power and/or toggle reset for the target chip. This patch replaces these callbacks with regulator API. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sccnxp.c | 21 +++++++++++++++------ include/linux/platform_data/serial-sccnxp.h | 4 ---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index b1d04aabd50..c7730415541 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -27,6 +27,7 @@ #include #include #include +#include #define SCCNXP_NAME "uart-sccnxp" #define SCCNXP_MAJOR 204 @@ -131,6 +132,8 @@ struct sccnxp_port { struct timer_list timer; struct sccnxp_pdata pdata; + + struct regulator *regulator; }; static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift) @@ -916,6 +919,16 @@ static int sccnxp_probe(struct platform_device *pdev) goto err_out; } + s->regulator = devm_regulator_get(&pdev->dev, "VCC"); + if (!IS_ERR(s->regulator)) { + ret = regulator_enable(s->regulator); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable regulator: %i\n", ret); + return ret; + } + } + membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(membase)) { ret = PTR_ERR(membase); @@ -965,10 +978,6 @@ static int sccnxp_probe(struct platform_device *pdev) s->imr = 0; sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0); - /* Board specific configure */ - if (s->pdata.init) - s->pdata.init(); - if (!s->poll) { ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, @@ -1009,8 +1018,8 @@ static int sccnxp_remove(struct platform_device *pdev) uart_unregister_driver(&s->uart); platform_set_drvdata(pdev, NULL); - if (s->pdata.exit) - s->pdata.exit(); + if (!IS_ERR(s->regulator)) + return regulator_disable(s->regulator); return 0; } diff --git a/include/linux/platform_data/serial-sccnxp.h b/include/linux/platform_data/serial-sccnxp.h index 215574d1e81..bdc510d0324 100644 --- a/include/linux/platform_data/serial-sccnxp.h +++ b/include/linux/platform_data/serial-sccnxp.h @@ -86,10 +86,6 @@ struct sccnxp_pdata { const u32 mctrl_cfg[SCCNXP_MAX_UARTS]; /* Timer value for polling mode (usecs) */ const unsigned int poll_time_us; - /* Called during startup */ - void (*init)(void); - /* Called before finish */ - void (*exit)(void); }; #endif -- cgit v1.2.3-18-g5258 From 579a00a5c2f0a85fb4602a53e2c1beb77a646528 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 15 Apr 2013 11:06:06 -0400 Subject: tty: Fix unsafe bit ops in tty_throttle_safe/unthrottle_safe tty->flags needs to be atomically modified. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index d119034877d..3500d411414 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -156,7 +156,7 @@ int tty_throttle_safe(struct tty_struct *tty) if (tty->flow_change != TTY_THROTTLE_SAFE) ret = 1; else { - __set_bit(TTY_THROTTLED, &tty->flags); + set_bit(TTY_THROTTLED, &tty->flags); if (tty->ops->throttle) tty->ops->throttle(tty); } @@ -187,7 +187,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) if (tty->flow_change != TTY_UNTHROTTLE_SAFE) ret = 1; else { - __clear_bit(TTY_THROTTLED, &tty->flags); + clear_bit(TTY_THROTTLED, &tty->flags); if (tty->ops->unthrottle) tty->ops->unthrottle(tty); } -- cgit v1.2.3-18-g5258 From 5a65dcc04cda41f4122aacc37a5a348454645399 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Mon, 15 Apr 2013 16:01:07 +0200 Subject: serial_core.c: add put_device() after device_find_child() The serial core uses device_find_child() but does not drop the reference to the retrieved child after using it. This patch add the missing put_device(). What I have done to test this issue. I used a machine with an AMBA PL011 serial driver. I tested the patch on next-20120408 because the last branch [next-20120415] does not boot on this board. For test purpose, I added some pr_info() messages to print the refcount after device_find_child() (lines: 1937,2009), and after put_device() (lines: 1947, 2021). Boot the machine *without* put_device(). Then: echo reboot > /sys/power/disk echo disk > /sys/power/state [ 87.058575] uart_suspend_port:1937 refcount 4 [ 87.058582] uart_suspend_port:1947 refcount 4 [ 87.098083] uart_resume_port:2009refcount 5 [ 87.098088] uart_resume_port:2021 refcount 5 echo disk > /sys/power/state [ 103.055574] uart_suspend_port:1937 refcount 6 [ 103.055580] uart_suspend_port:1947 refcount 6 [ 103.095322] uart_resume_port:2009 refcount 7 [ 103.095327] uart_resume_port:2021 refcount 7 echo disk > /sys/power/state [ 252.459580] uart_suspend_port:1937 refcount 8 [ 252.459586] uart_suspend_port:1947 refcount 8 [ 252.499611] uart_resume_port:2009 refcount 9 [ 252.499616] uart_resume_port:2021 refcount 9 The refcount continuously increased. Boot the machine *with* this patch. Then: echo reboot > /sys/power/disk echo disk > /sys/power/state [ 159.333559] uart_suspend_port:1937 refcount 4 [ 159.333566] uart_suspend_port:1947 refcount 3 [ 159.372751] uart_resume_port:2009 refcount 4 [ 159.372755] uart_resume_port:2021 refcount 3 echo disk > /sys/power/state [ 185.713614] uart_suspend_port:1937 refcount 4 [ 185.713621] uart_suspend_port:1947 refcount 3 [ 185.752935] uart_resume_port:2009 refcount 4 [ 185.752940] uart_resume_port:2021 refcount 3 echo disk > /sys/power/state [ 207.458584] uart_suspend_port:1937 refcount 4 [ 207.458591] uart_suspend_port:1947 refcount 3 [ 207.498598] uart_resume_port:2009 refcount 4 [ 207.498605] uart_resume_port:2021 refcount 3 The refcount correctly handled. Signed-off-by: Federico Vaga Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a400002dfa8..8fbb6d22cdc 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1941,6 +1941,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) mutex_unlock(&port->mutex); return 0; } + put_device(tty_dev); + if (console_suspend_enabled || !uart_console(uport)) uport->suspended = 1; @@ -2006,9 +2008,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) disable_irq_wake(uport->irq); uport->irq_wake = 0; } + put_device(tty_dev); mutex_unlock(&port->mutex); return 0; } + put_device(tty_dev); uport->suspended = 0; /* -- cgit v1.2.3-18-g5258 From 78506f223a7bb7d47b1772592932921689adc6d8 Mon Sep 17 00:00:00 2001 From: Jongsung Kim Date: Mon, 15 Apr 2013 14:45:25 +0900 Subject: ARM: PL011: add support for extended FIFO-size of PL011-r1p5 The latest r1p5-revision of the ARM PL011 UART has 32-byte FIFOs, while all earlier ones have 16-byte FIFOs. This patch suggests a way to set the FIFO-size correctly & flexibly by using a member function named get_fifosize, rather than using the fifosize member variable. The function takes the UARTPeriphID, and returns the correct FIFO size. Signed-off-by: Jongsung Kim Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 6cf861efb2a..b2e9e177a35 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -73,32 +73,44 @@ /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; - unsigned int fifosize; unsigned int lcrh_tx; unsigned int lcrh_rx; bool oversampling; bool dma_threshold; bool cts_event_workaround; + + unsigned int (*get_fifosize)(unsigned int periphid); }; +static unsigned int get_fifosize_arm(unsigned int periphid) +{ + unsigned int rev = (periphid >> 20) & 0xf; + return rev < 3 ? 16 : 32; +} + static struct vendor_data vendor_arm = { .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, - .fifosize = 16, .lcrh_tx = UART011_LCRH, .lcrh_rx = UART011_LCRH, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, + .get_fifosize = get_fifosize_arm, }; +static unsigned int get_fifosize_st(unsigned int periphid) +{ + return 64; +} + static struct vendor_data vendor_st = { .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, - .fifosize = 64, .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, .dma_threshold = true, .cts_event_workaround = true, + .get_fifosize = get_fifosize_st, }; static struct uart_amba_port *amba_ports[UART_NR]; @@ -2133,7 +2145,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; - uap->fifosize = vendor->fifosize; + uap->fifosize = vendor->get_fifosize(dev->periphid); uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; -- cgit v1.2.3-18-g5258 From 4829e7650f8a40645e4e32b26a37fb833a5e75f0 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 19 Apr 2013 21:12:17 +0200 Subject: serial: mxs: fix buffer overflow SMATCH correctly found an off-by-one error: drivers/tty/serial/mxs-auart.c:889 auart_console_write() error: buffer overflow 'auart_port' 5 <= 5 Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index d549fe1fa42..9d357da817d 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -883,7 +883,7 @@ auart_console_write(struct console *co, const char *str, unsigned int count) unsigned int old_ctrl0, old_ctrl2; unsigned int to = 1000; - if (co->index > MXS_AUART_PORTS || co->index < 0) + if (co->index >= MXS_AUART_PORTS || co->index < 0) return; s = auart_port[co->index]; -- cgit v1.2.3-18-g5258 From 4c24f2c9b671a1dacf2e25a3b059b54bb8899de1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 19 Apr 2013 21:06:20 +0200 Subject: serial: mxs: drop superfluous {get|put}_device Driver core already takes care of refcounting, no need to do this on driver level again. Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 9d357da817d..62e7d3b015a 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1103,7 +1103,7 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.fifosize = 16; s->port.uartclk = clk_get_rate(s->clk); s->port.type = PORT_IMX; - s->port.dev = s->dev = get_device(&pdev->dev); + s->port.dev = s->dev = &pdev->dev; s->ctrl = 0; @@ -1134,7 +1134,6 @@ out_free_irq: auart_port[pdev->id] = NULL; free_irq(s->irq, s); out_free_clk: - put_device(s->dev); clk_put(s->clk); out_free: kfree(s); @@ -1150,7 +1149,6 @@ static int mxs_auart_remove(struct platform_device *pdev) auart_port[pdev->id] = NULL; - put_device(s->dev); clk_put(s->clk); free_irq(s->irq, s); kfree(s); -- cgit v1.2.3-18-g5258 From 45efcb2d32d35f6509543e477568842d8467035d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Apr 2013 18:30:49 +0200 Subject: tty/serial/sirf: fix MODULE_DEVICE_TABLE This fixes building the sirfsorc-uart driver as a loadable module, which uses an incorrect MODULE_DEVICE_TABLE, by changing the reference to the correct symbol. Signed-off-by: Arnd Bergmann Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sirfsoc_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 6bbfe9934a4..03465b67394 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -758,7 +758,7 @@ static struct of_device_id sirfsoc_uart_ids[] = { { .compatible = "sirf,marco-uart", }, {} }; -MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match); +MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); static struct platform_driver sirfsoc_uart_driver = { .probe = sirfsoc_uart_probe, -- cgit v1.2.3-18-g5258