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

Commit 50e9fda8 authored by Dmitry Safonov's avatar Dmitry Safonov Committed by Greg Kroah-Hartman
Browse files

tty: Don't block on IO when ldisc change is pending



[ Upstream commit c96cf923a98d1b094df9f0cf97a83e118817e31b ]

There might be situations where tty_ldisc_lock() has blocked, but there
is already IO on tty and it prevents line discipline changes.
It might theoretically turn into dead-lock.

Basically, provide more priority to pending tty_ldisc_lock() than to
servicing reads/writes over tty.

User-visible issue was reported by Mikulas where on pa-risc with
Debian 5 reboot took either 80 seconds, 3 minutes or 3:25 after proper
locking in tty_reopen().

Cc: Jiri Slaby <jslaby@suse.com>
Reported-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarDmitry Safonov <dima@arista.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 01e5d179
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -613,7 +613,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
		}
			
		/* no data */
		if (file->f_flags & O_NONBLOCK) {
		if (tty_io_nonblock(tty, file)) {
			ret = -EAGAIN;
			break;
		}
@@ -680,7 +680,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
		if (tbuf)
			break;

		if (file->f_flags & O_NONBLOCK) {
		if (tty_io_nonblock(tty, file)) {
			error = -EAGAIN;
			break;
		}
+1 −1
Original line number Diff line number Diff line
@@ -1078,7 +1078,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
		pMsg = remove_msg(pInfo, pClient);
		if (pMsg == NULL) {
			/* no messages available. */
			if (file->f_flags & O_NONBLOCK) {
			if (tty_io_nonblock(tty, file)) {
				ret = -EAGAIN;
				goto unlock;
			}
+4 −4
Original line number Diff line number Diff line
@@ -1702,7 +1702,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,

	down_read(&tty->termios_rwsem);

	while (1) {
	do {
		/*
		 * When PARMRK is set, each input char may take up to 3 chars
		 * in the read buf; reduce the buffer space avail by 3x
@@ -1744,7 +1744,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
			fp += n;
		count -= n;
		rcvd += n;
	}
	} while (!test_bit(TTY_LDISC_CHANGING, &tty->flags));

	tty->receive_room = room;

@@ -2211,7 +2211,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
					break;
				if (!timeout)
					break;
				if (file->f_flags & O_NONBLOCK) {
				if (tty_io_nonblock(tty, file)) {
					retval = -EAGAIN;
					break;
				}
@@ -2365,7 +2365,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
		}
		if (!nr)
			break;
		if (file->f_flags & O_NONBLOCK) {
		if (tty_io_nonblock(tty, file)) {
			retval = -EAGAIN;
			break;
		}
+7 −0
Original line number Diff line number Diff line
@@ -336,6 +336,11 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
{
	int ret;

	/* Kindly asking blocked readers to release the read side */
	set_bit(TTY_LDISC_CHANGING, &tty->flags);
	wake_up_interruptible_all(&tty->read_wait);
	wake_up_interruptible_all(&tty->write_wait);

	ret = __tty_ldisc_lock(tty, timeout);
	if (!ret)
		return -EBUSY;
@@ -346,6 +351,8 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
void tty_ldisc_unlock(struct tty_struct *tty)
{
	clear_bit(TTY_LDISC_HALTED, &tty->flags);
	/* Can be cleared here - ldisc_unlock will wake up writers firstly */
	clear_bit(TTY_LDISC_CHANGING, &tty->flags);
	__tty_ldisc_unlock(tty);
}

+7 −0
Original line number Diff line number Diff line
@@ -366,6 +366,7 @@ struct tty_file_private {
#define TTY_NO_WRITE_SPLIT 	17	/* Preserve write boundaries to driver */
#define TTY_HUPPED 		18	/* Post driver->hangup() */
#define TTY_HUPPING		19	/* Hangup in progress */
#define TTY_LDISC_CHANGING	20	/* Change pending - non-block IO */
#define TTY_LDISC_HALTED	22	/* Line discipline is halted */

/* Values for tty->flow_change */
@@ -383,6 +384,12 @@ static inline void tty_set_flow_change(struct tty_struct *tty, int val)
	smp_mb();
}

static inline bool tty_io_nonblock(struct tty_struct *tty, struct file *file)
{
	return file->f_flags & O_NONBLOCK ||
		test_bit(TTY_LDISC_CHANGING, &tty->flags);
}

static inline bool tty_io_error(struct tty_struct *tty)
{
	return test_bit(TTY_IO_ERROR, &tty->flags);