Commit df862f62 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tty-3.15-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial fixes from Greg KH:
 "Here are some tty and serial driver fixes for things reported
  recently"

* tag 'tty-3.15-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  tty: Fix lockless tty buffer race
  Revert "tty: Fix race condition between __tty_buffer_request_room and flush_to_ldisc"
  drivers/tty/hvc: don't free hvc_console_setup after init
  n_tty: Fix n_tty_write crash when echoing in raw mode
  tty: serial: 8250_core.c Bug fix for Exar chips.
parents a1e74464 62a0d8d7
...@@ -190,7 +190,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) ...@@ -190,7 +190,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index)
return hvc_driver; return hvc_driver;
} }
static int __init hvc_console_setup(struct console *co, char *options) static int hvc_console_setup(struct console *co, char *options)
{ {
if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES)
return -ENODEV; return -ENODEV;
......
...@@ -2353,8 +2353,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ...@@ -2353,8 +2353,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
if (tty->ops->flush_chars) if (tty->ops->flush_chars)
tty->ops->flush_chars(tty); tty->ops->flush_chars(tty);
} else { } else {
struct n_tty_data *ldata = tty->disc_data;
while (nr > 0) { while (nr > 0) {
mutex_lock(&ldata->output_lock);
c = tty->ops->write(tty, b, nr); c = tty->ops->write(tty, b, nr);
mutex_unlock(&ldata->output_lock);
if (c < 0) { if (c < 0) {
retval = c; retval = c;
goto break_out; goto break_out;
......
...@@ -555,7 +555,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) ...@@ -555,7 +555,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
*/ */
if ((p->port.type == PORT_XR17V35X) || if ((p->port.type == PORT_XR17V35X) ||
(p->port.type == PORT_XR17D15X)) { (p->port.type == PORT_XR17D15X)) {
serial_out(p, UART_EXAR_SLEEP, 0xff); serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
return; return;
} }
......
...@@ -255,16 +255,15 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, ...@@ -255,16 +255,15 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size,
if (change || left < size) { if (change || left < size) {
/* This is the slow path - looking for new buffers to use */ /* This is the slow path - looking for new buffers to use */
if ((n = tty_buffer_alloc(port, size)) != NULL) { if ((n = tty_buffer_alloc(port, size)) != NULL) {
unsigned long iflags;
n->flags = flags; n->flags = flags;
buf->tail = n; buf->tail = n;
spin_lock_irqsave(&buf->flush_lock, iflags);
b->commit = b->used; b->commit = b->used;
/* paired w/ barrier in flush_to_ldisc(); ensures the
* latest commit value can be read before the head is
* advanced to the next buffer
*/
smp_wmb();
b->next = n; b->next = n;
spin_unlock_irqrestore(&buf->flush_lock, iflags);
} else if (change) } else if (change)
size = 0; size = 0;
else else
...@@ -448,27 +447,28 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -448,27 +447,28 @@ static void flush_to_ldisc(struct work_struct *work)
mutex_lock(&buf->lock); mutex_lock(&buf->lock);
while (1) { while (1) {
unsigned long flags;
struct tty_buffer *head = buf->head; struct tty_buffer *head = buf->head;
struct tty_buffer *next;
int count; int count;
/* Ldisc or user is trying to gain exclusive access */ /* Ldisc or user is trying to gain exclusive access */
if (atomic_read(&buf->priority)) if (atomic_read(&buf->priority))
break; break;
spin_lock_irqsave(&buf->flush_lock, flags); next = head->next;
/* paired w/ barrier in __tty_buffer_request_room();
* ensures commit value read is not stale if the head
* is advancing to the next buffer
*/
smp_rmb();
count = head->commit - head->read; count = head->commit - head->read;
if (!count) { if (!count) {
if (head->next == NULL) { if (next == NULL)
spin_unlock_irqrestore(&buf->flush_lock, flags);
break; break;
} buf->head = next;
buf->head = head->next;
spin_unlock_irqrestore(&buf->flush_lock, flags);
tty_buffer_free(port, head); tty_buffer_free(port, head);
continue; continue;
} }
spin_unlock_irqrestore(&buf->flush_lock, flags);
count = receive_buf(tty, head, count); count = receive_buf(tty, head, count);
if (!count) if (!count)
...@@ -523,7 +523,6 @@ void tty_buffer_init(struct tty_port *port) ...@@ -523,7 +523,6 @@ void tty_buffer_init(struct tty_port *port)
struct tty_bufhead *buf = &port->buf; struct tty_bufhead *buf = &port->buf;
mutex_init(&buf->lock); mutex_init(&buf->lock);
spin_lock_init(&buf->flush_lock);
tty_buffer_reset(&buf->sentinel, 0); tty_buffer_reset(&buf->sentinel, 0);
buf->head = &buf->sentinel; buf->head = &buf->sentinel;
buf->tail = &buf->sentinel; buf->tail = &buf->sentinel;
......
...@@ -61,7 +61,6 @@ struct tty_bufhead { ...@@ -61,7 +61,6 @@ struct tty_bufhead {
struct tty_buffer *head; /* Queue head */ struct tty_buffer *head; /* Queue head */
struct work_struct work; struct work_struct work;
struct mutex lock; struct mutex lock;
spinlock_t flush_lock;
atomic_t priority; atomic_t priority;
struct tty_buffer sentinel; struct tty_buffer sentinel;
struct llist_head free; /* Free queue head */ struct llist_head free; /* Free queue head */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment