diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-05-05 15:50:16 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-05-05 15:50:16 -0700 |
commit | df862f625d62c8d5aef41361a8d21e4f55a0e748 (patch) | |
tree | 53dcd0ee8d44c9d186461dfd2d5cb4caa594e70d /drivers/tty/tty_buffer.c | |
parent | a1e74464ffddf47e9a18f99a54242122bfd0997b (diff) | |
parent | 62a0d8d7c2b29f92850e4ee3c38e5dfd936e92b2 (diff) |
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.
Diffstat (limited to 'drivers/tty/tty_buffer.c')
-rw-r--r-- | drivers/tty/tty_buffer.c | 29 |
1 files changed, 14 insertions, 15 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f1d30f6945af..cf78d1985cd8 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -255,16 +255,15 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, if (change || left < size) { /* This is the slow path - looking for new buffers to use */ if ((n = tty_buffer_alloc(port, size)) != NULL) { - unsigned long iflags; - n->flags = flags; buf->tail = n; - - spin_lock_irqsave(&buf->flush_lock, iflags); 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; - spin_unlock_irqrestore(&buf->flush_lock, iflags); - } else if (change) size = 0; else @@ -448,27 +447,28 @@ static void flush_to_ldisc(struct work_struct *work) mutex_lock(&buf->lock); while (1) { - unsigned long flags; struct tty_buffer *head = buf->head; + struct tty_buffer *next; int count; /* Ldisc or user is trying to gain exclusive access */ if (atomic_read(&buf->priority)) 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; if (!count) { - if (head->next == NULL) { - spin_unlock_irqrestore(&buf->flush_lock, flags); + if (next == NULL) break; - } - buf->head = head->next; - spin_unlock_irqrestore(&buf->flush_lock, flags); + buf->head = next; tty_buffer_free(port, head); continue; } - spin_unlock_irqrestore(&buf->flush_lock, flags); count = receive_buf(tty, head, count); if (!count) @@ -523,7 +523,6 @@ void tty_buffer_init(struct tty_port *port) struct tty_bufhead *buf = &port->buf; mutex_init(&buf->lock); - spin_lock_init(&buf->flush_lock); tty_buffer_reset(&buf->sentinel, 0); buf->head = &buf->sentinel; buf->tail = &buf->sentinel; |