diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-09 12:09:24 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-09 12:09:24 -0800 | 
| commit | 5983faf942f260023e547f3c5f38c1033c35cc9b (patch) | |
| tree | f54ce89de5d9f7a05e99948937ac5456df09df30 /drivers/tty/tty_io.c | |
| parent | 21a2cb565a74bf794d343ce22300c5f6c1568ae1 (diff) | |
| parent | 995234da19b927f42722d796e8270384f33be11c (diff) | |
| download | olio-linux-3.10-5983faf942f260023e547f3c5f38c1033c35cc9b.tar.xz olio-linux-3.10-5983faf942f260023e547f3c5f38c1033c35cc9b.zip  | |
Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
* 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (65 commits)
  tty: serial: imx: move del_timer_sync() to avoid potential deadlock
  imx: add polled io uart methods
  imx: Add save/restore functions for UART control regs
  serial/imx: let probing fail for the dt case without a valid alias
  serial/imx: propagate error from of_alias_get_id instead of using -ENODEV
  tty: serial: imx: Allow UART to be a source for wakeup
  serial: driver for m32 arch should not have DEC alpha errata
  serial/documentation: fix documented name of DCD cpp symbol
  atmel_serial: fix spinlock lockup in RS485 code
  tty: Fix memory leak in virtual console when enable unicode translation
  serial: use DIV_ROUND_CLOSEST instead of open coding it
  serial: add support for 400 and 800 v3 series Titan cards
  serial: bfin-uart: Remove ASYNC_CTS_FLOW flag for hardware automatic CTS.
  serial: bfin-uart: Enable hardware automatic CTS only when CTS pin is available.
  serial: make FSL errata depend on 8250_CONSOLE, not just 8250
  serial: add irq handler for Freescale 16550 errata.
  serial: manually inline serial8250_handle_port
  serial: make 8250 timeout use the specified IRQ handler
  serial: export the key functions for an 8250 IRQ handler
  serial: clean up parameter passing for 8250 Rx IRQ handling
  ...
