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

Commit 4bd43f2c authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds
Browse files

tty: Fix close races in USB serial



USB serial has always had races where the tty port usage count can hit zero
during a receive event. The internal locking is a mutex so we can't use
that in the IRQ handlers.

With krefs we can tackle this differently but we still need to be careful.

Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 7e94b1d9
Loading
Loading
Loading
Loading
+10 −5
Original line number Original line Diff line number Diff line
@@ -269,15 +269,19 @@ static void serial_close(struct tty_struct *tty, struct file *filp)
		return;
		return;
	}
	}


	--port->port.count;
	if (port->port.count == 1)
	if (port->port.count == 0)
		/* only call the device specific close if this
		/* only call the device specific close if this
		 * port is being closed by the last owner */
		 * port is being closed by the last owner. Ensure we do
		 * this before we drop the port count. The call is protected
		 * by the port mutex
		 */
		port->serial->type->close(tty, port, filp);
		port->serial->type->close(tty, port, filp);


	if (port->port.count == (port->console? 1 : 0)) {
	if (port->port.count == (port->console ? 2 : 1)) {
		struct tty_struct *tty = tty_port_tty_get(&port->port);
		struct tty_struct *tty = tty_port_tty_get(&port->port);
		if (tty) {
		if (tty) {
			/* We must do this before we drop the port count to
			   zero. */
			if (tty->driver_data)
			if (tty->driver_data)
				tty->driver_data = NULL;
				tty->driver_data = NULL;
			tty_port_tty_set(&port->port, NULL);
			tty_port_tty_set(&port->port, NULL);
@@ -285,13 +289,14 @@ static void serial_close(struct tty_struct *tty, struct file *filp)
		}
		}
	}
	}


	if (port->port.count == 0) {
	if (port->port.count == 1) {
		mutex_lock(&port->serial->disc_mutex);
		mutex_lock(&port->serial->disc_mutex);
		if (!port->serial->disconnected)
		if (!port->serial->disconnected)
			usb_autopm_put_interface(port->serial->interface);
			usb_autopm_put_interface(port->serial->interface);
		mutex_unlock(&port->serial->disc_mutex);
		mutex_unlock(&port->serial->disc_mutex);
		module_put(port->serial->type->driver.owner);
		module_put(port->serial->type->driver.owner);
	}
	}
	--port->port.count;


	mutex_unlock(&port->mutex);
	mutex_unlock(&port->mutex);
	usb_serial_put(port->serial);
	usb_serial_put(port->serial);