From bf0bd9037be739d6b8a400a8dd22d4b79a66dc91 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 3 Mar 2014 09:20:54 -0500 Subject: [PATCH] Fix atomic sched BUG in tty low_latency (rhbz 1065087) --- kernel.spec | 7 ++ tty-Fix-low_latency-BUG.patch | 158 ++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 tty-Fix-low_latency-BUG.patch diff --git a/kernel.spec b/kernel.spec index 9e502c3a6..5bbdc8ff3 100644 --- a/kernel.spec +++ b/kernel.spec @@ -770,6 +770,9 @@ Patch25026: keyring-fix.patch #CVE-2014-0049 rhbz 1062368 1071837 Patch25027: kvm-x86-fix-emulator-buffer-overflow.patch +#rhbz 1065087 +Patch25028: tty-Fix-low_latency-BUG.patch + # END OF PATCH DEFINITIONS %endif @@ -1496,6 +1499,9 @@ ApplyPatch keyring-fix.patch #CVE-2014-0049 rhbz 1062368 1071837 ApplyPatch kvm-x86-fix-emulator-buffer-overflow.patch +#rhbz 1065087 +ApplyPatch tty-Fix-low_latency-BUG.patch + # END OF PATCH APPLICATIONS %endif @@ -2309,6 +2315,7 @@ fi %changelog * Mon Mar 03 2014 Josh Boyer - CVE-2014-0049 kvm: mmio_fragments out-of-bounds access (rhbz 1062368 1071837) +- Fix atomic sched BUG in tty low_latency (rhbz 1065087) * Fri Feb 28 2014 Josh Boyer - CVE-2014-0102 keyctl_link can be used to cause an oops (rhbz 1071396) diff --git a/tty-Fix-low_latency-BUG.patch b/tty-Fix-low_latency-BUG.patch new file mode 100644 index 000000000..60b395818 --- /dev/null +++ b/tty-Fix-low_latency-BUG.patch @@ -0,0 +1,158 @@ +Bugzilla: 1065087 +Upstream-status: 3.14 + +From a9c3f68f3cd8d55f809fbdb0c138ed061ea1bd25 Mon Sep 17 00:00:00 2001 +From: Peter Hurley +Date: Sat, 22 Feb 2014 12:31:21 +0000 +Subject: tty: Fix low_latency BUG + +The user-settable knob, low_latency, has been the source of +several BUG reports which stem from flush_to_ldisc() running +in interrupt context. Since 3.12, which added several sleeping +locks (termios_rwsem and buf->lock) to the input processing path, +the frequency of these BUG reports has increased. + +Note that changes in 3.12 did not introduce this regression; +sleeping locks were first added to the input processing path +with the removal of the BKL from N_TTY in commit +a88a69c91256418c5907c2f1f8a0ec0a36f9e6cc, +'n_tty: Fix loss of echoed characters and remove bkl from n_tty' +and later in commit 38db89799bdf11625a831c5af33938dcb11908b6, +'tty: throttling race fix'. Since those changes, executing +flush_to_ldisc() in interrupt_context (ie, low_latency set), is unsafe. + +However, since most devices do not validate if the low_latency +setting is appropriate for the context (process or interrupt) in +which they receive data, some reports are due to misconfiguration. +Further, serial dma devices for which dma fails, resort to +interrupt receiving as a backup without resetting low_latency. + +Historically, low_latency was used to force wake-up the reading +process rather than wait for the next scheduler tick. The +effect was to trim multiple milliseconds of latency from +when the process would receive new data. + +Recent tests [1] have shown that the reading process now receives +data with only 10's of microseconds latency without low_latency set. + +Remove the low_latency rx steering from tty_flip_buffer_push(); +however, leave the knob as an optional hint to drivers that can +tune their rx fifos and such like. Cleanup stale code comments +regarding low_latency. + +[1] https://lkml.org/lkml/2014/2/20/434 + +"Yay.. thats an annoying historical pain in the butt gone." + -- Alan Cox + +Reported-by: Beat Bolli +Reported-by: Pavel Roskin +Acked-by: David Sterba +Cc: Grant Edwards +Cc: Stanislaw Gruszka +Cc: Hal Murray +Cc: # 3.12.x+ +Signed-off-by: Peter Hurley +Signed-off-by: Alan Cox +Signed-off-by: Greg Kroah-Hartman +--- +diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c +index ebd5bff..17ee3bf 100644 +--- a/drivers/tty/ipwireless/tty.c ++++ b/drivers/tty/ipwireless/tty.c +@@ -176,9 +176,6 @@ void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, + ": %d chars not inserted to flip buffer!\n", + length - work); + +- /* +- * This may sleep if ->low_latency is set +- */ + if (work) + tty_flip_buffer_push(&tty->port); + } +diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c +index 765125d..8ebd9f8 100644 +--- a/drivers/tty/tty_buffer.c ++++ b/drivers/tty/tty_buffer.c +@@ -351,14 +351,11 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); + * Takes any pending buffers and transfers their ownership to the + * ldisc side of the queue. It then schedules those characters for + * processing by the line discipline. +- * Note that this function can only be used when the low_latency flag +- * is unset. Otherwise the workqueue won't be flushed. + */ + + void tty_schedule_flip(struct tty_port *port) + { + struct tty_bufhead *buf = &port->buf; +- WARN_ON(port->low_latency); + + buf->tail->commit = buf->tail->used; + schedule_work(&buf->work); +@@ -482,17 +479,15 @@ static void flush_to_ldisc(struct work_struct *work) + */ + void tty_flush_to_ldisc(struct tty_struct *tty) + { +- if (!tty->port->low_latency) +- flush_work(&tty->port->buf.work); ++ flush_work(&tty->port->buf.work); + } + + /** + * tty_flip_buffer_push - terminal + * @port: tty port to push + * +- * Queue a push of the terminal flip buffers to the line discipline. This +- * function must not be called from IRQ context if port->low_latency is +- * set. ++ * Queue a push of the terminal flip buffers to the line discipline. ++ * Can be called from IRQ/atomic context. + * + * In the event of the queue being busy for flipping the work will be + * held off and retried later. +@@ -500,14 +495,7 @@ void tty_flush_to_ldisc(struct tty_struct *tty) + + void tty_flip_buffer_push(struct tty_port *port) + { +- struct tty_bufhead *buf = &port->buf; +- +- buf->tail->commit = buf->tail->used; +- +- if (port->low_latency) +- flush_to_ldisc(&buf->work); +- else +- schedule_work(&buf->work); ++ tty_schedule_flip(port); + } + EXPORT_SYMBOL(tty_flip_buffer_push); + +diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c +index b369292..ad0aca8 100644 +--- a/drivers/usb/gadget/u_serial.c ++++ b/drivers/usb/gadget/u_serial.c +@@ -549,8 +549,8 @@ static void gs_rx_push(unsigned long _port) + port->read_started--; + } + +- /* Push from tty to ldisc; without low_latency set this is handled by +- * a workqueue, so we won't get callbacks and can hold port_lock ++ /* Push from tty to ldisc; this is handled by a workqueue, ++ * so we won't get callbacks and can hold port_lock + */ + if (do_push) + tty_flip_buffer_push(&port->port); +diff --git a/include/linux/tty.h b/include/linux/tty.h +index 90b4fdc..b90b5c2 100644 +--- a/include/linux/tty.h ++++ b/include/linux/tty.h +@@ -208,7 +208,7 @@ struct tty_port { + wait_queue_head_t delta_msr_wait; /* Modem status change */ + unsigned long flags; /* TTY flags ASY_*/ + unsigned char console:1, /* port is a console */ +- low_latency:1; /* direct buffer flush */ ++ low_latency:1; /* optional: tune for latency */ + struct mutex mutex; /* Locking */ + struct mutex buf_mutex; /* Buffer alloc lock */ + unsigned char *xmit_buf; /* Optional buffer */ +-- +cgit v0.9.2