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 /drivers/tty/tty_buffer.c | |
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>
Diffstat (limited to 'drivers/tty/tty_buffer.c')
-rw-r--r-- | drivers/tty/tty_buffer.c | 40 |
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 | ||
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; |