diff options
Diffstat (limited to 'drivers/tty/tty_buffer.c')
| -rw-r--r-- | drivers/tty/tty_buffer.c | 41 |
1 files changed, 27 insertions, 14 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 75661641f5fe..2f78b77f0f81 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c | |||
| @@ -37,6 +37,28 @@ | |||
| 37 | 37 | ||
| 38 | #define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) | 38 | #define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) |
| 39 | 39 | ||
| 40 | /* | ||
| 41 | * If all tty flip buffers have been processed by flush_to_ldisc() or | ||
| 42 | * dropped by tty_buffer_flush(), check if the linked pty has been closed. | ||
| 43 | * If so, wake the reader/poll to process | ||
| 44 | */ | ||
| 45 | static inline void check_other_closed(struct tty_struct *tty) | ||
| 46 | { | ||
| 47 | unsigned long flags, old; | ||
| 48 | |||
| 49 | /* transition from TTY_OTHER_CLOSED => TTY_OTHER_DONE must be atomic */ | ||
| 50 | for (flags = ACCESS_ONCE(tty->flags); | ||
| 51 | test_bit(TTY_OTHER_CLOSED, &flags); | ||
| 52 | ) { | ||
| 53 | old = flags; | ||
| 54 | __set_bit(TTY_OTHER_DONE, &flags); | ||
| 55 | flags = cmpxchg(&tty->flags, old, flags); | ||
| 56 | if (old == flags) { | ||
| 57 | wake_up_interruptible(&tty->read_wait); | ||
| 58 | break; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 40 | 62 | ||
| 41 | /** | 63 | /** |
| 42 | * tty_buffer_lock_exclusive - gain exclusive access to buffer | 64 | * tty_buffer_lock_exclusive - gain exclusive access to buffer |
| @@ -229,6 +251,8 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) | |||
| 229 | if (ld && ld->ops->flush_buffer) | 251 | if (ld && ld->ops->flush_buffer) |
| 230 | ld->ops->flush_buffer(tty); | 252 | ld->ops->flush_buffer(tty); |
| 231 | 253 | ||
| 254 | check_other_closed(tty); | ||
| 255 | |||
| 232 | atomic_dec(&buf->priority); | 256 | atomic_dec(&buf->priority); |
| 233 | mutex_unlock(&buf->lock); | 257 | mutex_unlock(&buf->lock); |
| 234 | } | 258 | } |
| @@ -471,8 +495,10 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 471 | smp_rmb(); | 495 | smp_rmb(); |
| 472 | count = head->commit - head->read; | 496 | count = head->commit - head->read; |
| 473 | if (!count) { | 497 | if (!count) { |
| 474 | if (next == NULL) | 498 | if (next == NULL) { |
| 499 | check_other_closed(tty); | ||
| 475 | break; | 500 | break; |
| 501 | } | ||
| 476 | buf->head = next; | 502 | buf->head = next; |
| 477 | tty_buffer_free(port, head); | 503 | tty_buffer_free(port, head); |
| 478 | continue; | 504 | continue; |
| @@ -489,19 +515,6 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 489 | } | 515 | } |
| 490 | 516 | ||
| 491 | /** | 517 | /** |
| 492 | * tty_flush_to_ldisc | ||
| 493 | * @tty: tty to push | ||
| 494 | * | ||
| 495 | * Push the terminal flip buffers to the line discipline. | ||
| 496 | * | ||
| 497 | * Must not be called from IRQ context. | ||
| 498 | */ | ||
| 499 | void tty_flush_to_ldisc(struct tty_struct *tty) | ||
| 500 | { | ||
| 501 | flush_work(&tty->port->buf.work); | ||
| 502 | } | ||
| 503 | |||
| 504 | /** | ||
| 505 | * tty_flip_buffer_push - terminal | 518 | * tty_flip_buffer_push - terminal |
| 506 | * @port: tty port to push | 519 | * @port: tty port to push |
| 507 | * | 520 | * |
