diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-05-05 18:50:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-05-05 18:50:16 -0400 |
commit | df862f625d62c8d5aef41361a8d21e4f55a0e748 (patch) | |
tree | 53dcd0ee8d44c9d186461dfd2d5cb4caa594e70d | |
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.
-rw-r--r-- | drivers/tty/hvc/hvc_console.c | 2 | ||||
-rw-r--r-- | drivers/tty/n_tty.c | 4 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_core.c | 2 | ||||
-rw-r--r-- | drivers/tty/tty_buffer.c | 29 | ||||
-rw-r--r-- | include/linux/tty.h | 1 |
5 files changed, 20 insertions, 18 deletions
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 94f9e3a38412..0ff7fda0742f 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c | |||
@@ -190,7 +190,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) | |||
190 | return hvc_driver; | 190 | return hvc_driver; |
191 | } | 191 | } |
192 | 192 | ||
193 | static int __init hvc_console_setup(struct console *co, char *options) | 193 | static int hvc_console_setup(struct console *co, char *options) |
194 | { | 194 | { |
195 | if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) | 195 | if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) |
196 | return -ENODEV; | 196 | return -ENODEV; |
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 41fe8a047d37..fe9d129c8735 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c | |||
@@ -2353,8 +2353,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, | |||
2353 | if (tty->ops->flush_chars) | 2353 | if (tty->ops->flush_chars) |
2354 | tty->ops->flush_chars(tty); | 2354 | tty->ops->flush_chars(tty); |
2355 | } else { | 2355 | } else { |
2356 | struct n_tty_data *ldata = tty->disc_data; | ||
2357 | |||
2356 | while (nr > 0) { | 2358 | while (nr > 0) { |
2359 | mutex_lock(&ldata->output_lock); | ||
2357 | c = tty->ops->write(tty, b, nr); | 2360 | c = tty->ops->write(tty, b, nr); |
2361 | mutex_unlock(&ldata->output_lock); | ||
2358 | if (c < 0) { | 2362 | if (c < 0) { |
2359 | retval = c; | 2363 | retval = c; |
2360 | goto break_out; | 2364 | goto break_out; |
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 0e1bf8858431..2d4bd3929e50 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c | |||
@@ -555,7 +555,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) | |||
555 | */ | 555 | */ |
556 | if ((p->port.type == PORT_XR17V35X) || | 556 | if ((p->port.type == PORT_XR17V35X) || |
557 | (p->port.type == PORT_XR17D15X)) { | 557 | (p->port.type == PORT_XR17D15X)) { |
558 | serial_out(p, UART_EXAR_SLEEP, 0xff); | 558 | serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0); |
559 | return; | 559 | return; |
560 | } | 560 | } |
561 | 561 | ||
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, | |||
255 | if (change || left < size) { | 255 | if (change || left < size) { |
256 | /* This is the slow path - looking for new buffers to use */ | 256 | /* This is the slow path - looking for new buffers to use */ |
257 | if ((n = tty_buffer_alloc(port, size)) != NULL) { | 257 | if ((n = tty_buffer_alloc(port, size)) != NULL) { |
258 | unsigned long iflags; | ||
259 | |||
260 | n->flags = flags; | 258 | n->flags = flags; |
261 | buf->tail = n; | 259 | buf->tail = n; |
262 | |||
263 | spin_lock_irqsave(&buf->flush_lock, iflags); | ||
264 | b->commit = b->used; | 260 | b->commit = b->used; |
261 | /* paired w/ barrier in flush_to_ldisc(); ensures the | ||
262 | * latest commit value can be read before the head is | ||
263 | * advanced to the next buffer | ||
264 | */ | ||
265 | smp_wmb(); | ||
265 | b->next = n; | 266 | b->next = n; |
266 | spin_unlock_irqrestore(&buf->flush_lock, iflags); | ||
267 | |||
268 | } else if (change) | 267 | } else if (change) |
269 | size = 0; | 268 | size = 0; |
270 | else | 269 | else |
@@ -448,27 +447,28 @@ static void flush_to_ldisc(struct work_struct *work) | |||
448 | mutex_lock(&buf->lock); | 447 | mutex_lock(&buf->lock); |
449 | 448 | ||
450 | while (1) { | 449 | while (1) { |
451 | unsigned long flags; | ||
452 | struct tty_buffer *head = buf->head; | 450 | struct tty_buffer *head = buf->head; |
451 | struct tty_buffer *next; | ||
453 | int count; | 452 | int count; |
454 | 453 | ||
455 | /* Ldisc or user is trying to gain exclusive access */ | 454 | /* Ldisc or user is trying to gain exclusive access */ |
456 | if (atomic_read(&buf->priority)) | 455 | if (atomic_read(&buf->priority)) |
457 | break; | 456 | break; |
458 | 457 | ||
459 | spin_lock_irqsave(&buf->flush_lock, flags); | 458 | next = head->next; |
459 | /* paired w/ barrier in __tty_buffer_request_room(); | ||
460 | * ensures commit value read is not stale if the head | ||
461 | * is advancing to the next buffer | ||
462 | */ | ||
463 | smp_rmb(); | ||
460 | count = head->commit - head->read; | 464 | count = head->commit - head->read; |
461 | if (!count) { | 465 | if (!count) { |
462 | if (head->next == NULL) { | 466 | if (next == NULL) |
463 | spin_unlock_irqrestore(&buf->flush_lock, flags); | ||
464 | break; | 467 | break; |
465 | } | 468 | buf->head = next; |
466 | buf->head = head->next; | ||
467 | spin_unlock_irqrestore(&buf->flush_lock, flags); | ||
468 | tty_buffer_free(port, head); | 469 | tty_buffer_free(port, head); |
469 | continue; | 470 | continue; |
470 | } | 471 | } |
471 | spin_unlock_irqrestore(&buf->flush_lock, flags); | ||
472 | 472 | ||
473 | count = receive_buf(tty, head, count); | 473 | count = receive_buf(tty, head, count); |
474 | if (!count) | 474 | if (!count) |
@@ -523,7 +523,6 @@ void tty_buffer_init(struct tty_port *port) | |||
523 | struct tty_bufhead *buf = &port->buf; | 523 | struct tty_bufhead *buf = &port->buf; |
524 | 524 | ||
525 | mutex_init(&buf->lock); | 525 | mutex_init(&buf->lock); |
526 | spin_lock_init(&buf->flush_lock); | ||
527 | tty_buffer_reset(&buf->sentinel, 0); | 526 | tty_buffer_reset(&buf->sentinel, 0); |
528 | buf->head = &buf->sentinel; | 527 | buf->head = &buf->sentinel; |
529 | buf->tail = &buf->sentinel; | 528 | buf->tail = &buf->sentinel; |
diff --git a/include/linux/tty.h b/include/linux/tty.h index 036cccd80d9f..1c3316a47d7e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -61,7 +61,6 @@ struct tty_bufhead { | |||
61 | struct tty_buffer *head; /* Queue head */ | 61 | struct tty_buffer *head; /* Queue head */ |
62 | struct work_struct work; | 62 | struct work_struct work; |
63 | struct mutex lock; | 63 | struct mutex lock; |
64 | spinlock_t flush_lock; | ||
65 | atomic_t priority; | 64 | atomic_t priority; |
66 | struct tty_buffer sentinel; | 65 | struct tty_buffer sentinel; |
67 | struct llist_head free; /* Free queue head */ | 66 | struct llist_head free; /* Free queue head */ |