aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/tty_buffer.c
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2013-06-15 09:36:10 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-23 19:47:09 -0400
commite9975fdec0138f1b2a85b9624e41660abd9865d4 (patch)
tree6c90bac37cfe6843b4e78cb9280aa10524da351d /drivers/tty/tty_buffer.c
parente8437d7ecbc50198705331449367d401ebb3181f (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>
Diffstat (limited to 'drivers/tty/tty_buffer.c')
-rw-r--r--drivers/tty/tty_buffer.c40
1 files changed, 19 insertions, 21 deletions
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
164static void __tty_buffer_flush(struct tty_port *port) 162static 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
188void tty_buffer_flush(struct tty_struct *tty) 187void 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
416static void flush_to_ldisc(struct work_struct *work) 415static 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
521void tty_buffer_init(struct tty_port *port) 519void 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;