Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1638fc6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull tty/serial driver fixes from Greg KH:
 "Here are some tty and serial driver fixes for 4.11-rc4.

  One of these fix a long-standing issue in the ldisc code that was
  found by Dmitry Vyukov with his great fuzzing work. The other fixes
  resolve other reported issues, and there is one revert of a patch in
  4.11-rc1 that wasn't correct.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'tty-4.11-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  tty: fix data race in tty_ldisc_ref_wait()
  tty: don't panic on OOM in tty_set_ldisc()
  Revert "tty: serial: pl011: add ttyAMA for matching pl011 console"
  tty: acpi/spcr: QDF2400 E44 checks for wrong OEM revision
  serial: 8250_dw: Fix breakage when HAVE_CLK=n
  serial: 8250_dw: Honor clk_round_rate errors in dw8250_set_termios
parents 53b4d591 a4a3e061
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ static bool qdf2400_erratum_44_present(struct acpi_table_header *h)
		return true;

	if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) &&
			h->oem_revision == 0)
			h->oem_revision == 1)
		return true;

	return false;
+7 −2
Original line number Diff line number Diff line
@@ -257,7 +257,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
{
	unsigned int baud = tty_termios_baud_rate(termios);
	struct dw8250_data *d = p->private_data;
	unsigned int rate;
	long rate;
	int ret;

	if (IS_ERR(d->clk) || !old)
@@ -265,6 +265,11 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,

	clk_disable_unprepare(d->clk);
	rate = clk_round_rate(d->clk, baud * 16);
	if (rate < 0)
		ret = rate;
	else if (rate == 0)
		ret = -ENOENT;
	else
		ret = clk_set_rate(d->clk, rate);
	clk_prepare_enable(d->clk);

+1 −1
Original line number Diff line number Diff line
@@ -2373,7 +2373,7 @@ static int __init pl011_console_match(struct console *co, char *name, int idx,
	if (strcmp(name, "qdf2400_e44") == 0) {
		pr_info_once("UART: Working around QDF2400 SoC erratum 44");
		qdf2400_e44_present = true;
	} else if (strcmp(name, "pl011") != 0 || strcmp(name, "ttyAMA") != 0) {
	} else if (strcmp(name, "pl011") != 0) {
		return -ENODEV;
	}

+21 −71
Original line number Diff line number Diff line
@@ -271,10 +271,13 @@ const struct file_operations tty_ldiscs_proc_fops = {

struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
{
	struct tty_ldisc *ld;

	ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
	if (!tty->ldisc)
	ld = tty->ldisc;
	if (!ld)
		ldsem_up_read(&tty->ldisc_sem);
	return tty->ldisc;
	return ld;
}
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);

@@ -488,41 +491,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
	tty_ldisc_debug(tty, "%p: closed\n", ld);
}

/**
 *	tty_ldisc_restore	-	helper for tty ldisc change
 *	@tty: tty to recover
 *	@old: previous ldisc
 *
 *	Restore the previous line discipline or N_TTY when a line discipline
 *	change fails due to an open error
 */

static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
{
	struct tty_ldisc *new_ldisc;
	int r;

	/* There is an outstanding reference here so this is safe */
	old = tty_ldisc_get(tty, old->ops->num);
	WARN_ON(IS_ERR(old));
	tty->ldisc = old;
	tty_set_termios_ldisc(tty, old->ops->num);
	if (tty_ldisc_open(tty, old) < 0) {
		tty_ldisc_put(old);
		/* This driver is always present */
		new_ldisc = tty_ldisc_get(tty, N_TTY);
		if (IS_ERR(new_ldisc))
			panic("n_tty: get");
		tty->ldisc = new_ldisc;
		tty_set_termios_ldisc(tty, N_TTY);
		r = tty_ldisc_open(tty, new_ldisc);
		if (r < 0)
			panic("Couldn't open N_TTY ldisc for "
			      "%s --- error %d.",
			      tty_name(tty), r);
	}
}

/**
 *	tty_set_ldisc		-	set line discipline
 *	@tty: the terminal to set
@@ -536,12 +504,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)

int tty_set_ldisc(struct tty_struct *tty, int disc)
{
	int retval;
	struct tty_ldisc *old_ldisc, *new_ldisc;

	new_ldisc = tty_ldisc_get(tty, disc);
	if (IS_ERR(new_ldisc))
		return PTR_ERR(new_ldisc);
	int retval, old_disc;

	tty_lock(tty);
	retval = tty_ldisc_lock(tty, 5 * HZ);
@@ -554,7 +517,8 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
	}

	/* Check the no-op case */
	if (tty->ldisc->ops->num == disc)
	old_disc = tty->ldisc->ops->num;
	if (old_disc == disc)
		goto out;

	if (test_bit(TTY_HUPPED, &tty->flags)) {
@@ -563,34 +527,25 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
		goto out;
	}

	old_ldisc = tty->ldisc;

	/* Shutdown the old discipline. */
	tty_ldisc_close(tty, old_ldisc);

	/* Now set up the new line discipline. */
	tty->ldisc = new_ldisc;
	tty_set_termios_ldisc(tty, disc);

	retval = tty_ldisc_open(tty, new_ldisc);
	retval = tty_ldisc_reinit(tty, disc);
	if (retval < 0) {
		/* Back to the old one or N_TTY if we can't */
		tty_ldisc_put(new_ldisc);
		tty_ldisc_restore(tty, old_ldisc);
		if (tty_ldisc_reinit(tty, old_disc) < 0) {
			pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n");
			if (tty_ldisc_reinit(tty, N_TTY) < 0) {
				/* At this point we have tty->ldisc == NULL. */
				pr_err("tty: reinitializing N_TTY failed\n");
			}
		}
	}

	if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) {
	if (tty->ldisc && tty->ldisc->ops->num != old_disc &&
	    tty->ops->set_ldisc) {
		down_read(&tty->termios_rwsem);
		tty->ops->set_ldisc(tty);
		up_read(&tty->termios_rwsem);
	}

	/* At this point we hold a reference to the new ldisc and a
	   reference to the old ldisc, or we hold two references to
	   the old ldisc (if it was restored as part of error cleanup
	   above). In either case, releasing a single reference from
	   the old ldisc is correct. */
	new_ldisc = old_ldisc;
out:
	tty_ldisc_unlock(tty);

@@ -598,7 +553,6 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
	   already running */
	tty_buffer_restart_work(tty->port);
err:
	tty_ldisc_put(new_ldisc);	/* drop the extra reference */
	tty_unlock(tty);
	return retval;
}
@@ -659,10 +613,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
	int retval;

	ld = tty_ldisc_get(tty, disc);
	if (IS_ERR(ld)) {
		BUG_ON(disc == N_TTY);
	if (IS_ERR(ld))
		return PTR_ERR(ld);
	}

	if (tty->ldisc) {
		tty_ldisc_close(tty, tty->ldisc);
@@ -674,11 +626,9 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
	tty_set_termios_ldisc(tty, disc);
	retval = tty_ldisc_open(tty, tty->ldisc);
	if (retval) {
		if (!WARN_ON(disc == N_TTY)) {
		tty_ldisc_put(tty->ldisc);
		tty->ldisc = NULL;
	}
	}
	return retval;
}