Commit cf54a5af authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] Update termios to use per tty semaphore

This makes the agreed change of termios locking to be semaphore based
sleep locking. This is needed for USB in particular as it has to use
messaging to issue terminal mode changes.

This code passes Torvalds test grades 0, 1 and 2 (it looks ok, it
compiles and it booted). It does mean that a driver cannot take an
atomic peek at termios data during an interrupt. Nobody seems to be
doing this although some of the driver receive paths for line
disciplines will eventually want to (n_tty currently doesn't do this
locked on the receive path). Since the ldisc is given a chance to copy
any essential bits on the ->set_termios path this seems not to be a
problem.
parent d5978a21
......@@ -60,8 +60,8 @@ chars_in_buffer() - Report the number of bytes in the buffer.
set_termios() - Called on termios structure changes. The caller
passes the old termios data and the current data
is in the tty. Called under the termios lock so
may not sleep. Serialized against itself only.
is in the tty. Called under the termios semaphore so
allowed to sleep. Serialized against itself only.
read() - Move data from the line discipline to the user.
Multiple read calls may occur in parallel and the
......@@ -158,8 +158,8 @@ write_room() - Return the number of characters tht can be stuffed
ioctl() - Called when an ioctl may be for the driver
set_termios() - Called on termios change, may get parallel calls,
may block for now (may change that)
set_termios() - Called on termios change, serialized against
itself by a semaphore. May sleep.
set_ldisc() - Notifier for discipline change. At the point this
is done the discipline is not yet usable. Can now
......
......@@ -130,8 +130,6 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */
/* Semaphore to protect creating and releasing a tty. This is shared with
vt.c for deeply disgusting hack reasons */
DECLARE_MUTEX(tty_sem);
/* Lock for tty_termios changes - private to tty_io/tty_ioctl */
spinlock_t tty_termios_lock = SPIN_LOCK_UNLOCKED;
#ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
......@@ -239,10 +237,9 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
{
unsigned long flags;
spin_lock_irqsave(&tty_termios_lock, flags);
down(&tty->termios_sem);
tty->termios->c_line = num;
spin_unlock_irqrestore(&tty_termios_lock, flags);
up(&tty->termios_sem);
}
/*
......@@ -811,10 +808,9 @@ void do_tty_hangup(void *data)
*/
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
{
unsigned long flags;
spin_lock_irqsave(&tty_termios_lock, flags);
down(&tty->termios_sem);
*tty->termios = tty->driver->init_termios;
spin_unlock_irqrestore(&tty_termios_lock, flags);
up(&tty->termios_sem);
}
/* Defer ldisc switch */
......@@ -2606,6 +2602,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
INIT_WORK(&tty->flip.work, flush_to_ldisc, tty);
init_MUTEX(&tty->flip.pty_sem);
init_MUTEX(&tty->termios_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
......
......@@ -29,8 +29,6 @@
#undef DEBUG
extern spinlock_t tty_termios_lock;
/*
* Internal flag options for termios setting behavior
*/
......@@ -101,7 +99,6 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
int canon_change;
struct termios old_termios = *tty->termios;
struct tty_ldisc *ld;
unsigned long flags;
/*
* Perform the actual termios internal changes under lock.
......@@ -110,7 +107,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
/* FIXME: we need to decide on some locking/ordering semantics
for the set_termios notification eventually */
spin_lock_irqsave(&tty_termios_lock, flags);
down(&tty->termios_sem);
*tty->termios = *new_termios;
unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
......@@ -145,13 +142,6 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
wake_up_interruptible(&tty->link->read_wait);
}
}
/*
* Fixme! We should really try to protect the driver and ldisc
* termios usage too. But they need to be able to sleep, so
* the global termios spinlock is not the right thing.
*/
spin_unlock_irqrestore(&tty_termios_lock, flags);
if (tty->driver->set_termios)
(*tty->driver->set_termios)(tty, &old_termios);
......@@ -162,6 +152,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
(ld->set_termios)(tty, &old_termios);
tty_ldisc_deref(ld);
}
up(&tty->termios_sem);
}
static int set_termios(struct tty_struct * tty, void __user *arg, int opt)
......@@ -255,15 +246,14 @@ static int get_sgflags(struct tty_struct * tty)
static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
{
struct sgttyb tmp;
unsigned long flags;
spin_lock_irqsave(&tty_termios_lock, flags);
down(&tty->termios_sem);
tmp.sg_ispeed = 0;
tmp.sg_ospeed = 0;
tmp.sg_erase = tty->termios->c_cc[VERASE];
tmp.sg_kill = tty->termios->c_cc[VKILL];
tmp.sg_flags = get_sgflags(tty);
spin_unlock_irqrestore(&tty_termios_lock, flags);
up(&tty->termios_sem);
return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
......@@ -299,7 +289,6 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
int retval;
struct sgttyb tmp;
struct termios termios;
unsigned long flags;
retval = tty_check_change(tty);
if (retval)
......@@ -307,13 +296,13 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
return -EFAULT;
spin_lock_irqsave(&tty_termios_lock, flags);
down(&tty->termios_sem);
termios = *tty->termios;
termios.c_cc[VERASE] = tmp.sg_erase;
termios.c_cc[VKILL] = tmp.sg_kill;
set_sgflags(&termios, tmp.sg_flags);
spin_unlock_irqrestore(&tty_termios_lock, flags);
up(&tty->termios_sem);
change_termios(tty, &termios);
return 0;
}
......@@ -549,11 +538,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
case TIOCSSOFTCAR:
if (get_user(arg, (unsigned int __user *) arg))
return -EFAULT;
spin_lock_irqsave(&tty_termios_lock, flags);
down(&tty->termios_sem);
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
spin_unlock_irqrestore(&tty_termios_lock, flags);
up(&tty->termios_sem);
return 0;
default:
return -ENOIOCTLCMD;
......
......@@ -244,6 +244,7 @@ struct tty_struct {
struct tty_driver *driver;
int index;
struct tty_ldisc ldisc;
struct semaphore termios_sem;
struct termios *termios, *termios_locked;
char name[64];
int pgrp;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment