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); |
