Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Peter Hurley and committed by
Greg Kroah-Hartman
86c80a8e 276a661a

+15 -11
+8 -2
drivers/tty/tty_buffer.c
··· 202 202 /** 203 203 * tty_buffer_flush - flush full tty buffers 204 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 210 * Locking: takes buffer lock to ensure single-threaded flip buffer 209 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 216 struct tty_port *port = tty->port; 215 217 struct tty_bufhead *buf = &port->buf; ··· 225 223 buf->head = next; 226 224 } 227 225 buf->head->read = buf->head->commit; 226 + 227 + if (ld && ld->ops->flush_buffer) 228 + ld->ops->flush_buffer(tty); 229 + 228 230 atomic_dec(&buf->priority); 229 231 mutex_unlock(&buf->lock); 230 232 }
+1 -1
drivers/tty/tty_io.c
··· 2890 2890 case TCIFLUSH: 2891 2891 case TCIOFLUSH: 2892 2892 /* flush tty buffer and allow ldisc to process ioctl */ 2893 - tty_buffer_flush(tty); 2893 + tty_buffer_flush(tty, NULL); 2894 2894 break; 2895 2895 } 2896 2896 break;
+5 -7
drivers/tty/tty_ldisc.c
··· 397 397 * tty_ldisc_flush - flush line discipline queue 398 398 * @tty: tty 399 399 * 400 - * Flush the line discipline queue (if any) for this tty. If there 401 - * is no line discipline active this is a no-op. 400 + * Flush the line discipline queue (if any) and the tty flip buffers 401 + * for this tty. 402 402 */ 403 403 404 404 void tty_ldisc_flush(struct tty_struct *tty) 405 405 { 406 406 struct tty_ldisc *ld = tty_ldisc_ref(tty); 407 - if (ld) { 408 - if (ld->ops->flush_buffer) 409 - ld->ops->flush_buffer(tty); 407 + 408 + tty_buffer_flush(tty, ld); 409 + if (ld) 410 410 tty_ldisc_deref(ld); 411 - } 412 - tty_buffer_flush(tty); 413 411 } 414 412 EXPORT_SYMBOL_GPL(tty_ldisc_flush); 415 413
+1 -1
include/linux/tty.h
··· 441 441 extern void no_tty(void); 442 442 extern void tty_flush_to_ldisc(struct tty_struct *tty); 443 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 445 extern void tty_buffer_init(struct tty_port *port); 446 446 extern speed_t tty_termios_baud_rate(struct ktermios *termios); 447 447 extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);