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

Commit 04f378b1 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds
Browse files

tty: BKL pushdown



- Push the BKL down into the line disciplines
- Switch the tty layer to unlocked_ioctl
- Introduce a new ctrl_lock spin lock for the control bits
- Eliminate much of the lock_kernel use in n_tty
- Prepare to (but don't yet) call the drivers with the lock dropped
  on the paths that historically held the lock

BKL now primarily protects open/close/ldisc change in the tty layer

[jirislaby@gmail.com: a couple of fixes]
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarJiri Slaby <jirislaby@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e5238442
Loading
Loading
Loading
Loading
+18 −6
Original line number Original line Diff line number Diff line
@@ -578,27 +578,37 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
		return -EFAULT;
		return -EFAULT;
	}
	}


	lock_kernel();

	for (;;) {
	for (;;) {
		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
		if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
			unlock_kernel();
			return -EIO;
			return -EIO;
		}


		n_hdlc = tty2n_hdlc (tty);
		n_hdlc = tty2n_hdlc (tty);
		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
			 tty != n_hdlc->tty)
			 tty != n_hdlc->tty) {
			unlock_kernel();
			return 0;
			return 0;
		}


		rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
		rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
		if (rbuf)
		if (rbuf)
			break;
			break;
			
			
		/* no data */
		/* no data */
		if (file->f_flags & O_NONBLOCK)
		if (file->f_flags & O_NONBLOCK) {
			unlock_kernel();
			return -EAGAIN;
			return -EAGAIN;
		}
			
			
		interruptible_sleep_on (&tty->read_wait);
		interruptible_sleep_on (&tty->read_wait);
		if (signal_pending(current))
		if (signal_pending(current)) {
			unlock_kernel();
			return -EINTR;
			return -EINTR;
		}
		}
	}
		
		
	if (rbuf->count > nr)
	if (rbuf->count > nr)
		/* frame too large for caller's buffer (discard frame) */
		/* frame too large for caller's buffer (discard frame) */
@@ -618,7 +628,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
		kfree(rbuf);
		kfree(rbuf);
	else	
	else	
		n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
		n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
	
	unlock_kernel();
	return ret;
	return ret;
	
	
}	/* end of n_hdlc_tty_read() */
}	/* end of n_hdlc_tty_read() */
@@ -661,6 +671,8 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
		count = maxframe;
		count = maxframe;
	}
	}
	
	
	lock_kernel();

	add_wait_queue(&tty->write_wait, &wait);
	add_wait_queue(&tty->write_wait, &wait);
	set_current_state(TASK_INTERRUPTIBLE);
	set_current_state(TASK_INTERRUPTIBLE);
	
	
@@ -695,7 +707,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
		n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
		n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
		n_hdlc_send_frames(n_hdlc,tty);
		n_hdlc_send_frames(n_hdlc,tty);
	}
	}

	unlock_kernel();
	return error;
	return error;
	
	
}	/* end of n_hdlc_tty_write() */
}	/* end of n_hdlc_tty_write() */
+14 −2
Original line number Original line Diff line number Diff line
@@ -1075,12 +1075,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,


	TRACE_L("read()");
	TRACE_L("read()");


	lock_kernel();

	pClient = findClient(pInfo, task_pid(current));
	pClient = findClient(pInfo, task_pid(current));
	if (pClient) {
	if (pClient) {
		pMsg = remove_msg(pInfo, pClient);
		pMsg = remove_msg(pInfo, pClient);
		if (pMsg == NULL) {
		if (pMsg == NULL) {
			/* no messages available. */
			/* no messages available. */
			if (file->f_flags & O_NONBLOCK) {
			if (file->f_flags & O_NONBLOCK) {
				unlock_kernel();
				return -EAGAIN;
				return -EAGAIN;
			}
			}
			/* block until there is a message: */
			/* block until there is a message: */
@@ -1090,8 +1093,10 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,


		/* If we still haven't got a message, we must have been signalled */
		/* If we still haven't got a message, we must have been signalled */


		if (!pMsg)
		if (!pMsg) {
			unlock_kernel();
			return -EINTR;
			return -EINTR;
		}


		/* deliver msg to client process: */
		/* deliver msg to client process: */
		theMsg.msg_id = pMsg->msg_id;
		theMsg.msg_id = pMsg->msg_id;
@@ -1102,12 +1107,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
		kfree(pMsg);
		kfree(pMsg);
		TRACE_M("r3964_read - msg kfree %p", pMsg);
		TRACE_M("r3964_read - msg kfree %p", pMsg);


		if (copy_to_user(buf, &theMsg, count))
		if (copy_to_user(buf, &theMsg, count)) {
			unlock_kernel();
			return -EFAULT;
			return -EFAULT;
		}


		TRACE_PS("read - return %d", count);
		TRACE_PS("read - return %d", count);
		return count;
		return count;
	}
	}
	unlock_kernel();
	return -EPERM;
	return -EPERM;
}
}


