diff options
Diffstat (limited to 'drivers/tty/tty_ioctl.c')
| -rw-r--r-- | drivers/tty/tty_ioctl.c | 378 | 
1 files changed, 214 insertions, 164 deletions
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 0c188997145..6fd60fece6b 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1,6 +1,4 @@  /* - *  linux/drivers/char/tty_ioctl.c - *   *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds   *   * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines @@ -21,10 +19,10 @@  #include <linux/module.h>  #include <linux/bitops.h>  #include <linux/mutex.h> +#include <linux/compat.h>  #include <asm/io.h>  #include <asm/uaccess.h> -#include <asm/system.h>  #undef TTY_DEBUG_WAIT_UNTIL_SENT @@ -96,19 +94,20 @@ EXPORT_SYMBOL(tty_driver_flush_buffer);   *	@tty: terminal   *   *	Indicate that a tty should stop transmitting data down the stack. - *	Takes the termios mutex to protect against parallel throttle/unthrottle + *	Takes the termios rwsem to protect against parallel throttle/unthrottle   *	and also to ensure the driver can consistently reference its own   *	termios data at this point when implementing software flow control.   */  void tty_throttle(struct tty_struct *tty)  { -	mutex_lock(&tty->termios_mutex); +	down_write(&tty->termios_rwsem);  	/* check TTY_THROTTLED first so it indicates our state */  	if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&  	    tty->ops->throttle)  		tty->ops->throttle(tty); -	mutex_unlock(&tty->termios_mutex); +	tty->flow_change = 0; +	up_write(&tty->termios_rwsem);  }  EXPORT_SYMBOL(tty_throttle); @@ -117,7 +116,7 @@ EXPORT_SYMBOL(tty_throttle);   *	@tty: terminal   *   *	Indicate that a tty may continue transmitting data down the stack. - *	Takes the termios mutex to protect against parallel throttle/unthrottle + *	Takes the termios rwsem to protect against parallel throttle/unthrottle   *	and also to ensure the driver can consistently reference its own   *	termios data at this point when implementing software flow control.   * @@ -127,15 +126,78 @@ EXPORT_SYMBOL(tty_throttle);  void tty_unthrottle(struct tty_struct *tty)  { -	mutex_lock(&tty->termios_mutex); +	down_write(&tty->termios_rwsem);  	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&  	    tty->ops->unthrottle)  		tty->ops->unthrottle(tty); -	mutex_unlock(&tty->termios_mutex); +	tty->flow_change = 0; +	up_write(&tty->termios_rwsem);  }  EXPORT_SYMBOL(tty_unthrottle);  /** + *	tty_throttle_safe	-	flow control + *	@tty: terminal + * + *	Similar to tty_throttle() but will only attempt throttle + *	if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental + *	throttle due to race conditions when throttling is conditional + *	on factors evaluated prior to throttling. + * + *	Returns 0 if tty is throttled (or was already throttled) + */ + +int tty_throttle_safe(struct tty_struct *tty) +{ +	int ret = 0; + +	mutex_lock(&tty->throttle_mutex); +	if (!test_bit(TTY_THROTTLED, &tty->flags)) { +		if (tty->flow_change != TTY_THROTTLE_SAFE) +			ret = 1; +		else { +			set_bit(TTY_THROTTLED, &tty->flags); +			if (tty->ops->throttle) +				tty->ops->throttle(tty); +		} +	} +	mutex_unlock(&tty->throttle_mutex); + +	return ret; +} + +/** + *	tty_unthrottle_safe	-	flow control + *	@tty: terminal + * + *	Similar to tty_unthrottle() but will only attempt unthrottle + *	if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental + *	unthrottle due to race conditions when unthrottling is conditional + *	on factors evaluated prior to unthrottling. + * + *	Returns 0 if tty is unthrottled (or was already unthrottled) + */ + +int tty_unthrottle_safe(struct tty_struct *tty) +{ +	int ret = 0; + +	mutex_lock(&tty->throttle_mutex); +	if (test_bit(TTY_THROTTLED, &tty->flags)) { +		if (tty->flow_change != TTY_UNTHROTTLE_SAFE) +			ret = 1; +		else { +			clear_bit(TTY_THROTTLED, &tty->flags); +			if (tty->ops->unthrottle) +				tty->ops->unthrottle(tty); +		} +	} +	mutex_unlock(&tty->throttle_mutex); + +	return ret; +} + +/**   *	tty_wait_until_sent	-	wait for I/O to finish   *	@tty: tty we are waiting for   *	@timeout: how long we will wait @@ -247,7 +309,7 @@ speed_t tty_termios_baud_rate(struct ktermios *termios)  	cbaud = termios->c_cflag & CBAUD;  #ifdef BOTHER -	/* Magic token for arbitary speed via c_ispeed/c_ospeed */ +	/* Magic token for arbitrary speed via c_ispeed/c_ospeed */  	if (cbaud == BOTHER)  		return termios->c_ospeed;  #endif @@ -283,7 +345,7 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios)  	if (cbaud == B0)  		return tty_termios_baud_rate(termios); -	/* Magic token for arbitary speed via c_ispeed*/ +	/* Magic token for arbitrary speed via c_ispeed*/  	if (cbaud == BOTHER)  		return termios->c_ispeed; @@ -309,7 +371,7 @@ EXPORT_SYMBOL(tty_termios_input_baud_rate);   *	@ospeed: output speed   *   *	Encode the speeds set into the passed termios structure. This is - *	used as a library helper for drivers os that they can report back + *	used as a library helper for drivers so that they can report back   *	the actual speed selected when it differs from the speed requested   *   *	For maximal back compatibility with legacy SYS5/POSIX *nix behaviour @@ -406,50 +468,22 @@ EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);   *	@obad: output baud rate   *   *	Update the current termios data for the tty with the new speed - *	settings. The caller must hold the termios_mutex for the tty in + *	settings. The caller must hold the termios_rwsem for the tty in   *	question.   */  void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)  { -	tty_termios_encode_baud_rate(tty->termios, ibaud, obaud); +	tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud);  }  EXPORT_SYMBOL_GPL(tty_encode_baud_rate);  /** - *	tty_get_baud_rate	-	get tty bit rates - *	@tty: tty to query - * - *	Returns the baud rate as an integer for this terminal. The - *	termios lock must be held by the caller and the terminal bit - *	flags may be updated. - * - *	Locking: none - */ - -speed_t tty_get_baud_rate(struct tty_struct *tty) -{ -	speed_t baud = tty_termios_baud_rate(tty->termios); - -	if (baud == 38400 && tty->alt_speed) { -		if (!tty->warned) { -			printk(KERN_WARNING "Use of setserial/setrocket to " -					    "set SPD_* flags is deprecated\n"); -			tty->warned = 1; -		} -		baud = tty->alt_speed; -	} - -	return baud; -} -EXPORT_SYMBOL(tty_get_baud_rate); - -/**   *	tty_termios_copy_hw	-	copy hardware settings   *	@new: New termios   *	@old: Old termios   * - *	Propogate the hardware specific terminal setting bits from + *	Propagate the hardware specific terminal setting bits from   *	the old termios structure to the new one. This is used in cases   *	where the hardware does not support reconfiguration or as a helper   *	in some cases where only minimal reconfiguration is supported @@ -486,7 +520,7 @@ int tty_termios_hw_change(struct ktermios *a, struct ktermios *b)  EXPORT_SYMBOL(tty_termios_hw_change);  /** - *	change_termios		-	update termios values + *	tty_set_termios		-	update termios values   *	@tty: tty to update   *	@new_termios: desired new value   * @@ -494,10 +528,10 @@ EXPORT_SYMBOL(tty_termios_hw_change);   *	is a bit of layering violation here with n_tty in terms of the   *	internal knowledge of this function.   * - *	Locking: termios_mutex + *	Locking: termios_rwsem   */ -static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) +int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)  {  	struct ktermios old_termios;  	struct tty_ldisc *ld; @@ -510,15 +544,15 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)  	/* FIXME: we need to decide on some locking/ordering semantics  	   for the set_termios notification eventually */ -	mutex_lock(&tty->termios_mutex); -	old_termios = *tty->termios; -	*tty->termios = *new_termios; -	unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); +	down_write(&tty->termios_rwsem); +	old_termios = tty->termios; +	tty->termios = *new_termios; +	unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked);  	/* See if packet mode change of state. */  	if (tty->link && tty->link->packet) {  		int extproc = (old_termios.c_lflag & EXTPROC) | -				(tty->termios->c_lflag & EXTPROC); +				(tty->termios.c_lflag & EXTPROC);  		int old_flow = ((old_termios.c_iflag & IXON) &&  				(old_termios.c_cc[VSTOP] == '\023') &&  				(old_termios.c_cc[VSTART] == '\021')); @@ -544,7 +578,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)  	if (tty->ops->set_termios)  		(*tty->ops->set_termios)(tty, &old_termios);  	else -		tty_termios_copy_hw(tty->termios, &old_termios); +		tty_termios_copy_hw(&tty->termios, &old_termios);  	ld = tty_ldisc_ref(tty);  	if (ld != NULL) { @@ -552,8 +586,10 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)  			(ld->ops->set_termios)(tty, &old_termios);  		tty_ldisc_deref(ld);  	} -	mutex_unlock(&tty->termios_mutex); +	up_write(&tty->termios_rwsem); +	return 0;  } +EXPORT_SYMBOL_GPL(tty_set_termios);  /**   *	set_termios		-	set termios values for a tty @@ -562,10 +598,10 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)   *	@opt: option information   *   *	Helper function to prepare termios data and run necessary other - *	functions before using change_termios to do the actual changes. + *	functions before using tty_set_termios to do the actual changes.   *   *	Locking: - *		Called functions take ldisc and termios_mutex locks + *		Called functions take ldisc and termios_rwsem locks   */  static int set_termios(struct tty_struct *tty, void __user *arg, int opt) @@ -577,9 +613,9 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)  	if (retval)  		return retval; -	mutex_lock(&tty->termios_mutex); -	memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios)); -	mutex_unlock(&tty->termios_mutex); +	down_read(&tty->termios_rwsem); +	tmp_termios = tty->termios; +	up_read(&tty->termios_rwsem);  	if (opt & TERMIOS_TERMIO) {  		if (user_termio_to_kernel_termios(&tmp_termios, @@ -617,10 +653,10 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)  	if (opt & TERMIOS_WAIT) {  		tty_wait_until_sent(tty, 0);  		if (signal_pending(current)) -			return -EINTR; +			return -ERESTARTSYS;  	} -	change_termios(tty, &tmp_termios); +	tty_set_termios(tty, &tmp_termios);  	/* FIXME: Arguably if tmp_termios == tty->termios AND the  	   actual requested termios was not tmp_termios then we may @@ -631,16 +667,16 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)  static void copy_termios(struct tty_struct *tty, struct ktermios *kterm)  { -	mutex_lock(&tty->termios_mutex); -	memcpy(kterm, tty->termios, sizeof(struct ktermios)); -	mutex_unlock(&tty->termios_mutex); +	down_read(&tty->termios_rwsem); +	*kterm = tty->termios; +	up_read(&tty->termios_rwsem);  }  static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)  { -	mutex_lock(&tty->termios_mutex); -	memcpy(kterm, tty->termios_locked, sizeof(struct ktermios)); -	mutex_unlock(&tty->termios_mutex); +	down_read(&tty->termios_rwsem); +	*kterm = tty->termios_locked; +	up_read(&tty->termios_rwsem);  }  static int get_termio(struct tty_struct *tty, struct termio __user *termio) @@ -684,13 +720,13 @@ static int set_termiox(struct tty_struct *tty, void __user *arg, int opt)  	if (opt & TERMIOS_WAIT) {  		tty_wait_until_sent(tty, 0);  		if (signal_pending(current)) -			return -EINTR; +			return -ERESTARTSYS;  	} -	mutex_lock(&tty->termios_mutex); +	down_write(&tty->termios_rwsem);  	if (tty->ops->set_termiox)  		tty->ops->set_termiox(tty, &tnew); -	mutex_unlock(&tty->termios_mutex); +	up_write(&tty->termios_rwsem);  	return 0;  } @@ -707,16 +743,16 @@ static int get_sgflags(struct tty_struct *tty)  {  	int flags = 0; -	if (!(tty->termios->c_lflag & ICANON)) { -		if (tty->termios->c_lflag & ISIG) +	if (!(tty->termios.c_lflag & ICANON)) { +		if (tty->termios.c_lflag & ISIG)  			flags |= 0x02;		/* cbreak */  		else  			flags |= 0x20;		/* raw */  	} -	if (tty->termios->c_lflag & ECHO) +	if (tty->termios.c_lflag & ECHO)  		flags |= 0x08;			/* echo */ -	if (tty->termios->c_oflag & OPOST) -		if (tty->termios->c_oflag & ONLCR) +	if (tty->termios.c_oflag & OPOST) +		if (tty->termios.c_oflag & ONLCR)  			flags |= 0x10;		/* crmod */  	return flags;  } @@ -725,13 +761,13 @@ static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)  {  	struct sgttyb tmp; -	mutex_lock(&tty->termios_mutex); -	tmp.sg_ispeed = tty->termios->c_ispeed; -	tmp.sg_ospeed = tty->termios->c_ospeed; -	tmp.sg_erase = tty->termios->c_cc[VERASE]; -	tmp.sg_kill = tty->termios->c_cc[VKILL]; +	down_read(&tty->termios_rwsem); +	tmp.sg_ispeed = tty->termios.c_ispeed; +	tmp.sg_ospeed = tty->termios.c_ospeed; +	tmp.sg_erase = tty->termios.c_cc[VERASE]; +	tmp.sg_kill = tty->termios.c_cc[VKILL];  	tmp.sg_flags = get_sgflags(tty); -	mutex_unlock(&tty->termios_mutex); +	up_read(&tty->termios_rwsem);  	return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;  } @@ -770,7 +806,7 @@ static void set_sgflags(struct ktermios *termios, int flags)   *	Updates a terminal from the legacy BSD style terminal information   *	structure.   * - *	Locking: termios_mutex + *	Locking: termios_rwsem   */  static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) @@ -786,8 +822,8 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)  	if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))  		return -EFAULT; -	mutex_lock(&tty->termios_mutex); -	termios = *tty->termios; +	down_write(&tty->termios_rwsem); +	termios = tty->termios;  	termios.c_cc[VERASE] = tmp.sg_erase;  	termios.c_cc[VKILL] = tmp.sg_kill;  	set_sgflags(&termios, tmp.sg_flags); @@ -796,8 +832,8 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)  	tty_termios_encode_baud_rate(&termios, termios.c_ispeed,  						termios.c_ospeed);  #endif -	mutex_unlock(&tty->termios_mutex); -	change_termios(tty, &termios); +	up_write(&tty->termios_rwsem); +	tty_set_termios(tty, &termios);  	return 0;  }  #endif @@ -807,14 +843,14 @@ static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars)  {  	struct tchars tmp; -	mutex_lock(&tty->termios_mutex); -	tmp.t_intrc = tty->termios->c_cc[VINTR]; -	tmp.t_quitc = tty->termios->c_cc[VQUIT]; -	tmp.t_startc = tty->termios->c_cc[VSTART]; -	tmp.t_stopc = tty->termios->c_cc[VSTOP]; -	tmp.t_eofc = tty->termios->c_cc[VEOF]; -	tmp.t_brkc = tty->termios->c_cc[VEOL2];	/* what is brkc anyway? */ -	mutex_unlock(&tty->termios_mutex); +	down_read(&tty->termios_rwsem); +	tmp.t_intrc = tty->termios.c_cc[VINTR]; +	tmp.t_quitc = tty->termios.c_cc[VQUIT]; +	tmp.t_startc = tty->termios.c_cc[VSTART]; +	tmp.t_stopc = tty->termios.c_cc[VSTOP]; +	tmp.t_eofc = tty->termios.c_cc[VEOF]; +	tmp.t_brkc = tty->termios.c_cc[VEOL2];	/* what is brkc anyway? */ +	up_read(&tty->termios_rwsem);  	return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;  } @@ -824,14 +860,14 @@ static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars)  	if (copy_from_user(&tmp, tchars, sizeof(tmp)))  		return -EFAULT; -	mutex_lock(&tty->termios_mutex); -	tty->termios->c_cc[VINTR] = tmp.t_intrc; -	tty->termios->c_cc[VQUIT] = tmp.t_quitc; -	tty->termios->c_cc[VSTART] = tmp.t_startc; -	tty->termios->c_cc[VSTOP] = tmp.t_stopc; -	tty->termios->c_cc[VEOF] = tmp.t_eofc; -	tty->termios->c_cc[VEOL2] = tmp.t_brkc;	/* what is brkc anyway? */ -	mutex_unlock(&tty->termios_mutex); +	down_write(&tty->termios_rwsem); +	tty->termios.c_cc[VINTR] = tmp.t_intrc; +	tty->termios.c_cc[VQUIT] = tmp.t_quitc; +	tty->termios.c_cc[VSTART] = tmp.t_startc; +	tty->termios.c_cc[VSTOP] = tmp.t_stopc; +	tty->termios.c_cc[VEOF] = tmp.t_eofc; +	tty->termios.c_cc[VEOL2] = tmp.t_brkc;	/* what is brkc anyway? */ +	up_write(&tty->termios_rwsem);  	return 0;  }  #endif @@ -841,16 +877,16 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)  {  	struct ltchars tmp; -	mutex_lock(&tty->termios_mutex); -	tmp.t_suspc = tty->termios->c_cc[VSUSP]; +	down_read(&tty->termios_rwsem); +	tmp.t_suspc = tty->termios.c_cc[VSUSP];  	/* what is dsuspc anyway? */ -	tmp.t_dsuspc = tty->termios->c_cc[VSUSP]; -	tmp.t_rprntc = tty->termios->c_cc[VREPRINT]; +	tmp.t_dsuspc = tty->termios.c_cc[VSUSP]; +	tmp.t_rprntc = tty->termios.c_cc[VREPRINT];  	/* what is flushc anyway? */ -	tmp.t_flushc = tty->termios->c_cc[VEOL2]; -	tmp.t_werasc = tty->termios->c_cc[VWERASE]; -	tmp.t_lnextc = tty->termios->c_cc[VLNEXT]; -	mutex_unlock(&tty->termios_mutex); +	tmp.t_flushc = tty->termios.c_cc[VEOL2]; +	tmp.t_werasc = tty->termios.c_cc[VWERASE]; +	tmp.t_lnextc = tty->termios.c_cc[VLNEXT]; +	up_read(&tty->termios_rwsem);  	return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;  } @@ -861,16 +897,16 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)  	if (copy_from_user(&tmp, ltchars, sizeof(tmp)))  		return -EFAULT; -	mutex_lock(&tty->termios_mutex); -	tty->termios->c_cc[VSUSP] = tmp.t_suspc; +	down_write(&tty->termios_rwsem); +	tty->termios.c_cc[VSUSP] = tmp.t_suspc;  	/* what is dsuspc anyway? */ -	tty->termios->c_cc[VEOL2] = tmp.t_dsuspc; -	tty->termios->c_cc[VREPRINT] = tmp.t_rprntc; +	tty->termios.c_cc[VEOL2] = tmp.t_dsuspc; +	tty->termios.c_cc[VREPRINT] = tmp.t_rprntc;  	/* what is flushc anyway? */ -	tty->termios->c_cc[VEOL2] = tmp.t_flushc; -	tty->termios->c_cc[VWERASE] = tmp.t_werasc; -	tty->termios->c_cc[VLNEXT] = tmp.t_lnextc; -	mutex_unlock(&tty->termios_mutex); +	tty->termios.c_cc[VEOL2] = tmp.t_flushc; +	tty->termios.c_cc[VWERASE] = tmp.t_werasc; +	tty->termios.c_cc[VLNEXT] = tmp.t_lnextc; +	up_write(&tty->termios_rwsem);  	return 0;  }  #endif @@ -910,7 +946,7 @@ static int send_prio_char(struct tty_struct *tty, char ch)   *	@arg: enable/disable CLOCAL   *   *	Perform a change to the CLOCAL state and call into the driver - *	layer to make it visible. All done with the termios mutex + *	layer to make it visible. All done with the termios rwsem   */  static int tty_change_softcar(struct tty_struct *tty, int arg) @@ -919,15 +955,15 @@ static int tty_change_softcar(struct tty_struct *tty, int arg)  	int bit = arg ? CLOCAL : 0;  	struct ktermios old; -	mutex_lock(&tty->termios_mutex); -	old = *tty->termios; -	tty->termios->c_cflag &= ~CLOCAL; -	tty->termios->c_cflag |= bit; +	down_write(&tty->termios_rwsem); +	old = tty->termios; +	tty->termios.c_cflag &= ~CLOCAL; +	tty->termios.c_cflag |= bit;  	if (tty->ops->set_termios)  		tty->ops->set_termios(tty, &old); -	if ((tty->termios->c_cflag & CLOCAL) != bit) +	if ((tty->termios.c_cflag & CLOCAL) != bit)  		ret = -EINVAL; -	mutex_unlock(&tty->termios_mutex); +	up_write(&tty->termios_rwsem);  	return ret;  } @@ -951,6 +987,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,  	int ret = 0;  	struct ktermios kterm; +	BUG_ON(file == NULL); +  	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&  	    tty->driver->subtype == PTY_TYPE_MASTER)  		real_tty = tty->link; @@ -1028,9 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,  		if (user_termios_to_kernel_termios(&kterm,  					       (struct termios __user *) arg))  			return -EFAULT; -		mutex_lock(&real_tty->termios_mutex); -		memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); -		mutex_unlock(&real_tty->termios_mutex); +		down_write(&real_tty->termios_rwsem); +		real_tty->termios_locked = kterm; +		up_write(&real_tty->termios_rwsem);  		return 0;  #else  	case TIOCGLCKTRMIOS: @@ -1045,9 +1083,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,  		if (user_termios_to_kernel_termios_1(&kterm,  					       (struct termios __user *) arg))  			return -EFAULT; -		mutex_lock(&real_tty->termios_mutex); -		memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); -		mutex_unlock(&real_tty->termios_mutex); +		down_write(&real_tty->termios_rwsem); +		real_tty->termios_locked = kterm; +		up_write(&real_tty->termios_rwsem);  		return ret;  #endif  #ifdef TCGETX @@ -1055,9 +1093,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,  		struct termiox ktermx;  		if (real_tty->termiox == NULL)  			return -EINVAL; -		mutex_lock(&real_tty->termios_mutex); +		down_read(&real_tty->termios_rwsem);  		memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox)); -		mutex_unlock(&real_tty->termios_mutex); +		up_read(&real_tty->termios_rwsem);  		if (copy_to_user(p, &ktermx, sizeof(struct termiox)))  			ret = -EFAULT;  		return ret; @@ -1084,39 +1122,52 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,  }  EXPORT_SYMBOL_GPL(tty_mode_ioctl); -int tty_perform_flush(struct tty_struct *tty, unsigned long arg) + +/* Caller guarantees ldisc reference is held */ +static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg)  { -	struct tty_ldisc *ld; -	int retval = tty_check_change(tty); -	if (retval) -		return retval; +	struct tty_ldisc *ld = tty->ldisc; -	ld = tty_ldisc_ref_wait(tty);  	switch (arg) {  	case TCIFLUSH: -		if (ld && ld->ops->flush_buffer) +		if (ld && ld->ops->flush_buffer) {  			ld->ops->flush_buffer(tty); +			tty_unthrottle(tty); +		}  		break;  	case TCIOFLUSH: -		if (ld && ld->ops->flush_buffer) +		if (ld && ld->ops->flush_buffer) {  			ld->ops->flush_buffer(tty); +			tty_unthrottle(tty); +		}  		/* fall through */  	case TCOFLUSH:  		tty_driver_flush_buffer(tty);  		break;  	default: -		tty_ldisc_deref(ld);  		return -EINVAL;  	} -	tty_ldisc_deref(ld);  	return 0;  } + +int tty_perform_flush(struct tty_struct *tty, unsigned long arg) +{ +	struct tty_ldisc *ld; +	int retval = tty_check_change(tty); +	if (retval) +		return retval; + +	ld = tty_ldisc_ref_wait(tty); +	retval = __tty_perform_flush(tty, arg); +	if (ld) +		tty_ldisc_deref(ld); +	return retval; +}  EXPORT_SYMBOL_GPL(tty_perform_flush);  int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,  		       unsigned int cmd, unsigned long arg)  { -	unsigned long flags;  	int retval;  	switch (cmd) { @@ -1150,30 +1201,29 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,  		}  		return 0;  	case TCFLSH: -		return tty_perform_flush(tty, arg); -	case TIOCPKT: -	{ -		int pktmode; - -		if (tty->driver->type != TTY_DRIVER_TYPE_PTY || -		    tty->driver->subtype != PTY_TYPE_MASTER) -			return -ENOTTY; -		if (get_user(pktmode, (int __user *) arg)) -			return -EFAULT; -		spin_lock_irqsave(&tty->ctrl_lock, flags); -		if (pktmode) { -			if (!tty->packet) { -				tty->packet = 1; -				tty->link->ctrl_status = 0; -			} -		} else -			tty->packet = 0; -		spin_unlock_irqrestore(&tty->ctrl_lock, flags); -		return 0; -	} +		retval = tty_check_change(tty); +		if (retval) +			return retval; +		return __tty_perform_flush(tty, arg);  	default:  		/* Try the mode commands */  		return tty_mode_ioctl(tty, file, cmd, arg);  	}  }  EXPORT_SYMBOL(n_tty_ioctl_helper); + +#ifdef CONFIG_COMPAT +long n_tty_compat_ioctl_helper(struct tty_struct *tty, struct file *file, +					unsigned int cmd, unsigned long arg) +{ +	switch (cmd) { +	case TIOCGLCKTRMIOS: +	case TIOCSLCKTRMIOS: +		return tty_mode_ioctl(tty, file, cmd, (unsigned long) compat_ptr(arg)); +	default: +		return -ENOIOCTLCMD; +	} +} +EXPORT_SYMBOL(n_tty_compat_ioctl_helper); +#endif +  | 
