aboutsummaryrefslogtreecommitdiffstats
path: root/tty
diff options
Diffstat (limited to 'tty')
-rw-r--r--tty/8250-fix-set_ldisc-operation.patch39
-rw-r--r--tty/cdc-acm-remove-dead-code.patch62
-rw-r--r--tty/max3110-sanity-check-a-register.patch53
-rw-r--r--tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch969
-rw-r--r--tty/serial-add-port-helpers.patch117
-rw-r--r--tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch33
-rw-r--r--tty/serial-change-the-wait-for-carrier-locking.patch184
-rw-r--r--tty/serial-trim-locking-on-the-helpers.patch54
-rw-r--r--tty/serial-use-block_til_ready-helper.patch124
-rw-r--r--tty/tty-fix-console_sem-lock-order.patch42
-rw-r--r--tty/tty-implement-btm-as-mutex-instead-of-bkl.patch139
-rw-r--r--tty/tty-introduce-wait_event_interruptible_tty.patch191
-rw-r--r--tty/tty-make-vt-s-have-a-tty_port.patch53
-rw-r--r--tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch134
-rw-r--r--tty/tty-never-hold-btm-while-getting-tty_mutex.patch111
-rw-r--r--tty/tty-release-btm-while-sleeping-in-block_til_ready.patch175
-rw-r--r--tty/tty-remove-tty_lock_nested.patch219
-rw-r--r--tty/tty-reorder-ldisc-locking.patch117
-rw-r--r--tty/tty-replace-bkl-with-a-new-tty_lock.patch1041
-rw-r--r--tty/tty-untangle-locking-of-wait_until_sent.patch175
-rw-r--r--tty/vc-locking-clean-up.patch81
-rw-r--r--tty/vt-clean-up-the-code-use-kernel-library.patch41
22 files changed, 4154 insertions, 0 deletions
diff --git a/tty/8250-fix-set_ldisc-operation.patch b/tty/8250-fix-set_ldisc-operation.patch
new file mode 100644
index 00000000000000..fee1599e8cbe0f
--- /dev/null
+++ b/tty/8250-fix-set_ldisc-operation.patch
@@ -0,0 +1,39 @@
+From arnd@arndb.de Wed Jun 16 13:48:20 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:11 +0200
+Subject: 8250: fix set_ldisc operation
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-32-git-send-email-arnd@arndb.de>
+
+
+The ldisc number now gets passed into ->set_ldisc.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/serial/8250.c | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/serial/8250.c
++++ b/drivers/serial/8250.c
+@@ -2409,14 +2409,9 @@ serial8250_set_termios(struct uart_port
+ }
+
+ static void
+-serial8250_set_ldisc(struct uart_port *port)
++serial8250_set_ldisc(struct uart_port *port, int new)
+ {
+- int line = port->line;
+-
+- if (line >= port->state->port.tty->driver->num)
+- return;
+-
+- if (port->state->port.tty->ldisc->ops->num == N_PPS) {
++ if (new == N_PPS) {
+ port->flags |= UPF_HARDPPS_CD;
+ serial8250_enable_ms(port);
+ } else
diff --git a/tty/cdc-acm-remove-dead-code.patch b/tty/cdc-acm-remove-dead-code.patch
new file mode 100644
index 00000000000000..6b587a7f169571
--- /dev/null
+++ b/tty/cdc-acm-remove-dead-code.patch
@@ -0,0 +1,62 @@
+From arnd@arndb.de Wed Jun 16 13:42:49 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:04 +0200
+Subject: cdc-acm: remove dead code
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-25-git-send-email-arnd@arndb.de>
+
+
+The wait_event_interruptible_timeout in acm_port_down is
+never reached. Remove it to avoid possible deadlocks
+with the big tty mutex if someone were to start using
+the blocking version of acm_port_down.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 12 +++---------
+ 1 file changed, 3 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/class/cdc-acm.c
++++ b/drivers/usb/class/cdc-acm.c
+@@ -636,19 +636,13 @@ static void acm_tty_unregister(struct ac
+
+ static int acm_tty_chars_in_buffer(struct tty_struct *tty);
+
+-static void acm_port_down(struct acm *acm, int drain)
++static void acm_port_down(struct acm *acm)
+ {
+ int i, nr = acm->rx_buflimit;
+ mutex_lock(&open_mutex);
+ if (acm->dev) {
+ usb_autopm_get_interface(acm->control);
+ acm_set_control(acm, acm->ctrlout = 0);
+- /* try letting the last writes drain naturally */
+- if (drain) {
+- wait_event_interruptible_timeout(acm->drain_wait,
+- (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
+- ACM_CLOSE_TIMEOUT * HZ);
+- }
+ usb_kill_urb(acm->ctrlurb);
+ for (i = 0; i < ACM_NW; i++)
+ usb_kill_urb(acm->wb[i].urb);
+@@ -664,7 +658,7 @@ static void acm_tty_hangup(struct tty_st
+ {
+ struct acm *acm = tty->driver_data;
+ tty_port_hangup(&acm->port);
+- acm_port_down(acm, 0);
++ acm_port_down(acm);
+ }
+
+ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
+@@ -685,7 +679,7 @@ static void acm_tty_close(struct tty_str
+ mutex_unlock(&open_mutex);
+ return;
+ }
+- acm_port_down(acm, 0);
++ acm_port_down(acm);
+ tty_port_close_end(&acm->port, tty);
+ tty_port_tty_set(&acm->port, NULL);
+ }
diff --git a/tty/max3110-sanity-check-a-register.patch b/tty/max3110-sanity-check-a-register.patch
new file mode 100644
index 00000000000000..3dd69b4b2095ba
--- /dev/null
+++ b/tty/max3110-sanity-check-a-register.patch
@@ -0,0 +1,53 @@
+From alan@linux.intel.com Wed Jun 16 13:50:26 2010
+From: jianwei.yang <jianwei.yang@intel.com>
+Date: Wed, 16 Jun 2010 14:46:49 +0100
+Subject: max3110 sanity check a register
+To: greg@kroah.com, linux-serial@vger.kernel.org
+Message-ID: <20100616134616.12686.11518.stgit@localhost.localdomain>
+
+
+From: jianwei.yang <jianwei.yang@intel.com>
+
+MAX3111 is the SPI/UART IC installed on the MRST SPI Port Card as a serial
+debug goal, and the SPI Port Card will be frequently mounted and unmounted
+from the main board by developers depending whether debug serial is
+required or not.
+
+As the MAX3111 has no subvendor or product id registers available, the patch
+will try to access one register to decide if this IC is present or not.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/mrst_max3110.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+--- a/drivers/serial/mrst_max3110.c
++++ b/drivers/serial/mrst_max3110.c
+@@ -721,7 +721,7 @@ static int serial_m3110_probe(struct spi
+ struct uart_max3110 *max;
+ int ret;
+ unsigned char *buffer;
+-
++ u16 res;
+ max = kzalloc(sizeof(*max), GFP_KERNEL);
+ if (!max)
+ return -ENOMEM;
+@@ -753,7 +753,16 @@ static int serial_m3110_probe(struct spi
+
+ max->cur_conf = 0;
+ atomic_set(&max->irq_pending, 0);
++ /* Check if reading configuration register returns something sane */
+
++ res = RC_TAG;
++ ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
++ if (ret < 0 || res == 0 || res == 0xffff) {
++ printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)",
++ res);
++ ret = -ENODEV;
++ goto err_get_page;
++ }
+ buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!buffer) {
+ ret = -ENOMEM;
diff --git a/tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch b/tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch
new file mode 100644
index 00000000000000..011a3d5aa18780
--- /dev/null
+++ b/tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch
@@ -0,0 +1,969 @@
+From alan@linux.intel.com Wed Jun 16 13:50:11 2010
+From: Alan Cox <alan@linux.intel.com>
+Date: Wed, 16 Jun 2010 14:46:09 +0100
+Subject: mrst_max3110: add UART driver for Max3110 on Moorestown
+To: greg@kroah.com, linux-serial@vger.kernel.org
+Message-ID: <20100616134554.12686.94694.stgit@localhost.localdomain>
+
+
+From: Feng Tang <feng.tang@intel.com>
+
+This driver enable the max3110 device, it can be used as
+a system console. the IRQ needs be enabled if user want a
+better performance. MRST max3110 works in 3.684MHz clock,
+which supports 230400 as its maximum rate.
+
+Signed-off-by: Feng Tang <feng.tang@intel.com>
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/serial/Kconfig | 17
+ drivers/serial/Makefile | 1
+ drivers/serial/mrst_max3110.c | 844 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/serial/mrst_max3110.h | 59 ++
+ 4 files changed, 921 insertions(+)
+
+--- a/drivers/serial/Kconfig
++++ b/drivers/serial/Kconfig
+@@ -698,6 +698,23 @@ config SERIAL_SA1100_CONSOLE
+ your boot loader (lilo or loadlin) about how to pass options to the
+ kernel at boot time.)
+
++config SERIAL_MRST_MAX3110
++ tristate "SPI UART driver for Max3110"
++ depends on SPI_DW_PCI
++ select SERIAL_CORE
++ select SERIAL_CORE_CONSOLE
++ help
++ This is the UART protocol driver for the MAX3110 device on
++ the Intel Moorestown platform. On other systems use the max3100
++ driver.
++
++config MRST_MAX3110_IRQ
++ boolean "Enable GPIO IRQ for Max3110 over Moorestown"
++ default n
++ depends on SERIAL_MRST_MAX3110 && GPIO_LANGWELL
++ help
++ This has to be enabled after Moorestown GPIO driver is loaded
++
+ config SERIAL_BFIN
+ tristate "Blackfin serial port support"
+ depends on BLACKFIN
+--- a/drivers/serial/Makefile
++++ b/drivers/serial/Makefile
+@@ -84,3 +84,4 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbu
+ obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
+ obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
+ obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
++obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
+--- /dev/null
++++ b/drivers/serial/mrst_max3110.c
+@@ -0,0 +1,844 @@
++/*
++ * max3110.c - spi uart protocol driver for Maxim 3110 on Moorestown
++ *
++ * Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope 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.,
++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++/*
++ * Note:
++ * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
++ * 1 word. If SPI master controller doesn't support sclk frequency change,
++ * then the char need be sent out one by one with some delay
++ *
++ * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE
++ * interrupt for a low speed UART device
++ */
++
++#include <linux/module.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++#include <linux/platform_device.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/serial_core.h>
++#include <linux/serial_reg.h>
++
++#include <linux/kthread.h>
++#include <linux/delay.h>
++#include <asm/atomic.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/dw_spi.h>
++
++#include "mrst_max3110.h"
++
++#define PR_FMT "mrst_max3110: "
++
++struct uart_max3110 {
++ struct uart_port port;
++ struct spi_device *spi;
++ char *name;
++
++ wait_queue_head_t wq;
++ struct task_struct *main_thread;
++ struct task_struct *read_thread;
++ int mthread_up;
++ spinlock_t lock;
++
++ u32 baud;
++ u16 cur_conf;
++ u8 clock;
++ u8 parity, word_7bits;
++
++ atomic_t uart_tx_need;
++
++ /* console related */
++ struct circ_buf con_xmit;
++ atomic_t con_tx_need;
++
++ /* irq related */
++ u16 irq;
++ atomic_t irq_pending;
++};
++
++/* global data structure, may need be removed */
++struct uart_max3110 *pmax;
++static inline void receive_char(struct uart_max3110 *max, u8 ch);
++static void receive_chars(struct uart_max3110 *max,
++ unsigned char *str, int len);
++static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf);
++static void max3110_console_receive(struct uart_max3110 *max);
++
++int max3110_write_then_read(struct uart_max3110 *max,
++ const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast)
++{
++ struct spi_device *spi = max->spi;
++ struct spi_message message;
++ struct spi_transfer x;
++ int ret;
++
++ if (!txbuf || !rxbuf)
++ return -EINVAL;
++
++ spi_message_init(&message);
++ memset(&x, 0, sizeof x);
++ x.len = len;
++ x.tx_buf = txbuf;
++ x.rx_buf = rxbuf;
++ spi_message_add_tail(&x, &message);
++
++ if (always_fast)
++ x.speed_hz = 3125000;
++ else if (max->baud)
++ x.speed_hz = max->baud;
++
++ /* Do the i/o */
++ ret = spi_sync(spi, &message);
++ return ret;
++}
++
++/* Write a u16 to the device, and return one u16 read back */
++int max3110_out(struct uart_max3110 *max, const u16 out)
++{
++ u16 tmp;
++ int ret;
++
++ ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1);
++ if (ret)
++ return ret;
++
++ /* If some valid data is read back */
++ if (tmp & MAX3110_READ_DATA_AVAILABLE)
++ receive_char(max, (tmp & 0xff));
++
++ return ret;
++}
++
++#define MAX_READ_LEN 20
++/*
++ * This is usually used to read data from SPIC RX FIFO, which doesn't
++ * need any delay like flushing character out. It returns how many
++ * valide bytes are read back
++ */
++static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf)
++{
++ u16 out[MAX_READ_LEN], in[MAX_READ_LEN];
++ u8 *pbuf, valid_str[MAX_READ_LEN];
++ int i, j, bytelen;
++
++ if (len > MAX_READ_LEN) {
++ pr_err(PR_FMT "read len %d is too large\n", len);
++ return 0;
++ }
++
++ bytelen = len * 2;
++ memset(out, 0, bytelen);
++ memset(in, 0, bytelen);
++
++ if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1))
++ return 0;
++
++ /* If caller don't provide a buffer, then handle received char */
++ pbuf = buf ? buf : valid_str;
++
++ for (i = 0, j = 0; i < len; i++) {
++ if (in[i] & MAX3110_READ_DATA_AVAILABLE)
++ pbuf[j++] = (u8)(in[i] & 0xff);
++ }
++
++ if (j && (pbuf == valid_str))
++ receive_chars(max, valid_str, j);
++
++ return j;
++}
++
++static void serial_m3110_con_putchar(struct uart_port *port, int ch)
++{
++ struct uart_max3110 *max =
++ container_of(port, struct uart_max3110, port);
++ struct circ_buf *xmit = &max->con_xmit;
++
++ if (uart_circ_chars_free(xmit)) {
++ xmit->buf[xmit->head] = (char)ch;
++ xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
++ }
++
++ if (!atomic_read(&max->con_tx_need)) {
++ atomic_set(&max->con_tx_need, 1);
++ wake_up_process(max->main_thread);
++ }
++}
++
++/*
++ * Print a string to the serial port trying not to disturb
++ * any possible real use of the port...
++ *
++ * The console_lock must be held when we get here.
++ */
++static void serial_m3110_con_write(struct console *co,
++ const char *s, unsigned int count)
++{
++ if (!pmax)
++ return;
++
++ uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
++}
++
++static int __init
++serial_m3110_con_setup(struct console *co, char *options)
++{
++ struct uart_max3110 *max = pmax;
++ int baud = 115200;
++ int bits = 8;
++ int parity = 'n';
++ int flow = 'n';
++
++ pr_info(PR_FMT "setting up console\n");
++
++ if (!max) {
++ pr_err(PR_FMT "pmax is NULL, return");
++ return -ENODEV;
++ }
++
++ if (options)
++ uart_parse_options(options, &baud, &parity, &bits, &flow);
++
++ return uart_set_options(&max->port, co, baud, parity, bits, flow);
++}
++
++static struct tty_driver *serial_m3110_con_device(struct console *co,
++ int *index)
++{
++ struct uart_driver *p = co->data;
++ *index = co->index;
++ return p->tty_driver;
++}
++
++static struct uart_driver serial_m3110_reg;
++static struct console serial_m3110_console = {
++ .name = "ttyS",
++ .write = serial_m3110_con_write,
++ .device = serial_m3110_con_device,
++ .setup = serial_m3110_con_setup,
++ .flags = CON_PRINTBUFFER,
++ .index = -1,
++ .data = &serial_m3110_reg,
++};
++
++#define MRST_CONSOLE (&serial_m3110_console)
++
++static unsigned int serial_m3110_tx_empty(struct uart_port *port)
++{
++ return 1;
++}
++
++static void serial_m3110_stop_tx(struct uart_port *port)
++{
++ return;
++}
++
++/* stop_rx will be called in spin_lock env */
++static void serial_m3110_stop_rx(struct uart_port *port)
++{
++ return;
++}
++
++#define WORDS_PER_XFER 128
++static inline void send_circ_buf(struct uart_max3110 *max,
++ struct circ_buf *xmit)
++{
++ int len, left = 0;
++ u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER];
++ u8 valid_str[WORDS_PER_XFER];
++ int i, j;
++
++ while (!uart_circ_empty(xmit)) {
++ left = uart_circ_chars_pending(xmit);
++ while (left) {
++ len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left;
++
++ memset(obuf, 0, len * 2);
++ memset(ibuf, 0, len * 2);
++ for (i = 0; i < len; i++) {
++ obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
++ xmit->tail = (xmit->tail + 1) &
++ (UART_XMIT_SIZE - 1);
++ }
++ max3110_write_then_read(max, (u8 *)obuf,
++ (u8 *)ibuf, len * 2, 0);
++
++ for (i = 0, j = 0; i < len; i++) {
++ if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE)
++ valid_str[j++] = (u8)(ibuf[i] & 0xff);
++ }
++
++ if (j)
++ receive_chars(max, valid_str, j);
++
++ max->port.icount.tx += len;
++ left -= len;
++ }
++ }
++}
++
++static void transmit_char(struct uart_max3110 *max)
++{
++ struct uart_port *port = &max->port;
++ struct circ_buf *xmit = &port->state->xmit;
++
++ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
++ return;
++
++ send_circ_buf(max, xmit);
++
++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
++ uart_write_wakeup(port);
++
++ if (uart_circ_empty(xmit))
++ serial_m3110_stop_tx(port);
++}
++
++/* This will be called by uart_write() and tty_write, can't
++ * go to sleep */
++static void serial_m3110_start_tx(struct uart_port *port)
++{
++ struct uart_max3110 *max =
++ container_of(port, struct uart_max3110, port);
++
++ if (!atomic_read(&max->uart_tx_need)) {
++ atomic_set(&max->uart_tx_need, 1);
++ wake_up_process(max->main_thread);
++ }
++}
++
++static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len)
++{
++ struct uart_port *port = &max->port;
++ struct tty_struct *tty;
++ int usable;
++
++ /* If uart is not opened, just return */
++ if (!port->state)
++ return;
++
++ tty = port->state->port.tty;
++ if (!tty)
++ return; /* receive some char before the tty is opened */
++
++ while (len) {
++ usable = tty_buffer_request_room(tty, len);
++ if (usable) {
++ tty_insert_flip_string(tty, str, usable);
++ str += usable;
++ port->icount.rx += usable;
++ tty_flip_buffer_push(tty);
++ }
++ len -= usable;
++ }
++}
++
++static inline void receive_char(struct uart_max3110 *max, u8 ch)
++{
++ receive_chars(max, &ch, 1);
++}
++
++static void max3110_console_receive(struct uart_max3110 *max)
++{
++ int loop = 1, num, total = 0;
++ u8 recv_buf[512], *pbuf;
++
++ pbuf = recv_buf;
++ do {
++ num = max3110_read_multi(max, 8, pbuf);
++
++ if (num) {
++ loop = 10;
++ pbuf += num;
++ total += num;
++
++ if (total >= 500) {
++ receive_chars(max, recv_buf, total);
++ pbuf = recv_buf;
++ total = 0;
++ }
++ }
++ } while (--loop);
++
++ if (total)
++ receive_chars(max, recv_buf, total);
++}
++
++static int max3110_main_thread(void *_max)
++{
++ struct uart_max3110 *max = _max;
++ wait_queue_head_t *wq = &max->wq;
++ int ret = 0;
++ struct circ_buf *xmit = &max->con_xmit;
++
++ init_waitqueue_head(wq);
++ pr_info(PR_FMT "start main thread\n");
++
++ do {
++ wait_event_interruptible(*wq, (atomic_read(&max->irq_pending) ||
++ atomic_read(&max->con_tx_need) ||
++ atomic_read(&max->uart_tx_need)) ||
++ kthread_should_stop());
++ max->mthread_up = 1;
++
++#ifdef CONFIG_MRST_MAX3110_IRQ
++ if (atomic_read(&max->irq_pending)) {
++ max3110_console_receive(max);
++ atomic_set(&max->irq_pending, 0);
++ }
++#endif
++
++ /* first handle console output */
++ if (atomic_read(&max->con_tx_need)) {
++ send_circ_buf(max, xmit);
++ atomic_set(&max->con_tx_need, 0);
++ }
++
++ /* handle uart output */
++ if (atomic_read(&max->uart_tx_need)) {
++ transmit_char(max);
++ atomic_set(&max->uart_tx_need, 0);
++ }
++ max->mthread_up = 0;
++ } while (!kthread_should_stop());
++
++ return ret;
++}
++
++#ifdef CONFIG_MRST_MAX3110_IRQ
++static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
++{
++ struct uart_max3110 *max = dev_id;
++
++ /* max3110's irq is a falling edge, not level triggered,
++ * so no need to disable the irq */
++ if (!atomic_read(&max->irq_pending)) {
++ atomic_inc(&max->irq_pending);
++ wake_up_process(max->main_thread);
++ }
++ return IRQ_HANDLED;
++}
++#else
++/* if don't use RX IRQ, then need a thread to polling read */
++static int max3110_read_thread(void *_max)
++{
++ struct uart_max3110 *max = _max;
++
++ pr_info(PR_FMT "start read thread\n");
++ do {
++ if (!max->mthread_up)
++ max3110_console_receive(max);
++
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ / 20);
++ } while (!kthread_should_stop());
++
++ return 0;
++}
++#endif
++
++static int serial_m3110_startup(struct uart_port *port)
++{
++ struct uart_max3110 *max =
++ container_of(port, struct uart_max3110, port);
++ u16 config = 0;
++ int ret = 0;
++
++ if (port->line != 0)
++ pr_err(PR_FMT "uart port startup failed\n");
++
++ /* firstly disable all IRQ and config it to 115200, 8n1 */
++ config = WC_TAG | WC_FIFO_ENABLE
++ | WC_1_STOPBITS
++ | WC_8BIT_WORD
++ | WC_BAUD_DR2;
++ ret = max3110_out(max, config);
++
++ /* as we use thread to handle tx/rx, need set low latency */
++ port->state->port.tty->low_latency = 1;
++
++#ifdef CONFIG_MRST_MAX3110_IRQ
++ ret = request_irq(max->irq, serial_m3110_irq,
++ IRQ_TYPE_EDGE_FALLING, "max3110", max);
++ if (ret)
++ return ret;
++
++ /* enable RX IRQ only */
++ config |= WC_RXA_IRQ_ENABLE;
++ max3110_out(max, config);
++#else
++ /* if IRQ is disabled, start a read thread for input data */
++ max->read_thread =
++ kthread_run(max3110_read_thread, max, "max3110_read");
++#endif
++
++ max->cur_conf = config;
++ return 0;
++}
++
++static void serial_m3110_shutdown(struct uart_port *port)
++{
++ struct uart_max3110 *max =
++ container_of(port, struct uart_max3110, port);
++ u16 config;
++
++ if (max->read_thread) {
++ kthread_stop(max->read_thread);
++ max->read_thread = NULL;
++ }
++
++#ifdef CONFIG_MRST_MAX3110_IRQ
++ free_irq(max->irq, max);
++#endif
++
++ /* Disable interrupts from this port */
++ config = WC_TAG | WC_SW_SHDI;
++ max3110_out(max, config);
++}
++
++static void serial_m3110_release_port(struct uart_port *port)
++{
++}
++
++static int serial_m3110_request_port(struct uart_port *port)
++{
++ return 0;
++}
++
++static void serial_m3110_config_port(struct uart_port *port, int flags)
++{
++ /* give it fake type */
++ port->type = PORT_PXA;
++}
++
++static int
++serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++ /* we don't want the core code to modify any port params */
++ return -EINVAL;
++}
++
++
++static const char *serial_m3110_type(struct uart_port *port)
++{
++ struct uart_max3110 *max =
++ container_of(port, struct uart_max3110, port);
++ return max->name;
++}
++
++static void
++serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
++ struct ktermios *old)
++{
++ struct uart_max3110 *max =
++ container_of(port, struct uart_max3110, port);
++ unsigned char cval;
++ unsigned int baud, parity = 0;
++ int clk_div = -1;
++ u16 new_conf = max->cur_conf;
++
++ switch (termios->c_cflag & CSIZE) {
++ case CS7:
++ cval = UART_LCR_WLEN7;
++ new_conf |= WC_7BIT_WORD;
++ break;
++ default:
++ case CS8:
++ cval = UART_LCR_WLEN8;
++ new_conf |= WC_8BIT_WORD;
++ break;
++ }
++
++ baud = uart_get_baud_rate(port, termios, old, 0, 230400);
++
++ /* first calc the div for 1.8MHZ clock case */
++ switch (baud) {
++ case 300:
++ clk_div = WC_BAUD_DR384;
++ break;
++ case 600:
++ clk_div = WC_BAUD_DR192;
++ break;
++ case 1200:
++ clk_div = WC_BAUD_DR96;
++ break;
++ case 2400:
++ clk_div = WC_BAUD_DR48;
++ break;
++ case 4800:
++ clk_div = WC_BAUD_DR24;
++ break;
++ case 9600:
++ clk_div = WC_BAUD_DR12;
++ break;
++ case 19200:
++ clk_div = WC_BAUD_DR6;
++ break;
++ case 38400:
++ clk_div = WC_BAUD_DR3;
++ break;
++ case 57600:
++ clk_div = WC_BAUD_DR2;
++ break;
++ case 115200:
++ clk_div = WC_BAUD_DR1;
++ break;
++ case 230400:
++ if (max->clock & MAX3110_HIGH_CLK)
++ break;
++ default:
++ /* pick the previous baud rate */
++ baud = max->baud;
++ clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
++ tty_termios_encode_baud_rate(termios, baud, baud);
++ }
++
++ if (max->clock & MAX3110_HIGH_CLK) {
++ clk_div += 1;
++ /* high clk version max3110 doesn't support B300 */
++ if (baud == 300)
++ baud = 600;
++ if (baud == 230400)
++ clk_div = WC_BAUD_DR1;
++ tty_termios_encode_baud_rate(termios, baud, baud);
++ }
++
++ new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
++ if (termios->c_cflag & CSTOPB)
++ new_conf |= WC_2_STOPBITS;
++ else
++ new_conf &= ~WC_2_STOPBITS;
++
++ if (termios->c_cflag & PARENB) {
++ new_conf |= WC_PARITY_ENABLE;
++ parity |= UART_LCR_PARITY;
++ } else
++ new_conf &= ~WC_PARITY_ENABLE;
++
++ if (!(termios->c_cflag & PARODD))
++ parity |= UART_LCR_EPAR;
++ max->parity = parity;
++
++ uart_update_timeout(port, termios->c_cflag, baud);
++
++ new_conf |= WC_TAG;
++ if (new_conf != max->cur_conf) {
++ max3110_out(max, new_conf);
++ max->cur_conf = new_conf;
++ max->baud = baud;
++ }
++}
++
++/* don't handle hw handshaking */
++static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
++{
++ return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
++}
++
++static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++}
++
++static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
++{
++}
++
++static void serial_m3110_pm(struct uart_port *port, unsigned int state,
++ unsigned int oldstate)
++{
++}
++
++static void serial_m3110_enable_ms(struct uart_port *port)
++{
++}
++
++struct uart_ops serial_m3110_ops = {
++ .tx_empty = serial_m3110_tx_empty,
++ .set_mctrl = serial_m3110_set_mctrl,
++ .get_mctrl = serial_m3110_get_mctrl,
++ .stop_tx = serial_m3110_stop_tx,
++ .start_tx = serial_m3110_start_tx,
++ .stop_rx = serial_m3110_stop_rx,
++ .enable_ms = serial_m3110_enable_ms,
++ .break_ctl = serial_m3110_break_ctl,
++ .startup = serial_m3110_startup,
++ .shutdown = serial_m3110_shutdown,
++ .set_termios = serial_m3110_set_termios, /* must have */
++ .pm = serial_m3110_pm,
++ .type = serial_m3110_type,
++ .release_port = serial_m3110_release_port,
++ .request_port = serial_m3110_request_port,
++ .config_port = serial_m3110_config_port,
++ .verify_port = serial_m3110_verify_port,
++};
++
++static struct uart_driver serial_m3110_reg = {
++ .owner = THIS_MODULE,
++ .driver_name = "MRST serial",
++ .dev_name = "ttyS",
++ .major = TTY_MAJOR,
++ .minor = 64,
++ .nr = 1,
++ .cons = MRST_CONSOLE,
++};
++
++static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state)
++{
++ return 0;
++}
++
++static int serial_m3110_resume(struct spi_device *spi)
++{
++ return 0;
++}
++
++static struct dw_spi_chip spi0_uart = {
++ .poll_mode = 1,
++ .enable_dma = 0,
++ .type = SPI_FRF_SPI,
++};
++
++static int serial_m3110_probe(struct spi_device *spi)
++{
++ struct uart_max3110 *max;
++ int ret;
++ unsigned char *buffer;
++
++ max = kzalloc(sizeof(*max), GFP_KERNEL);
++ if (!max)
++ return -ENOMEM;
++
++ /* set spi info */
++ spi->mode = SPI_MODE_0;
++ spi->bits_per_word = 16;
++ max->clock = MAX3110_HIGH_CLK;
++ spi->controller_data = &spi0_uart;
++
++ spi_setup(spi);
++
++ max->port.type = PORT_PXA; /* need apply for a max3110 type */
++ max->port.fifosize = 2; /* only have 16b buffer */
++ max->port.ops = &serial_m3110_ops;
++ max->port.line = 0;
++ max->port.dev = &spi->dev;
++ max->port.uartclk = 115200;
++
++ max->spi = spi;
++ max->name = spi->modalias; /* use spi name as the name */
++ max->irq = (u16)spi->irq;
++
++ spin_lock_init(&max->lock);
++
++ max->word_7bits = 0;
++ max->parity = 0;
++ max->baud = 0;
++
++ max->cur_conf = 0;
++ atomic_set(&max->irq_pending, 0);
++
++ buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
++ if (!buffer) {
++ ret = -ENOMEM;
++ goto err_get_page;
++ }
++ max->con_xmit.buf = (unsigned char *)buffer;
++ max->con_xmit.head = max->con_xmit.tail = 0;
++
++ max->main_thread = kthread_run(max3110_main_thread,
++ max, "max3110_main");
++ if (IS_ERR(max->main_thread)) {
++ ret = PTR_ERR(max->main_thread);
++ goto err_kthread;
++ }
++
++ pmax = max;
++ /* give membase a psudo value to pass serial_core's check */
++ max->port.membase = (void *)0xff110000;
++ uart_add_one_port(&serial_m3110_reg, &max->port);
++
++ return 0;
++
++err_kthread:
++ free_page((unsigned long)buffer);
++err_get_page:
++ pmax = NULL;
++ kfree(max);
++ return ret;
++}
++
++static int max3110_remove(struct spi_device *dev)
++{
++ struct uart_max3110 *max = pmax;
++
++ if (!pmax)
++ return 0;
++
++ pmax = NULL;
++ uart_remove_one_port(&serial_m3110_reg, &max->port);
++
++ free_page((unsigned long)max->con_xmit.buf);
++
++ if (max->main_thread)
++ kthread_stop(max->main_thread);
++
++ kfree(max);
++ return 0;
++}
++
++static struct spi_driver uart_max3110_driver = {
++ .driver = {
++ .name = "spi_max3111",
++ .bus = &spi_bus_type,
++ .owner = THIS_MODULE,
++ },
++ .probe = serial_m3110_probe,
++ .remove = __devexit_p(max3110_remove),
++ .suspend = serial_m3110_suspend,
++ .resume = serial_m3110_resume,
++};
++
++
++int __init serial_m3110_init(void)
++{
++ int ret = 0;
++
++ ret = uart_register_driver(&serial_m3110_reg);
++ if (ret)
++ return ret;
++
++ ret = spi_register_driver(&uart_max3110_driver);
++ if (ret)
++ uart_unregister_driver(&serial_m3110_reg);
++
++ return ret;
++}
++
++void __exit serial_m3110_exit(void)
++{
++ spi_unregister_driver(&uart_max3110_driver);
++ uart_unregister_driver(&serial_m3110_reg);
++}
++
++module_init(serial_m3110_init);
++module_exit(serial_m3110_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("max3110-uart");
+--- /dev/null
++++ b/drivers/serial/mrst_max3110.h
+@@ -0,0 +1,59 @@
++#ifndef _MRST_MAX3110_H
++#define _MRST_MAX3110_H
++
++#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */
++#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */
++
++/* status bits for all 4 MAX3110 operate modes */
++#define MAX3110_READ_DATA_AVAILABLE (1 << 15)
++#define MAX3110_WRITE_BUF_EMPTY (1 << 14)
++
++#define WC_TAG (3 << 14)
++#define RC_TAG (1 << 14)
++#define WD_TAG (2 << 14)
++#define RD_TAG (0 << 14)
++
++/* bits def for write configuration */
++#define WC_FIFO_ENABLE_MASK (1 << 13)
++#define WC_FIFO_ENABLE (0 << 13)
++
++#define WC_SW_SHDI (1 << 12)
++
++#define WC_IRQ_MASK (0xF << 8)
++#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */
++#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */
++#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9)
++#define WC_REC_ACT_IRQ_ENABLE (1 << 8)
++
++#define WC_IRDA_ENABLE (1 << 7)
++
++#define WC_STOPBITS_MASK (1 << 6)
++#define WC_2_STOPBITS (1 << 6)
++#define WC_1_STOPBITS (0 << 6)
++
++#define WC_PARITY_ENABLE_MASK (1 << 5)
++#define WC_PARITY_ENABLE (1 << 5)
++
++#define WC_WORDLEN_MASK (1 << 4)
++#define WC_7BIT_WORD (1 << 4)
++#define WC_8BIT_WORD (0 << 4)
++
++#define WC_BAUD_DIV_MASK (0xF)
++#define WC_BAUD_DR1 (0x0)
++#define WC_BAUD_DR2 (0x1)
++#define WC_BAUD_DR4 (0x2)
++#define WC_BAUD_DR8 (0x3)
++#define WC_BAUD_DR16 (0x4)
++#define WC_BAUD_DR32 (0x5)
++#define WC_BAUD_DR64 (0x6)
++#define WC_BAUD_DR128 (0x7)
++#define WC_BAUD_DR3 (0x8)
++#define WC_BAUD_DR6 (0x9)
++#define WC_BAUD_DR12 (0xA)
++#define WC_BAUD_DR24 (0xB)
++#define WC_BAUD_DR48 (0xC)
++#define WC_BAUD_DR96 (0xD)
++#define WC_BAUD_DR192 (0xE)
++#define WC_BAUD_DR384 (0xF)
++
++#endif
diff --git a/tty/serial-add-port-helpers.patch b/tty/serial-add-port-helpers.patch
new file mode 100644
index 00000000000000..3c849d8b6c6a18
--- /dev/null
+++ b/tty/serial-add-port-helpers.patch
@@ -0,0 +1,117 @@
+From arnd@arndb.de Wed Jun 16 13:35:51 2010
+From: Alan Cox <alan@linux.intel.com>
+Date: Tue, 1 Jun 2010 22:52:58 +0200
+Subject: serial: add port helpers
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1275425591-8803-19-git-send-email-arnd@arndb.de>
+
+
+From: Alan Cox <alan@linux.intel.com>
+
+We can make this the same as the ones that will be needed by the tty_port
+helper logic that we want to move to but still call them from the existing
+code base.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/serial_core.c | 51 +++++++++++++++++++++++++++++++------------
+ 1 file changed, 37 insertions(+), 14 deletions(-)
+
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1501,6 +1501,34 @@ static void uart_update_termios(struct t
+ }
+ }
+
++static int uart_carrier_raised(struct tty_port *port)
++{
++ struct uart_state *state = container_of(port, struct uart_state, port);
++ struct uart_port *uport = state->uart_port;
++ int mctrl;
++ mutex_lock(&port->mutex);
++ spin_lock_irq(&uport->lock);
++ uport->ops->enable_ms(uport);
++ mctrl = uport->ops->get_mctrl(uport);
++ spin_unlock_irq(&uport->lock);
++ mutex_unlock(&port->mutex);
++ if (mctrl & TIOCM_CAR)
++ return 1;
++ return 0;
++}
++
++static void uart_dtr_rts(struct tty_port *port, int onoff)
++{
++ struct uart_state *state = container_of(port, struct uart_state, port);
++ struct uart_port *uport = state->uart_port;
++ mutex_lock(&port->mutex);
++ if (onoff)
++ uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
++ else
++ uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
++ mutex_unlock(&port->mutex);
++}
++
+ /*
+ * Block the open until the port is ready. We must be called with
+ * the per-port semaphore held.
+@@ -1509,9 +1537,7 @@ static int
+ uart_block_til_ready(struct file *filp, struct uart_state *state)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+- struct uart_port *uport = state->uart_port;
+ struct tty_port *port = &state->port;
+- unsigned int mctrl;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+@@ -1555,23 +1581,14 @@ uart_block_til_ready(struct file *filp,
+ * not set RTS here - we want to make sure we catch
+ * the data from the modem.
+ */
+- if (port->tty->termios->c_cflag & CBAUD) {
+- mutex_lock(&port->mutex);
+- uart_set_mctrl(uport, TIOCM_DTR);
+- mutex_unlock(&port->mutex);
+- }
++ if (port->tty->termios->c_cflag & CBAUD)
++ tty_port_raise_dtr_rts(port);
+
+ /*
+ * and wait for the carrier to indicate that the
+ * modem is ready for us.
+ */
+- mutex_lock(&port->mutex);
+- spin_lock_irq(&uport->lock);
+- uport->ops->enable_ms(uport);
+- mctrl = uport->ops->get_mctrl(uport);
+- spin_unlock_irq(&uport->lock);
+- mutex_unlock(&port->mutex);
+- if (mctrl & TIOCM_CAR)
++ if (tty_port_carrier_raised(port))
+ break;
+
+ schedule();
+@@ -2349,6 +2366,11 @@ static const struct tty_operations uart_
+ #endif
+ };
+
++static const struct tty_port_operations uart_port_ops = {
++ .carrier_raised = uart_carrier_raised,
++ .dtr_rts = uart_dtr_rts,
++};
++
+ /**
+ * uart_register_driver - register a driver with the uart core layer
+ * @drv: low level driver structure
+@@ -2405,6 +2427,7 @@ int uart_register_driver(struct uart_dri
+ struct tty_port *port = &state->port;
+
+ tty_port_init(port);
++ port->ops = &uart_port_ops;
+ port->close_delay = 500; /* .5 seconds */
+ port->closing_wait = 30000; /* 30 seconds */
+ tasklet_init(&state->tlet, uart_tasklet_action,
diff --git a/tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch b/tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch
new file mode 100644
index 00000000000000..86907075767144
--- /dev/null
+++ b/tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch
@@ -0,0 +1,33 @@
+From yegor_sub1@visionsystems.de Wed Jun 16 13:49:47 2010
+From: Yegor Yefremov <yegor_sub1@visionsystems.de>
+Date: Wed, 16 Jun 2010 16:29:55 +0200
+Subject: serial: add UART_CAP_EFR and UART_CAP_SLEEP flags to 16C950 UARTs definition
+To: linux-serial@vger.kernel.org
+Cc: Greg KH <greg@kroah.com>
+Message-ID: <4C18DFE3.2030109@visionsystems.de>
+
+
+Adding UART_CAP_EFR and UART_CAP_SLEEP flags will enable sleep mode
+and automatic CTS flow control for 16C950 UARTs. It will also avoid
+capabilities detection warning like this:
+
+"ttyS0: detected caps 00000700 should be 00000100"
+
+Signed-off-by: Yegor Yefremov <yegorslists@googlemail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/8250.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/serial/8250.c
++++ b/drivers/serial/8250.c
+@@ -241,7 +241,7 @@ static const struct serial8250_config ua
+ .fifo_size = 128,
+ .tx_loadsz = 128,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+- .flags = UART_CAP_FIFO,
++ .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+ },
+ [PORT_16654] = {
+ .name = "ST16654",
diff --git a/tty/serial-change-the-wait-for-carrier-locking.patch b/tty/serial-change-the-wait-for-carrier-locking.patch
new file mode 100644
index 00000000000000..9ec2e928168bac
--- /dev/null
+++ b/tty/serial-change-the-wait-for-carrier-locking.patch
@@ -0,0 +1,184 @@
+From arnd@arndb.de Wed Jun 16 13:35:17 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:52:57 +0200
+Subject: serial: Change the wait for carrier locking
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1275425591-8803-18-git-send-email-arnd@arndb.de>
+
+
+From: Alan Cox <alan@linux.intel.com>
+
+We want to push the lock/unlock into the helper functions so that we
+can prepare to move to using the tty_port helper. The expansion initially
+comes out a bit ugly but its worth the temporary expansion IMHO just so
+we can produce a nice testable series of changes.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/serial_core.c | 44 ++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 35 insertions(+), 9 deletions(-)
+
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1272,6 +1272,7 @@ static void uart_close(struct tty_struct
+ struct uart_state *state = tty->driver_data;
+ struct tty_port *port;
+ struct uart_port *uport;
++ unsigned long flags;
+
+ BUG_ON(!kernel_locked());
+
+@@ -1284,9 +1285,12 @@ static void uart_close(struct tty_struct
+ pr_debug("uart_close(%d) called\n", uport->line);
+
+ mutex_lock(&port->mutex);
++ spin_lock_irqsave(&port->lock, flags);
+
+- if (tty_hung_up_p(filp))
++ if (tty_hung_up_p(filp)) {
++ spin_unlock_irqrestore(&port->lock, flags);
+ goto done;
++ }
+
+ if ((tty->count == 1) && (port->count != 1)) {
+ /*
+@@ -1305,8 +1309,10 @@ static void uart_close(struct tty_struct
+ tty->name, port->count);
+ port->count = 0;
+ }
+- if (port->count)
++ if (port->count) {
++ spin_unlock_irqrestore(&port->lock, flags);
+ goto done;
++ }
+
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+@@ -1314,6 +1320,7 @@ static void uart_close(struct tty_struct
+ * setting tty->closing.
+ */
+ tty->closing = 1;
++ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait));
+@@ -1340,20 +1347,26 @@ static void uart_close(struct tty_struct
+
+ tty_ldisc_flush(tty);
+
+- tty->closing = 0;
+ tty_port_tty_set(port, NULL);
++ spin_lock_irqsave(&port->lock, flags);
++ tty->closing = 0;
+
+ if (port->blocked_open) {
++ spin_unlock_irqrestore(&port->lock, flags);
+ if (port->close_delay)
+ msleep_interruptible(port->close_delay);
++ spin_lock_irqsave(&port->lock, flags);
+ } else if (!uart_console(uport)) {
++ spin_unlock_irqrestore(&port->lock, flags);
+ uart_change_pm(state, 3);
++ spin_lock_irqsave(&port->lock, flags);
+ }
+
+ /*
+ * Wake up anyone trying to open this port.
+ */
+ clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
++ spin_unlock_irqrestore(&port->lock, flags);
+ wake_up_interruptible(&port->open_wait);
+
+ done:
+@@ -1429,6 +1442,7 @@ static void uart_hangup(struct tty_struc
+ {
+ struct uart_state *state = tty->driver_data;
+ struct tty_port *port = &state->port;
++ unsigned long flags;
+
+ BUG_ON(!kernel_locked());
+ pr_debug("uart_hangup(%d)\n", state->uart_port->line);
+@@ -1437,8 +1451,10 @@ static void uart_hangup(struct tty_struc
+ if (port->flags & ASYNC_NORMAL_ACTIVE) {
+ uart_flush_buffer(tty);
+ uart_shutdown(tty, state);
++ spin_lock_irqsave(&port->lock, flags);
+ port->count = 0;
+ clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
++ spin_unlock_irqrestore(&port->lock, flags);
+ tty_port_tty_set(port, NULL);
+ wake_up_interruptible(&port->open_wait);
+ wake_up_interruptible(&port->delta_msr_wait);
+@@ -1496,9 +1512,13 @@ uart_block_til_ready(struct file *filp,
+ struct uart_port *uport = state->uart_port;
+ struct tty_port *port = &state->port;
+ unsigned int mctrl;
++ unsigned long flags;
+
++ spin_lock_irqsave(&port->lock, flags);
++ if (!tty_hung_up_p(filp))
++ port->count--;
+ port->blocked_open++;
+- port->count--;
++ spin_unlock_irqrestore(&port->lock, flags);
+
+ add_wait_queue(&port->open_wait, &wait);
+ while (1) {
+@@ -1535,23 +1555,26 @@ uart_block_til_ready(struct file *filp,
+ * not set RTS here - we want to make sure we catch
+ * the data from the modem.
+ */
+- if (port->tty->termios->c_cflag & CBAUD)
++ if (port->tty->termios->c_cflag & CBAUD) {
++ mutex_lock(&port->mutex);
+ uart_set_mctrl(uport, TIOCM_DTR);
++ mutex_unlock(&port->mutex);
++ }
+
+ /*
+ * and wait for the carrier to indicate that the
+ * modem is ready for us.
+ */
++ mutex_lock(&port->mutex);
+ spin_lock_irq(&uport->lock);
+ uport->ops->enable_ms(uport);
+ mctrl = uport->ops->get_mctrl(uport);
+ spin_unlock_irq(&uport->lock);
++ mutex_unlock(&port->mutex);
+ if (mctrl & TIOCM_CAR)
+ break;
+
+- mutex_unlock(&port->mutex);
+ schedule();
+- mutex_lock(&port->mutex);
+
+ if (signal_pending(current))
+ break;
+@@ -1559,8 +1582,11 @@ uart_block_til_ready(struct file *filp,
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&port->open_wait, &wait);
+
+- port->count++;
++ spin_lock_irqsave(&port->lock, flags);
++ if (!tty_hung_up_p(filp))
++ port->count++;
+ port->blocked_open--;
++ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+@@ -1677,9 +1703,9 @@ static int uart_open(struct tty_struct *
+ /*
+ * If we succeeded, wait until the port is ready.
+ */
++ mutex_unlock(&port->mutex);
+ if (retval == 0)
+ retval = uart_block_til_ready(filp, state);
+- mutex_unlock(&port->mutex);
+
+ /*
+ * If this is the first open to succeed, adjust things to suit.
diff --git a/tty/serial-trim-locking-on-the-helpers.patch b/tty/serial-trim-locking-on-the-helpers.patch
new file mode 100644
index 00000000000000..9f0337a4500478
--- /dev/null
+++ b/tty/serial-trim-locking-on-the-helpers.patch
@@ -0,0 +1,54 @@
+From arnd@arndb.de Wed Jun 16 13:36:19 2010
+From: Alan Cox <alan@linux.intel.com>
+Date: Tue, 1 Jun 2010 22:52:59 +0200
+Subject: serial: trim locking on the helpers
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1275425591-8803-20-git-send-email-arnd@arndb.de>
+
+
+From: Alan Cox <alan@linux.intel.com>
+
+The port mutex protects port->tty, but these paths never need to walk from
+port->tty. They do need the low level lock as the API expects that but they
+already also take it.
+
+Thus we can drop the extra mutex lock calls here.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/serial_core.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1506,12 +1506,10 @@ static int uart_carrier_raised(struct tt
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport = state->uart_port;
+ int mctrl;
+- mutex_lock(&port->mutex);
+ spin_lock_irq(&uport->lock);
+ uport->ops->enable_ms(uport);
+ mctrl = uport->ops->get_mctrl(uport);
+ spin_unlock_irq(&uport->lock);
+- mutex_unlock(&port->mutex);
+ if (mctrl & TIOCM_CAR)
+ return 1;
+ return 0;
+@@ -1521,12 +1519,11 @@ static void uart_dtr_rts(struct tty_port
+ {
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport = state->uart_port;
+- mutex_lock(&port->mutex);
++
+ if (onoff)
+ uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+ else
+ uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+- mutex_unlock(&port->mutex);
+ }
+
+ /*
diff --git a/tty/serial-use-block_til_ready-helper.patch b/tty/serial-use-block_til_ready-helper.patch
new file mode 100644
index 00000000000000..a87944d7935e9e
--- /dev/null
+++ b/tty/serial-use-block_til_ready-helper.patch
@@ -0,0 +1,124 @@
+From arnd@arndb.de Wed Jun 16 13:36:52 2010
+From: Alan Cox <alan@linux.intel.com>
+Date: Tue, 1 Jun 2010 22:53:00 +0200
+Subject: serial: Use block_til_ready helper
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1275425591-8803-21-git-send-email-arnd@arndb.de>
+
+
+From: Alan Cox <alan@linux.intel.com>
+
+Our code now rather closely resembles the helper, so switch to it.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/serial_core.c | 87 -------------------------------------------
+ 1 file changed, 1 insertion(+), 86 deletions(-)
+
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1526,91 +1526,6 @@ static void uart_dtr_rts(struct tty_port
+ uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+ }
+
+-/*
+- * Block the open until the port is ready. We must be called with
+- * the per-port semaphore held.
+- */
+-static int
+-uart_block_til_ready(struct file *filp, struct uart_state *state)
+-{
+- DECLARE_WAITQUEUE(wait, current);
+- struct tty_port *port = &state->port;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&port->lock, flags);
+- if (!tty_hung_up_p(filp))
+- port->count--;
+- port->blocked_open++;
+- spin_unlock_irqrestore(&port->lock, flags);
+-
+- add_wait_queue(&port->open_wait, &wait);
+- while (1) {
+- set_current_state(TASK_INTERRUPTIBLE);
+-
+- /*
+- * If we have been hung up, tell userspace/restart open.
+- */
+- if (tty_hung_up_p(filp) || port->tty == NULL)
+- break;
+-
+- /*
+- * If the port has been closed, tell userspace/restart open.
+- */
+- if (!(port->flags & ASYNC_INITIALIZED))
+- break;
+-
+- /*
+- * If non-blocking mode is set, or CLOCAL mode is set,
+- * we don't want to wait for the modem status lines to
+- * indicate that the port is ready.
+- *
+- * Also, if the port is not enabled/configured, we want
+- * to allow the open to succeed here. Note that we will
+- * have set TTY_IO_ERROR for a non-existant port.
+- */
+- if ((filp->f_flags & O_NONBLOCK) ||
+- (port->tty->termios->c_cflag & CLOCAL) ||
+- (port->tty->flags & (1 << TTY_IO_ERROR)))
+- break;
+-
+- /*
+- * Set DTR to allow modem to know we're waiting. Do
+- * not set RTS here - we want to make sure we catch
+- * the data from the modem.
+- */
+- if (port->tty->termios->c_cflag & CBAUD)
+- tty_port_raise_dtr_rts(port);
+-
+- /*
+- * and wait for the carrier to indicate that the
+- * modem is ready for us.
+- */
+- if (tty_port_carrier_raised(port))
+- break;
+-
+- schedule();
+-
+- if (signal_pending(current))
+- break;
+- }
+- set_current_state(TASK_RUNNING);
+- remove_wait_queue(&port->open_wait, &wait);
+-
+- spin_lock_irqsave(&port->lock, flags);
+- if (!tty_hung_up_p(filp))
+- port->count++;
+- port->blocked_open--;
+- spin_unlock_irqrestore(&port->lock, flags);
+-
+- if (signal_pending(current))
+- return -ERESTARTSYS;
+-
+- if (!port->tty || tty_hung_up_p(filp))
+- return -EAGAIN;
+-
+- return 0;
+-}
+-
+ static struct uart_state *uart_get(struct uart_driver *drv, int line)
+ {
+ struct uart_state *state;
+@@ -1719,7 +1634,7 @@ static int uart_open(struct tty_struct *
+ */
+ mutex_unlock(&port->mutex);
+ if (retval == 0)
+- retval = uart_block_til_ready(filp, state);
++ retval = tty_port_block_til_ready(port, tty, filp);
+
+ /*
+ * If this is the first open to succeed, adjust things to suit.
diff --git a/tty/tty-fix-console_sem-lock-order.patch b/tty/tty-fix-console_sem-lock-order.patch
new file mode 100644
index 00000000000000..2a79928346d3d4
--- /dev/null
+++ b/tty/tty-fix-console_sem-lock-order.patch
@@ -0,0 +1,42 @@
+From arnd@arndb.de Wed Jun 16 13:42:16 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:03 +0200
+Subject: tty: fix console_sem lock order
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-24-git-send-email-arnd@arndb.de>
+
+
+vgacon_do_font_op releases and reacquires the BTM while holding
+console_sem. This violates the rule that BTM has to be the
+outer lock whenever we hold both.
+
+There does not seem to be any reason to give up the BTM here,
+so just stop doing that.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/video/console/vgacon.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/video/console/vgacon.c
++++ b/drivers/video/console/vgacon.c
+@@ -1108,7 +1108,6 @@ static int vgacon_do_font_op(struct vgas
+ charmap += 4 * cmapsz;
+ #endif
+
+- tty_unlock();
+ spin_lock_irq(&vga_lock);
+ /* First, the Sequencer */
+ vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
+@@ -1192,7 +1191,6 @@ static int vgacon_do_font_op(struct vgas
+ vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);
+ }
+ spin_unlock_irq(&vga_lock);
+- tty_lock();
+ return 0;
+ }
+
diff --git a/tty/tty-implement-btm-as-mutex-instead-of-bkl.patch b/tty/tty-implement-btm-as-mutex-instead-of-bkl.patch
new file mode 100644
index 00000000000000..ce84ae064e6b9a
--- /dev/null
+++ b/tty/tty-implement-btm-as-mutex-instead-of-bkl.patch
@@ -0,0 +1,139 @@
+From arnd@arndb.de Wed Jun 16 13:46:10 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:09 +0200
+Subject: tty: implement BTM as mutex instead of BKL
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-30-git-send-email-arnd@arndb.de>
+
+
+The tty locking now follows the rules for mutexes, so
+we can replace the BKL usage with a new subsystem
+wide mutex.
+
+This patch for now makes the new behaviour an optional
+experimental feature that can be enabled for testing
+purposes.
+
+Using a regular mutex here will change the behaviour
+when blocked on the BTM from spinning to sleeping,
+but that should not be visible to the user.
+
+Using the mutex also means that all the BTM is now
+covered by lockdep.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/Makefile | 1 +
+ drivers/char/tty_mutex.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/tty.h | 8 ++++++++
+ lib/Kconfig.debug | 10 ++++++++++
+ 4 files changed, 66 insertions(+)
+
+--- a/drivers/char/Makefile
++++ b/drivers/char/Makefile
+@@ -9,6 +9,7 @@ FONTMAPFILE = cp437.uni
+
+ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
+
++obj-$(CONFIG_TTY_MUTEX) += tty_mutex.o
+ obj-$(CONFIG_LEGACY_PTYS) += pty.o
+ obj-$(CONFIG_UNIX98_PTYS) += pty.o
+ obj-y += misc.o
+--- /dev/null
++++ b/drivers/char/tty_mutex.c
+@@ -0,0 +1,47 @@
++/*
++ * drivers/char/tty_lock.c
++ */
++#include <linux/tty.h>
++#include <linux/module.h>
++#include <linux/kallsyms.h>
++#include <linux/semaphore.h>
++#include <linux/sched.h>
++
++/*
++ * The 'big tty mutex'
++ *
++ * This mutex is taken and released by tty_lock() and tty_unlock(),
++ * replacing the older big kernel lock.
++ * It can no longer be taken recursively, and does not get
++ * released implicitly while sleeping.
++ *
++ * Don't use in new code.
++ */
++static DEFINE_MUTEX(big_tty_mutex);
++struct task_struct *__big_tty_mutex_owner;
++EXPORT_SYMBOL_GPL(__big_tty_mutex_owner);
++
++/*
++ * Getting the big tty mutex.
++ */
++void __lockfunc tty_lock(void)
++{
++ struct task_struct *task = current;
++
++ WARN_ON(__big_tty_mutex_owner == task);
++
++ mutex_lock(&big_tty_mutex);
++ __big_tty_mutex_owner = task;
++}
++EXPORT_SYMBOL(tty_lock);
++
++void __lockfunc tty_unlock(void)
++{
++ struct task_struct *task = current;
++
++ WARN_ON(__big_tty_mutex_owner != task);
++ __big_tty_mutex_owner = NULL;
++
++ mutex_unlock(&big_tty_mutex);
++}
++EXPORT_SYMBOL(tty_unlock);
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -574,7 +574,14 @@ extern int vt_ioctl(struct tty_struct *t
+ extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+
++/* tty_mutex.c */
+ /* functions for preparation of BKL removal */
++#ifdef CONFIG_TTY_MUTEX
++extern void __lockfunc tty_lock(void) __acquires(tty_lock);
++extern void __lockfunc tty_unlock(void) __releases(tty_lock);
++extern struct task_struct *__big_tty_mutex_owner;
++#define tty_locked() (current == __big_tty_mutex_owner)
++#else
+ static inline void tty_lock(void) __acquires(kernel_lock)
+ {
+ #ifdef CONFIG_LOCK_KERNEL
+@@ -588,6 +595,7 @@ static inline void tty_unlock(void) __re
+ unlock_kernel();
+ }
+ #define tty_locked() (kernel_locked())
++#endif
+
+ /*
+ * wait_event_interruptible_tty -- wait for a condition with the tty lock held
+--- a/lib/Kconfig.debug
++++ b/lib/Kconfig.debug
+@@ -428,6 +428,16 @@ config RT_MUTEX_TESTER
+ help
+ This option enables a rt-mutex tester.
+
++config TTY_MUTEX
++ bool "Use a mutex instead of BKL for TTY locking"
++ depends on EXPERIMENTAL && SMP
++ help
++ The TTY subsystem traditionally depends on the big kernel lock
++ for serialization. Saying Y here replaces the BKL with the Big
++ TTY Mutex (BTM).
++ Building a kernel without the BKL is only possible with TTY_MUTEX
++ enabled.
++
+ config DEBUG_SPINLOCK
+ bool "Spinlock and rw-lock debugging: basic checks"
+ depends on DEBUG_KERNEL
diff --git a/tty/tty-introduce-wait_event_interruptible_tty.patch b/tty/tty-introduce-wait_event_interruptible_tty.patch
new file mode 100644
index 00000000000000..47f854759382de
--- /dev/null
+++ b/tty/tty-introduce-wait_event_interruptible_tty.patch
@@ -0,0 +1,191 @@
+From arnd@arndb.de Wed Jun 16 13:43:07 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:05 +0200
+Subject: tty: introduce wait_event_interruptible_tty
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-26-git-send-email-arnd@arndb.de>
+
+
+Calling wait_event_interruptible implicitly
+releases the BKL when it sleeps, but we need
+to do this explcitly when we have converted
+it to a mutex.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/cyclades.c | 2 +-
+ drivers/char/istallion.c | 12 ++++++++----
+ drivers/char/n_r3964.c | 2 +-
+ drivers/char/tty_port.c | 2 +-
+ drivers/char/vt_ioctl.c | 2 +-
+ drivers/serial/crisv10.c | 4 ++--
+ include/linux/tty.h | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 56 insertions(+), 10 deletions(-)
+
+--- a/drivers/char/cyclades.c
++++ b/drivers/char/cyclades.c
+@@ -1607,7 +1607,7 @@ static int cy_open(struct tty_struct *tt
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
+- wait_event_interruptible(info->port.close_wait,
++ wait_event_interruptible_tty(info->port.close_wait,
+ !(info->port.flags & ASYNC_CLOSING));
+ return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
+ }
+--- a/drivers/char/istallion.c
++++ b/drivers/char/istallion.c
+@@ -954,7 +954,7 @@ static int stli_rawopen(struct stlibrd *
+ * order of opens and closes may not be preserved across shared
+ * memory, so we must wait until it is complete.
+ */
+- wait_event_interruptible(portp->raw_wait,
++ wait_event_interruptible_tty(portp->raw_wait,
+ !test_bit(ST_CLOSING, &portp->state));
+ if (signal_pending(current)) {
+ return -ERESTARTSYS;
+@@ -989,7 +989,7 @@ static int stli_rawopen(struct stlibrd *
+ set_bit(ST_OPENING, &portp->state);
+ spin_unlock_irqrestore(&brd_lock, flags);
+
+- wait_event_interruptible(portp->raw_wait,
++ wait_event_interruptible_tty(portp->raw_wait,
+ !test_bit(ST_OPENING, &portp->state));
+ if (signal_pending(current))
+ rc = -ERESTARTSYS;
+@@ -1020,7 +1020,7 @@ static int stli_rawclose(struct stlibrd
+ * occurs on this port.
+ */
+ if (wait) {
+- wait_event_interruptible(portp->raw_wait,
++ wait_event_interruptible_tty(portp->raw_wait,
+ !test_bit(ST_CLOSING, &portp->state));
+ if (signal_pending(current)) {
+ return -ERESTARTSYS;
+@@ -1052,7 +1052,7 @@ static int stli_rawclose(struct stlibrd
+ * to come back.
+ */
+ rc = 0;
+- wait_event_interruptible(portp->raw_wait,
++ wait_event_interruptible_tty(portp->raw_wait,
+ !test_bit(ST_CLOSING, &portp->state));
+ if (signal_pending(current))
+ rc = -ERESTARTSYS;
+@@ -1073,6 +1073,10 @@ static int stli_rawclose(struct stlibrd
+
+ static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
+ {
++ /*
++ * no need for wait_event_tty because clearing ST_CMDING cannot block
++ * on BTM
++ */
+ wait_event_interruptible(portp->raw_wait,
+ !test_bit(ST_CMDING, &portp->state));
+ if (signal_pending(current))
+--- a/drivers/char/n_r3964.c
++++ b/drivers/char/n_r3964.c
+@@ -1079,7 +1079,7 @@ static ssize_t r3964_read(struct tty_str
+ goto unlock;
+ }
+ /* block until there is a message: */
+- wait_event_interruptible(pInfo->read_wait,
++ wait_event_interruptible_tty(pInfo->read_wait,
+ (pMsg = remove_msg(pInfo, pClient)));
+ }
+
+--- a/drivers/char/tty_port.c
++++ b/drivers/char/tty_port.c
+@@ -231,7 +231,7 @@ int tty_port_block_til_ready(struct tty_
+
+ /* block if port is in the process of being closed */
+ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+- wait_event_interruptible(port->close_wait,
++ wait_event_interruptible_tty(port->close_wait,
+ !(port->flags & ASYNC_CLOSING));
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+--- a/drivers/char/vt_ioctl.c
++++ b/drivers/char/vt_ioctl.c
+@@ -133,7 +133,7 @@ static void vt_event_wait(struct vt_even
+ list_add(&vw->list, &vt_events);
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+ /* Wait for it to pass */
+- wait_event_interruptible(vt_event_waitqueue, vw->done);
++ wait_event_interruptible_tty(vt_event_waitqueue, vw->done);
+ /* Dequeue it */
+ spin_lock_irqsave(&vt_event_lock, flags);
+ list_del(&vw->list);
+--- a/drivers/serial/crisv10.c
++++ b/drivers/serial/crisv10.c
+@@ -3981,7 +3981,7 @@ block_til_ready(struct tty_struct *tty,
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+- wait_event_interruptible(info->close_wait,
++ wait_event_interruptible_tty(info->close_wait,
+ !(info->flags & ASYNC_CLOSING));
+ #ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+@@ -4139,7 +4139,7 @@ rs_open(struct tty_struct *tty, struct f
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+- wait_event_interruptible(info->close_wait,
++ wait_event_interruptible_tty(info->close_wait,
+ !(info->flags & ASYNC_CLOSING));
+ #ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -603,5 +603,47 @@ static inline void tty_unlock(void) __re
+ }
+ #define tty_locked() (kernel_locked())
+
++/*
++ * wait_event_interruptible_tty -- wait for a condition with the tty lock held
++ *
++ * The condition we are waiting for might take a long time to
++ * become true, or might depend on another thread taking the
++ * BTM. In either case, we need to drop the BTM to guarantee
++ * forward progress. This is a leftover from the conversion
++ * from the BKL and should eventually get removed as the BTM
++ * falls out of use.
++ *
++ * Do not use in new code.
++ */
++#define wait_event_interruptible_tty(wq, condition) \
++({ \
++ int __ret = 0; \
++ if (!(condition)) { \
++ __wait_event_interruptible_tty(wq, condition, __ret); \
++ } \
++ __ret; \
++})
++
++#define __wait_event_interruptible_tty(wq, condition, ret) \
++do { \
++ DEFINE_WAIT(__wait); \
++ \
++ for (;;) { \
++ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
++ if (condition) \
++ break; \
++ if (!signal_pending(current)) { \
++ tty_unlock(); \
++ schedule(); \
++ tty_lock(); \
++ continue; \
++ } \
++ ret = -ERESTARTSYS; \
++ break; \
++ } \
++ finish_wait(&wq, &__wait); \
++} while (0)
++
++
+ #endif /* __KERNEL__ */
+ #endif
diff --git a/tty/tty-make-vt-s-have-a-tty_port.patch b/tty/tty-make-vt-s-have-a-tty_port.patch
new file mode 100644
index 00000000000000..2ae2becfbe6431
--- /dev/null
+++ b/tty/tty-make-vt-s-have-a-tty_port.patch
@@ -0,0 +1,53 @@
+From arnd@arndb.de Wed Jun 16 13:31:12 2010
+From: Alan Cox <alan@linux.intel.com>
+Date: Tue, 1 Jun 2010 22:52:55 +0200
+Subject: tty: Make vt's have a tty_port
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1275425591-8803-16-git-send-email-arnd@arndb.de>
+
+
+From: Alan Cox <alan@linux.intel.com>
+
+The vt layer isn't safely handling reference counts to tty object on the input
+side. Add a tty port structure to the vt layer in order to implement this using
+the standard helpers.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/vt.c | 2 ++
+ include/linux/console_struct.h | 2 ++
+ 2 files changed, 4 insertions(+)
+
+--- a/drivers/char/vt.c
++++ b/drivers/char/vt.c
+@@ -773,6 +773,7 @@ int vc_allocate(unsigned int currcons) /
+ if (!vc)
+ return -ENOMEM;
+ vc_cons[currcons].d = vc;
++ tty_port_init(&vc->port);
+ INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
+ visual_init(vc, currcons, 1);
+ if (!*vc->vc_uni_pagedir_loc)
+@@ -2910,6 +2911,7 @@ static int __init con_init(void)
+ for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
+ vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
+ INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
++ tty_port_init(&vc->port);
+ visual_init(vc, currcons, 1);
+ vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
+ vc_init(vc, vc->vc_rows, vc->vc_cols,
+--- a/include/linux/console_struct.h
++++ b/include/linux/console_struct.h
+@@ -21,6 +21,8 @@ struct vt_struct;
+ #define NPAR 16
+
+ struct vc_data {
++ struct tty_port port; /* Upper level data */
++
+ unsigned short vc_num; /* Console number */
+ unsigned int vc_cols; /* [#] Console size */
+ unsigned int vc_rows;
diff --git a/tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch b/tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch
new file mode 100644
index 00000000000000..47781353c35c88
--- /dev/null
+++ b/tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch
@@ -0,0 +1,134 @@
+From arnd@arndb.de Wed Jun 16 13:33:03 2010
+From: Alan Cox <alan@linux.intel.com>
+Date: Tue, 1 Jun 2010 22:52:56 +0200
+Subject: tty: Move the vt_tty field from the vc_data into the standard tty_port
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1275425591-8803-17-git-send-email-arnd@arndb.de>
+
+
+From: Alan Cox <alan@linux.intel.com>
+
+This takes all the tty references through the expected interface points so
+we can refcount them.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/keyboard.c | 10 +++++-----
+ drivers/char/vt.c | 10 +++++-----
+ drivers/char/vt_ioctl.c | 2 +-
+ include/linux/console_struct.h | 1 -
+ 4 files changed, 11 insertions(+), 12 deletions(-)
+
+--- a/drivers/char/keyboard.c
++++ b/drivers/char/keyboard.c
+@@ -299,7 +299,7 @@ int kbd_rate(struct kbd_repeat *rep)
+ */
+ static void put_queue(struct vc_data *vc, int ch)
+ {
+- struct tty_struct *tty = vc->vc_tty;
++ struct tty_struct *tty = vc->port.tty;
+
+ if (tty) {
+ tty_insert_flip_char(tty, ch, 0);
+@@ -309,7 +309,7 @@ static void put_queue(struct vc_data *vc
+
+ static void puts_queue(struct vc_data *vc, char *cp)
+ {
+- struct tty_struct *tty = vc->vc_tty;
++ struct tty_struct *tty = vc->port.tty;
+
+ if (!tty)
+ return;
+@@ -485,7 +485,7 @@ static void fn_show_ptregs(struct vc_dat
+
+ static void fn_hold(struct vc_data *vc)
+ {
+- struct tty_struct *tty = vc->vc_tty;
++ struct tty_struct *tty = vc->port.tty;
+
+ if (rep || !tty)
+ return;
+@@ -563,7 +563,7 @@ static void fn_inc_console(struct vc_dat
+
+ static void fn_send_intr(struct vc_data *vc)
+ {
+- struct tty_struct *tty = vc->vc_tty;
++ struct tty_struct *tty = vc->port.tty;
+
+ if (!tty)
+ return;
+@@ -1162,7 +1162,7 @@ static void kbd_keycode(unsigned int key
+ struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
+ int rc;
+
+- tty = vc->vc_tty;
++ tty = vc->port.tty;
+
+ if (tty && (!tty->driver_data)) {
+ /* No driver data? Strange. Okay we fix it then. */
+--- a/drivers/char/vt.c
++++ b/drivers/char/vt.c
+@@ -962,12 +962,12 @@ static int vc_do_resize(struct tty_struc
+ * Resize a virtual console as seen from the console end of things. We
+ * use the common vc_do_resize methods to update the structures. The
+ * caller must hold the console sem to protect console internals and
+- * vc->vc_tty
++ * vc->port.tty
+ */
+
+ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
+ {
+- return vc_do_resize(vc->vc_tty, vc, cols, rows);
++ return vc_do_resize(vc->port.tty, vc, cols, rows);
+ }
+
+ /**
+@@ -2796,12 +2796,12 @@ static int con_open(struct tty_struct *t
+ struct vc_data *vc = vc_cons[currcons].d;
+
+ /* Still being freed */
+- if (vc->vc_tty) {
++ if (vc->port.tty) {
+ release_console_sem();
+ return -ERESTARTSYS;
+ }
+ tty->driver_data = vc;
+- vc->vc_tty = tty;
++ vc->port.tty = tty;
+
+ if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
+ tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
+@@ -2829,7 +2829,7 @@ static void con_shutdown(struct tty_stru
+ struct vc_data *vc = tty->driver_data;
+ BUG_ON(vc == NULL);
+ acquire_console_sem();
+- vc->vc_tty = NULL;
++ vc->port.tty = NULL;
+ release_console_sem();
+ tty_shutdown(tty);
+ }
+--- a/drivers/char/vt_ioctl.c
++++ b/drivers/char/vt_ioctl.c
+@@ -1369,7 +1369,7 @@ void vc_SAK(struct work_struct *work)
+ acquire_console_sem();
+ vc = vc_con->d;
+ if (vc) {
+- tty = vc->vc_tty;
++ tty = vc->port.tty;
+ /*
+ * SAK should also work in all raw modes and reset
+ * them properly.
+--- a/include/linux/console_struct.h
++++ b/include/linux/console_struct.h
+@@ -58,7 +58,6 @@ struct vc_data {
+ /* VT terminal data */
+ unsigned int vc_state; /* Escape sequence parser state */
+ unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */
+- struct tty_struct *vc_tty; /* TTY we are attached to */
+ /* data for manual vt switching */
+ struct vt_mode vt_mode;
+ struct pid *vt_pid;
diff --git a/tty/tty-never-hold-btm-while-getting-tty_mutex.patch b/tty/tty-never-hold-btm-while-getting-tty_mutex.patch
new file mode 100644
index 00000000000000..568d2747ee81ca
--- /dev/null
+++ b/tty/tty-never-hold-btm-while-getting-tty_mutex.patch
@@ -0,0 +1,111 @@
+From arnd@arndb.de Wed Jun 16 13:41:48 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:02 +0200
+Subject: tty: never hold BTM while getting tty_mutex
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-23-git-send-email-arnd@arndb.de>
+
+
+tty_mutex is never taken with the BTM held, except for
+two corner cases that are worked around here.
+We give up the BTM before calling tty_release() in the
+error path of tty_open().
+Similarly, we reorder the locking in ptmx_open()
+to get tty_mutex before the BTM.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/pty.c | 24 +++++++++++-------------
+ drivers/char/tty_io.c | 12 ++++++------
+ 2 files changed, 17 insertions(+), 19 deletions(-)
+
+--- a/drivers/char/pty.c
++++ b/drivers/char/pty.c
+@@ -626,7 +626,7 @@ static const struct tty_operations pty_u
+ * allocated_ptys_lock handles the list of free pty numbers
+ */
+
+-static int __ptmx_open(struct inode *inode, struct file *filp)
++static int ptmx_open(struct inode *inode, struct file *filp)
+ {
+ struct tty_struct *tty;
+ int retval;
+@@ -635,11 +635,14 @@ static int __ptmx_open(struct inode *ino
+ nonseekable_open(inode, filp);
+
+ /* find a device that is not in use. */
++ tty_lock();
+ index = devpts_new_index(inode);
++ tty_unlock();
+ if (index < 0)
+ return index;
+
+ mutex_lock(&tty_mutex);
++ tty_lock();
+ tty = tty_init_dev(ptm_driver, index, 1);
+ mutex_unlock(&tty_mutex);
+
+@@ -657,24 +660,19 @@ static int __ptmx_open(struct inode *ino
+ goto out1;
+
+ retval = ptm_driver->ops->open(tty, filp);
+- if (!retval)
+- return 0;
++ if (retval)
++ goto out2;
+ out1:
++ tty_unlock();
++ return retval;
++out2:
++ tty_unlock();
+ tty_release(inode, filp);
+ return retval;
+ out:
+ devpts_kill_index(inode, index);
+- return retval;
+-}
+-
+-static int ptmx_open(struct inode *inode, struct file *filp)
+-{
+- int ret;
+-
+- tty_lock();
+- ret = __ptmx_open(inode, filp);
+ tty_unlock();
+- return ret;
++ return retval;
+ }
+
+ static struct file_operations ptmx_fops;
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -1866,19 +1866,19 @@ got_driver:
+ printk(KERN_DEBUG "error %d in opening %s...", retval,
+ tty->name);
+ #endif
++ tty_unlock(); /* need to call tty_release without BTM */
+ tty_release(inode, filp);
+- if (retval != -ERESTARTSYS) {
+- tty_unlock();
++ if (retval != -ERESTARTSYS)
+ return retval;
+- }
+- if (signal_pending(current)) {
+- tty_unlock();
++
++ if (signal_pending(current))
+ return retval;
+- }
++
+ schedule();
+ /*
+ * Need to reset f_op in case a hangup happened.
+ */
++ tty_lock();
+ if (filp->f_op == &hung_up_tty_fops)
+ filp->f_op = &tty_fops;
+ tty_unlock();
diff --git a/tty/tty-release-btm-while-sleeping-in-block_til_ready.patch b/tty/tty-release-btm-while-sleeping-in-block_til_ready.patch
new file mode 100644
index 00000000000000..6d7656ab1da5ad
--- /dev/null
+++ b/tty/tty-release-btm-while-sleeping-in-block_til_ready.patch
@@ -0,0 +1,175 @@
+From arnd@arndb.de Wed Jun 16 13:47:56 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:10 +0200
+Subject: tty: release BTM while sleeping in block_til_ready
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-31-git-send-email-arnd@arndb.de>
+
+
+Most tty drivers may block while opening a device.
+Since this possibly depends on another thread
+closing it first and both threads may need the BTM,
+we need to release it here.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/amiserial.c | 2 ++
+ drivers/char/ip2/ip2main.c | 4 ++++
+ drivers/char/serial167.c | 4 +++-
+ drivers/char/specialix.c | 2 ++
+ drivers/char/synclink.c | 2 ++
+ drivers/char/synclink_gt.c | 2 ++
+ drivers/char/synclinkmp.c | 2 ++
+ drivers/char/tty_port.c | 2 ++
+ drivers/serial/68328serial.c | 2 ++
+ drivers/serial/68360serial.c | 2 ++
+ drivers/serial/crisv10.c | 2 ++
+ 11 files changed, 25 insertions(+), 1 deletion(-)
+
+--- a/drivers/char/amiserial.c
++++ b/drivers/char/amiserial.c
+@@ -1710,7 +1710,9 @@ static int block_til_ready(struct tty_st
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+ #endif
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&info->open_wait, &wait);
+--- a/drivers/char/ip2/ip2main.c
++++ b/drivers/char/ip2/ip2main.c
+@@ -1486,7 +1486,9 @@ ip2_open( PTTY tty, struct file *pFile )
+
+ if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
+ if ( pCh->flags & ASYNC_CLOSING ) {
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+ if ( tty_hung_up_p(pFile) ) {
+ set_current_state( TASK_RUNNING );
+@@ -1548,7 +1550,9 @@ ip2_open( PTTY tty, struct file *pFile )
+ rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
+ break;
+ }
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+ set_current_state( TASK_RUNNING );
+ remove_wait_queue(&pCh->open_wait, &wait);
+--- a/drivers/char/serial167.c
++++ b/drivers/char/serial167.c
+@@ -1786,7 +1786,9 @@ block_til_ready(struct tty_struct *tty,
+ tty->name, info->count);
+ /**/
+ #endif
+- schedule();
++ tty_unlock();
++ schedule();
++ tty_lock();
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&info->open_wait, &wait);
+--- a/drivers/char/specialix.c
++++ b/drivers/char/specialix.c
+@@ -1365,7 +1365,9 @@ static int block_til_ready(struct tty_st
+ retval = -ERESTARTSYS;
+ break;
+ }
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+
+ set_current_state(TASK_RUNNING);
+--- a/drivers/char/synclink.c
++++ b/drivers/char/synclink.c
+@@ -3349,7 +3349,9 @@ static int block_til_ready(struct tty_st
+ printk("%s(%d):block_til_ready blocking on %s count=%d\n",
+ __FILE__,__LINE__, tty->driver->name, port->count );
+
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+
+ set_current_state(TASK_RUNNING);
+--- a/drivers/char/synclink_gt.c
++++ b/drivers/char/synclink_gt.c
+@@ -3244,7 +3244,9 @@ static int block_til_ready(struct tty_st
+ }
+
+ DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+
+ set_current_state(TASK_RUNNING);
+--- a/drivers/char/synclinkmp.c
++++ b/drivers/char/synclinkmp.c
+@@ -3365,7 +3365,9 @@ static int block_til_ready(struct tty_st
+ printk("%s(%d):%s block_til_ready() count=%d\n",
+ __FILE__,__LINE__, tty->driver->name, port->count );
+
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+
+ set_current_state(TASK_RUNNING);
+--- a/drivers/char/tty_port.c
++++ b/drivers/char/tty_port.c
+@@ -294,7 +294,9 @@ int tty_port_block_til_ready(struct tty_
+ retval = -ERESTARTSYS;
+ break;
+ }
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+ finish_wait(&port->open_wait, &wait);
+
+--- a/drivers/serial/68328serial.c
++++ b/drivers/serial/68328serial.c
+@@ -1235,7 +1235,9 @@ static int block_til_ready(struct tty_st
+ retval = -ERESTARTSYS;
+ break;
+ }
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+--- a/drivers/serial/68360serial.c
++++ b/drivers/serial/68360serial.c
+@@ -1860,7 +1860,9 @@ static int block_til_ready(struct tty_st
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+ #endif
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+--- a/drivers/serial/crisv10.c
++++ b/drivers/serial/crisv10.c
+@@ -4055,7 +4055,9 @@ block_til_ready(struct tty_struct *tty,
+ printk("block_til_ready blocking: ttyS%d, count = %d\n",
+ info->line, info->count);
+ #endif
++ tty_unlock();
+ schedule();
++ tty_lock();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&info->open_wait, &wait);
diff --git a/tty/tty-remove-tty_lock_nested.patch b/tty/tty-remove-tty_lock_nested.patch
new file mode 100644
index 00000000000000..544676d2f12f76
--- /dev/null
+++ b/tty/tty-remove-tty_lock_nested.patch
@@ -0,0 +1,219 @@
+From arnd@arndb.de Wed Jun 16 13:45:36 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:08 +0200
+Subject: tty: remove tty_lock_nested
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-29-git-send-email-arnd@arndb.de>
+
+
+This changes all remaining users of tty_lock_nested
+to be non-recursive, which lets us kill this function.
+As a consequence, we won't need to keep the lock count
+any more, which allows more simplifications later.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/pty.c | 2 +-
+ drivers/char/selection.c | 4 ++--
+ drivers/char/tty_io.c | 41 ++++++++++++++++++++---------------------
+ drivers/char/tty_ldisc.c | 3 +--
+ include/linux/tty.h | 16 +---------------
+ 5 files changed, 25 insertions(+), 41 deletions(-)
+
+--- a/drivers/char/pty.c
++++ b/drivers/char/pty.c
+@@ -62,7 +62,7 @@ static void pty_close(struct tty_struct
+ if (tty->driver == ptm_driver)
+ devpts_pty_kill(tty->link);
+ #endif
+- tty_vhangup(tty->link);
++ tty_vhangup_locked(tty->link);
+ }
+ }
+
+--- a/drivers/char/selection.c
++++ b/drivers/char/selection.c
+@@ -313,7 +313,8 @@ int paste_selection(struct tty_struct *t
+ struct tty_ldisc *ld;
+ DECLARE_WAITQUEUE(wait, current);
+
+- tty_lock_nested(); /* always called with BTM from vt_ioctl */
++ /* always called with BTM from vt_ioctl */
++ WARN_ON(!tty_locked());
+
+ acquire_console_sem();
+ poke_blanked_console();
+@@ -343,6 +344,5 @@ int paste_selection(struct tty_struct *t
+ __set_current_state(TASK_RUNNING);
+
+ tty_ldisc_deref(ld);
+- tty_unlock();
+ return 0;
+ }
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -492,10 +492,8 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
+ * tasklist_lock to walk task list for hangup event
+ * ->siglock to protect ->signal/->sighand
+ */
+-static void do_tty_hangup(struct work_struct *work)
++void tty_vhangup_locked(struct tty_struct *tty)
+ {
+- struct tty_struct *tty =
+- container_of(work, struct tty_struct, hangup_work);
+ struct file *cons_filp = NULL;
+ struct file *filp, *f = NULL;
+ struct task_struct *p;
+@@ -517,8 +515,6 @@ static void do_tty_hangup(struct work_st
+ /* inuse_filps is protected by the single tty lock,
+ this really needs to change if we want to flush the
+ workqueue with the lock held */
+- tty_lock_nested(); /* called with BTM held from pty_close and
+- others */
+ check_tty_count(tty, "do_tty_hangup");
+
+ file_list_lock();
+@@ -598,11 +594,20 @@ static void do_tty_hangup(struct work_st
+ */
+ set_bit(TTY_HUPPED, &tty->flags);
+ tty_ldisc_enable(tty);
+- tty_unlock();
+ if (f)
+ fput(f);
+ }
+
++static void do_tty_hangup(struct work_struct *work)
++{
++ struct tty_struct *tty =
++ container_of(work, struct tty_struct, hangup_work);
++
++ tty_lock();
++ tty_vhangup_locked(tty);
++ tty_unlock();
++}
++
+ /**
+ * tty_hangup - trigger a hangup event
+ * @tty: tty to hangup
+@@ -638,7 +643,9 @@ void tty_vhangup(struct tty_struct *tty)
+
+ printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
+ #endif
+- do_tty_hangup(&tty->hangup_work);
++ tty_lock();
++ tty_vhangup_locked(tty);
++ tty_unlock();
+ }
+
+ EXPORT_SYMBOL(tty_vhangup);
+@@ -719,10 +726,12 @@ void disassociate_ctty(int on_exit)
+ tty = get_current_tty();
+ if (tty) {
+ tty_pgrp = get_pid(tty->pgrp);
+- tty_lock_nested(); /* see above */
+- if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
+- tty_vhangup(tty);
+- tty_unlock();
++ if (on_exit) {
++ tty_lock();
++ if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
++ tty_vhangup_locked(tty);
++ tty_unlock();
++ }
+ tty_kref_put(tty);
+ } else if (on_exit) {
+ struct pid *old_pgrp;
+@@ -1213,18 +1222,14 @@ static int tty_driver_install_tty(struct
+ int ret;
+
+ if (driver->ops->install) {
+- tty_lock_nested(); /* already called with BTM held */
+ ret = driver->ops->install(driver, tty);
+- tty_unlock();
+ return ret;
+ }
+
+ if (tty_init_termios(tty) == 0) {
+- tty_lock_nested();
+ tty_driver_kref_get(driver);
+ tty->count++;
+ driver->ttys[idx] = tty;
+- tty_unlock();
+ return 0;
+ }
+ return -ENOMEM;
+@@ -1317,15 +1322,11 @@ struct tty_struct *tty_init_dev(struct t
+ struct tty_struct *tty;
+ int retval;
+
+- tty_lock_nested(); /* always called with tty lock held already */
+-
+ /* Check if pty master is being opened multiple times */
+ if (driver->subtype == PTY_TYPE_MASTER &&
+ (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+- tty_unlock();
+ return ERR_PTR(-EIO);
+ }
+- tty_unlock();
+
+ /*
+ * First time open is complex, especially for PTY devices.
+@@ -1369,9 +1370,7 @@ release_mem_out:
+ if (printk_ratelimit())
+ printk(KERN_INFO "tty_init_dev: ldisc open failed, "
+ "clearing slot %d\n", idx);
+- tty_lock_nested();
+ release_tty(tty, idx);
+- tty_unlock();
+ return ERR_PTR(retval);
+ }
+
+--- a/drivers/char/tty_ldisc.c
++++ b/drivers/char/tty_ldisc.c
+@@ -450,9 +450,8 @@ static int tty_ldisc_open(struct tty_str
+ if (ld->ops->open) {
+ int ret;
+ /* BTM here locks versus a hangup event */
+- tty_lock_nested(); /* always held here already */
++ WARN_ON(!tty_locked());
+ ret = ld->ops->open(tty);
+- tty_unlock();
+ return ret;
+ }
+ return 0;
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -416,6 +416,7 @@ extern int is_ignored(int sig);
+ extern int tty_signal(int sig, struct tty_struct *tty);
+ extern void tty_hangup(struct tty_struct *tty);
+ extern void tty_vhangup(struct tty_struct *tty);
++extern void tty_vhangup_locked(struct tty_struct *tty);
+ extern void tty_vhangup_self(void);
+ extern void tty_unhangup(struct file *filp);
+ extern int tty_hung_up_p(struct file *filp);
+@@ -574,21 +575,6 @@ extern long vt_compat_ioctl(struct tty_s
+ unsigned int cmd, unsigned long arg);
+
+ /* functions for preparation of BKL removal */
+-
+-/*
+- * tty_lock_nested get the tty_lock while potentially holding it
+- *
+- * The Big TTY Mutex is a recursive lock, meaning you can take it
+- * from a thread that is already holding it.
+- * This is bad for a number of reasons, so tty_lock_nested should
+- * really be used as rarely as possible. If a code location can
+- * be shown to never get called with this held already, it should
+- * use tty_lock() instead.
+- */
+-static inline void __lockfunc tty_lock_nested(void) __acquires(kernel_lock)
+-{
+- lock_kernel();
+-}
+ static inline void tty_lock(void) __acquires(kernel_lock)
+ {
+ #ifdef CONFIG_LOCK_KERNEL
diff --git a/tty/tty-reorder-ldisc-locking.patch b/tty/tty-reorder-ldisc-locking.patch
new file mode 100644
index 00000000000000..0ef070703e46b4
--- /dev/null
+++ b/tty/tty-reorder-ldisc-locking.patch
@@ -0,0 +1,117 @@
+From arnd@arndb.de Wed Jun 16 13:44:56 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:06 +0200
+Subject: tty: reorder ldisc locking
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-27-git-send-email-arnd@arndb.de>
+
+
+We need to release the BTM in paste_selection() when
+sleeping in tty_ldisc_ref_wait to avoid deadlocks
+with tty_ldisc_enable.
+
+In tty_set_ldisc, we now always grab the BTM before
+taking the ldisc_mutex in order to avoid AB-BA
+deadlocks between the two.
+
+tty_ldisc_halt potentially blocks on a workqueue
+function that takes the BTM, so we must release
+the BTM before calling it.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/selection.c | 9 +++++++--
+ drivers/char/tty_ldisc.c | 24 ++++++++++++++++++++----
+ 2 files changed, 27 insertions(+), 6 deletions(-)
+
+--- a/drivers/char/selection.c
++++ b/drivers/char/selection.c
+@@ -319,8 +319,13 @@ int paste_selection(struct tty_struct *t
+ poke_blanked_console();
+ release_console_sem();
+
+- ld = tty_ldisc_ref_wait(tty);
+-
++ ld = tty_ldisc_ref(tty);
++ if (!ld) {
++ tty_unlock();
++ ld = tty_ldisc_ref_wait(tty);
++ tty_lock();
++ }
++
+ add_wait_queue(&vc->paste_wait, &wait);
+ while (sel_buffer && sel_buffer_lth > pasted) {
+ set_current_state(TASK_INTERRUPTIBLE);
+--- a/drivers/char/tty_ldisc.c
++++ b/drivers/char/tty_ldisc.c
+@@ -582,6 +582,7 @@ int tty_set_ldisc(struct tty_struct *tty
+
+ tty_wait_until_sent(tty, 0);
+
++ tty_lock();
+ mutex_lock(&tty->ldisc_mutex);
+
+ /*
+@@ -591,13 +592,13 @@ int tty_set_ldisc(struct tty_struct *tty
+
+ while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
+ mutex_unlock(&tty->ldisc_mutex);
++ tty_unlock();
+ wait_event(tty_ldisc_wait,
+ test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
++ tty_lock();
+ mutex_lock(&tty->ldisc_mutex);
+ }
+
+- tty_lock();
+-
+ set_bit(TTY_LDISC_CHANGING, &tty->flags);
+
+ /*
+@@ -634,8 +635,8 @@ int tty_set_ldisc(struct tty_struct *tty
+
+ flush_scheduled_work();
+
+- mutex_lock(&tty->ldisc_mutex);
+ tty_lock();
++ mutex_lock(&tty->ldisc_mutex);
+ if (test_bit(TTY_HUPPED, &tty->flags)) {
+ /* We were raced by the hangup method. It will have stomped
+ the ldisc data and closed the ldisc down */
+@@ -782,7 +783,20 @@ void tty_ldisc_hangup(struct tty_struct
+ * Avoid racing set_ldisc or tty_ldisc_release
+ */
+ mutex_lock(&tty->ldisc_mutex);
+- tty_ldisc_halt(tty);
++
++ /*
++ * this is like tty_ldisc_halt, but we need to give up
++ * the BTM before calling cancel_delayed_work_sync,
++ * which may need to wait for another function taking the BTM
++ */
++ clear_bit(TTY_LDISC, &tty->flags);
++ tty_unlock();
++ cancel_delayed_work_sync(&tty->buf.work);
++ mutex_unlock(&tty->ldisc_mutex);
++
++ tty_lock();
++ 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
+@@ -853,8 +867,10 @@ void tty_ldisc_release(struct tty_struct
+ * race with the set_ldisc code path.
+ */
+
++ tty_unlock();
+ tty_ldisc_halt(tty);
+ flush_scheduled_work();
++ tty_lock();
+
+ mutex_lock(&tty->ldisc_mutex);
+ /*
diff --git a/tty/tty-replace-bkl-with-a-new-tty_lock.patch b/tty/tty-replace-bkl-with-a-new-tty_lock.patch
new file mode 100644
index 00000000000000..2f262e2222f876
--- /dev/null
+++ b/tty/tty-replace-bkl-with-a-new-tty_lock.patch
@@ -0,0 +1,1041 @@
+From arnd@arndb.de Wed Jun 16 13:40:23 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:01 +0200
+Subject: tty: replace BKL with a new tty_lock
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-22-git-send-email-arnd@arndb.de>
+
+
+As a preparation for replacing the big kernel lock
+in the TTY layer, wrap all the callers in new
+macros tty_lock, tty_lock_nested and tty_unlock.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/amiserial.c | 16 ++---
+ drivers/char/briq_panel.c | 6 +-
+ drivers/char/n_hdlc.c | 16 ++---
+ drivers/char/n_r3964.c | 8 +-
+ drivers/char/pty.c | 4 -
+ drivers/char/selection.c | 4 -
+ drivers/char/serial167.c | 4 -
+ drivers/char/sx.c | 12 ++--
+ drivers/char/tty_io.c | 115 ++++++++++++++++++++++-------------------
+ drivers/char/tty_ldisc.c | 24 ++++----
+ drivers/char/vc_screen.c | 4 -
+ drivers/char/vt_ioctl.c | 10 +--
+ drivers/serial/68360serial.c | 4 -
+ drivers/serial/crisv10.c | 4 -
+ drivers/serial/serial_core.c | 10 +--
+ drivers/video/console/vgacon.c | 4 -
+ include/linux/tty.h | 31 +++++++++++
+ 17 files changed, 161 insertions(+), 115 deletions(-)
+
+--- a/drivers/char/amiserial.c
++++ b/drivers/char/amiserial.c
+@@ -1072,7 +1072,7 @@ static int get_serial_info(struct async_
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+- lock_kernel();
++ tty_lock();
+ tmp.type = state->type;
+ tmp.line = state->line;
+ tmp.port = state->port;
+@@ -1083,7 +1083,7 @@ static int get_serial_info(struct async_
+ tmp.close_delay = state->close_delay;
+ tmp.closing_wait = state->closing_wait;
+ tmp.custom_divisor = state->custom_divisor;
+- unlock_kernel();
++ tty_unlock();
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+@@ -1100,14 +1100,14 @@ static int set_serial_info(struct async_
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+
+- lock_kernel();
++ tty_lock();
+ state = info->state;
+ old_state = *state;
+
+ change_irq = new_serial.irq != state->irq;
+ change_port = (new_serial.port != state->port);
+ if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) {
+- unlock_kernel();
++ tty_unlock();
+ return -EINVAL;
+ }
+
+@@ -1127,7 +1127,7 @@ static int set_serial_info(struct async_
+ }
+
+ if (new_serial.baud_base < 9600) {
+- unlock_kernel();
++ tty_unlock();
+ return -EINVAL;
+ }
+
+@@ -1163,7 +1163,7 @@ check_and_exit:
+ }
+ } else
+ retval = startup(info);
+- unlock_kernel();
++ tty_unlock();
+ return retval;
+ }
+
+@@ -1538,7 +1538,7 @@ static void rs_wait_until_sent(struct tt
+
+ orig_jiffies = jiffies;
+
+- lock_kernel();
++ tty_lock_nested(); /* tty_wait_until_sent is called from lots of places */
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+@@ -1579,7 +1579,7 @@ static void rs_wait_until_sent(struct tt
+ break;
+ }
+ __set_current_state(TASK_RUNNING);
+- unlock_kernel();
++ tty_unlock();
+ #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+ #endif
+--- a/drivers/char/briq_panel.c
++++ b/drivers/char/briq_panel.c
+@@ -67,15 +67,15 @@ static void set_led(char state)
+
+ static int briq_panel_open(struct inode *ino, struct file *filep)
+ {
+- lock_kernel();
++ tty_lock();
+ /* enforce single access, vfd_is_open is protected by BKL */
+ if (vfd_is_open) {
+- unlock_kernel();
++ tty_unlock();
+ return -EBUSY;
+ }
+ vfd_is_open = 1;
+
+- unlock_kernel();
++ tty_unlock();
+ return 0;
+ }
+
+--- a/drivers/char/n_hdlc.c
++++ b/drivers/char/n_hdlc.c
+@@ -598,18 +598,18 @@ static ssize_t n_hdlc_tty_read(struct tt
+ return -EFAULT;
+ }
+
+- lock_kernel();
++ tty_lock();
+
+ for (;;) {
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
+- unlock_kernel();
++ tty_unlock();
+ return -EIO;
+ }
+
+ n_hdlc = tty2n_hdlc (tty);
+ if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
+ tty != n_hdlc->tty) {
+- unlock_kernel();
++ tty_unlock();
+ return 0;
+ }
+
+@@ -619,13 +619,13 @@ static ssize_t n_hdlc_tty_read(struct tt
+
+ /* no data */
+ if (file->f_flags & O_NONBLOCK) {
+- unlock_kernel();
++ tty_unlock();
+ return -EAGAIN;
+ }
+
+ interruptible_sleep_on (&tty->read_wait);
+ if (signal_pending(current)) {
+- unlock_kernel();
++ tty_unlock();
+ return -EINTR;
+ }
+ }
+@@ -648,7 +648,7 @@ static ssize_t n_hdlc_tty_read(struct tt
+ kfree(rbuf);
+ else
+ n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+
+ } /* end of n_hdlc_tty_read() */
+@@ -691,7 +691,7 @@ static ssize_t n_hdlc_tty_write(struct t
+ count = maxframe;
+ }
+
+- lock_kernel();
++ tty_lock();
+
+ add_wait_queue(&tty->write_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+@@ -731,7 +731,7 @@ static ssize_t n_hdlc_tty_write(struct t
+ n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
+ n_hdlc_send_frames(n_hdlc,tty);
+ }
+- unlock_kernel();
++ tty_unlock();
+ return error;
+
+ } /* end of n_hdlc_tty_write() */
+--- a/drivers/char/n_r3964.c
++++ b/drivers/char/n_r3964.c
+@@ -1067,7 +1067,7 @@ static ssize_t r3964_read(struct tty_str
+
+ TRACE_L("read()");
+
+- lock_kernel();
++ tty_lock();
+
+ pClient = findClient(pInfo, task_pid(current));
+ if (pClient) {
+@@ -1109,7 +1109,7 @@ static ssize_t r3964_read(struct tty_str
+ }
+ ret = -EPERM;
+ unlock:
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+ }
+
+@@ -1158,7 +1158,7 @@ static ssize_t r3964_write(struct tty_st
+ pHeader->locks = 0;
+ pHeader->owner = NULL;
+
+- lock_kernel();
++ tty_lock();
+
+ pClient = findClient(pInfo, task_pid(current));
+ if (pClient) {
+@@ -1177,7 +1177,7 @@ static ssize_t r3964_write(struct tty_st
+ add_tx_queue(pInfo, pHeader);
+ trigger_transmit(pInfo);
+
+- unlock_kernel();
++ tty_unlock();
+
+ return 0;
+ }
+--- a/drivers/char/pty.c
++++ b/drivers/char/pty.c
+@@ -671,9 +671,9 @@ static int ptmx_open(struct inode *inode
+ {
+ int ret;
+
+- lock_kernel();
++ tty_lock();
+ ret = __ptmx_open(inode, filp);
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+ }
+
+--- a/drivers/char/selection.c
++++ b/drivers/char/selection.c
+@@ -313,7 +313,7 @@ int paste_selection(struct tty_struct *t
+ struct tty_ldisc *ld;
+ DECLARE_WAITQUEUE(wait, current);
+
+- lock_kernel();
++ tty_lock_nested(); /* always called with BTM from vt_ioctl */
+
+ acquire_console_sem();
+ poke_blanked_console();
+@@ -338,6 +338,6 @@ int paste_selection(struct tty_struct *t
+ __set_current_state(TASK_RUNNING);
+
+ tty_ldisc_deref(ld);
+- unlock_kernel();
++ tty_unlock();
+ return 0;
+ }
+--- a/drivers/char/serial167.c
++++ b/drivers/char/serial167.c
+@@ -1505,7 +1505,7 @@ cy_ioctl(struct tty_struct *tty, struct
+ printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */
+ #endif
+
+- lock_kernel();
++ tty_lock();
+
+ switch (cmd) {
+ case CYGETMON:
+@@ -1561,7 +1561,7 @@ cy_ioctl(struct tty_struct *tty, struct
+ default:
+ ret_val = -ENOIOCTLCMD;
+ }
+- unlock_kernel();
++ tty_unlock();
+
+ #ifdef SERIAL_DEBUG_OTHER
+ printk("cy_ioctl done\n");
+--- a/drivers/char/sx.c
++++ b/drivers/char/sx.c
+@@ -1699,7 +1699,7 @@ static long sx_fw_ioctl(struct file *fil
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+- lock_kernel();
++ tty_lock();
+
+ sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg);
+
+@@ -1848,7 +1848,7 @@ static long sx_fw_ioctl(struct file *fil
+ break;
+ }
+ out:
+- unlock_kernel();
++ tty_unlock();
+ func_exit();
+ return rc;
+ }
+@@ -1859,7 +1859,7 @@ static int sx_break(struct tty_struct *t
+ int rv;
+
+ func_enter();
+- lock_kernel();
++ tty_lock();
+
+ if (flag)
+ rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK);
+@@ -1868,7 +1868,7 @@ static int sx_break(struct tty_struct *t
+ if (rv != 1)
+ printk(KERN_ERR "sx: couldn't send break (%x).\n",
+ read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat)));
+- unlock_kernel();
++ tty_unlock();
+ func_exit();
+ return 0;
+ }
+@@ -1909,7 +1909,7 @@ static int sx_ioctl(struct tty_struct *t
+ /* func_enter2(); */
+
+ rc = 0;
+- lock_kernel();
++ tty_lock();
+ switch (cmd) {
+ case TIOCGSERIAL:
+ rc = gs_getserial(&port->gs, argp);
+@@ -1921,7 +1921,7 @@ static int sx_ioctl(struct tty_struct *t
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+- unlock_kernel();
++ tty_unlock();
+
+ /* func_exit(); */
+ return rc;
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -149,6 +149,7 @@ static long tty_compat_ioctl(struct file
+ #else
+ #define tty_compat_ioctl NULL
+ #endif
++static int __tty_fasync(int fd, struct file *filp, int on);
+ static int tty_fasync(int fd, struct file *filp, int on);
+ static void release_tty(struct tty_struct *tty, int idx);
+ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+@@ -483,7 +484,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
+ * remains intact.
+ *
+ * Locking:
+- * BKL
++ * BTM
+ * redirect lock for undoing redirection
+ * file list lock for manipulating list of ttys
+ * tty_ldisc_lock from called functions
+@@ -513,8 +514,11 @@ static void do_tty_hangup(struct work_st
+ }
+ spin_unlock(&redirect_lock);
+
+- /* inuse_filps is protected by the single kernel lock */
+- lock_kernel();
++ /* inuse_filps is protected by the single tty lock,
++ this really needs to change if we want to flush the
++ workqueue with the lock held */
++ tty_lock_nested(); /* called with BTM held from pty_close and
++ others */
+ check_tty_count(tty, "do_tty_hangup");
+
+ file_list_lock();
+@@ -525,7 +529,7 @@ static void do_tty_hangup(struct work_st
+ if (filp->f_op->write != tty_write)
+ continue;
+ closecount++;
+- tty_fasync(-1, filp, 0); /* can't block */
++ __tty_fasync(-1, filp, 0); /* can't block */
+ filp->f_op = &hung_up_tty_fops;
+ }
+ file_list_unlock();
+@@ -594,7 +598,7 @@ static void do_tty_hangup(struct work_st
+ */
+ set_bit(TTY_HUPPED, &tty->flags);
+ tty_ldisc_enable(tty);
+- unlock_kernel();
++ tty_unlock();
+ if (f)
+ fput(f);
+ }
+@@ -696,7 +700,8 @@ static void session_clear_tty(struct pid
+ * exiting; it is 0 if called by the ioctl TIOCNOTTY.
+ *
+ * Locking:
+- * BKL is taken for hysterical raisins
++ * BTM is taken for hysterical raisins, and held when
++ * called from no_tty().
+ * tty_mutex is taken to protect tty
+ * ->siglock is taken to protect ->signal/->sighand
+ * tasklist_lock is taken to walk process list for sessions
+@@ -714,10 +719,10 @@ void disassociate_ctty(int on_exit)
+ tty = get_current_tty();
+ if (tty) {
+ tty_pgrp = get_pid(tty->pgrp);
+- lock_kernel();
++ tty_lock_nested(); /* see above */
+ if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
+ tty_vhangup(tty);
+- unlock_kernel();
++ tty_unlock();
+ tty_kref_put(tty);
+ } else if (on_exit) {
+ struct pid *old_pgrp;
+@@ -774,9 +779,9 @@ void disassociate_ctty(int on_exit)
+ void no_tty(void)
+ {
+ struct task_struct *tsk = current;
+- lock_kernel();
++ tty_lock();
+ disassociate_ctty(0);
+- unlock_kernel();
++ tty_unlock();
+ proc_clear_tty(tsk);
+ }
+
+@@ -1013,19 +1018,19 @@ out:
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ *
+- * We must still hold the BKL and test the CLOSING flag for the moment.
++ * We must still hold the BTM and test the CLOSING flag for the moment.
+ */
+
+ void tty_write_message(struct tty_struct *tty, char *msg)
+ {
+ if (tty) {
+ mutex_lock(&tty->atomic_write_lock);
+- lock_kernel();
++ tty_lock();
+ if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+- unlock_kernel();
++ tty_unlock();
+ tty->ops->write(tty, msg, strlen(msg));
+ } else
+- unlock_kernel();
++ tty_unlock();
+ tty_write_unlock(tty);
+ }
+ return;
+@@ -1208,18 +1213,18 @@ static int tty_driver_install_tty(struct
+ int ret;
+
+ if (driver->ops->install) {
+- lock_kernel();
++ tty_lock_nested(); /* already called with BTM held */
+ ret = driver->ops->install(driver, tty);
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+ }
+
+ if (tty_init_termios(tty) == 0) {
+- lock_kernel();
++ tty_lock_nested();
+ tty_driver_kref_get(driver);
+ tty->count++;
+ driver->ttys[idx] = tty;
+- unlock_kernel();
++ tty_unlock();
+ return 0;
+ }
+ return -ENOMEM;
+@@ -1312,14 +1317,15 @@ struct tty_struct *tty_init_dev(struct t
+ struct tty_struct *tty;
+ int retval;
+
+- lock_kernel();
++ tty_lock_nested(); /* always called with tty lock held already */
++
+ /* Check if pty master is being opened multiple times */
+ if (driver->subtype == PTY_TYPE_MASTER &&
+ (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+- unlock_kernel();
++ tty_unlock();
+ return ERR_PTR(-EIO);
+ }
+- unlock_kernel();
++ tty_unlock();
+
+ /*
+ * First time open is complex, especially for PTY devices.
+@@ -1363,9 +1369,9 @@ release_mem_out:
+ if (printk_ratelimit())
+ printk(KERN_INFO "tty_init_dev: ldisc open failed, "
+ "clearing slot %d\n", idx);
+- lock_kernel();
++ tty_lock_nested();
+ release_tty(tty, idx);
+- unlock_kernel();
++ tty_unlock();
+ return ERR_PTR(retval);
+ }
+
+@@ -1512,10 +1518,10 @@ int tty_release(struct inode *inode, str
+ if (tty_paranoia_check(tty, inode, "tty_release_dev"))
+ return 0;
+
+- lock_kernel();
++ tty_lock();
+ check_tty_count(tty, "tty_release_dev");
+
+- tty_fasync(-1, filp, 0);
++ __tty_fasync(-1, filp, 0);
+
+ idx = tty->index;
+ pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+@@ -1527,18 +1533,18 @@ int tty_release(struct inode *inode, str
+ if (idx < 0 || idx >= tty->driver->num) {
+ printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
+ "free (%s)\n", tty->name);
+- unlock_kernel();
++ tty_unlock();
+ return 0;
+ }
+ if (!devpts) {
+ if (tty != tty->driver->ttys[idx]) {
+- unlock_kernel();
++ tty_unlock();
+ printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
+ "for (%s)\n", idx, tty->name);
+ return 0;
+ }
+ if (tty->termios != tty->driver->termios[idx]) {
+- unlock_kernel();
++ tty_unlock();
+ printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
+ "for (%s)\n",
+ idx, tty->name);
+@@ -1556,21 +1562,21 @@ int tty_release(struct inode *inode, str
+ if (tty->driver->other &&
+ !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+ if (o_tty != tty->driver->other->ttys[idx]) {
+- unlock_kernel();
++ tty_unlock();
+ printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
+ "not o_tty for (%s)\n",
+ idx, tty->name);
+ return 0 ;
+ }
+ if (o_tty->termios != tty->driver->other->termios[idx]) {
+- unlock_kernel();
++ tty_unlock();
+ printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
+ "not o_termios for (%s)\n",
+ idx, tty->name);
+ return 0;
+ }
+ if (o_tty->link != tty) {
+- unlock_kernel();
++ tty_unlock();
+ printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
+ return 0;
+ }
+@@ -1579,7 +1585,7 @@ int tty_release(struct inode *inode, str
+ if (tty->ops->close)
+ tty->ops->close(tty, filp);
+
+- unlock_kernel();
++ tty_unlock();
+ /*
+ * Sanity check: if tty->count is going to zero, there shouldn't be
+ * any waiters on tty->read_wait or tty->write_wait. We test the
+@@ -1602,7 +1608,7 @@ int tty_release(struct inode *inode, str
+ opens on /dev/tty */
+
+ mutex_lock(&tty_mutex);
+- lock_kernel();
++ tty_lock();
+ tty_closing = tty->count <= 1;
+ o_tty_closing = o_tty &&
+ (o_tty->count <= (pty_master ? 1 : 0));
+@@ -1633,7 +1639,7 @@ int tty_release(struct inode *inode, str
+
+ printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
+ "active!\n", tty_name(tty, buf));
+- unlock_kernel();
++ tty_unlock();
+ mutex_unlock(&tty_mutex);
+ schedule();
+ }
+@@ -1698,7 +1704,7 @@ int tty_release(struct inode *inode, str
+
+ /* check whether both sides are closing ... */
+ if (!tty_closing || (o_tty && !o_tty_closing)) {
+- unlock_kernel();
++ tty_unlock();
+ return 0;
+ }
+
+@@ -1718,7 +1724,7 @@ int tty_release(struct inode *inode, str
+ /* Make this pty number available for reallocation */
+ if (devpts)
+ devpts_kill_index(inode, idx);
+- unlock_kernel();
++ tty_unlock();
+ return 0;
+ }
+
+@@ -1760,12 +1766,12 @@ retry_open:
+ retval = 0;
+
+ mutex_lock(&tty_mutex);
+- lock_kernel();
++ tty_lock();
+
+ if (device == MKDEV(TTYAUX_MAJOR, 0)) {
+ tty = get_current_tty();
+ if (!tty) {
+- unlock_kernel();
++ tty_unlock();
+ mutex_unlock(&tty_mutex);
+ return -ENXIO;
+ }
+@@ -1797,14 +1803,14 @@ retry_open:
+ goto got_driver;
+ }
+ }
+- unlock_kernel();
++ tty_unlock();
+ mutex_unlock(&tty_mutex);
+ return -ENODEV;
+ }
+
+ driver = get_tty_driver(device, &index);
+ if (!driver) {
+- unlock_kernel();
++ tty_unlock();
+ mutex_unlock(&tty_mutex);
+ return -ENODEV;
+ }
+@@ -1814,7 +1820,7 @@ got_driver:
+ tty = tty_driver_lookup_tty(driver, inode, index);
+
+ if (IS_ERR(tty)) {
+- unlock_kernel();
++ tty_unlock();
+ mutex_unlock(&tty_mutex);
+ return PTR_ERR(tty);
+ }
+@@ -1830,7 +1836,7 @@ got_driver:
+ mutex_unlock(&tty_mutex);
+ tty_driver_kref_put(driver);
+ if (IS_ERR(tty)) {
+- unlock_kernel();
++ tty_unlock();
+ return PTR_ERR(tty);
+ }
+
+@@ -1862,11 +1868,11 @@ got_driver:
+ #endif
+ tty_release(inode, filp);
+ if (retval != -ERESTARTSYS) {
+- unlock_kernel();
++ tty_unlock();
+ return retval;
+ }
+ if (signal_pending(current)) {
+- unlock_kernel();
++ tty_unlock();
+ return retval;
+ }
+ schedule();
+@@ -1875,14 +1881,14 @@ got_driver:
+ */
+ if (filp->f_op == &hung_up_tty_fops)
+ filp->f_op = &tty_fops;
+- unlock_kernel();
++ tty_unlock();
+ goto retry_open;
+ }
+- unlock_kernel();
++ tty_unlock();
+
+
+ mutex_lock(&tty_mutex);
+- lock_kernel();
++ tty_lock();
+ spin_lock_irq(&current->sighand->siglock);
+ if (!noctty &&
+ current->signal->leader &&
+@@ -1890,7 +1896,7 @@ got_driver:
+ tty->session == NULL)
+ __proc_set_tty(current, tty);
+ spin_unlock_irq(&current->sighand->siglock);
+- unlock_kernel();
++ tty_unlock();
+ mutex_unlock(&tty_mutex);
+ return 0;
+ }
+@@ -1926,13 +1932,12 @@ static unsigned int tty_poll(struct file
+ return ret;
+ }
+
+-static int tty_fasync(int fd, struct file *filp, int on)
++static int __tty_fasync(int fd, struct file *filp, int on)
+ {
+ struct tty_struct *tty;
+ unsigned long flags;
+ int retval = 0;
+
+- lock_kernel();
+ tty = (struct tty_struct *)filp->private_data;
+ if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
+ goto out;
+@@ -1966,7 +1971,15 @@ static int tty_fasync(int fd, struct fil
+ }
+ retval = 0;
+ out:
+- unlock_kernel();
++ return retval;
++}
++
++static int tty_fasync(int fd, struct file *filp, int on)
++{
++ int retval;
++ tty_lock();
++ retval = __tty_fasync(fd, filp, on);
++ tty_unlock();
+ return retval;
+ }
+
+--- a/drivers/char/tty_ldisc.c
++++ b/drivers/char/tty_ldisc.c
+@@ -440,6 +440,8 @@ static void tty_set_termios_ldisc(struct
+ *
+ * A helper opening method. Also a convenient debugging and check
+ * point.
++ *
++ * Locking: always called with BTM already held.
+ */
+
+ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
+@@ -447,10 +449,10 @@ static int tty_ldisc_open(struct tty_str
+ WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
+ if (ld->ops->open) {
+ int ret;
+- /* BKL here locks verus a hangup event */
+- lock_kernel();
++ /* BTM here locks versus a hangup event */
++ tty_lock_nested(); /* always held here already */
+ ret = ld->ops->open(tty);
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+ }
+ return 0;
+@@ -553,7 +555,7 @@ int tty_set_ldisc(struct tty_struct *tty
+ if (IS_ERR(new_ldisc))
+ return PTR_ERR(new_ldisc);
+
+- lock_kernel();
++ tty_lock();
+ /*
+ * We need to look at the tty locking here for pty/tty pairs
+ * when both sides try to change in parallel.
+@@ -567,12 +569,12 @@ int tty_set_ldisc(struct tty_struct *tty
+ */
+
+ if (tty->ldisc->ops->num == ldisc) {
+- unlock_kernel();
++ tty_unlock();
+ tty_ldisc_put(new_ldisc);
+ return 0;
+ }
+
+- unlock_kernel();
++ tty_unlock();
+ /*
+ * Problem: What do we do if this blocks ?
+ * We could deadlock here
+@@ -594,7 +596,7 @@ int tty_set_ldisc(struct tty_struct *tty
+ mutex_lock(&tty->ldisc_mutex);
+ }
+
+- lock_kernel();
++ tty_lock();
+
+ set_bit(TTY_LDISC_CHANGING, &tty->flags);
+
+@@ -607,7 +609,7 @@ int tty_set_ldisc(struct tty_struct *tty
+
+ o_ldisc = tty->ldisc;
+
+- unlock_kernel();
++ tty_unlock();
+ /*
+ * Make sure we don't change while someone holds a
+ * reference to the line discipline. The TTY_LDISC bit
+@@ -633,14 +635,14 @@ int tty_set_ldisc(struct tty_struct *tty
+ flush_scheduled_work();
+
+ mutex_lock(&tty->ldisc_mutex);
+- lock_kernel();
++ tty_lock();
+ if (test_bit(TTY_HUPPED, &tty->flags)) {
+ /* We were raced by the hangup method. It will have stomped
+ the ldisc data and closed the ldisc down */
+ clear_bit(TTY_LDISC_CHANGING, &tty->flags);
+ mutex_unlock(&tty->ldisc_mutex);
+ tty_ldisc_put(new_ldisc);
+- unlock_kernel();
++ tty_unlock();
+ return -EIO;
+ }
+
+@@ -682,7 +684,7 @@ int tty_set_ldisc(struct tty_struct *tty
+ if (o_work)
+ schedule_delayed_work(&o_tty->buf.work, 1);
+ mutex_unlock(&tty->ldisc_mutex);
+- unlock_kernel();
++ tty_unlock();
+ return retval;
+ }
+
+--- a/drivers/char/vc_screen.c
++++ b/drivers/char/vc_screen.c
+@@ -463,10 +463,10 @@ vcs_open(struct inode *inode, struct fil
+ unsigned int currcons = iminor(inode) & 127;
+ int ret = 0;
+
+- lock_kernel();
++ tty_lock();
+ if(currcons && !vc_cons_allocated(currcons-1))
+ ret = -ENXIO;
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+ }
+
+--- a/drivers/char/vt_ioctl.c
++++ b/drivers/char/vt_ioctl.c
+@@ -509,7 +509,7 @@ int vt_ioctl(struct tty_struct *tty, str
+
+ console = vc->vc_num;
+
+- lock_kernel();
++ tty_lock();
+
+ if (!vc_cons_allocated(console)) { /* impossible? */
+ ret = -ENOIOCTLCMD;
+@@ -1336,7 +1336,7 @@ int vt_ioctl(struct tty_struct *tty, str
+ ret = -ENOIOCTLCMD;
+ }
+ out:
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+ eperm:
+ ret = -EPERM;
+@@ -1503,7 +1503,7 @@ long vt_compat_ioctl(struct tty_struct *
+
+ console = vc->vc_num;
+
+- lock_kernel();
++ tty_lock();
+
+ if (!vc_cons_allocated(console)) { /* impossible? */
+ ret = -ENOIOCTLCMD;
+@@ -1571,11 +1571,11 @@ long vt_compat_ioctl(struct tty_struct *
+ goto fallback;
+ }
+ out:
+- unlock_kernel();
++ tty_unlock();
+ return ret;
+
+ fallback:
+- unlock_kernel();
++ tty_unlock();
+ return vt_ioctl(tty, file, cmd, arg);
+ }
+
+--- a/drivers/serial/68360serial.c
++++ b/drivers/serial/68360serial.c
+@@ -1705,7 +1705,7 @@ static void rs_360_wait_until_sent(struc
+ printk("jiff=%lu...", jiffies);
+ #endif
+
+- lock_kernel();
++ tty_lock_nested(); /* always held already since we come from ->close */
+ /* We go through the loop at least once because we can't tell
+ * exactly when the last character exits the shifter. There can
+ * be at least two characters waiting to be sent after the buffers
+@@ -1734,7 +1734,7 @@ static void rs_360_wait_until_sent(struc
+ bdp--;
+ } while (bdp->status & BD_SC_READY);
+ current->state = TASK_RUNNING;
+- unlock_kernel();
++ tty_unlock();
+ #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+ #endif
+--- a/drivers/serial/crisv10.c
++++ b/drivers/serial/crisv10.c
+@@ -3924,7 +3924,7 @@ static void rs_wait_until_sent(struct tt
+ * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
+ * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
+ */
+- lock_kernel();
++ tty_lock_nested(); /* locked already when coming from close */
+ orig_jiffies = jiffies;
+ while (info->xmit.head != info->xmit.tail || /* More in send queue */
+ (*info->ostatusadr & 0x007f) || /* more in FIFO */
+@@ -3941,7 +3941,7 @@ static void rs_wait_until_sent(struct tt
+ curr_time_usec - info->last_tx_active_usec;
+ }
+ set_current_state(TASK_RUNNING);
+- unlock_kernel();
++ tty_unlock();
+ }
+
+ /*
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -1274,7 +1274,7 @@ static void uart_close(struct tty_struct
+ struct uart_port *uport;
+ unsigned long flags;
+
+- BUG_ON(!kernel_locked());
++ BUG_ON(!tty_locked());
+
+ if (!state)
+ return;
+@@ -1382,7 +1382,7 @@ static void uart_wait_until_sent(struct
+ if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+ return;
+
+- lock_kernel();
++ tty_lock_nested(); /* already locked when coming from close */
+
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+@@ -1429,7 +1429,7 @@ static void uart_wait_until_sent(struct
+ break;
+ }
+ set_current_state(TASK_RUNNING); /* might not be needed */
+- unlock_kernel();
++ tty_unlock();
+ }
+
+ /*
+@@ -1444,7 +1444,7 @@ static void uart_hangup(struct tty_struc
+ struct tty_port *port = &state->port;
+ unsigned long flags;
+
+- BUG_ON(!kernel_locked());
++ BUG_ON(!tty_locked());
+ pr_debug("uart_hangup(%d)\n", state->uart_port->line);
+
+ mutex_lock(&port->mutex);
+@@ -1570,7 +1570,7 @@ static int uart_open(struct tty_struct *
+ struct tty_port *port;
+ int retval, line = tty->index;
+
+- BUG_ON(!kernel_locked());
++ BUG_ON(!tty_locked());
+ pr_debug("uart_open(%d) called\n", line);
+
+ /*
+--- a/drivers/video/console/vgacon.c
++++ b/drivers/video/console/vgacon.c
+@@ -1108,7 +1108,7 @@ static int vgacon_do_font_op(struct vgas
+ charmap += 4 * cmapsz;
+ #endif
+
+- unlock_kernel();
++ tty_unlock();
+ spin_lock_irq(&vga_lock);
+ /* First, the Sequencer */
+ vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
+@@ -1192,7 +1192,7 @@ static int vgacon_do_font_op(struct vgas
+ vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);
+ }
+ spin_unlock_irq(&vga_lock);
+- lock_kernel();
++ tty_lock();
+ return 0;
+ }
+
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -13,6 +13,7 @@
+ #include <linux/tty_driver.h>
+ #include <linux/tty_ldisc.h>
+ #include <linux/mutex.h>
++#include <linux/smp_lock.h>
+
+ #include <asm/system.h>
+
+@@ -572,5 +573,35 @@ extern int vt_ioctl(struct tty_struct *t
+ extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+
++/* functions for preparation of BKL removal */
++
++/*
++ * tty_lock_nested get the tty_lock while potentially holding it
++ *
++ * The Big TTY Mutex is a recursive lock, meaning you can take it
++ * from a thread that is already holding it.
++ * This is bad for a number of reasons, so tty_lock_nested should
++ * really be used as rarely as possible. If a code location can
++ * be shown to never get called with this held already, it should
++ * use tty_lock() instead.
++ */
++static inline void __lockfunc tty_lock_nested(void) __acquires(kernel_lock)
++{
++ lock_kernel();
++}
++static inline void tty_lock(void) __acquires(kernel_lock)
++{
++#ifdef CONFIG_LOCK_KERNEL
++ /* kernel_locked is 1 for !CONFIG_LOCK_KERNEL */
++ WARN_ON(kernel_locked());
++#endif
++ lock_kernel();
++}
++static inline void tty_unlock(void) __releases(kernel_lock)
++{
++ unlock_kernel();
++}
++#define tty_locked() (kernel_locked())
++
+ #endif /* __KERNEL__ */
+ #endif
diff --git a/tty/tty-untangle-locking-of-wait_until_sent.patch b/tty/tty-untangle-locking-of-wait_until_sent.patch
new file mode 100644
index 00000000000000..da72b6ed9d6e02
--- /dev/null
+++ b/tty/tty-untangle-locking-of-wait_until_sent.patch
@@ -0,0 +1,175 @@
+From arnd@arndb.de Wed Jun 16 13:45:16 2010
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 1 Jun 2010 22:53:07 +0200
+Subject: tty: untangle locking of wait_until_sent
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>
+Message-ID: <1275425591-8803-28-git-send-email-arnd@arndb.de>
+
+
+Some wait_until_sent versions require the big
+tty mutex, others don't and some callers of
+wait_until_sent already hold it while other don't.
+That leads to recursive use of the BTM in these
+functions, which we're trying to get rid of.
+
+This turns all cleans up the locking there so
+that the driver's wait_until_sent function
+never takes the BTM itself if it is already
+called with that lock held.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/amiserial.c | 11 +++++++++--
+ drivers/serial/68360serial.c | 2 --
+ drivers/serial/crisv10.c | 2 --
+ drivers/serial/serial_core.c | 31 ++++++++++++++++++++++---------
+ 4 files changed, 31 insertions(+), 15 deletions(-)
+
+--- a/drivers/char/amiserial.c
++++ b/drivers/char/amiserial.c
+@@ -1528,6 +1528,7 @@ static void rs_wait_until_sent(struct tt
+ {
+ struct async_struct * info = tty->driver_data;
+ unsigned long orig_jiffies, char_time;
++ int tty_was_locked = tty_locked();
+ int lsr;
+
+ if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
+@@ -1538,7 +1539,12 @@ static void rs_wait_until_sent(struct tt
+
+ orig_jiffies = jiffies;
+
+- tty_lock_nested(); /* tty_wait_until_sent is called from lots of places */
++ /*
++ * tty_wait_until_sent is called from lots of places,
++ * with or without the BTM.
++ */
++ if (!tty_was_locked)
++ tty_lock();
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+@@ -1579,7 +1585,8 @@ static void rs_wait_until_sent(struct tt
+ break;
+ }
+ __set_current_state(TASK_RUNNING);
+- tty_unlock();
++ if (!tty_was_locked)
++ tty_unlock();
+ #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+ #endif
+--- a/drivers/serial/68360serial.c
++++ b/drivers/serial/68360serial.c
+@@ -1705,7 +1705,6 @@ static void rs_360_wait_until_sent(struc
+ printk("jiff=%lu...", jiffies);
+ #endif
+
+- tty_lock_nested(); /* always held already since we come from ->close */
+ /* We go through the loop at least once because we can't tell
+ * exactly when the last character exits the shifter. There can
+ * be at least two characters waiting to be sent after the buffers
+@@ -1734,7 +1733,6 @@ static void rs_360_wait_until_sent(struc
+ bdp--;
+ } while (bdp->status & BD_SC_READY);
+ current->state = TASK_RUNNING;
+- tty_unlock();
+ #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+ #endif
+--- a/drivers/serial/crisv10.c
++++ b/drivers/serial/crisv10.c
+@@ -3924,7 +3924,6 @@ static void rs_wait_until_sent(struct tt
+ * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
+ * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
+ */
+- tty_lock_nested(); /* locked already when coming from close */
+ orig_jiffies = jiffies;
+ while (info->xmit.head != info->xmit.tail || /* More in send queue */
+ (*info->ostatusadr & 0x007f) || /* more in FIFO */
+@@ -3941,7 +3940,6 @@ static void rs_wait_until_sent(struct tt
+ curr_time_usec - info->last_tx_active_usec;
+ }
+ set_current_state(TASK_RUNNING);
+- tty_unlock();
+ }
+
+ /*
+--- a/drivers/serial/serial_core.c
++++ b/drivers/serial/serial_core.c
+@@ -60,7 +60,7 @@ static struct lock_class_key port_lock_k
+
+ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
+ struct ktermios *old_termios);
+-static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
++static void __uart_wait_until_sent(struct uart_port *port, int timeout);
+ static void uart_change_pm(struct uart_state *state, int pm_state);
+
+ /*
+@@ -1322,8 +1322,16 @@ static void uart_close(struct tty_struct
+ tty->closing = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+- if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+- tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait));
++ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
++ /*
++ * hack: open-coded tty_wait_until_sent to avoid
++ * recursive tty_lock
++ */
++ long timeout = msecs_to_jiffies(port->closing_wait);
++ if (wait_event_interruptible_timeout(tty->write_wait,
++ !tty_chars_in_buffer(tty), timeout) >= 0)
++ __uart_wait_until_sent(uport, timeout);
++ }
+
+ /*
+ * At this point, we stop accepting input. To do this, we
+@@ -1339,7 +1347,7 @@ static void uart_close(struct tty_struct
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+- uart_wait_until_sent(tty, uport->timeout);
++ __uart_wait_until_sent(uport, uport->timeout);
+ }
+
+ uart_shutdown(tty, state);
+@@ -1373,17 +1381,13 @@ done:
+ mutex_unlock(&port->mutex);
+ }
+
+-static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
++static void __uart_wait_until_sent(struct uart_port *port, int timeout)
+ {
+- struct uart_state *state = tty->driver_data;
+- struct uart_port *port = state->uart_port;
+ unsigned long char_time, expire;
+
+ if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+ return;
+
+- tty_lock_nested(); /* already locked when coming from close */
+-
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+@@ -1429,6 +1433,15 @@ static void uart_wait_until_sent(struct
+ break;
+ }
+ set_current_state(TASK_RUNNING); /* might not be needed */
++}
++
++static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
++{
++ struct uart_state *state = tty->driver_data;
++ struct uart_port *port = state->uart_port;
++
++ tty_lock();
++ __uart_wait_until_sent(port, timeout);
+ tty_unlock();
+ }
+
diff --git a/tty/vc-locking-clean-up.patch b/tty/vc-locking-clean-up.patch
new file mode 100644
index 00000000000000..866122ce1597f2
--- /dev/null
+++ b/tty/vc-locking-clean-up.patch
@@ -0,0 +1,81 @@
+From arnd@arndb.de Wed Jun 16 13:30:44 2010
+From: Alan Cox <alan@linux.intel.com>
+Date: Tue, 1 Jun 2010 22:52:54 +0200
+Subject: vc: Locking clean up
+To: Greg KH <gregkh@suse.de>
+Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1275425591-8803-15-git-send-email-arnd@arndb.de>
+
+
+From: Alan Cox <alan@linux.intel.com>
+
+The virtual console layer uses the BKL for various things that don't really
+need it. Clean them out.
+
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/selection.c | 4 ++++
+ drivers/char/vt.c | 7 ++++---
+ 2 files changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/char/selection.c
++++ b/drivers/char/selection.c
+@@ -26,6 +26,7 @@
+ #include <linux/selection.h>
+ #include <linux/tiocl.h>
+ #include <linux/console.h>
++#include <linux/smp_lock.h>
+
+ /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
+ #define isspace(c) ((c) == ' ')
+@@ -312,6 +313,8 @@ int paste_selection(struct tty_struct *t
+ struct tty_ldisc *ld;
+ DECLARE_WAITQUEUE(wait, current);
+
++ lock_kernel();
++
+ acquire_console_sem();
+ poke_blanked_console();
+ release_console_sem();
+@@ -335,5 +338,6 @@ int paste_selection(struct tty_struct *t
+ __set_current_state(TASK_RUNNING);
+
+ tty_ldisc_deref(ld);
++ unlock_kernel();
+ return 0;
+ }
+--- a/drivers/char/vt.c
++++ b/drivers/char/vt.c
+@@ -281,8 +281,12 @@ static inline unsigned short *screenpos(
+ return p;
+ }
+
++/* Called from the keyboard irq path.. */
+ static inline void scrolldelta(int lines)
+ {
++ /* FIXME */
++ /* scrolldelta needs some kind of consistency lock, but the BKL was
++ and still is not protecting versus the scheduled back end */
+ scrollback_delta += lines;
+ schedule_console_callback();
+ }
+@@ -2605,8 +2609,6 @@ int tioclinux(struct tty_struct *tty, un
+ return -EFAULT;
+ ret = 0;
+
+- lock_kernel();
+-
+ switch (type)
+ {
+ case TIOCL_SETSEL:
+@@ -2681,7 +2683,6 @@ int tioclinux(struct tty_struct *tty, un
+ ret = -EINVAL;
+ break;
+ }
+- unlock_kernel();
+ return ret;
+ }
+
diff --git a/tty/vt-clean-up-the-code-use-kernel-library.patch b/tty/vt-clean-up-the-code-use-kernel-library.patch
new file mode 100644
index 00000000000000..ebdb7353978d2b
--- /dev/null
+++ b/tty/vt-clean-up-the-code-use-kernel-library.patch
@@ -0,0 +1,41 @@
+From andy.shevchenko@gmail.com Wed Jun 16 13:22:07 2010
+From: Andy Shevchenko <andy.shevchenko@gmail.com>
+Date: Tue, 15 Jun 2010 17:24:16 +0300
+Subject: vt: clean up the code - use kernel library
+To: linux-kernel@vger.kernel.org
+Cc: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>, Andrew Morton <akpm@linux-foundation.org>, Greg Kroah-Hartman <gregkh@suse.de>, Alan Cox <alan@linux.intel.com>
+Message-ID: <1276611856-28232-1-git-send-email-andy.shevchenko@gmail.com>
+
+
+From: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>
+
+Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>
+Cc: Andrew Morton <akpm@linux-foundation.org>
+Cc: Alan Cox <alan@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/char/vt.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/char/vt.c
++++ b/drivers/char/vt.c
+@@ -104,6 +104,7 @@
+ #include <linux/io.h>
+ #include <asm/system.h>
+ #include <linux/uaccess.h>
++#include <linux/ctype.h>
+
+ #define MAX_NR_CON_DRIVER 16
+
+@@ -1789,8 +1790,8 @@ static void do_con_trol(struct tty_struc
+ vc->vc_state = ESnormal;
+ return;
+ case ESpalette:
+- if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
+- vc->vc_par[vc->vc_npar++] = (c > '9' ? (c & 0xDF) - 'A' + 10 : c - '0');
++ if (isxdigit(c)) {
++ vc->vc_par[vc->vc_npar++] = hex_to_bin(c);
+ if (vc->vc_npar == 7) {
+ int i = vc->vc_par[0] * 3, j = 1;
+ vc->vc_palette[i] = 16 * vc->vc_par[j++];