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

Commit 8117e347 authored by Johannes Thumshirn's avatar Johannes Thumshirn Committed by Greg Kroah-Hartman
Browse files

tty: serial: men_z135_uart.c: Fix race between IRQ and set_termios()



Fix panic caused by a race between men_z135_intr() and men_z135_set_termios().

men_z135_intr() and men_z135_set_termios() both hold the struct uart_port::lock
spinlock, but men_z135_intr() does a spin_lock_irqsave() and
men_z135_set_termios() does a normal spin_lock(), which can lead to a deadlock
when an interrupt is called while the lock is being helt by
men_z135_set_termios().

This was discovered using a insmod, hardware looppback send/receive, rmmod
stress test.

Signed-off-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Cc: Andreas Werner <andreas.werner@men.de>
Cc: stable@vger.kernel.org # v4.0+
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1d700277
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -392,7 +392,6 @@ static irqreturn_t men_z135_intr(int irq, void *data)
	struct men_z135_port *uart = (struct men_z135_port *)data;
	struct uart_port *port = &uart->port;
	bool handled = false;
	unsigned long flags;
	int irq_id;

	uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
@@ -401,7 +400,7 @@ static irqreturn_t men_z135_intr(int irq, void *data)
	if (!irq_id)
		goto out;

	spin_lock_irqsave(&port->lock, flags);
	spin_lock(&port->lock);
	/* It's save to write to IIR[7:6] RXC[9:8] */
	iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG);

@@ -427,7 +426,7 @@ static irqreturn_t men_z135_intr(int irq, void *data)
		handled = true;
	}

	spin_unlock_irqrestore(&port->lock, flags);
	spin_unlock(&port->lock);
out:
	return IRQ_RETVAL(handled);
}
@@ -717,7 +716,7 @@ static void men_z135_set_termios(struct uart_port *port,

	baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16);

	spin_lock(&port->lock);
	spin_lock_irq(&port->lock);
	if (tty_termios_baud_rate(termios))
		tty_termios_encode_baud_rate(termios, baud, baud);

@@ -725,7 +724,7 @@ static void men_z135_set_termios(struct uart_port *port,
	iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG);

	uart_update_timeout(port, termios->c_cflag, baud);
	spin_unlock(&port->lock);
	spin_unlock_irq(&port->lock);
}

static const char *men_z135_type(struct uart_port *port)