diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2014-11-05 12:13:09 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-11-05 21:50:43 -0500 |
commit | 86c80a8e2ab443e9c4261b3499de4ce808399104 (patch) | |
tree | aec087bbbc43f28deb617f958007599bce5146fe | |
parent | 276a661a4d75258b3aa28701b0748f99b773563b (diff) |
tty: Flush ldisc buffer atomically with tty flip buffers
tty_ldisc_flush() first clears the line discipline input buffer,
then clears the tty flip buffers. However, this allows for existing
data in the tty flip buffers to be added after the ldisc input
buffer has been cleared, but before the flip buffers have been cleared.
Add an optional ldisc parameter to tty_buffer_flush() to allow
tty_ldisc_flush() to pass the ldisc to clear.
NB: Initially, the plan was to do this automatically in
tty_buffer_flush(). However, an audit of the behavior of existing
line disciplines showed that performing a ldisc buffer flush on
ioctl(TCFLSH) was not always the outcome. For example, some line
disciplines have flush_buffer() methods but not ioctl() methods,
so a ->flush_buffer() command would be unexpected.
Reviewed-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/tty_buffer.c | 10 | ||||
-rw-r--r-- | drivers/tty/tty_io.c | 2 | ||||
-rw-r--r-- | drivers/tty/tty_ldisc.c | 12 | ||||
-rw-r--r-- | include/linux/tty.h | 2 |
4 files changed, 15 insertions, 11 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 143deb62467d..3605103fc1ac 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c | |||
@@ -202,14 +202,16 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) | |||
202 | /** | 202 | /** |
203 | * tty_buffer_flush - flush full tty buffers | 203 | * tty_buffer_flush - flush full tty buffers |
204 | * @tty: tty to flush | 204 | * @tty: tty to flush |
205 | * @ld: optional ldisc ptr (must be referenced) | ||
205 | * | 206 | * |
206 | * flush all the buffers containing receive data. | 207 | * flush all the buffers containing receive data. If ld != NULL, |
208 | * flush the ldisc input buffer. | ||
207 | * | 209 | * |
208 | * Locking: takes buffer lock to ensure single-threaded flip buffer | 210 | * Locking: takes buffer lock to ensure single-threaded flip buffer |
209 | * 'consumer' | 211 | * 'consumer' |
210 | */ | 212 | */ |
211 | 213 | ||
212 | void tty_buffer_flush(struct tty_struct *tty) | 214 | void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) |
213 | { | 215 | { |
214 | struct tty_port *port = tty->port; | 216 | struct tty_port *port = tty->port; |
215 | struct tty_bufhead *buf = &port->buf; | 217 | struct tty_bufhead *buf = &port->buf; |
@@ -223,6 +225,10 @@ void tty_buffer_flush(struct tty_struct *tty) | |||
223 | buf->head = next; | 225 | buf->head = next; |
224 | } | 226 | } |
225 | buf->head->read = buf->head->commit; | 227 | buf->head->read = buf->head->commit; |
228 | |||
229 | if (ld && ld->ops->flush_buffer) | ||
230 | ld->ops->flush_buffer(tty); | ||
231 | |||
226 | atomic_dec(&buf->priority); | 232 | atomic_dec(&buf->priority); |
227 | mutex_unlock(&buf->lock); | 233 | mutex_unlock(&buf->lock); |
228 | } | 234 | } |
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4ecee2856ece..aa83cd1bf071 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c | |||
@@ -2890,7 +2890,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
2890 | case TCIFLUSH: | 2890 | case TCIFLUSH: |
2891 | case TCIOFLUSH: | 2891 | case TCIOFLUSH: |
2892 | /* flush tty buffer and allow ldisc to process ioctl */ | 2892 | /* flush tty buffer and allow ldisc to process ioctl */ |
2893 | tty_buffer_flush(tty); | 2893 | tty_buffer_flush(tty, NULL); |
2894 | break; | 2894 | break; |
2895 | } | 2895 | } |
2896 | break; | 2896 | break; |
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 6368dd95e137..b66a81d0549e 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c | |||
@@ -397,19 +397,17 @@ static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, | |||
397 | * tty_ldisc_flush - flush line discipline queue | 397 | * tty_ldisc_flush - flush line discipline queue |
398 | * @tty: tty | 398 | * @tty: tty |
399 | * | 399 | * |
400 | * Flush the line discipline queue (if any) for this tty. If there | 400 | * Flush the line discipline queue (if any) and the tty flip buffers |
401 | * is no line discipline active this is a no-op. | 401 | * for this tty. |
402 | */ | 402 | */ |
403 | 403 | ||
404 | void tty_ldisc_flush(struct tty_struct *tty) | 404 | void tty_ldisc_flush(struct tty_struct *tty) |
405 | { | 405 | { |
406 | struct tty_ldisc *ld = tty_ldisc_ref(tty); | 406 | struct tty_ldisc *ld = tty_ldisc_ref(tty); |
407 | if (ld) { | 407 | |
408 | if (ld->ops->flush_buffer) | 408 | tty_buffer_flush(tty, ld); |
409 | ld->ops->flush_buffer(tty); | 409 | if (ld) |
410 | tty_ldisc_deref(ld); | 410 | tty_ldisc_deref(ld); |
411 | } | ||
412 | tty_buffer_flush(tty); | ||
413 | } | 411 | } |
414 | EXPORT_SYMBOL_GPL(tty_ldisc_flush); | 412 | EXPORT_SYMBOL_GPL(tty_ldisc_flush); |
415 | 413 | ||
diff --git a/include/linux/tty.h b/include/linux/tty.h index e33ea1a0c2d0..f6835ea1079a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -441,7 +441,7 @@ extern void __do_SAK(struct tty_struct *tty); | |||
441 | extern void no_tty(void); | 441 | extern void no_tty(void); |
442 | extern void tty_flush_to_ldisc(struct tty_struct *tty); | 442 | extern void tty_flush_to_ldisc(struct tty_struct *tty); |
443 | extern void tty_buffer_free_all(struct tty_port *port); | 443 | extern void tty_buffer_free_all(struct tty_port *port); |
444 | extern void tty_buffer_flush(struct tty_struct *tty); | 444 | extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld); |
445 | extern void tty_buffer_init(struct tty_port *port); | 445 | extern void tty_buffer_init(struct tty_port *port); |
446 | extern speed_t tty_termios_baud_rate(struct ktermios *termios); | 446 | extern speed_t tty_termios_baud_rate(struct ktermios *termios); |
447 | extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); | 447 | extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); |