aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2014-11-05 12:13:09 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-05 21:50:43 -0500
commit86c80a8e2ab443e9c4261b3499de4ce808399104 (patch)
treeaec087bbbc43f28deb617f958007599bce5146fe
parent276a661a4d75258b3aa28701b0748f99b773563b (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.c10
-rw-r--r--drivers/tty/tty_io.c2
-rw-r--r--drivers/tty/tty_ldisc.c12
-rw-r--r--include/linux/tty.h2
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
212void tty_buffer_flush(struct tty_struct *tty) 214void 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
404void tty_ldisc_flush(struct tty_struct *tty) 404void 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}
414EXPORT_SYMBOL_GPL(tty_ldisc_flush); 412EXPORT_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);
441extern void no_tty(void); 441extern void no_tty(void);
442extern void tty_flush_to_ldisc(struct tty_struct *tty); 442extern void tty_flush_to_ldisc(struct tty_struct *tty);
443extern void tty_buffer_free_all(struct tty_port *port); 443extern void tty_buffer_free_all(struct tty_port *port);
444extern void tty_buffer_flush(struct tty_struct *tty); 444extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
445extern void tty_buffer_init(struct tty_port *port); 445extern void tty_buffer_init(struct tty_port *port);
446extern speed_t tty_termios_baud_rate(struct ktermios *termios); 446extern speed_t tty_termios_baud_rate(struct ktermios *termios);
447extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); 447extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);