@@ -1156,6 +1164,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
	pHeader->locks = 0;
	pHeader->locks = 0;
	pHeader->owner = NULL;
	pHeader->owner = NULL;


	lock_kernel();

	pClient = findClient(pInfo, task_pid(current));
	pClient = findClient(pInfo, task_pid(current));
	if (pClient) {
	if (pClient) {
		pHeader->owner = pClient;
		pHeader->owner = pClient;
@@ -1173,6 +1183,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
	add_tx_queue(pInfo, pHeader);
	add_tx_queue(pInfo, pHeader);
	trigger_transmit(pInfo);
	trigger_transmit(pInfo);


	unlock_kernel();

	return 0;
	return 0;
}
}


+25 −7
Original line number Original line Diff line number Diff line
@@ -183,22 +183,24 @@ static void reset_buffer_flags(struct tty_struct *tty)
 *	at hangup) or when the N_TTY line discipline internally has to
 *	at hangup) or when the N_TTY line discipline internally has to
 *	clean the pending queue (for example some signals).
 *	clean the pending queue (for example some signals).
 *
 *
 *	FIXME: tty->ctrl_status is not spinlocked and relies on
 *	Locking: ctrl_lock
 *	lock_kernel() still.
 */
 */


static void n_tty_flush_buffer(struct tty_struct *tty)
static void n_tty_flush_buffer(struct tty_struct *tty)
{
{
	unsigned long flags;
	/* clear everything and unthrottle the driver */
	/* clear everything and unthrottle the driver */
	reset_buffer_flags(tty);
	reset_buffer_flags(tty);


	if (!tty->link)
	if (!tty->link)
		return;
		return;


	spin_lock_irqsave(&tty->ctrl_lock, flags);
	if (tty->link->packet) {
	if (tty->link->packet) {
		tty->ctrl_status |= TIOCPKT_FLUSHREAD;
		tty->ctrl_status |= TIOCPKT_FLUSHREAD;
		wake_up_interruptible(&tty->link->read_wait);
		wake_up_interruptible(&tty->link->read_wait);
	}
	}
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
}
}


/**
/**
@@ -264,7 +266,7 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty)
 *	relevant in the world today. If you ever need them, add them here.
 *	relevant in the world today. If you ever need them, add them here.
 *
 *
 *	Called from both the receive and transmit sides and can be called
 *	Called from both the receive and transmit sides and can be called
 *	re-entrantly. Relies on lock_kernel() still.
 *	re-entrantly. Relies on lock_kernel() for tty->column state.
 */
 */


static int opost(unsigned char c, struct tty_struct *tty)
static int opost(unsigned char c, struct tty_struct *tty)
@@ -275,6 +277,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
	if (!space)
	if (!space)
		return -1;
		return -1;


	lock_kernel();
	if (O_OPOST(tty)) {
	if (O_OPOST(tty)) {
		switch (c) {
		switch (c) {
		case '\n':
		case '\n':
@@ -323,6 +326,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
		}
		}
	}
	}
	tty->driver->put_char(tty, c);
	tty->driver->put_char(tty, c);
	unlock_kernel();
	return 0;
	return 0;
}
}


