diff options
| author | Peter Hurley <peter@hurleysoftware.com> | 2013-06-15 09:36:10 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-23 19:47:09 -0400 |
| commit | e9975fdec0138f1b2a85b9624e41660abd9865d4 (patch) | |
| tree | 6c90bac37cfe6843b4e78cb9280aa10524da351d | |
| parent | e8437d7ecbc50198705331449367d401ebb3181f (diff) | |
tty: Ensure single-threaded flip buffer consumer with mutex
The buffer work may race with parallel tty_buffer_flush. Use a
mutex to guarantee exclusive modify access to the head flip
buffer.
Remove the unneeded spin lock.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/staging/dgrp/dgrp_tty.c | 2 | ||||
| -rw-r--r-- | drivers/tty/tty_buffer.c | 40 | ||||
| -rw-r--r-- | include/linux/tty.h | 2 |
3 files changed, 22 insertions, 22 deletions
diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index 654f6010b473..0d52de3729c6 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c | |||
| @@ -1120,7 +1120,9 @@ static void dgrp_tty_close(struct tty_struct *tty, struct file *file) | |||
| 1120 | if (!sent_printer_offstr) | 1120 | if (!sent_printer_offstr) |
| 1121 | dgrp_tty_flush_buffer(tty); | 1121 | dgrp_tty_flush_buffer(tty); |
| 1122 | 1122 | ||
| 1123 | spin_unlock_irqrestore(&nd->nd_lock, lock_flags); | ||
| 1123 | tty_ldisc_flush(tty); | 1124 | tty_ldisc_flush(tty); |
| 1125 | spin_lock_irqsave(&nd->nd_lock, lock_flags); | ||
| 1124 | break; | 1126 | break; |
| 1125 | } | 1127 | } |
| 1126 | 1128 | ||
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 685757c6ce87..c3c606c52722 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c | |||
| @@ -157,8 +157,6 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) | |||
| 157 | * flush all the buffers containing receive data. Caller must | 157 | * flush all the buffers containing receive data. Caller must |
| 158 | * hold the buffer lock and must have ensured no parallel flush to | 158 | * hold the buffer lock and must have ensured no parallel flush to |
| 159 | * ldisc is running. | 159 | * ldisc is running. |
| 160 | * | ||
| 161 | * Locking: Caller must hold tty->buf.lock | ||
| 162 | */ | 160 | */ |
| 163 | 161 | ||
| 164 | static void __tty_buffer_flush(struct tty_port *port) | 162 | static void __tty_buffer_flush(struct tty_port *port) |
| @@ -182,29 +180,29 @@ static void __tty_buffer_flush(struct tty_port *port) | |||
| 182 | * being processed by flush_to_ldisc then we defer the processing | 180 | * being processed by flush_to_ldisc then we defer the processing |
| 183 | * to that function | 181 | * to that function |
| 184 | * | 182 | * |
| 185 | * Locking: none | 183 | * Locking: takes flush_mutex to ensure single-threaded flip buffer |
| 184 | * 'consumer' | ||
| 186 | */ | 185 | */ |
| 187 | 186 | ||
| 188 | void tty_buffer_flush(struct tty_struct *tty) | 187 | void tty_buffer_flush(struct tty_struct *tty) |
| 189 | { | 188 | { |
| 190 | struct tty_port *port = tty->port; | 189 | struct tty_port *port = tty->port; |
| 191 | struct tty_bufhead *buf = &port->buf; | 190 | struct tty_bufhead *buf = &port->buf; |
| 192 | unsigned long flags; | ||
| 193 | |||
| 194 | spin_lock_irqsave(&buf->lock, flags); | ||
| 195 | 191 | ||
| 192 | mutex_lock(&buf->flush_mutex); | ||
| 196 | /* If the data is being pushed to the tty layer then we can't | 193 | /* If the data is being pushed to the tty layer then we can't |
| 197 | process it here. Instead set a flag and the flush_to_ldisc | 194 | process it here. Instead set a flag and the flush_to_ldisc |
| 198 | path will process the flush request before it exits */ | 195 | path will process the flush request before it exits */ |
| 199 | if (test_bit(TTYP_FLUSHING, &port->iflags)) { | 196 | if (test_bit(TTYP_FLUSHING, &port->iflags)) { |
| 200 | set_bit(TTYP_FLUSHPENDING, &port->iflags); | 197 | set_bit(TTYP_FLUSHPENDING, &port->iflags); |
| 201 | spin_unlock_irqrestore(&buf->lock, flags); | 198 | mutex_unlock(&buf->flush_mutex); |
| 202 | wait_event(tty->read_wait, | 199 | wait_event(tty->read_wait, |
| 203 | test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); | 200 | test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); |
| 204 | return; | 201 | return; |
| 205 | } else | 202 | } |
| 206 | __tty_buffer_flush(port); | 203 | |
| 207 | spin_unlock_irqrestore(&buf->lock, flags); | 204 | __tty_buffer_flush(port); |
| 205 | mutex_unlock(&buf->flush_mutex); | ||
| 208 | } | 206 | } |
| 209 | 207 | ||
| 210 | /** | 208 | /** |
| @@ -408,9 +406,10 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) | |||
| 408 | * This routine is called out of the software interrupt to flush data | 406 | * This routine is called out of the software interrupt to flush data |
| 409 | * from the buffer chain to the line discipline. | 407 | * from the buffer chain to the line discipline. |
| 410 | * | 408 | * |
| 411 | * Locking: holds tty->buf.lock to guard buffer list. Drops the lock | 409 | * The receive_buf method is single threaded for each tty instance. |
| 412 | * while invoking the line discipline receive_buf method. The | 410 | * |
| 413 | * receive_buf method is single threaded for each tty instance. | 411 | * Locking: takes flush_mutex to ensure single-threaded flip buffer |
| 412 | * 'consumer' | ||
| 414 | */ | 413 | */ |
| 415 | 414 | ||
| 416 | static void flush_to_ldisc(struct work_struct *work) | 415 | static void flush_to_ldisc(struct work_struct *work) |
| @@ -418,7 +417,6 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 418 | struct tty_port *port = container_of(work, struct tty_port, buf.work); | 417 | struct tty_port *port = container_of(work, struct tty_port, buf.work); |
| 419 | struct tty_bufhead *buf = &port->buf; | 418 | struct tty_bufhead *buf = &port->buf; |
| 420 | struct tty_struct *tty; | 419 | struct tty_struct *tty; |
| 421 | unsigned long flags; | ||
| 422 | struct tty_ldisc *disc; | 420 | struct tty_ldisc *disc; |
| 423 | 421 | ||
| 424 | tty = port->itty; | 422 | tty = port->itty; |
| @@ -429,7 +427,7 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 429 | if (disc == NULL) | 427 | if (disc == NULL) |
| 430 | return; | 428 | return; |
| 431 | 429 | ||
| 432 | spin_lock_irqsave(&buf->lock, flags); | 430 | mutex_lock(&buf->flush_mutex); |
| 433 | 431 | ||
| 434 | if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { | 432 | if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { |
| 435 | while (1) { | 433 | while (1) { |
| @@ -444,11 +442,13 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 444 | tty_buffer_free(port, head); | 442 | tty_buffer_free(port, head); |
| 445 | continue; | 443 | continue; |
| 446 | } | 444 | } |
| 447 | spin_unlock_irqrestore(&buf->lock, flags); | 445 | |
| 446 | mutex_unlock(&buf->flush_mutex); | ||
| 448 | 447 | ||
| 449 | count = receive_buf(tty, head, count); | 448 | count = receive_buf(tty, head, count); |
| 450 | 449 | ||
| 451 | spin_lock_irqsave(&buf->lock, flags); | 450 | mutex_lock(&buf->flush_mutex); |
| 451 | |||
| 452 | /* Ldisc or user is trying to flush the buffers. | 452 | /* Ldisc or user is trying to flush the buffers. |
| 453 | We may have a deferred request to flush the | 453 | We may have a deferred request to flush the |
| 454 | input buffer, if so pull the chain under the lock | 454 | input buffer, if so pull the chain under the lock |
| @@ -464,7 +464,7 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 464 | clear_bit(TTYP_FLUSHING, &port->iflags); | 464 | clear_bit(TTYP_FLUSHING, &port->iflags); |
| 465 | } | 465 | } |
| 466 | 466 | ||
| 467 | spin_unlock_irqrestore(&buf->lock, flags); | 467 | mutex_unlock(&buf->flush_mutex); |
| 468 | 468 | ||
| 469 | tty_ldisc_deref(disc); | 469 | tty_ldisc_deref(disc); |
| 470 | } | 470 | } |
| @@ -514,15 +514,13 @@ EXPORT_SYMBOL(tty_flip_buffer_push); | |||
| 514 | * | 514 | * |
| 515 | * Set up the initial state of the buffer management for a tty device. | 515 | * Set up the initial state of the buffer management for a tty device. |
| 516 | * Must be called before the other tty buffer functions are used. | 516 | * Must be called before the other tty buffer functions are used. |
| 517 | * | ||
| 518 | * Locking: none | ||
| 519 | */ | 517 | */ |
| 520 | 518 | ||
| 521 | void tty_buffer_init(struct tty_port *port) | 519 | void tty_buffer_init(struct tty_port *port) |
| 522 | { | 520 | { |
| 523 | struct tty_bufhead *buf = &port->buf; | 521 | struct tty_bufhead *buf = &port->buf; |
| 524 | 522 | ||
| 525 | spin_lock_init(&buf->lock); | 523 | mutex_init(&buf->flush_mutex); |
| 526 | tty_buffer_reset(&buf->sentinel, 0); | 524 | tty_buffer_reset(&buf->sentinel, 0); |
| 527 | buf->head = &buf->sentinel; | 525 | buf->head = &buf->sentinel; |
| 528 | buf->tail = &buf->sentinel; | 526 | buf->tail = &buf->sentinel; |
diff --git a/include/linux/tty.h b/include/linux/tty.h index 7c124541f011..1c8fef0e3ff6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
| @@ -66,7 +66,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) | |||
| 66 | 66 | ||
| 67 | struct tty_bufhead { | 67 | struct tty_bufhead { |
| 68 | struct work_struct work; | 68 | struct work_struct work; |
| 69 | spinlock_t lock; | 69 | struct mutex flush_mutex; |
| 70 | struct tty_buffer sentinel; | 70 | struct tty_buffer sentinel; |
| 71 | struct tty_buffer *head; /* Queue head */ | 71 | struct tty_buffer *head; /* Queue head */ |
| 72 | struct tty_buffer *tail; /* Active buffer */ | 72 | struct tty_buffer *tail; /* Active buffer */ |