Diffstat (limited to 'drivers/tty/tty_io.c')
| -rw-r--r-- | drivers/tty/tty_io.c | 309 | 
1 files changed, 180 insertions, 129 deletions
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 3fdebd306b9..e41b9bbc107 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -790,19 +790,24 @@ static void session_clear_tty(struct pid *session)  void disassociate_ctty(int on_exit)  {  	struct tty_struct *tty; -	struct pid *tty_pgrp = NULL;  	if (!current->signal->leader)  		return;  	tty = get_current_tty();  	if (tty) { -		tty_pgrp = get_pid(tty->pgrp); +		struct pid *tty_pgrp = get_pid(tty->pgrp);  		if (on_exit) {  			if (tty->driver->type != TTY_DRIVER_TYPE_PTY)  				tty_vhangup(tty);  		}  		tty_kref_put(tty); +		if (tty_pgrp) { +			kill_pgrp(tty_pgrp, SIGHUP, on_exit); +			if (!on_exit) +				kill_pgrp(tty_pgrp, SIGCONT, on_exit); +			put_pid(tty_pgrp); +		}  	} else if (on_exit) {  		struct pid *old_pgrp;  		spin_lock_irq(¤t->sighand->siglock); @@ -816,12 +821,6 @@ void disassociate_ctty(int on_exit)  		}  		return;  	} -	if (tty_pgrp) { -		kill_pgrp(tty_pgrp, SIGHUP, on_exit); -		if (!on_exit) -			kill_pgrp(tty_pgrp, SIGCONT, on_exit); -		put_pid(tty_pgrp); -	}  	spin_lock_irq(¤t->sighand->siglock);  	put_pid(current->signal->tty_old_pgrp); @@ -1558,6 +1557,59 @@ static void release_tty(struct tty_struct *tty, int idx)  }  /** + *	tty_release_checks - check a tty before real release + *	@tty: tty to check + *	@o_tty: link of @tty (if any) + *	@idx: index of the tty + * + *	Performs some paranoid checking before true release of the @tty. + *	This is a no-op unless TTY_PARANOIA_CHECK is defined. + */ +static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, +		int idx) +{ +#ifdef TTY_PARANOIA_CHECK +	if (idx < 0 || idx >= tty->driver->num) { +		printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n", +				__func__, tty->name); +		return -1; +	} + +	/* not much to check for devpts */ +	if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) +		return 0; + +	if (tty != tty->driver->ttys[idx]) { +		printk(KERN_DEBUG "%s: driver.table[%d] not tty for (%s)\n", +				__func__, idx, tty->name); +		return -1; +	} +	if (tty->termios != tty->driver->termios[idx]) { +		printk(KERN_DEBUG "%s: driver.termios[%d] not termios for (%s)\n", +				__func__, idx, tty->name); +		return -1; +	} +	if (tty->driver->other) { +		if (o_tty != tty->driver->other->ttys[idx]) { +			printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n", +					__func__, idx, tty->name); +			return -1; +		} +		if (o_tty->termios != tty->driver->other->termios[idx]) { +			printk(KERN_DEBUG "%s: other->termios[%d] not o_termios for (%s)\n", +					__func__, idx, tty->name); +			return -1; +		} +		if (o_tty->link != tty) { +			printk(KERN_DEBUG "%s: bad pty pointers\n", __func__); +			return -1; +		} +	} +#endif +	return 0; +} + +/**   *	tty_release		-	vfs callback for close   *	@inode: inode of tty   *	@filp: file pointer for handle to tty @@ -1585,11 +1637,11 @@ int tty_release(struct inode *inode, struct file *filp)  	int	idx;  	char	buf[64]; -	if (tty_paranoia_check(tty, inode, "tty_release_dev")) +	if (tty_paranoia_check(tty, inode, __func__))  		return 0;  	tty_lock(); -	check_tty_count(tty, "tty_release_dev"); +	check_tty_count(tty, __func__);  	__tty_fasync(-1, filp, 0); @@ -1599,59 +1651,16 @@ int tty_release(struct inode *inode, struct file *filp)  	devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;  	o_tty = tty->link; -#ifdef TTY_PARANOIA_CHECK -	if (idx < 0 || idx >= tty->driver->num) { -		printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " -				  "free (%s)\n", tty->name); +	if (tty_release_checks(tty, o_tty, idx)) {  		tty_unlock();  		return 0;  	} -	if (!devpts) { -		if (tty != tty->driver->ttys[idx]) { -			tty_unlock(); -			printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " -			       "for (%s)\n", idx, tty->name); -			return 0; -		} -		if (tty->termios != tty->driver->termios[idx]) { -			tty_unlock(); -			printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " -			       "for (%s)\n", -			       idx, tty->name); -			return 0; -		} -	} -#endif  #ifdef TTY_DEBUG_HANGUP -	printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...", -	       tty_name(tty, buf), tty->count); +	printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__, +			tty_name(tty, buf), tty->count);  #endif -#ifdef TTY_PARANOIA_CHECK -	if (tty->driver->other && -	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { -		if (o_tty != tty->driver->other->ttys[idx]) { -			tty_unlock(); -			printk(KERN_DEBUG "tty_release_dev: other->table[%d] " -					  "not o_tty for (%s)\n", -			       idx, tty->name); -			return 0 ; -		} -		if (o_tty->termios != tty->driver->other->termios[idx]) { -			tty_unlock(); -			printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " -					  "not o_termios for (%s)\n", -			       idx, tty->name); -			return 0; -		} -		if (o_tty->link != tty) { -			tty_unlock(); -			printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); -			return 0; -		} -	} -#endif  	if (tty->ops->close)  		tty->ops->close(tty, filp); @@ -1707,8 +1716,8 @@ int tty_release(struct inode *inode, struct file *filp)  		if (!do_sleep)  			break; -		printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " -				    "active!\n", tty_name(tty, buf)); +		printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", +				__func__, tty_name(tty, buf));  		tty_unlock();  		mutex_unlock(&tty_mutex);  		schedule(); @@ -1721,15 +1730,14 @@ int tty_release(struct inode *inode, struct file *filp)  	 */  	if (pty_master) {  		if (--o_tty->count < 0) { -			printk(KERN_WARNING "tty_release_dev: bad pty slave count " -					    "(%d) for %s\n", -			       o_tty->count, tty_name(o_tty, buf)); +			printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", +				__func__, o_tty->count, tty_name(o_tty, buf));  			o_tty->count = 0;  		}  	}  	if (--tty->count < 0) { -		printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n", -		       tty->count, tty_name(tty, buf)); +		printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n", +				__func__, tty->count, tty_name(tty, buf));  		tty->count = 0;  	} @@ -1778,7 +1786,7 @@ int tty_release(struct inode *inode, struct file *filp)  	}  #ifdef TTY_DEBUG_HANGUP -	printk(KERN_DEBUG "freeing tty structure..."); +	printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__);  #endif  	/*  	 * Ask the line discipline code to release its structures @@ -1798,6 +1806,83 @@ int tty_release(struct inode *inode, struct file *filp)  }  /** + *	tty_open_current_tty - get tty of current task for open + *	@device: device number + *	@filp: file pointer to tty + *	@return: tty of the current task iff @device is /dev/tty + * + *	We cannot return driver and index like for the other nodes because + *	devpts will not work then. It expects inodes to be from devpts FS. + */ +static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) +{ +	struct tty_struct *tty; + +	if (device != MKDEV(TTYAUX_MAJOR, 0)) +		return NULL; + +	tty = get_current_tty(); +	if (!tty) +		return ERR_PTR(-ENXIO); + +	filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ +	/* noctty = 1; */ +	tty_kref_put(tty); +	/* FIXME: we put a reference and return a TTY! */ +	return tty; +} + +/** + *	tty_lookup_driver - lookup a tty driver for a given device file + *	@device: device number + *	@filp: file pointer to tty + *	@noctty: set if the device should not become a controlling tty + *	@index: index for the device in the @return driver + *	@return: driver for this inode (with increased refcount) + * + * 	If @return is not erroneous, the caller is responsible to decrement the + * 	refcount by tty_driver_kref_put. + * + *	Locking: tty_mutex protects get_tty_driver + */ +static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, +		int *noctty, int *index) +{ +	struct tty_driver *driver; + +	switch (device) { +#ifdef CONFIG_VT +	case MKDEV(TTY_MAJOR, 0): { +		extern struct tty_driver *console_driver; +		driver = tty_driver_kref_get(console_driver); +		*index = fg_console; +		*noctty = 1; +		break; +	} +#endif +	case MKDEV(TTYAUX_MAJOR, 1): { +		struct tty_driver *console_driver = console_device(index); +		if (console_driver) { +			driver = tty_driver_kref_get(console_driver); +			if (driver) { +				/* Don't let /dev/console block */ +				filp->f_flags |= O_NONBLOCK; +				*noctty = 1; +				break; +			} +		} +		return ERR_PTR(-ENODEV); +	} +	default: +		driver = get_tty_driver(device, index); +		if (!driver) +			return ERR_PTR(-ENODEV); +		break; +	} +	return driver; +} + +/**   *	tty_open		-	open a tty device   *	@inode: inode of device file   *	@filp: file pointer to tty @@ -1813,16 +1898,16 @@ int tty_release(struct inode *inode, struct file *filp)   *	The termios state of a pty is reset on first open so that   *	settings don't persist across reuse.   * - *	Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. + *	Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev.   *		 tty->count should protect the rest.   *		 ->siglock protects ->signal/->sighand   */  static int tty_open(struct inode *inode, struct file *filp)  { -	struct tty_struct *tty = NULL; +	struct tty_struct *tty;  	int noctty, retval; -	struct tty_driver *driver; +	struct tty_driver *driver = NULL;  	int index;  	dev_t device = inode->i_rdev;  	unsigned saved_flags = filp->f_flags; @@ -1841,66 +1926,22 @@ retry_open:  	mutex_lock(&tty_mutex);  	tty_lock(); -	if (device == MKDEV(TTYAUX_MAJOR, 0)) { -		tty = get_current_tty(); -		if (!tty) { -			tty_unlock(); -			mutex_unlock(&tty_mutex); -			tty_free_file(filp); -			return -ENXIO; -		} -		driver = tty_driver_kref_get(tty->driver); -		index = tty->index; -		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ -		/* noctty = 1; */ -		/* FIXME: Should we take a driver reference ? */ -		tty_kref_put(tty); -		goto got_driver; -	} -#ifdef CONFIG_VT -	if (device == MKDEV(TTY_MAJOR, 0)) { -		extern struct tty_driver *console_driver; -		driver = tty_driver_kref_get(console_driver); -		index = fg_console; -		noctty = 1; -		goto got_driver; -	} -#endif -	if (device == MKDEV(TTYAUX_MAJOR, 1)) { -		struct tty_driver *console_driver = console_device(&index); -		if (console_driver) { -			driver = tty_driver_kref_get(console_driver); -			if (driver) { -				/* Don't let /dev/console block */ -				filp->f_flags |= O_NONBLOCK; -				noctty = 1; -				goto got_driver; -			} +	tty = tty_open_current_tty(device, filp); +	if (IS_ERR(tty)) { +		retval = PTR_ERR(tty); +		goto err_unlock; +	} else if (!tty) { +		driver = tty_lookup_driver(device, filp, &noctty, &index); +		if (IS_ERR(driver)) { +			retval = PTR_ERR(driver); +			goto err_unlock;  		} -		tty_unlock(); -		mutex_unlock(&tty_mutex); -		tty_free_file(filp); -		return -ENODEV; -	} -	driver = get_tty_driver(device, &index); -	if (!driver) { -		tty_unlock(); -		mutex_unlock(&tty_mutex); -		tty_free_file(filp); -		return -ENODEV; -	} -got_driver: -	if (!tty) {  		/* check whether we're reopening an existing tty */  		tty = tty_driver_lookup_tty(driver, inode, index); -  		if (IS_ERR(tty)) { -			tty_unlock(); -			mutex_unlock(&tty_mutex); -			tty_driver_kref_put(driver); -			tty_free_file(filp); -			return PTR_ERR(tty); +			retval = PTR_ERR(tty); +			goto err_unlock;  		}  	} @@ -1912,21 +1953,22 @@ got_driver:  		tty = tty_init_dev(driver, index, 0);  	mutex_unlock(&tty_mutex); -	tty_driver_kref_put(driver); +	if (driver) +		tty_driver_kref_put(driver);  	if (IS_ERR(tty)) {  		tty_unlock(); -		tty_free_file(filp); -		return PTR_ERR(tty); +		retval = PTR_ERR(tty); +		goto err_file;  	}  	tty_add_file(tty, filp); -	check_tty_count(tty, "tty_open"); +	check_tty_count(tty, __func__);  	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&  	    tty->driver->subtype == PTY_TYPE_MASTER)  		noctty = 1;  #ifdef TTY_DEBUG_HANGUP -	printk(KERN_DEBUG "opening %s...", tty->name); +	printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name);  #endif  	if (tty->ops->open)  		retval = tty->ops->open(tty, filp); @@ -1940,8 +1982,8 @@ got_driver:  	if (retval) {  #ifdef TTY_DEBUG_HANGUP -		printk(KERN_DEBUG "error %d in opening %s...", retval, -		       tty->name); +		printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, +				retval, tty->name);  #endif  		tty_unlock(); /* need to call tty_release without BTM */  		tty_release(inode, filp); @@ -1976,6 +2018,15 @@ got_driver:  	tty_unlock();  	mutex_unlock(&tty_mutex);  	return 0; +err_unlock: +	tty_unlock(); +	mutex_unlock(&tty_mutex); +	/* after locks to avoid deadlock */ +	if (!IS_ERR_OR_NULL(driver)) +		tty_driver_kref_put(driver); +err_file: +	tty_free_file(filp); +	return retval;  }  |