diff options
Diffstat (limited to 'tty')
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(¤t->sighand->siglock); + if (!noctty && + current->signal->leader && +@@ -1890,7 +1896,7 @@ got_driver: + tty->session == NULL) + __proc_set_tty(current, tty); + spin_unlock_irq(¤t->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++]; |
