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

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

tty: Extract various bits of ldisc code



Before trying to tackle the ldisc bugs the code needs to be a good deal
more readable, so do the simple extractions of routines first.

Signed-off-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5f0878ac
Loading
Loading
Loading
Loading
+19 −5
Original line number Diff line number Diff line
@@ -2481,6 +2481,24 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
	return tty->ops->tiocmset(tty, file, set, clear);
}

struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
{
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
	    tty->driver->subtype == PTY_TYPE_MASTER)
		tty = tty->link;
	return tty;
}
EXPORT_SYMBOL(tty_pair_get_tty);

struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
{
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
	    tty->driver->subtype == PTY_TYPE_MASTER)
	    return tty;
	return tty->link;
}
EXPORT_SYMBOL(tty_pair_get_pty);

/*
 * Split this up, as gcc can choke on it otherwise..
 */
@@ -2496,11 +2514,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
		return -EINVAL;

	real_tty = tty;
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
	    tty->driver->subtype == PTY_TYPE_MASTER)
		real_tty = tty->link;

	real_tty = tty_pair_get_tty(tty);

	/*
	 * Factor out some common prep work
+66 −31
Original line number Diff line number Diff line
@@ -443,6 +443,50 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
	}
}

/**
 *	tty_ldisc_halt		-	shutdown the line discipline
 *	@tty: tty device
 *
 *	Shut down the line discipline and work queue for this tty device.
 *	The TTY_LDISC flag being cleared ensures no further references can
 *	be obtained while the delayed work queue halt ensures that no more
 *	data is fed to the ldisc.
 *
 *	In order to wait for any existing references to complete see 
 *	tty_ldisc_wait_idle.
 */

static void tty_ldisc_halt(struct tty_struct *tty)
{
	clear_bit(TTY_LDISC, &tty->flags);
	cancel_delayed_work(&tty->buf.work);
	/*
	 * Wait for ->hangup_work and ->buf.work handlers to terminate
	 */
	flush_scheduled_work();
}

/**
 *	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 void tty_ldisc_wait_idle(struct tty_struct *tty)
{
	unsigned long flags;
	spin_lock_irqsave(&tty_ldisc_lock, flags);
	while (tty->ldisc.refcount) {
		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
		wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
		spin_lock_irqsave(&tty_ldisc_lock, flags);
	}
	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
}

/**
 *	tty_set_ldisc		-	set line discipline
 *	@tty: the terminal to set
@@ -636,6 +680,21 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
	return 0;
}

static void tty_ldisc_reinit(struct tty_struct *tty)
{
	struct tty_ldisc ld;

	if (tty->ldisc.ops->close)
		(tty->ldisc.ops->close)(tty);
	tty_ldisc_put(tty->ldisc.ops);
	/*
	 *	Switch the line discipline back
	 */
	WARN_ON(tty_ldisc_get(N_TTY, &ld));
	tty_ldisc_assign(tty, &ld);
	tty_set_termios_ldisc(tty, N_TTY);
}

/**
 *	tty_ldisc_release		-	release line discipline
 *	@tty: tty being shut down
@@ -647,58 +706,34 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)

void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
{
	unsigned long flags;
	struct tty_ldisc ld;

	/*
	 * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
	 * kill any delayed work. As this is the final close it does not
	 * race with the set_ldisc code path.
	 */
	clear_bit(TTY_LDISC, &tty->flags);
	cancel_delayed_work(&tty->buf.work);

	/*
	 * Wait for ->hangup_work and ->buf.work handlers to terminate
	 */

	flush_scheduled_work();
	tty_ldisc_halt(tty);

	/*
	 * Wait for any short term users (we know they are just driver
	 * side waiters as the file is closing so user count on the file
	 * side is zero.
	 */
	spin_lock_irqsave(&tty_ldisc_lock, flags);
	while (tty->ldisc.refcount) {
		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
		wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
		spin_lock_irqsave(&tty_ldisc_lock, flags);
	}
	spin_unlock_irqrestore(&tty_ldisc_lock, flags);

	tty_ldisc_wait_idle(tty);

	/*
	 * Shutdown the current line discipline, and reset it to N_TTY.
	 *
	 * FIXME: this MUST get fixed for the new reflocking
	 */
	if (tty->ldisc.ops->close)
		(tty->ldisc.ops->close)(tty);
	tty_ldisc_put(tty->ldisc.ops);

	/*
	 *	Switch the line discipline back
	 */
	WARN_ON(tty_ldisc_get(N_TTY, &ld));
	tty_ldisc_assign(tty, &ld);
	tty_set_termios_ldisc(tty, N_TTY);
	tty_ldisc_reinit(tty);
	if (o_tty) {
		/* FIXME: could o_tty be in setldisc here ? */
		clear_bit(TTY_LDISC, &o_tty->flags);
		if (o_tty->ldisc.ops->close)
			(o_tty->ldisc.ops->close)(o_tty);
		tty_ldisc_put(o_tty->ldisc.ops);
		WARN_ON(tty_ldisc_get(N_TTY, &ld));
		tty_ldisc_assign(o_tty, &ld);
		tty_set_termios_ldisc(o_tty, N_TTY);
		tty_ldisc_reinit(o_tty);
	}
}

+3 −0
Original line number Diff line number Diff line
@@ -428,6 +428,9 @@ extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
extern void tty_release_dev(struct file *filp);
extern int tty_init_termios(struct tty_struct *tty);

extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty);
extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty);

extern struct mutex tty_mutex;

extern void tty_write_unlock(struct tty_struct *tty);