From 547039ec502076e60034eeb79611df3433a99b7d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 13:46:38 -0400 Subject: serial: Fix divide-by-zero fault in uart_get_divisor() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uart_get_baud_rate() will return baud == 0 if the max rate is set to the "magic" 38400 rate and the SPD_* flags are also specified. On the first iteration, if the current baud rate is higher than the max, the baud rate is clamped at the max (which in the degenerate case is 38400). On the second iteration, the now-"magic" 38400 baud rate selects the possibly higher alternate baud rate indicated by the SPD_* flag. Since only two loop iterations are performed, the loop is exited, a kernel WARNING is generated and a baud rate of 0 is returned. Reproducible with: setserial /dev/ttyS0 spd_hi base_baud 38400 Only perform the "magic" 38400 -> SPD_* baud transform on the first loop iteration, which prevents the degenerate case from recognizing the clamped baud rate as the "magic" 38400 value. Reported-by: Robert Święcki Cc: # all Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index df3a8c7..eaeb9a0 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -363,7 +363,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, * The spd_hi, spd_vhi, spd_shi, spd_warp kludge... * Die! Die! Die! */ - if (baud == 38400) + if (try == 0 && baud == 38400) baud = altbaud; /* -- cgit v0.10.2 From 37b164578826406a173ca7c20d9ba7430134d23e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 13:51:30 -0400 Subject: tty: Fix high cpu load if tty is unreleaseable Kernel oops can cause the tty to be unreleaseable (for example, if n_tty_read() crashes while on the read_wait queue). This will cause tty_release() to endlessly loop without sleeping. Use a killable sleep timeout which grows by 2n+1 jiffies over the interval [0, 120 secs.) and then jumps to forever (but still killable). NB: killable just allows for the task to be rewoken manually, not to be terminated. Cc: # since before 2.6.32 Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 16a2c02..4021c10 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1709,6 +1709,7 @@ int tty_release(struct inode *inode, struct file *filp) int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; char buf[64]; + long timeout = 0; if (tty_paranoia_check(tty, inode, __func__)) return 0; @@ -1793,7 +1794,11 @@ int tty_release(struct inode *inode, struct file *filp) __func__, tty_name(tty, buf)); tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); - schedule(); + schedule_timeout_killable(timeout); + if (timeout < 120 * HZ) + timeout = 2 * timeout + 1; + else + timeout = MAX_SCHEDULE_TIMEOUT; } /* -- cgit v0.10.2 From 494c1eac7e73f719af9d474a96ec8494c33efd6a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 16 Oct 2014 13:54:36 -0400 Subject: tty: Prevent "read/write wait queue active!" log flooding Only print one warning when a task is on the read_wait or write_wait wait queue at final tty release. Cc: # 3.4.x+ Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4021c10..0508a1d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1710,6 +1710,7 @@ int tty_release(struct inode *inode, struct file *filp) int idx; char buf[64]; long timeout = 0; + int once = 1; if (tty_paranoia_check(tty, inode, __func__)) return 0; @@ -1790,8 +1791,11 @@ int tty_release(struct inode *inode, struct file *filp) if (!do_sleep) break; - printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", - __func__, tty_name(tty, buf)); + if (once) { + once = 0; + printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", + __func__, tty_name(tty, buf)); + } tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); schedule_timeout_killable(timeout); -- cgit v0.10.2