summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/hvc/hvc_dcc.c2
-rw-r--r--drivers/tty/pty.c26
-rw-r--r--drivers/tty/serial/Kconfig14
-rw-r--r--drivers/tty/serial/altera_jtaguart.c5
-rw-r--r--drivers/tty/serial/altera_uart.c4
-rw-r--r--drivers/tty/serial/imx.c11
-rw-r--r--drivers/tty/serial/jsm/jsm.h7
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c1
-rw-r--r--drivers/tty/serial/jsm/jsm_neo.c29
-rw-r--r--drivers/tty/serial/jsm/jsm_tty.c94
-rw-r--r--drivers/tty/serial/pch_uart.c13
-rw-r--r--drivers/tty/serial/pxa.c20
-rw-r--r--drivers/tty/serial/serial_core.c2
-rw-r--r--drivers/tty/serial/uartlite.c4
-rw-r--r--drivers/tty/tty_io.c48
-rw-r--r--drivers/tty/tty_ldisc.c30
16 files changed, 156 insertions, 154 deletions
diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c
index 435f6facbc2..44fbebab507 100644
--- a/drivers/tty/hvc/hvc_dcc.c
+++ b/drivers/tty/hvc/hvc_dcc.c
@@ -46,6 +46,7 @@ static inline char __dcc_getchar(void)
asm volatile("mrc p14, 0, %0, c0, c5, 0 @ read comms data reg"
: "=r" (__c));
+ isb();
return __c;
}
@@ -55,6 +56,7 @@ static inline void __dcc_putchar(char c)
asm volatile("mcr p14, 0, %0, c0, c5, 0 @ write a char"
: /* no output register */
: "r" (c));
+ isb();
}
static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count)
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index e809e9d4683..e18604b3fc7 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -670,12 +670,18 @@ static int ptmx_open(struct inode *inode, struct file *filp)
nonseekable_open(inode, filp);
+ retval = tty_alloc_file(filp);
+ if (retval)
+ return retval;
+
/* find a device that is not in use. */
tty_lock();
index = devpts_new_index(inode);
tty_unlock();
- if (index < 0)
- return index;
+ if (index < 0) {
+ retval = index;
+ goto err_file;
+ }
mutex_lock(&tty_mutex);
tty_lock();
@@ -689,27 +695,27 @@ static int ptmx_open(struct inode *inode, struct file *filp)
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
- retval = tty_add_file(tty, filp);
- if (retval)
- goto out;
+ tty_add_file(tty, filp);
retval = devpts_pty_new(inode, tty->link);
if (retval)
- goto out1;
+ goto err_release;
retval = ptm_driver->ops->open(tty, filp);
if (retval)
- goto out2;
-out1:
+ goto err_release;
+
tty_unlock();
- return retval;
-out2:
+ return 0;
+err_release:
tty_unlock();
tty_release(inode, filp);
return retval;
out:
devpts_kill_index(inode, index);
tty_unlock();
+err_file:
+ tty_free_file(filp);
return retval;
}
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 6deb387415e..9c3f4c4b99a 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1578,7 +1578,7 @@ config SERIAL_IFX6X60
Support for the IFX6x60 modem devices on Intel MID platforms.
config SERIAL_PCH_UART
- tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223) UART"
+ tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART"
depends on PCI
select SERIAL_CORE
help
@@ -1586,12 +1586,12 @@ config SERIAL_PCH_UART
which is an IOH(Input/Output Hub) for x86 embedded processor.
Enabling PCH_DMA, this PCH UART works as DMA mode.
- This driver also can be used for OKI SEMICONDUCTOR IOH(Input/
- Output Hub), ML7213 and ML7223.
- ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is
- for MP(Media Phone) use.
- ML7213/ML7223 is companion chip for Intel Atom E6xx series.
- ML7213/ML7223 is completely compatible for Intel EG20T PCH.
+ This driver also can be used for LAPIS Semiconductor IOH(Input/
+ Output Hub), ML7213, ML7223 and ML7831.
+ ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
+ for MP(Media Phone) use and ML7831 IOH is for general purpose use.
+ ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
+ ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
config SERIAL_MSM_SMD
bool "Enable tty device interface for some SMD ports"
diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c
index 60e049b041a..af46c56080d 100644
--- a/drivers/tty/serial/altera_jtaguart.c
+++ b/drivers/tty/serial/altera_jtaguart.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/console.h>
+#include <linux/of.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
@@ -472,8 +473,6 @@ static struct of_device_id altera_jtaguart_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
-#else
-#define altera_jtaguart_match NULL
#endif /* CONFIG_OF */
static struct platform_driver altera_jtaguart_platform_driver = {
@@ -482,7 +481,7 @@ static struct platform_driver altera_jtaguart_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
- .of_match_table = altera_jtaguart_match,
+ .of_match_table = of_match_ptr(altera_jtaguart_match),
},
};
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index 50bc5a5ac65..0abd31d2361 100644
--- a/drivers/tty/serial/altera_uart.c
+++ b/drivers/tty/serial/altera_uart.c
@@ -616,8 +616,6 @@ static struct of_device_id altera_uart_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, altera_uart_match);
-#else
-#define altera_uart_match NULL
#endif /* CONFIG_OF */
static struct platform_driver altera_uart_platform_driver = {
@@ -626,7 +624,7 @@ static struct platform_driver altera_uart_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
- .of_match_table = altera_uart_match,
+ .of_match_table = of_match_ptr(altera_uart_match),
},
};
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 7e91b3d368c..99898773706 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1286,17 +1286,20 @@ static int serial_imx_resume(struct platform_device *dev)
static int serial_imx_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
- static int portnum = 0;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(imx_uart_dt_ids, &pdev->dev);
+ int ret;
if (!np)
return -ENODEV;
- sport->port.line = portnum++;
- if (sport->port.line >= UART_NR)
- return -EINVAL;
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return -ENODEV;
+ }
+ sport->port.line = ret;
if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
sport->have_rtscts = 1;
diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h
index b704c8ce0d7..5b837e749c1 100644
--- a/drivers/tty/serial/jsm/jsm.h
+++ b/drivers/tty/serial/jsm/jsm.h
@@ -183,10 +183,8 @@ struct jsm_board
/* Our Read/Error/Write queue sizes */
#define RQUEUEMASK 0x1FFF /* 8 K - 1 */
#define EQUEUEMASK 0x1FFF /* 8 K - 1 */
-#define WQUEUEMASK 0x0FFF /* 4 K - 1 */
#define RQUEUESIZE (RQUEUEMASK + 1)
#define EQUEUESIZE RQUEUESIZE
-#define WQUEUESIZE (WQUEUEMASK + 1)
/************************************************************************
@@ -226,10 +224,6 @@ struct jsm_channel {
u16 ch_e_head; /* Head location of the error queue */
u16 ch_e_tail; /* Tail location of the error queue */
- u8 *ch_wqueue; /* Our write queue buffer - malloc'ed */
- u16 ch_w_head; /* Head location of the write queue */
- u16 ch_w_tail; /* Tail location of the write queue */
-
u64 ch_rxcount; /* total of data received so far */
u64 ch_txcount; /* total of data transmitted so far */
@@ -378,7 +372,6 @@ extern int jsm_debug;
* Prototypes for non-static functions used in more than one module
*
*************************************************************************/
-int jsm_tty_write(struct uart_port *port);
int jsm_tty_init(struct jsm_board *);
int jsm_uart_port_init(struct jsm_board *);
int jsm_remove_uart_port(struct jsm_board *);
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index 96da17868cf..2aaafa9c58a 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -211,7 +211,6 @@ static void __devexit jsm_remove_one(struct pci_dev *pdev)
if (brd->channels[i]) {
kfree(brd->channels[i]->ch_rqueue);
kfree(brd->channels[i]->ch_equeue);
- kfree(brd->channels[i]->ch_wqueue);
kfree(brd->channels[i]);
}
}
diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c
index 4538c3e3646..bd6e84699e1 100644
--- a/drivers/tty/serial/jsm/jsm_neo.c
+++ b/drivers/tty/serial/jsm/jsm_neo.c
@@ -496,12 +496,15 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
int s;
int qlen;
u32 len_written = 0;
+ struct circ_buf *circ;
if (!ch)
return;
+ circ = &ch->uart_port.state->xmit;
+
/* No data to write to the UART */
- if (ch->ch_w_tail == ch->ch_w_head)
+ if (uart_circ_empty(circ))
return;
/* If port is "stopped", don't send any data to the UART */
@@ -517,11 +520,10 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
if (ch->ch_cached_lsr & UART_LSR_THRE) {
ch->ch_cached_lsr &= ~(UART_LSR_THRE);
- writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx);
+ writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx);
jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev,
- "Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]);
- ch->ch_w_tail++;
- ch->ch_w_tail &= WQUEUEMASK;
+ "Tx data: %x\n", circ->buf[circ->head]);
+ circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1);
ch->ch_txcount++;
}
return;
@@ -536,36 +538,36 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
/* cache head and tail of queue */
- head = ch->ch_w_head & WQUEUEMASK;
- tail = ch->ch_w_tail & WQUEUEMASK;
- qlen = (head - tail) & WQUEUEMASK;
+ head = circ->head & (UART_XMIT_SIZE - 1);
+ tail = circ->tail & (UART_XMIT_SIZE - 1);
+ qlen = uart_circ_chars_pending(circ);
/* Find minimum of the FIFO space, versus queue length */
n = min(n, qlen);
while (n > 0) {
- s = ((head >= tail) ? head : WQUEUESIZE) - tail;
+ s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail;
s = min(s, n);
if (s <= 0)
break;
- memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s);
+ memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s);
/* Add and flip queue if needed */
- tail = (tail + s) & WQUEUEMASK;
+ tail = (tail + s) & (UART_XMIT_SIZE - 1);
n -= s;
ch->ch_txcount += s;
len_written += s;
}
/* Update the final tail */
- ch->ch_w_tail = tail & WQUEUEMASK;
+ circ->tail = tail & (UART_XMIT_SIZE - 1);
if (len_written >= ch->ch_t_tlevel)
ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
- if (!jsm_tty_write(&ch->uart_port))
+ if (uart_circ_empty(circ))
uart_write_wakeup(&ch->uart_port);
}
@@ -946,7 +948,6 @@ static void neo_param(struct jsm_channel *ch)
if ((ch->ch_c_cflag & (CBAUD)) == 0) {
ch->ch_r_head = ch->ch_r_tail = 0;
ch->ch_e_head = ch->ch_e_tail = 0;
- ch->ch_w_head = ch->ch_w_tail = 0;
neo_flush_uart_write(ch);
neo_flush_uart_read(ch);
diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c
index 7a4a914ecff..434bd881fca 100644
--- a/drivers/tty/serial/jsm/jsm_tty.c
+++ b/drivers/tty/serial/jsm/jsm_tty.c
@@ -118,6 +118,19 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
udelay(10);
}
+/*
+ * jsm_tty_write()
+ *
+ * Take data from the user or kernel and send it out to the FEP.
+ * In here exists all the Transparent Print magic as well.
+ */
+static void jsm_tty_write(struct uart_port *port)
+{
+ struct jsm_channel *channel;
+ channel = container_of(port, struct jsm_channel, uart_port);
+ channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);
+}
+
static void jsm_tty_start_tx(struct uart_port *port)
{
struct jsm_channel *channel = (struct jsm_channel *)port;
@@ -216,14 +229,6 @@ static int jsm_tty_open(struct uart_port *port)
return -ENOMEM;
}
}
- if (!channel->ch_wqueue) {
- channel->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL);
- if (!channel->ch_wqueue) {
- jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
- "unable to allocate write queue buf");
- return -ENOMEM;
- }
- }
channel->ch_flags &= ~(CH_OPENING);
/*
@@ -237,7 +242,6 @@ static int jsm_tty_open(struct uart_port *port)
*/
channel->ch_r_head = channel->ch_r_tail = 0;
channel->ch_e_head = channel->ch_e_tail = 0;
- channel->ch_w_head = channel->ch_w_tail = 0;
brd->bd_ops->flush_uart_write(channel);
brd->bd_ops->flush_uart_read(channel);
@@ -836,75 +840,3 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
}
}
}
-
-/*
- * jsm_tty_write()
- *
- * Take data from the user or kernel and send it out to the FEP.
- * In here exists all the Transparent Print magic as well.
- */
-int jsm_tty_write(struct uart_port *port)
-{
- int bufcount;
- int data_count = 0,data_count1 =0;
- u16 head;
- u16 tail;
- u16 tmask;
- u32 remain;
- int temp_tail = port->state->xmit.tail;
- struct jsm_channel *channel = (struct jsm_channel *)port;
-
- tmask = WQUEUEMASK;
- head = (channel->ch_w_head) & tmask;
- tail = (channel->ch_w_tail) & tmask;
-
- if ((bufcount = tail - head - 1) < 0)
- bufcount += WQUEUESIZE;
-
- bufcount = min(bufcount, 56);
- remain = WQUEUESIZE - head;
-
- data_count = 0;
- if (bufcount >= remain) {
- bufcount -= remain;
- while ((port->state->xmit.head != temp_tail) &&
- (data_count < remain)) {
- channel->ch_wqueue[head++] =
- port->state->xmit.buf[temp_tail];
-
- temp_tail++;
- temp_tail &= (UART_XMIT_SIZE - 1);
- data_count++;
- }
- if (data_count == remain) head = 0;
- }
-
- data_count1 = 0;
- if (bufcount > 0) {
- remain = bufcount;
- while ((port->state->xmit.head != temp_tail) &&
- (data_count1 < remain)) {
- channel->ch_wqueue[head++] =
- port->state->xmit.buf[temp_tail];
-
- temp_tail++;
- temp_tail &= (UART_XMIT_SIZE - 1);
- data_count1++;
-
- }
- }
-
- port->state->xmit.tail = temp_tail;
-
- data_count += data_count1;
- if (data_count) {
- head &= tmask;
- channel->ch_w_head = head;
- }
-
- if (data_count) {
- channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);
- }
-
- return data_count;
-}
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index b46218d679e..f913ed054c1 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -256,6 +256,8 @@ enum pch_uart_num_t {
pch_ml7213_uart2,
pch_ml7223_uart0,
pch_ml7223_uart1,
+ pch_ml7831_uart0,
+ pch_ml7831_uart1,
};
static struct pch_uart_driver_data drv_dat[] = {
@@ -268,6 +270,8 @@ static struct pch_uart_driver_data drv_dat[] = {
[pch_ml7213_uart2] = {PCH_UART_2LINE, 2},
[pch_ml7223_uart0] = {PCH_UART_8LINE, 0},
[pch_ml7223_uart1] = {PCH_UART_2LINE, 1},
+ [pch_ml7831_uart0] = {PCH_UART_8LINE, 0},
+ [pch_ml7831_uart1] = {PCH_UART_2LINE, 1},
};
static unsigned int default_baud = 9600;
@@ -626,6 +630,7 @@ static void pch_request_dma(struct uart_port *port)
dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n",
__func__);
dma_release_channel(priv->chan_tx);
+ priv->chan_tx = NULL;
return;
}
@@ -1213,8 +1218,7 @@ static void pch_uart_shutdown(struct uart_port *port)
dev_err(priv->port.dev,
"pch_uart_hal_set_fifo Failed(ret=%d)\n", ret);
- if (priv->use_dma_flag)
- pch_free_dma(port);
+ pch_free_dma(port);
free_irq(priv->port.irq, priv);
}
@@ -1278,6 +1282,7 @@ static void pch_uart_set_termios(struct uart_port *port,
if (rtn)
goto out;
+ pch_uart_set_mctrl(&priv->port, priv->port.mctrl);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
@@ -1550,6 +1555,10 @@ static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = {
.driver_data = pch_ml7223_uart0},
{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x800D),
.driver_data = pch_ml7223_uart1},
+ {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8811),
+ .driver_data = pch_ml7831_uart0},
+ {PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8812),
+ .driver_data = pch_ml7831_uart1},
{0,},
};
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index 531931c1b25..5c8e3bba6c8 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -100,6 +100,16 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status)
int max_count = 256;
do {
+ /* work around Errata #20 according to
+ * Intel(R) PXA27x Processor Family
+ * Specification Update (May 2005)
+ *
+ * Step 2
+ * Disable the Reciever Time Out Interrupt via IER[RTOEI]
+ */
+ up->ier &= ~UART_IER_RTOIE;
+ serial_out(up, UART_IER, up->ier);
+
ch = serial_in(up, UART_RX);
flag = TTY_NORMAL;
up->port.icount.rx++;
@@ -156,6 +166,16 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status)
*status = serial_in(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
tty_flip_buffer_push(tty);
+
+ /* work around Errata #20 according to
+ * Intel(R) PXA27x Processor Family
+ * Specification Update (May 2005)
+ *
+ * Step 6:
+ * No more data in FIFO: Re-enable RTO interrupt via IER[RTOIE]
+ */
+ up->ier |= UART_IER_RTOIE;
+ serial_out(up, UART_IER, up->ier);
}
static void transmit_chars(struct uart_pxa_port *up)
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index a3efbea5dbb..25f3094ec74 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2008,6 +2008,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
if (port->tty && port->tty->termios && termios.c_cflag == 0)
termios = *(port->tty->termios);
+ if (console_suspend_enabled)
+ uart_change_pm(state, 0);
uport->ops->set_termios(uport, &termios, NULL);
if (console_suspend_enabled)
console_start(uport->cons);
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 8af1ed83a4c..0aed022d21b 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -568,8 +568,6 @@ static struct of_device_id ulite_of_match[] __devinitdata = {
{}
};
MODULE_DEVICE_TABLE(of, ulite_of_match);
-#else /* CONFIG_OF */
-#define ulite_of_match NULL
#endif /* CONFIG_OF */
static int __devinit ulite_probe(struct platform_device *pdev)
@@ -609,7 +607,7 @@ static struct platform_driver ulite_platform_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "uartlite",
- .of_match_table = ulite_of_match,
+ .of_match_table = of_match_ptr(ulite_of_match),
},
};
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 4f1fc81112e..1a890e270be 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -194,8 +194,7 @@ static inline struct tty_struct *file_tty(struct file *file)
return ((struct tty_file_private *)file->private_data)->tty;
}
-/* Associate a new file with the tty structure */
-int tty_add_file(struct tty_struct *tty, struct file *file)
+int tty_alloc_file(struct file *file)
{
struct tty_file_private *priv;
@@ -203,15 +202,36 @@ int tty_add_file(struct tty_struct *tty, struct file *file)
if (!priv)
return -ENOMEM;
+ file->private_data = priv;
+
+ return 0;
+}
+
+/* Associate a new file with the tty structure */
+void tty_add_file(struct tty_struct *tty, struct file *file)
+{
+ struct tty_file_private *priv = file->private_data;
+
priv->tty = tty;
priv->file = file;
- file->private_data = priv;
spin_lock(&tty_files_lock);
list_add(&priv->list, &tty->tty_files);
spin_unlock(&tty_files_lock);
+}
- return 0;
+/**
+ * tty_free_file - free file->private_data
+ *
+ * This shall be used only for fail path handling when tty_add_file was not
+ * called yet.
+ */
+void tty_free_file(struct file *file)
+{
+ struct tty_file_private *priv = file->private_data;
+
+ file->private_data = NULL;
+ kfree(priv);
}
/* Delete file from its tty */
@@ -222,8 +242,7 @@ void tty_del_file(struct file *file)
spin_lock(&tty_files_lock);
list_del(&priv->list);
spin_unlock(&tty_files_lock);
- file->private_data = NULL;
- kfree(priv);
+ tty_free_file(file);
}
@@ -1811,6 +1830,10 @@ static int tty_open(struct inode *inode, struct file *filp)
nonseekable_open(inode, filp);
retry_open:
+ retval = tty_alloc_file(filp);
+ if (retval)
+ return -ENOMEM;
+
noctty = filp->f_flags & O_NOCTTY;
index = -1;
retval = 0;
@@ -1823,6 +1846,7 @@ retry_open:
if (!tty) {
tty_unlock();
mutex_unlock(&tty_mutex);
+ tty_free_file(filp);
return -ENXIO;
}
driver = tty_driver_kref_get(tty->driver);
@@ -1855,6 +1879,7 @@ retry_open:
}
tty_unlock();
mutex_unlock(&tty_mutex);
+ tty_free_file(filp);
return -ENODEV;
}
@@ -1862,6 +1887,7 @@ retry_open:
if (!driver) {
tty_unlock();
mutex_unlock(&tty_mutex);
+ tty_free_file(filp);
return -ENODEV;
}
got_driver:
@@ -1872,6 +1898,8 @@ got_driver:
if (IS_ERR(tty)) {
tty_unlock();
mutex_unlock(&tty_mutex);
+ tty_driver_kref_put(driver);
+ tty_free_file(filp);
return PTR_ERR(tty);
}
}
@@ -1887,15 +1915,11 @@ got_driver:
tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
tty_unlock();
+ tty_free_file(filp);
return PTR_ERR(tty);
}
- retval = tty_add_file(tty, filp);
- if (retval) {
- tty_unlock();
- tty_release(inode, filp);
- return retval;
- }
+ tty_add_file(tty, filp);
check_tty_count(tty, "tty_open");
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index ef925d58171..a76c808afad 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -36,6 +36,7 @@
#include <linux/kmod.h>
#include <linux/nsproxy.h>
+#include <linux/ratelimit.h>
/*
* This guards the refcounted line discipline lists. The lock
@@ -548,15 +549,16 @@ static void tty_ldisc_flush_works(struct tty_struct *tty)
/**
* tty_ldisc_wait_idle - wait for the ldisc to become idle
* @tty: tty to wait for
+ * @timeout: for how long to wait at most
*
* Wait for the line discipline to become idle. The discipline must
* have been halted for this to guarantee it remains idle.
*/
-static int tty_ldisc_wait_idle(struct tty_struct *tty)
+static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout)
{
- int ret;
+ long ret;
ret = wait_event_timeout(tty_ldisc_idle,
- atomic_read(&tty->ldisc->users) == 1, 5 * HZ);
+ atomic_read(&tty->ldisc->users) == 1, timeout);
if (ret < 0)
return ret;
return ret > 0 ? 0 : -EBUSY;
@@ -666,7 +668,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty_ldisc_flush_works(tty);
- retval = tty_ldisc_wait_idle(tty);
+ retval = tty_ldisc_wait_idle(tty, 5 * HZ);
tty_lock();
mutex_lock(&tty->ldisc_mutex);
@@ -763,8 +765,6 @@ static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
if (IS_ERR(ld))
return -1;
- WARN_ON_ONCE(tty_ldisc_wait_idle(tty));
-
tty_ldisc_close(tty, tty->ldisc);
tty_ldisc_put(tty->ldisc);
tty->ldisc = NULL;
@@ -839,7 +839,7 @@ void tty_ldisc_hangup(struct tty_struct *tty)
tty_unlock();
cancel_work_sync(&tty->buf.work);
mutex_unlock(&tty->ldisc_mutex);
-
+retry:
tty_lock();
mutex_lock(&tty->ldisc_mutex);
@@ -848,6 +848,22 @@ void tty_ldisc_hangup(struct tty_struct *tty)
it means auditing a lot of other paths so this is
a FIXME */
if (tty->ldisc) { /* Not yet closed */
+ if (atomic_read(&tty->ldisc->users) != 1) {
+ char cur_n[TASK_COMM_LEN], tty_n[64];
+ long timeout = 3 * HZ;
+ tty_unlock();
+
+ while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) {
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ printk_ratelimited(KERN_WARNING
+ "%s: waiting (%s) for %s took too long, but we keep waiting...\n",
+ __func__, get_task_comm(cur_n, current),
+ tty_name(tty, tty_n));
+ }
+ mutex_unlock(&tty->ldisc_mutex);
+ goto retry;
+ }
+
if (reset == 0) {
if (!tty_ldisc_reinit(tty, tty->termios->c_line))