diff options
-rw-r--r-- | Documentation/serial/tty.txt | 3 | ||||
-rw-r--r-- | drivers/tty/n_hdlc.c | 4 | ||||
-rw-r--r-- | drivers/tty/n_tty.c | 22 | ||||
-rw-r--r-- | drivers/tty/pty.c | 5 | ||||
-rw-r--r-- | drivers/tty/tty_buffer.c | 41 | ||||
-rw-r--r-- | include/linux/tty.h | 2 |
6 files changed, 54 insertions, 23 deletions
diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt index 1e52d67d0abf..dbe6623fed1c 100644 --- a/Documentation/serial/tty.txt +++ b/Documentation/serial/tty.txt | |||
@@ -198,6 +198,9 @@ TTY_IO_ERROR If set, causes all subsequent userspace read/write | |||
198 | 198 | ||
199 | TTY_OTHER_CLOSED Device is a pty and the other side has closed. | 199 | TTY_OTHER_CLOSED Device is a pty and the other side has closed. |
200 | 200 | ||
201 | TTY_OTHER_DONE Device is a pty and the other side has closed and | ||
202 | all pending input processing has been completed. | ||
203 | |||
201 | TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into | 204 | TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into |
202 | smaller chunks. | 205 | smaller chunks. |
203 | 206 | ||
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 644ddb841d9f..bbc4ce66c2c1 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c | |||
@@ -600,7 +600,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, | |||
600 | add_wait_queue(&tty->read_wait, &wait); | 600 | add_wait_queue(&tty->read_wait, &wait); |
601 | 601 | ||
602 | for (;;) { | 602 | for (;;) { |
603 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { | 603 | if (test_bit(TTY_OTHER_DONE, &tty->flags)) { |
604 | ret = -EIO; | 604 | ret = -EIO; |
605 | break; | 605 | break; |
606 | } | 606 | } |
@@ -828,7 +828,7 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, | |||
828 | /* set bits for operations that won't block */ | 828 | /* set bits for operations that won't block */ |
829 | if (n_hdlc->rx_buf_list.head) | 829 | if (n_hdlc->rx_buf_list.head) |
830 | mask |= POLLIN | POLLRDNORM; /* readable */ | 830 | mask |= POLLIN | POLLRDNORM; /* readable */ |
831 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) | 831 | if (test_bit(TTY_OTHER_DONE, &tty->flags)) |
832 | mask |= POLLHUP; | 832 | mask |= POLLHUP; |
833 | if (tty_hung_up_p(filp)) | 833 | if (tty_hung_up_p(filp)) |
834 | mask |= POLLHUP; | 834 | mask |= POLLHUP; |
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cf6e0f2e1331..cc57a3a6b02b 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c | |||
@@ -1949,6 +1949,18 @@ static inline int input_available_p(struct tty_struct *tty, int poll) | |||
1949 | return ldata->commit_head - ldata->read_tail >= amt; | 1949 | return ldata->commit_head - ldata->read_tail >= amt; |
1950 | } | 1950 | } |
1951 | 1951 | ||
1952 | static inline int check_other_done(struct tty_struct *tty) | ||
1953 | { | ||
1954 | int done = test_bit(TTY_OTHER_DONE, &tty->flags); | ||
1955 | if (done) { | ||
1956 | /* paired with cmpxchg() in check_other_closed(); ensures | ||
1957 | * read buffer head index is not stale | ||
1958 | */ | ||
1959 | smp_mb__after_atomic(); | ||
1960 | } | ||
1961 | return done; | ||
1962 | } | ||
1963 | |||
1952 | /** | 1964 | /** |
1953 | * copy_from_read_buf - copy read data directly | 1965 | * copy_from_read_buf - copy read data directly |
1954 | * @tty: terminal device | 1966 | * @tty: terminal device |
@@ -2167,7 +2179,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, | |||
2167 | struct n_tty_data *ldata = tty->disc_data; | 2179 | struct n_tty_data *ldata = tty->disc_data; |
2168 | unsigned char __user *b = buf; | 2180 | unsigned char __user *b = buf; |
2169 | DEFINE_WAIT_FUNC(wait, woken_wake_function); | 2181 | DEFINE_WAIT_FUNC(wait, woken_wake_function); |
2170 | int c; | 2182 | int c, done; |
2171 | int minimum, time; | 2183 | int minimum, time; |
2172 | ssize_t retval = 0; | 2184 | ssize_t retval = 0; |
2173 | long timeout; | 2185 | long timeout; |
@@ -2235,8 +2247,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, | |||
2235 | ((minimum - (b - buf)) >= 1)) | 2247 | ((minimum - (b - buf)) >= 1)) |
2236 | ldata->minimum_to_wake = (minimum - (b - buf)); | 2248 | ldata->minimum_to_wake = (minimum - (b - buf)); |
2237 | 2249 | ||
2250 | done = check_other_done(tty); | ||
2251 | |||
2238 | if (!input_available_p(tty, 0)) { | 2252 | if (!input_available_p(tty, 0)) { |
2239 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { | 2253 | if (done) { |
2240 | retval = -EIO; | 2254 | retval = -EIO; |
2241 | break; | 2255 | break; |
2242 | } | 2256 | } |
@@ -2443,12 +2457,12 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, | |||
2443 | 2457 | ||
2444 | poll_wait(file, &tty->read_wait, wait); | 2458 | poll_wait(file, &tty->read_wait, wait); |
2445 | poll_wait(file, &tty->write_wait, wait); | 2459 | poll_wait(file, &tty->write_wait, wait); |
2460 | if (check_other_done(tty)) | ||
2461 | mask |= POLLHUP; | ||
2446 | if (input_available_p(tty, 1)) | 2462 | if (input_available_p(tty, 1)) |
2447 | mask |= POLLIN | POLLRDNORM; | 2463 | mask |= POLLIN | POLLRDNORM; |
2448 | if (tty->packet && tty->link->ctrl_status) | 2464 | if (tty->packet && tty->link->ctrl_status) |
2449 | mask |= POLLPRI | POLLIN | POLLRDNORM; | 2465 | mask |= POLLPRI | POLLIN | POLLRDNORM; |
2450 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) | ||
2451 | mask |= POLLHUP; | ||
2452 | if (tty_hung_up_p(file)) | 2466 | if (tty_hung_up_p(file)) |
2453 | mask |= POLLHUP; | 2467 | mask |= POLLHUP; |
2454 | if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { | 2468 | if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { |
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index e72ee629cead..4d5e8409769c 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c | |||
@@ -53,9 +53,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp) | |||
53 | /* Review - krefs on tty_link ?? */ | 53 | /* Review - krefs on tty_link ?? */ |
54 | if (!tty->link) | 54 | if (!tty->link) |
55 | return; | 55 | return; |
56 | tty_flush_to_ldisc(tty->link); | ||
57 | set_bit(TTY_OTHER_CLOSED, &tty->link->flags); | 56 | set_bit(TTY_OTHER_CLOSED, &tty->link->flags); |
58 | wake_up_interruptible(&tty->link->read_wait); | 57 | tty_flip_buffer_push(tty->link->port); |
59 | wake_up_interruptible(&tty->link->write_wait); | 58 | wake_up_interruptible(&tty->link->write_wait); |
60 | if (tty->driver->subtype == PTY_TYPE_MASTER) { | 59 | if (tty->driver->subtype == PTY_TYPE_MASTER) { |
61 | set_bit(TTY_OTHER_CLOSED, &tty->flags); | 60 | set_bit(TTY_OTHER_CLOSED, &tty->flags); |
@@ -243,7 +242,9 @@ static int pty_open(struct tty_struct *tty, struct file *filp) | |||
243 | goto out; | 242 | goto out; |
244 | 243 | ||
245 | clear_bit(TTY_IO_ERROR, &tty->flags); | 244 | clear_bit(TTY_IO_ERROR, &tty->flags); |
245 | /* TTY_OTHER_CLOSED must be cleared before TTY_OTHER_DONE */ | ||
246 | clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); | 246 | clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); |
247 | clear_bit(TTY_OTHER_DONE, &tty->link->flags); | ||
247 | set_bit(TTY_THROTTLED, &tty->flags); | 248 | set_bit(TTY_THROTTLED, &tty->flags); |
248 | return 0; | 249 | return 0; |
249 | 250 | ||
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 | * |
diff --git a/include/linux/tty.h b/include/linux/tty.h index fe5623c9af71..d76631f615c2 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -339,6 +339,7 @@ struct tty_file_private { | |||
339 | #define TTY_EXCLUSIVE 3 /* Exclusive open mode */ | 339 | #define TTY_EXCLUSIVE 3 /* Exclusive open mode */ |
340 | #define TTY_DEBUG 4 /* Debugging */ | 340 | #define TTY_DEBUG 4 /* Debugging */ |
341 | #define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ | 341 | #define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ |
342 | #define TTY_OTHER_DONE 6 /* Closed pty has completed input processing */ | ||
342 | #define TTY_LDISC_OPEN 11 /* Line discipline is open */ | 343 | #define TTY_LDISC_OPEN 11 /* Line discipline is open */ |
343 | #define TTY_PTY_LOCK 16 /* pty private */ | 344 | #define TTY_PTY_LOCK 16 /* pty private */ |
344 | #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ | 345 | #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ |
@@ -462,7 +463,6 @@ extern int tty_hung_up_p(struct file *filp); | |||
462 | extern void do_SAK(struct tty_struct *tty); | 463 | extern void do_SAK(struct tty_struct *tty); |
463 | extern void __do_SAK(struct tty_struct *tty); | 464 | extern void __do_SAK(struct tty_struct *tty); |
464 | extern void no_tty(void); | 465 | extern void no_tty(void); |
465 | extern void tty_flush_to_ldisc(struct tty_struct *tty); | ||
466 | extern void tty_buffer_free_all(struct tty_port *port); | 466 | extern void tty_buffer_free_all(struct tty_port *port); |
467 | extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld); | 467 | extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld); |
468 | extern void tty_buffer_init(struct tty_port *port); | 468 | extern void tty_buffer_init(struct tty_port *port); |