@@ -337,7 +341,8 @@ static int opost(unsigned char c, struct tty_struct *tty)
 *	the simple cases normally found and helps to generate blocks of
 *	the simple cases normally found and helps to generate blocks of
 *	symbols for the console driver and thus improve performance.
 *	symbols for the console driver and thus improve performance.
 *
 *
 *	Called from write_chan under the tty layer write lock.
 *	Called from write_chan under the tty layer write lock. Relies
 *	on lock_kernel for the tty->column state.
 */
 */


static ssize_t opost_block(struct tty_struct *tty,
static ssize_t opost_block(struct tty_struct *tty,
@@ -353,6 +358,7 @@ static ssize_t opost_block(struct tty_struct *tty,
	if (nr > space)
	if (nr > space)
		nr = space;
		nr = space;


	lock_kernel();
	for (i = 0, cp = buf; i < nr; i++, cp++) {
	for (i = 0, cp = buf; i < nr; i++, cp++) {
		switch (*cp) {
		switch (*cp) {
		case '\n':
		case '\n':
@@ -387,6 +393,7 @@ static ssize_t opost_block(struct tty_struct *tty,
	if (tty->driver->flush_chars)
	if (tty->driver->flush_chars)
		tty->driver->flush_chars(tty);
		tty->driver->flush_chars(tty);
	i = tty->driver->write(tty, buf, i);
	i = tty->driver->write(tty, buf, i);
	unlock_kernel();
	return i;
	return i;
}
}


@@ -1194,6 +1201,11 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *,
 *	Perform job control management checks on this file/tty descriptor
 *	Perform job control management checks on this file/tty descriptor
 *	and if appropriate send any needed signals and return a negative
 *	and if appropriate send any needed signals and return a negative
 *	error code if action should be taken.
 *	error code if action should be taken.
 *
 *	FIXME:
 *	Locking: None - redirected write test is safe, testing
 *	current->signal should possibly lock current->sighand
 *	pgrp locking ?
 */
 */


static int job_control(struct tty_struct *tty, struct file *file)
static int job_control(struct tty_struct *tty, struct file *file)
@@ -1246,6 +1258,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
	ssize_t size;
	ssize_t size;
	long timeout;
	long timeout;
	unsigned long flags;
	unsigned long flags;
	int packet;


do_it_again:
do_it_again:


@@ -1289,16 +1302,19 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
		if (mutex_lock_interruptible(&tty->atomic_read_lock))
		if (mutex_lock_interruptible(&tty->atomic_read_lock))
			return -ERESTARTSYS;
			return -ERESTARTSYS;
	}
	}
	packet = tty->packet;


	add_wait_queue(&tty->read_wait, &wait);
	add_wait_queue(&tty->read_wait, &wait);
	while (nr) {
	while (nr) {
		/* First test for status change. */
		/* First test for status change. */
		if (tty->packet && tty->link->ctrl_status) {
		if (packet && tty->link->ctrl_status) {
			unsigned char cs;
			unsigned char cs;
			if (b != buf)
			if (b != buf)
				break;
				break;
			spin_lock_irqsave(&tty->link->ctrl_lock, flags);
			cs = tty->link->ctrl_status;
			cs = tty->link->ctrl_status;
			tty->link->ctrl_status = 0;
			tty->link->ctrl_status = 0;
			spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
			if (tty_put_user(tty, cs, b++)) {
			if (tty_put_user(tty, cs, b++)) {
				retval = -EFAULT;
				retval = -EFAULT;
				b--;
				b--;
@@ -1333,6 +1349,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
				retval = -ERESTARTSYS;
				retval = -ERESTARTSYS;
				break;
				break;
			}
			}
			/* FIXME: does n_tty_set_room need locking ? */
			n_tty_set_room(tty);
			n_tty_set_room(tty);
			timeout = schedule_timeout(timeout);
			timeout = schedule_timeout(timeout);
			continue;
			continue;
@@ -1340,7 +1357,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
		__set_current_state(TASK_RUNNING);
		__set_current_state(TASK_RUNNING);


		/* Deal with packet mode. */
		/* Deal with packet mode. */
		if (tty->packet && b == buf) {
		if (packet && b == buf) {
			if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
			if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
				retval = -EFAULT;
				retval = -EFAULT;
				b--;
				b--;
@@ -1388,6 +1405,8 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
				break;
				break;
		} else {
		} else {
			int uncopied;
			int uncopied;
			/* The copy function takes the read lock and handles
			   locking internally for this case */
			uncopied = copy_from_read_buf(tty, &b, &nr);
			uncopied = copy_from_read_buf(tty, &b, &nr);
			uncopied += copy_from_read_buf(tty, &b, &nr);
			uncopied += copy_from_read_buf(tty, &b, &nr);
			if (uncopied) {
			if (uncopied) {
@@ -1429,7 +1448,6 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
		 goto do_it_again;
		 goto do_it_again;


	n_tty_set_room(tty);
	n_tty_set_room(tty);

	return retval;
	return retval;
}
}


+3 −0
Original line number Original line Diff line number Diff line
@@ -181,6 +181,7 @@ static int pty_set_lock(struct tty_struct *tty, int __user * arg)
static void pty_flush_buffer(struct tty_struct *tty)
static void pty_flush_buffer(struct tty_struct *tty)
{
{
	struct tty_struct *to = tty->link;
	struct tty_struct *to = tty->link;
	unsigned long flags;
	
	
	if (!to)
	if (!to)
		return;
		return;
@@ -189,8 +190,10 @@ static void pty_flush_buffer(struct tty_struct *tty)
		to->ldisc.flush_buffer(to);
		to->ldisc.flush_buffer(to);
	
	
	if (to->packet) {
	if (to->packet) {
		spin_lock_irqsave(&tty->ctrl_lock, flags);
		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
		wake_up_interruptible(&to->read_wait);
		wake_up_interruptible(&to->read_wait);
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	}
	}
}
}


+74 −33
Original line number Original line Diff line number Diff line
@@ -152,8 +152,7 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
static unsigned int tty_poll(struct file *, poll_table *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
static int tty_open(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *);
int tty_ioctl(struct inode *inode, struct file *file,
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
	      unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
#ifdef CONFIG_COMPAT
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg);
				unsigned long arg);
@@ -1205,7 +1204,7 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver);
 *	not in the foreground, send a SIGTTOU.  If the signal is blocked or
 *	not in the foreground, send a SIGTTOU.  If the signal is blocked or
 *	ignored, go ahead and perform the operation.  (POSIX 7.2)
 *	ignored, go ahead and perform the operation.  (POSIX 7.2)
 *
 *
 *	Locking: none
 *	Locking: none - FIXME: review this
 */
 */


int tty_check_change(struct tty_struct *tty)
int tty_check_change(struct tty_struct *tty)
@@ -1247,8 +1246,8 @@ static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
}
}


static int hung_up_tty_ioctl(struct inode *inode, struct file *file,
static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
			     unsigned int cmd, unsigned long arg)
		unsigned long arg)
{
{
	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
}
}
@@ -1264,7 +1263,7 @@ static const struct file_operations tty_fops = {
	.read		= tty_read,
	.read		= tty_read,
	.write		= tty_write,
	.write		= tty_write,
	.poll		= tty_poll,
	.poll		= tty_poll,
	.ioctl		= tty_ioctl,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= tty_open,
	.open		= tty_open,
	.release	= tty_release,
	.release	= tty_release,
@@ -1277,7 +1276,7 @@ static const struct file_operations ptmx_fops = {
	.read		= tty_read,
	.read		= tty_read,
	.write		= tty_write,
	.write		= tty_write,
	.poll		= tty_poll,
	.poll		= tty_poll,
	.ioctl		= tty_ioctl,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= ptmx_open,
	.open		= ptmx_open,
	.release	= tty_release,
	.release	= tty_release,
@@ -1290,7 +1289,7 @@ static const struct file_operations console_fops = {
	.read		= tty_read,
	.read		= tty_read,
	.write		= redirected_tty_write,
	.write		= redirected_tty_write,
	.poll		= tty_poll,
	.poll		= tty_poll,
	.ioctl		= tty_ioctl,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= tty_open,
	.open		= tty_open,
	.release	= tty_release,
	.release	= tty_release,
@@ -1302,7 +1301,7 @@ static const struct file_operations hung_up_tty_fops = {
	.read		= hung_up_tty_read,
	.read		= hung_up_tty_read,
	.write		= hung_up_tty_write,
	.write		= hung_up_tty_write,
	.poll		= hung_up_tty_poll,
	.poll		= hung_up_tty_poll,
	.ioctl		= hung_up_tty_ioctl,
	.unlocked_ioctl	= hung_up_tty_ioctl,
	.compat_ioctl	= hung_up_tty_compat_ioctl,
	.compat_ioctl	= hung_up_tty_compat_ioctl,
	.release	= tty_release,
	.release	= tty_release,
};
};
@@ -1626,16 +1625,17 @@ void disassociate_ctty(int on_exit)
	struct tty_struct *tty;
	struct tty_struct *tty;
	struct pid *tty_pgrp = NULL;
	struct pid *tty_pgrp = NULL;


	lock_kernel();


	mutex_lock(&tty_mutex);
	mutex_lock(&tty_mutex);
	tty = get_current_tty();
	tty = get_current_tty();
	if (tty) {
	if (tty) {
		tty_pgrp = get_pid(tty->pgrp);
		tty_pgrp = get_pid(tty->pgrp);
		mutex_unlock(&tty_mutex);
		mutex_unlock(&tty_mutex);
		lock_kernel();
		/* XXX: here we race, there is nothing protecting tty */
		/* XXX: here we race, there is nothing protecting tty */
		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
			tty_vhangup(tty);
			tty_vhangup(tty);
		unlock_kernel();
	} else if (on_exit) {
	} else if (on_exit) {
		struct pid *old_pgrp;
		struct pid *old_pgrp;
		spin_lock_irq(&current->sighand->siglock);
		spin_lock_irq(&current->sighand->siglock);
@@ -1648,7 +1648,6 @@ void disassociate_ctty(int on_exit)
			put_pid(old_pgrp);
			put_pid(old_pgrp);
		}
		}
		mutex_unlock(&tty_mutex);
		mutex_unlock(&tty_mutex);
		unlock_kernel();
		return;
		return;
	}
	}
	if (tty_pgrp) {
	if (tty_pgrp) {
@@ -1683,7 +1682,6 @@ void disassociate_ctty(int on_exit)
	read_lock(&tasklist_lock);
	read_lock(&tasklist_lock);
	session_clear_tty(task_session(current));
	session_clear_tty(task_session(current));
	read_unlock(&tasklist_lock);
	read_unlock(&tasklist_lock);
	unlock_kernel();
}
}


/**
/**
@@ -1693,8 +1691,10 @@ void disassociate_ctty(int on_exit)
void no_tty(void)
void no_tty(void)
{
{
	struct task_struct *tsk = current;
	struct task_struct *tsk = current;
	lock_kernel();
	if (tsk->signal->leader)
	if (tsk->signal->leader)
		disassociate_ctty(0);
		disassociate_ctty(0);
	unlock_kernel();
	proc_clear_tty(tsk);
	proc_clear_tty(tsk);
}
}


@@ -1714,19 +1714,24 @@ void no_tty(void)
 *	but not always.
 *	but not always.
 *
 *
 *	Locking:
 *	Locking:
 *		Broken. Relies on BKL which is unsafe here.
 *		Uses the tty control lock internally
 */
 */


void stop_tty(struct tty_struct *tty)
void stop_tty(struct tty_struct *tty)
{
{
	if (tty->stopped)
	unsigned long flags;
	spin_lock_irqsave(&tty->ctrl_lock, flags);
	if (tty->stopped) {
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
		return;
		return;
	}
	tty->stopped = 1;
	tty->stopped = 1;
	if (tty->link && tty->link->packet) {
	if (tty->link && tty->link->packet) {
		tty->ctrl_status &= ~TIOCPKT_START;
		tty->ctrl_status &= ~TIOCPKT_START;
		tty->ctrl_status |= TIOCPKT_STOP;
		tty->ctrl_status |= TIOCPKT_STOP;
		wake_up_interruptible(&tty->link->read_wait);
		wake_up_interruptible(&tty->link->read_wait);
	}
	}
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	if (tty->driver->stop)
	if (tty->driver->stop)
		(tty->driver->stop)(tty);
		(tty->driver->stop)(tty);
}
}
@@ -1743,19 +1748,24 @@ EXPORT_SYMBOL(stop_tty);
 *	driver start method is invoked and the line discipline woken.
 *	driver start method is invoked and the line discipline woken.
 *
 *
 *	Locking:
 *	Locking:
 *		Broken. Relies on BKL which is unsafe here.
 *		ctrl_lock
 */
 */


void start_tty(struct tty_struct *tty)
void start_tty(struct tty_struct *tty)
{
{
	if (!tty->stopped || tty->flow_stopped)
	unsigned long flags;
	spin_lock_irqsave(&tty->ctrl_lock, flags);
	if (!tty->stopped || tty->flow_stopped) {
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
		return;
		return;
	}
	tty->stopped = 0;
	tty->stopped = 0;
	if (tty->link && tty->link->packet) {
	if (tty->link && tty->link->packet) {
		tty->ctrl_status &= ~TIOCPKT_STOP;
		tty->ctrl_status &= ~TIOCPKT_STOP;
		tty->ctrl_status |= TIOCPKT_START;
		tty->ctrl_status |= TIOCPKT_START;
		wake_up_interruptible(&tty->link->read_wait);
		wake_up_interruptible(&tty->link->read_wait);
	}
	}
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
	if (tty->driver->start)
	if (tty->driver->start)
		(tty->driver->start)(tty);
		(tty->driver->start)(tty);
	/* If we have a running line discipline it may need kicking */
	/* If we have a running line discipline it may need kicking */
@@ -1799,13 +1809,11 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
	/* We want to wait for the line discipline to sort out in this
	/* We want to wait for the line discipline to sort out in this
	   situation */
	   situation */
	ld = tty_ldisc_ref_wait(tty);
	ld = tty_ldisc_ref_wait(tty);
	lock_kernel();
	if (ld->read)
	if (ld->read)
		i = (ld->read)(tty, file, buf, count);
		i = (ld->read)(tty, file, buf, count);
	else
	else
		i = -EIO;
		i = -EIO;
	tty_ldisc_deref(ld);
	tty_ldisc_deref(ld);
	unlock_kernel();
	if (i > 0)
	if (i > 0)
		inode->i_atime = current_fs_time(inode->i_sb);
		inode->i_atime = current_fs_time(inode->i_sb);
	return i;
	return i;
@@ -1893,9 +1901,7 @@ static inline ssize_t do_tty_write(
		ret = -EFAULT;
		ret = -EFAULT;
		if (copy_from_user(tty->write_buf, buf, size))
		if (copy_from_user(tty->write_buf, buf, size))
			break;
			break;
		lock_kernel();
		ret = write(tty, file, tty->write_buf, size);
		ret = write(tty, file, tty->write_buf, size);
		unlock_kernel();
		if (ret <= 0)
		if (ret <= 0)
			break;
			break;
		written += ret;
		written += ret;
@@ -3070,10 +3076,13 @@ static int fionbio(struct file *file, int __user *p)
	if (get_user(nonblock, p))
	if (get_user(nonblock, p))
		return -EFAULT;
		return -EFAULT;


	/* file->f_flags is still BKL protected in the fs layer - vomit */
	lock_kernel();
	if (nonblock)
	if (nonblock)
		file->f_flags |= O_NONBLOCK;
		file->f_flags |= O_NONBLOCK;
	else
	else
		file->f_flags &= ~O_NONBLOCK;
		file->f_flags &= ~O_NONBLOCK;
	unlock_kernel();
	return 0;
	return 0;
}
}


@@ -3162,7 +3171,7 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
 *	Set the process group of the tty to the session passed. Only
 *	Set the process group of the tty to the session passed. Only
 *	permitted where the tty session is our session.
 *	permitted where the tty session is our session.
 *
 *
 *	Locking: None
 *	Locking: RCU
 */
 */


static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
@@ -3237,10 +3246,16 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _
static int tiocsetd(struct tty_struct *tty, int __user *p)
static int tiocsetd(struct tty_struct *tty, int __user *p)
{
{
	int ldisc;
	int ldisc;
	int ret;


	if (get_user(ldisc, p))
	if (get_user(ldisc, p))
		return -EFAULT;
		return -EFAULT;
	return tty_set_ldisc(tty, ldisc);

	lock_kernel();
	ret = tty_set_ldisc(tty, ldisc);
	unlock_kernel();

	return ret;
}
}


/**
/**
@@ -3258,16 +3273,21 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)


static int send_break(struct tty_struct *tty, unsigned int duration)
static int send_break(struct tty_struct *tty, unsigned int duration)
{
{
	int retval = -EINTR;

	lock_kernel();
	if (tty_write_lock(tty, 0) < 0)
	if (tty_write_lock(tty, 0) < 0)
		return -EINTR;
		goto out;
	tty->driver->break_ctl(tty, -1);
	tty->driver->break_ctl(tty, -1);
	if (!signal_pending(current))
	if (!signal_pending(current))
		msleep_interruptible(duration);
		msleep_interruptible(duration);
	tty->driver->break_ctl(tty, 0);
	tty->driver->break_ctl(tty, 0);
	tty_write_unlock(tty);
	tty_write_unlock(tty);
	if (signal_pending(current))
	if (!signal_pending(current))
		return -EINTR;
		retval = 0;
	return 0;
out:
	unlock_kernel();
	return retval;
}
}


/**
/**
@@ -3287,7 +3307,9 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
	int retval = -EINVAL;
	int retval = -EINVAL;


	if (tty->driver->tiocmget) {
	if (tty->driver->tiocmget) {
		lock_kernel();
		retval = tty->driver->tiocmget(tty, file);
		retval = tty->driver->tiocmget(tty, file);
		unlock_kernel();


		if (retval >= 0)
		if (retval >= 0)
			retval = put_user(retval, p);
			retval = put_user(retval, p);
@@ -3337,7 +3359,9 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
		set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
		set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
		clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
		clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;


		lock_kernel();
		retval = tty->driver->tiocmset(tty, file, set, clear);
		retval = tty->driver->tiocmset(tty, file, set, clear);
		unlock_kernel();
	}
	}
	return retval;
	return retval;
}
}
@@ -3345,20 +3369,18 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
/*
/*
 * Split this up, as gcc can choke on it otherwise..
 * Split this up, as gcc can choke on it otherwise..
 */
 */
int tty_ioctl(struct inode *inode, struct file *file,
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
	      unsigned int cmd, unsigned long arg)
{
{
	struct tty_struct *tty, *real_tty;
	struct tty_struct *tty, *real_tty;
	void __user *p = (void __user *)arg;
	void __user *p = (void __user *)arg;
	int retval;
	int retval;
	struct tty_ldisc *ld;
	struct tty_ldisc *ld;
	struct inode *inode = file->f_dentry->d_inode;


	tty = (struct tty_struct *)file->private_data;
	tty = (struct tty_struct *)file->private_data;
	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
		return -EINVAL;
		return -EINVAL;


	/* CHECKME: is this safe as one end closes ? */

	real_tty = tty;
	real_tty = tty;
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
	    tty->driver->subtype == PTY_TYPE_MASTER)
	    tty->driver->subtype == PTY_TYPE_MASTER)
@@ -3367,13 +3389,19 @@ int tty_ioctl(struct inode *inode, struct file *file,
	/*
	/*
	 * Break handling by driver
	 * Break handling by driver
	 */
	 */

	retval = -EINVAL;

	if (!tty->driver->break_ctl) {
	if (!tty->driver->break_ctl) {
		switch (cmd) {
		switch (cmd) {
		case TIOCSBRK:
		case TIOCSBRK:
		case TIOCCBRK:
		case TIOCCBRK:
			if (tty->driver->ioctl)
			if (tty->driver->ioctl) {
				return tty->driver->ioctl(tty, file, cmd, arg);
				lock_kernel();
			return -EINVAL;
				retval = tty->driver->ioctl(tty, file, cmd, arg);
				unlock_kernel();
			}
			return retval;


		/* These two ioctl's always return success; even if */
		/* These two ioctl's always return success; even if */
		/* the driver doesn't support them. */
		/* the driver doesn't support them. */
@@ -3381,7 +3409,9 @@ int tty_ioctl(struct inode *inode, struct file *file,
		case TCSBRKP:
		case TCSBRKP:
			if (!tty->driver->ioctl)
			if (!tty->driver->ioctl)
				return 0;
				return 0;
			lock_kernel();
			retval = tty->driver->ioctl(tty, file, cmd, arg);
			retval = tty->driver->ioctl(tty, file, cmd, arg);
			unlock_kernel();
			if (retval == -ENOIOCTLCMD)
			if (retval == -ENOIOCTLCMD)
				retval = 0;
				retval = 0;
			return retval;
			return retval;
@@ -3401,7 +3431,9 @@ int tty_ioctl(struct inode *inode, struct file *file,
		if (retval)
		if (retval)
			return retval;
			return retval;
		if (cmd != TIOCCBRK) {
		if (cmd != TIOCCBRK) {
			lock_kernel();
			tty_wait_until_sent(tty, 0);
			tty_wait_until_sent(tty, 0);
			unlock_kernel();
			if (signal_pending(current))
			if (signal_pending(current))
				return -EINTR;
				return -EINTR;
		}
		}
@@ -3451,11 +3483,15 @@ int tty_ioctl(struct inode *inode, struct file *file,
	 * Break handling
	 * Break handling
	 */
	 */
	case TIOCSBRK:	/* Turn break on, unconditionally */
	case TIOCSBRK:	/* Turn break on, unconditionally */
		lock_kernel();
		tty->driver->break_ctl(tty, -1);
		tty->driver->break_ctl(tty, -1);
		unlock_kernel();
		return 0;
		return 0;


	case TIOCCBRK:	/* Turn break off, unconditionally */
	case TIOCCBRK:	/* Turn break off, unconditionally */
		lock_kernel();
		tty->driver->break_ctl(tty, 0);
		tty->driver->break_ctl(tty, 0);
		unlock_kernel();
		return 0;
		return 0;
	case TCSBRK:   /* SVID version: non-zero arg --> no break */
	case TCSBRK:   /* SVID version: non-zero arg --> no break */
		/* non-zero arg means wait for all output data
		/* non-zero arg means wait for all output data
@@ -3485,14 +3521,18 @@ int tty_ioctl(struct inode *inode, struct file *file,
		break;
		break;
	}
	}
	if (tty->driver->ioctl) {
	if (tty->driver->ioctl) {
		lock_kernel();
		retval = (tty->driver->ioctl)(tty, file, cmd, arg);
		retval = (tty->driver->ioctl)(tty, file, cmd, arg);
		unlock_kernel();
		if (retval != -ENOIOCTLCMD)
		if (retval != -ENOIOCTLCMD)
			return retval;
			return retval;
	}
	}
	ld = tty_ldisc_ref_wait(tty);
	ld = tty_ldisc_ref_wait(tty);
	retval = -EINVAL;
	retval = -EINVAL;
	if (ld->ioctl) {
	if (ld->ioctl) {
		lock_kernel();
		retval = ld->ioctl(tty, file, cmd, arg);
		retval = ld->ioctl(tty, file, cmd, arg);
		unlock_kernel();
		if (retval == -ENOIOCTLCMD)
		if (retval == -ENOIOCTLCMD)
			retval = -EINVAL;
			retval = -EINVAL;
	}
	}
@@ -3770,6 +3810,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
	mutex_init(&tty->atomic_read_lock);
	mutex_init(&tty->atomic_read_lock);
	mutex_init(&tty->atomic_write_lock);
	mutex_init(&tty->atomic_write_lock);
	spin_lock_init(&tty->read_lock);
	spin_lock_init(&tty->read_lock);
	spin_lock_init(&tty->ctrl_lock);
	INIT_LIST_HEAD(&tty->tty_files);
	INIT_LIST_HEAD(&tty->tty_files);
	INIT_WORK(&tty->SAK_work, do_SAK_work);
	INIT_WORK(&tty->SAK_work, do_SAK_work);
}
}
Loading