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 | * |