diff options
| -rw-r--r-- | drivers/tty/tty_ldisc.c | 29 | 
1 files changed, 29 insertions, 0 deletions
| diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 412f9775d19c..5bbf33ad49f1 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -47,6 +47,7 @@  static DEFINE_SPINLOCK(tty_ldisc_lock);  static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); +static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_idle);  /* Line disc dispatch table */  static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; @@ -83,6 +84,7 @@ static void put_ldisc(struct tty_ldisc *ld)  		return;  	}  	local_irq_restore(flags); +	wake_up(&tty_ldisc_idle);  }  /** @@ -531,6 +533,23 @@ static int tty_ldisc_halt(struct tty_struct *tty)  }  /** + *	tty_ldisc_wait_idle	-	wait for the ldisc to become idle + *	@tty: tty to wait for + * + *	Wait for the line discipline to become idle. The discipline must + *	have been halted for this to guarantee it remains idle. + */ +static int tty_ldisc_wait_idle(struct tty_struct *tty) +{ +	int ret; +	ret = wait_event_interruptible_timeout(tty_ldisc_idle, +			atomic_read(&tty->ldisc->users) == 1, 5 * HZ); +	if (ret < 0) +		return ret; +	return ret > 0 ? 0 : -EBUSY; +} + +/**   *	tty_set_ldisc		-	set line discipline   *	@tty: the terminal to set   *	@ldisc: the line discipline @@ -634,8 +653,17 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)  	flush_scheduled_work(); +	retval = tty_ldisc_wait_idle(tty); +  	tty_lock();  	mutex_lock(&tty->ldisc_mutex); + +	/* handle wait idle failure locked */ +	if (retval) { +		tty_ldisc_put(new_ldisc); +		goto enable; +	} +  	if (test_bit(TTY_HUPPED, &tty->flags)) {  		/* We were raced by the hangup method. It will have stomped  		   the ldisc data and closed the ldisc down */ @@ -669,6 +697,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)  	tty_ldisc_put(o_ldisc); +enable:  	/*  	 *	Allow ldisc referencing to occur again  	 */ | 
