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

Commit 591cee0a authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Greg Kroah-Hartman
Browse files

tty/amiserial: avoid interruptible_sleep_on



interruptible_sleep_on is generally problematic and we want to get
rid of it. In case of TIOCMIWAIT, that race is actually in user
space and does not get fixed since we can only detect changes after
entering the ioctl handler, but it removes one more caller.

This instance can not be trivially replaced with wait_event, so
I chose to open-code the wait loop using prepare_to_wait/finish_wait.

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f8e87cb4
Loading
Loading
Loading
Loading
+18 −8
Original line number Original line Diff line number Diff line
@@ -1248,6 +1248,8 @@ static int rs_ioctl(struct tty_struct *tty,
	struct async_icount cprev, cnow;	/* kernel counter temps */
	struct async_icount cprev, cnow;	/* kernel counter temps */
	void __user *argp = (void __user *)arg;
	void __user *argp = (void __user *)arg;
	unsigned long flags;
	unsigned long flags;
	DEFINE_WAIT(wait);
	int ret;


	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
		return -ENODEV;
		return -ENODEV;
@@ -1288,25 +1290,33 @@ static int rs_ioctl(struct tty_struct *tty,
			cprev = info->icount;
			cprev = info->icount;
			local_irq_restore(flags);
			local_irq_restore(flags);
			while (1) {
			while (1) {
				interruptible_sleep_on(&info->tport.delta_msr_wait);
				prepare_to_wait(&info->tport.delta_msr_wait,
				/* see if a signal did it */
						&wait, TASK_INTERRUPTIBLE);
				if (signal_pending(current))
					return -ERESTARTSYS;
				local_irq_save(flags);
				local_irq_save(flags);
				cnow = info->icount; /* atomic copy */
				cnow = info->icount; /* atomic copy */
				local_irq_restore(flags);
				local_irq_restore(flags);
				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
					return -EIO; /* no change => error */
					ret = -EIO; /* no change => error */
					break;
				}
				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
					return 0;
					ret = 0;
					break;
				}
				schedule();
				/* see if a signal did it */
				if (signal_pending(current)) {
					ret = -ERESTARTSYS;
					break;
				}
				}
				cprev = cnow;
				cprev = cnow;
			}
			}
			/* NOTREACHED */
			finish_wait(&info->tport.delta_msr_wait, &wait);
			return ret;


		case TIOCSERGWILD:
		case TIOCSERGWILD:
		case TIOCSERSWILD:
		case TIOCSERSWILD: