diff options
| author | Alan Cox <alan@lxorguk.ukuu.org.uk> | 2004-10-02 01:46:35 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-10-02 01:46:35 -0700 |
| commit | cf54a5af68a371d83b0aa0e4c14fc01dc4f09c45 (patch) | |
| tree | 7ccb387e4d763d28314c71e39b860deea55a6a53 | |
| parent | d5978a21713e6c3788d5100115d17c9c3d18913c (diff) | |
[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.
| -rw-r--r-- | Documentation/tty.txt | 8 | ||||
| -rw-r--r-- | drivers/char/tty_io.c | 13 | ||||
| -rw-r--r-- | drivers/char/tty_ioctl.c | 29 | ||||
| -rw-r--r-- | include/linux/tty.h | 1 |
4 files changed, 19 insertions, 32 deletions
diff --git a/Documentation/tty.txt b/Documentation/tty.txt index 7c046ac19206..a496056426c6 100644 --- a/Documentation/tty.txt +++ b/Documentation/tty.txt @@ -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 diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 57464870db3f..89457cc2ed72 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -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); diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 68402a251fc4..d2f55717d097 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -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; diff --git a/include/linux/tty.h b/include/linux/tty.h index 24b4a7383b53..f9fd14991956 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -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; |
