Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / drivers / char / tty_io.c
index 4d3c701..d71f0fc 100644 (file)
@@ -49,7 +49,7 @@
  * implement CONFIG_VT and generalize console device interface.
  *     -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
  *
- * Rewrote init_dev and release_dev to eliminate races.
+ * Rewrote tty_init_dev and tty_release_dev to eliminate races.
  *     -- Bill Hawes <whawes@star.net>, June 97
  *
  * Added devfs support.
@@ -78,6 +78,7 @@
 #include <linux/tty_flip.h>
 #include <linux/devpts_fs.h>
 #include <linux/file.h>
+#include <linux/fdtable.h>
 #include <linux/console.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
 #include <linux/device.h>
-#include <linux/idr.h>
 #include <linux/wait.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
+#include <linux/seq_file.h>
 
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <asm/system.h>
 
 #include <linux/kbd_kern.h>
@@ -135,25 +136,13 @@ LIST_HEAD(tty_drivers);                   /* linked list of tty drivers */
 DEFINE_MUTEX(tty_mutex);
 EXPORT_SYMBOL(tty_mutex);
 
-#ifdef CONFIG_UNIX98_PTYS
-extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
-extern int pty_limit;                  /* Config limit on Unix98 ptys */
-static DEFINE_IDR(allocated_ptys);
-static DEFINE_MUTEX(allocated_ptys_lock);
-static int ptmx_open(struct inode *, struct file *);
-#endif
-
-static void initialize_tty_struct(struct tty_struct *tty);
-
 static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
 static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
 ssize_t redirected_tty_write(struct file *, const char __user *,
                                                        size_t, loff_t *);
 static unsigned int tty_poll(struct file *, poll_table *);
 static int tty_open(struct inode *, struct file *);
-static int tty_release(struct inode *, struct file *);
-int tty_ioctl(struct inode *inode, struct file *file,
-             unsigned int cmd, unsigned long arg);
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 #ifdef CONFIG_COMPAT
 static long tty_compat_ioctl(struct file *file, unsigned int cmd,
                                unsigned long arg);
@@ -174,13 +163,11 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
  *     Locking: none
  */
 
-static struct tty_struct *alloc_tty_struct(void)
+struct tty_struct *alloc_tty_struct(void)
 {
        return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
 }
 
-static void tty_buffer_free_all(struct tty_struct *);
-
 /**
  *     free_tty_struct         -       free a disused tty
  *     @tty: tty struct to free
@@ -190,7 +177,7 @@ static void tty_buffer_free_all(struct tty_struct *);
  *     Locking: none. Must be called after tty is definitely unused
  */
 
-static inline void free_tty_struct(struct tty_struct *tty)
+void free_tty_struct(struct tty_struct *tty)
 {
        kfree(tty->write_buf);
        tty_buffer_free_all(tty);
@@ -225,909 +212,45 @@ int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
                              const char *routine)
 {
 #ifdef TTY_PARANOIA_CHECK
-       if (!tty) {
-               printk(KERN_WARNING
-                       "null TTY for (%d:%d) in %s\n",
-                       imajor(inode), iminor(inode), routine);
-               return 1;
-       }
-       if (tty->magic != TTY_MAGIC) {
-               printk(KERN_WARNING
-                       "bad magic number for tty struct (%d:%d) in %s\n",
-                       imajor(inode), iminor(inode), routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-static int check_tty_count(struct tty_struct *tty, const char *routine)
-{
-#ifdef CHECK_TTY_COUNT
-       struct list_head *p;
-       int count = 0;
-
-       file_list_lock();
-       list_for_each(p, &tty->tty_files) {
-               count++;
-       }
-       file_list_unlock();
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver->subtype == PTY_TYPE_SLAVE &&
-           tty->link && tty->link->count)
-               count++;
-       if (tty->count != count) {
-               printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
-                                   "!= #fd's(%d) in %s\n",
-                      tty->name, tty->count, count, routine);
-               return count;
-       }
-#endif
-       return 0;
-}
-
-/*
- * Tty buffer allocation management
- */
-
-/**
- *     tty_buffer_free_all             -       free buffers used by a tty
- *     @tty: tty to free from
- *
- *     Remove all the buffers pending on a tty whether queued with data
- *     or in the free ring. Must be called when the tty is no longer in use
- *
- *     Locking: none
- */
-
-static void tty_buffer_free_all(struct tty_struct *tty)
-{
-       struct tty_buffer *thead;
-       while ((thead = tty->buf.head) != NULL) {
-               tty->buf.head = thead->next;
-               kfree(thead);
-       }
-       while ((thead = tty->buf.free) != NULL) {
-               tty->buf.free = thead->next;
-               kfree(thead);
-       }
-       tty->buf.tail = NULL;
-       tty->buf.memory_used = 0;
-}
-
-/**
- *     tty_buffer_init         -       prepare a tty buffer structure
- *     @tty: tty to initialise
- *
- *     Set up the initial state of the buffer management for a tty device.
- *     Must be called before the other tty buffer functions are used.
- *
- *     Locking: none
- */
-
-static void tty_buffer_init(struct tty_struct *tty)
-{
-       spin_lock_init(&tty->buf.lock);
-       tty->buf.head = NULL;
-       tty->buf.tail = NULL;
-       tty->buf.free = NULL;
-       tty->buf.memory_used = 0;
-}
-
-/**
- *     tty_buffer_alloc        -       allocate a tty buffer
- *     @tty: tty device
- *     @size: desired size (characters)
- *
- *     Allocate a new tty buffer to hold the desired number of characters.
- *     Return NULL if out of memory or the allocation would exceed the
- *     per device queue
- *
- *     Locking: Caller must hold tty->buf.lock
- */
-
-static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
-{
-       struct tty_buffer *p;
-
-       if (tty->buf.memory_used + size > 65536)
-               return NULL;
-       p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
-       if (p == NULL)
-               return NULL;
-       p->used = 0;
-       p->size = size;
-       p->next = NULL;
-       p->commit = 0;
-       p->read = 0;
-       p->char_buf_ptr = (char *)(p->data);
-       p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
-       tty->buf.memory_used += size;
-       return p;
-}
-
-/**
- *     tty_buffer_free         -       free a tty buffer
- *     @tty: tty owning the buffer
- *     @b: the buffer to free
- *
- *     Free a tty buffer, or add it to the free list according to our
- *     internal strategy
- *
- *     Locking: Caller must hold tty->buf.lock
- */
-
-static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
-{
-       /* Dumb strategy for now - should keep some stats */
-       tty->buf.memory_used -= b->size;
-       WARN_ON(tty->buf.memory_used < 0);
-
-       if (b->size >= 512)
-               kfree(b);
-       else {
-               b->next = tty->buf.free;
-               tty->buf.free = b;
-       }
-}
-
-/**
- *     __tty_buffer_flush              -       flush full tty buffers
- *     @tty: tty to flush
- *
- *     flush all the buffers containing receive data. Caller must
- *     hold the buffer lock and must have ensured no parallel flush to
- *     ldisc is running.
- *
- *     Locking: Caller must hold tty->buf.lock
- */
-
-static void __tty_buffer_flush(struct tty_struct *tty)
-{
-       struct tty_buffer *thead;
-
-       while ((thead = tty->buf.head) != NULL) {
-               tty->buf.head = thead->next;
-               tty_buffer_free(tty, thead);
-       }
-       tty->buf.tail = NULL;
-}
-
-/**
- *     tty_buffer_flush                -       flush full tty buffers
- *     @tty: tty to flush
- *
- *     flush all the buffers containing receive data. If the buffer is
- *     being processed by flush_to_ldisc then we defer the processing
- *     to that function
- *
- *     Locking: none
- */
-
-static void tty_buffer_flush(struct tty_struct *tty)
-{
-       unsigned long flags;
-       spin_lock_irqsave(&tty->buf.lock, flags);
-
-       /* If the data is being pushed to the tty layer then we can't
-          process it here. Instead set a flag and the flush_to_ldisc
-          path will process the flush request before it exits */
-       if (test_bit(TTY_FLUSHING, &tty->flags)) {
-               set_bit(TTY_FLUSHPENDING, &tty->flags);
-               spin_unlock_irqrestore(&tty->buf.lock, flags);
-               wait_event(tty->read_wait,
-                               test_bit(TTY_FLUSHPENDING, &tty->flags) == 0);
-               return;
-       } else
-               __tty_buffer_flush(tty);
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
-}
-
-/**
- *     tty_buffer_find         -       find a free tty buffer
- *     @tty: tty owning the buffer
- *     @size: characters wanted
- *
- *     Locate an existing suitable tty buffer or if we are lacking one then
- *     allocate a new one. We round our buffers off in 256 character chunks
- *     to get better allocation behaviour.
- *
- *     Locking: Caller must hold tty->buf.lock
- */
-
-static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
-{
-       struct tty_buffer **tbh = &tty->buf.free;
-       while ((*tbh) != NULL) {
-               struct tty_buffer *t = *tbh;
-               if (t->size >= size) {
-                       *tbh = t->next;
-                       t->next = NULL;
-                       t->used = 0;
-                       t->commit = 0;
-                       t->read = 0;
-                       tty->buf.memory_used += t->size;
-                       return t;
-               }
-               tbh = &((*tbh)->next);
-       }
-       /* Round the buffer size out */
-       size = (size + 0xFF) & ~0xFF;
-       return tty_buffer_alloc(tty, size);
-       /* Should possibly check if this fails for the largest buffer we
-          have queued and recycle that ? */
-}
-
-/**
- *     tty_buffer_request_room         -       grow tty buffer if needed
- *     @tty: tty structure
- *     @size: size desired
- *
- *     Make at least size bytes of linear space available for the tty
- *     buffer. If we fail return the size we managed to find.
- *
- *     Locking: Takes tty->buf.lock
- */
-int tty_buffer_request_room(struct tty_struct *tty, size_t size)
-{
-       struct tty_buffer *b, *n;
-       int left;
-       unsigned long flags;
-
-       spin_lock_irqsave(&tty->buf.lock, flags);
-
-       /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
-          remove this conditional if its worth it. This would be invisible
-          to the callers */
-       if ((b = tty->buf.tail) != NULL)
-               left = b->size - b->used;
-       else
-               left = 0;
-
-       if (left < size) {
-               /* This is the slow path - looking for new buffers to use */
-               if ((n = tty_buffer_find(tty, size)) != NULL) {
-                       if (b != NULL) {
-                               b->next = n;
-                               b->commit = b->used;
-                       } else
-                               tty->buf.head = n;
-                       tty->buf.tail = n;
-               } else
-                       size = left;
-       }
-
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
-       return size;
-}
-EXPORT_SYMBOL_GPL(tty_buffer_request_room);
-
-/**
- *     tty_insert_flip_string  -       Add characters to the tty buffer
- *     @tty: tty structure
- *     @chars: characters
- *     @size: size
- *
- *     Queue a series of bytes to the tty buffering. All the characters
- *     passed are marked as without error. Returns the number added.
- *
- *     Locking: Called functions may take tty->buf.lock
- */
-
-int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
-                               size_t size)
-{
-       int copied = 0;
-       do {
-               int space = tty_buffer_request_room(tty, size - copied);
-               struct tty_buffer *tb = tty->buf.tail;
-               /* If there is no space then tb may be NULL */
-               if (unlikely(space == 0))
-                       break;
-               memcpy(tb->char_buf_ptr + tb->used, chars, space);
-               memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
-               tb->used += space;
-               copied += space;
-               chars += space;
-               /* There is a small chance that we need to split the data over
-                  several buffers. If this is the case we must loop */
-       } while (unlikely(size > copied));
-       return copied;
-}
-EXPORT_SYMBOL(tty_insert_flip_string);
-
-/**
- *     tty_insert_flip_string_flags    -       Add characters to the tty buffer
- *     @tty: tty structure
- *     @chars: characters
- *     @flags: flag bytes
- *     @size: size
- *
- *     Queue a series of bytes to the tty buffering. For each character
- *     the flags array indicates the status of the character. Returns the
- *     number added.
- *
- *     Locking: Called functions may take tty->buf.lock
- */
-
-int tty_insert_flip_string_flags(struct tty_struct *tty,
-               const unsigned char *chars, const char *flags, size_t size)
-{
-       int copied = 0;
-       do {
-               int space = tty_buffer_request_room(tty, size - copied);
-               struct tty_buffer *tb = tty->buf.tail;
-               /* If there is no space then tb may be NULL */
-               if (unlikely(space == 0))
-                       break;
-               memcpy(tb->char_buf_ptr + tb->used, chars, space);
-               memcpy(tb->flag_buf_ptr + tb->used, flags, space);
-               tb->used += space;
-               copied += space;
-               chars += space;
-               flags += space;
-               /* There is a small chance that we need to split the data over
-                  several buffers. If this is the case we must loop */
-       } while (unlikely(size > copied));
-       return copied;
-}
-EXPORT_SYMBOL(tty_insert_flip_string_flags);
-
-/**
- *     tty_schedule_flip       -       push characters to ldisc
- *     @tty: tty to push from
- *
- *     Takes any pending buffers and transfers their ownership to the
- *     ldisc side of the queue. It then schedules those characters for
- *     processing by the line discipline.
- *
- *     Locking: Takes tty->buf.lock
- */
-
-void tty_schedule_flip(struct tty_struct *tty)
-{
-       unsigned long flags;
-       spin_lock_irqsave(&tty->buf.lock, flags);
-       if (tty->buf.tail != NULL)
-               tty->buf.tail->commit = tty->buf.tail->used;
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
-       schedule_delayed_work(&tty->buf.work, 1);
-}
-EXPORT_SYMBOL(tty_schedule_flip);
-
-/**
- *     tty_prepare_flip_string         -       make room for characters
- *     @tty: tty
- *     @chars: return pointer for character write area
- *     @size: desired size
- *
- *     Prepare a block of space in the buffer for data. Returns the length
- *     available and buffer pointer to the space which is now allocated and
- *     accounted for as ready for normal characters. This is used for drivers
- *     that need their own block copy routines into the buffer. There is no
- *     guarantee the buffer is a DMA target!
- *
- *     Locking: May call functions taking tty->buf.lock
- */
-
-int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
-                                                               size_t size)
-{
-       int space = tty_buffer_request_room(tty, size);
-       if (likely(space)) {
-               struct tty_buffer *tb = tty->buf.tail;
-               *chars = tb->char_buf_ptr + tb->used;
-               memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
-               tb->used += space;
-       }
-       return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
-
-/**
- *     tty_prepare_flip_string_flags   -       make room for characters
- *     @tty: tty
- *     @chars: return pointer for character write area
- *     @flags: return pointer for status flag write area
- *     @size: desired size
- *
- *     Prepare a block of space in the buffer for data. Returns the length
- *     available and buffer pointer to the space which is now allocated and
- *     accounted for as ready for characters. This is used for drivers
- *     that need their own block copy routines into the buffer. There is no
- *     guarantee the buffer is a DMA target!
- *
- *     Locking: May call functions taking tty->buf.lock
- */
-
-int tty_prepare_flip_string_flags(struct tty_struct *tty,
-                       unsigned char **chars, char **flags, size_t size)
-{
-       int space = tty_buffer_request_room(tty, size);
-       if (likely(space)) {
-               struct tty_buffer *tb = tty->buf.tail;
-               *chars = tb->char_buf_ptr + tb->used;
-               *flags = tb->flag_buf_ptr + tb->used;
-               tb->used += space;
-       }
-       return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
-
-
-
-/**
- *     tty_set_termios_ldisc           -       set ldisc field
- *     @tty: tty structure
- *     @num: line discipline number
- *
- *     This is probably overkill for real world processors but
- *     they are not on hot paths so a little discipline won't do
- *     any harm.
- *
- *     Locking: takes termios_mutex
- */
-
-static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
-{
-       mutex_lock(&tty->termios_mutex);
-       tty->termios->c_line = num;
-       mutex_unlock(&tty->termios_mutex);
-}
-
-/*
- *     This guards the refcounted line discipline lists. The lock
- *     must be taken with irqs off because there are hangup path
- *     callers who will do ldisc lookups and cannot sleep.
- */
-
-static DEFINE_SPINLOCK(tty_ldisc_lock);
-static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
-/* Line disc dispatch table */
-static struct tty_ldisc tty_ldiscs[NR_LDISCS];
-
-/**
- *     tty_register_ldisc      -       install a line discipline
- *     @disc: ldisc number
- *     @new_ldisc: pointer to the ldisc object
- *
- *     Installs a new line discipline into the kernel. The discipline
- *     is set up as unreferenced and then made available to the kernel
- *     from this point onwards.
- *
- *     Locking:
- *             takes tty_ldisc_lock to guard against ldisc races
- */
-
-int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
-{
-       unsigned long flags;
-       int ret = 0;
-
-       if (disc < N_TTY || disc >= NR_LDISCS)
-               return -EINVAL;
-
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-       tty_ldiscs[disc] = *new_ldisc;
-       tty_ldiscs[disc].num = disc;
-       tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
-       tty_ldiscs[disc].refcount = 0;
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
-       return ret;
-}
-EXPORT_SYMBOL(tty_register_ldisc);
-
-/**
- *     tty_unregister_ldisc    -       unload a line discipline
- *     @disc: ldisc number
- *     @new_ldisc: pointer to the ldisc object
- *
- *     Remove a line discipline from the kernel providing it is not
- *     currently in use.
- *
- *     Locking:
- *             takes tty_ldisc_lock to guard against ldisc races
- */
-
-int tty_unregister_ldisc(int disc)
-{
-       unsigned long flags;
-       int ret = 0;
-
-       if (disc < N_TTY || disc >= NR_LDISCS)
-               return -EINVAL;
-
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-       if (tty_ldiscs[disc].refcount)
-               ret = -EBUSY;
-       else
-               tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
-       return ret;
-}
-EXPORT_SYMBOL(tty_unregister_ldisc);
-
-/**
- *     tty_ldisc_get           -       take a reference to an ldisc
- *     @disc: ldisc number
- *
- *     Takes a reference to a line discipline. Deals with refcounts and
- *     module locking counts. Returns NULL if the discipline is not available.
- *     Returns a pointer to the discipline and bumps the ref count if it is
- *     available
- *
- *     Locking:
- *             takes tty_ldisc_lock to guard against ldisc races
- */
-
-struct tty_ldisc *tty_ldisc_get(int disc)
-{
-       unsigned long flags;
-       struct tty_ldisc *ld;
-
-       if (disc < N_TTY || disc >= NR_LDISCS)
-               return NULL;
-
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-
-       ld = &tty_ldiscs[disc];
-       /* Check the entry is defined */
-       if (ld->flags & LDISC_FLAG_DEFINED) {
-               /* If the module is being unloaded we can't use it */
-               if (!try_module_get(ld->owner))
-                       ld = NULL;
-               else /* lock it */
-                       ld->refcount++;
-       } else
-               ld = NULL;
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-       return ld;
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_get);
-
-/**
- *     tty_ldisc_put           -       drop ldisc reference
- *     @disc: ldisc number
- *
- *     Drop a reference to a line discipline. Manage refcounts and
- *     module usage counts
- *
- *     Locking:
- *             takes tty_ldisc_lock to guard against ldisc races
- */
-
-void tty_ldisc_put(int disc)
-{
-       struct tty_ldisc *ld;
-       unsigned long flags;
-
-       BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
-
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-       ld = &tty_ldiscs[disc];
-       BUG_ON(ld->refcount == 0);
-       ld->refcount--;
-       module_put(ld->owner);
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_put);
-
-/**
- *     tty_ldisc_assign        -       set ldisc on a tty
- *     @tty: tty to assign
- *     @ld: line discipline
- *
- *     Install an instance of a line discipline into a tty structure. The
- *     ldisc must have a reference count above zero to ensure it remains/
- *     The tty instance refcount starts at zero.
- *
- *     Locking:
- *             Caller must hold references
- */
-
-static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
-{
-       tty->ldisc = *ld;
-       tty->ldisc.refcount = 0;
-}
-
-/**
- *     tty_ldisc_try           -       internal helper
- *     @tty: the tty
- *
- *     Make a single attempt to grab and bump the refcount on
- *     the tty ldisc. Return 0 on failure or 1 on success. This is
- *     used to implement both the waiting and non waiting versions
- *     of tty_ldisc_ref
- *
- *     Locking: takes tty_ldisc_lock
- */
-
-static int tty_ldisc_try(struct tty_struct *tty)
-{
-       unsigned long flags;
-       struct tty_ldisc *ld;
-       int ret = 0;
-
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-       ld = &tty->ldisc;
-       if (test_bit(TTY_LDISC, &tty->flags)) {
-               ld->refcount++;
-               ret = 1;
-       }
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-       return ret;
-}
-
-/**
- *     tty_ldisc_ref_wait      -       wait for the tty ldisc
- *     @tty: tty device
- *
- *     Dereference the line discipline for the terminal and take a
- *     reference to it. If the line discipline is in flux then
- *     wait patiently until it changes.
- *
- *     Note: Must not be called from an IRQ/timer context. The caller
- *     must also be careful not to hold other locks that will deadlock
- *     against a discipline change, such as an existing ldisc reference
- *     (which we check for)
- *
- *     Locking: call functions take tty_ldisc_lock
- */
-
-struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
-{
-       /* wait_event is a macro */
-       wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
-       if (tty->ldisc.refcount == 0)
-               printk(KERN_ERR "tty_ldisc_ref_wait\n");
-       return &tty->ldisc;
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
-
-/**
- *     tty_ldisc_ref           -       get the tty ldisc
- *     @tty: tty device
- *
- *     Dereference the line discipline for the terminal and take a
- *     reference to it. If the line discipline is in flux then
- *     return NULL. Can be called from IRQ and timer functions.
- *
- *     Locking: called functions take tty_ldisc_lock
- */
-
-struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
-{
-       if (tty_ldisc_try(tty))
-               return &tty->ldisc;
-       return NULL;
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_ref);
-
-/**
- *     tty_ldisc_deref         -       free a tty ldisc reference
- *     @ld: reference to free up
- *
- *     Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
- *     be called in IRQ context.
- *
- *     Locking: takes tty_ldisc_lock
- */
-
-void tty_ldisc_deref(struct tty_ldisc *ld)
-{
-       unsigned long flags;
-
-       BUG_ON(ld == NULL);
-
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-       if (ld->refcount == 0)
-               printk(KERN_ERR "tty_ldisc_deref: no references.\n");
-       else
-               ld->refcount--;
-       if (ld->refcount == 0)
-               wake_up(&tty_ldisc_wait);
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_deref);
-
-/**
- *     tty_ldisc_enable        -       allow ldisc use
- *     @tty: terminal to activate ldisc on
- *
- *     Set the TTY_LDISC flag when the line discipline can be called
- *     again. Do necessary wakeups for existing sleepers.
- *
- *     Note: nobody should set this bit except via this function. Clearing
- *     directly is allowed.
- */
-
-static void tty_ldisc_enable(struct tty_struct *tty)
-{
-       set_bit(TTY_LDISC, &tty->flags);
-       wake_up(&tty_ldisc_wait);
-}
-
-/**
- *     tty_set_ldisc           -       set line discipline
- *     @tty: the terminal to set
- *     @ldisc: the line discipline
- *
- *     Set the discipline of a tty line. Must be called from a process
- *     context.
- *
- *     Locking: takes tty_ldisc_lock.
- *              called functions take termios_mutex
- */
-
-static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
-{
-       int retval = 0;
-       struct tty_ldisc o_ldisc;
-       char buf[64];
-       int work;
-       unsigned long flags;
-       struct tty_ldisc *ld;
-       struct tty_struct *o_tty;
-
-       if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
-               return -EINVAL;
-
-restart:
-
-       ld = tty_ldisc_get(ldisc);
-       /* Eduardo Blanco <ejbs@cs.cs.com.uy> */
-       /* Cyrus Durgin <cider@speakeasy.org> */
-       if (ld == NULL) {
-               request_module("tty-ldisc-%d", ldisc);
-               ld = tty_ldisc_get(ldisc);
-       }
-       if (ld == NULL)
-               return -EINVAL;
-
-       /*
-        *      Problem: What do we do if this blocks ?
-        */
-
-       tty_wait_until_sent(tty, 0);
-
-       if (tty->ldisc.num == ldisc) {
-               tty_ldisc_put(ldisc);
-               return 0;
-       }
-
-       /*
-        *      No more input please, we are switching. The new ldisc
-        *      will update this value in the ldisc open function
-        */
-
-       tty->receive_room = 0;
-
-       o_ldisc = tty->ldisc;
-       o_tty = tty->link;
-
-       /*
-        *      Make sure we don't change while someone holds a
-        *      reference to the line discipline. The TTY_LDISC bit
-        *      prevents anyone taking a reference once it is clear.
-        *      We need the lock to avoid racing reference takers.
-        */
-
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-       if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
-               if (tty->ldisc.refcount) {
-                       /* Free the new ldisc we grabbed. Must drop the lock
-                          first. */
-                       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-                       tty_ldisc_put(ldisc);
-                       /*
-                        * There are several reasons we may be busy, including
-                        * random momentary I/O traffic. We must therefore
-                        * retry. We could distinguish between blocking ops
-                        * and retries if we made tty_ldisc_wait() smarter.
-                        * That is up for discussion.
-                        */
-                       if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
-                               return -ERESTARTSYS;
-                       goto restart;
-               }
-               if (o_tty && o_tty->ldisc.refcount) {
-                       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-                       tty_ldisc_put(ldisc);
-                       if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
-                               return -ERESTARTSYS;
-                       goto restart;
-               }
+       if (!tty) {
+               printk(KERN_WARNING
+                       "null TTY for (%d:%d) in %s\n",
+                       imajor(inode), iminor(inode), routine);
+               return 1;
        }
-       /*
-        *      If the TTY_LDISC bit is set, then we are racing against
-        *      another ldisc change
-        */
-       if (!test_bit(TTY_LDISC, &tty->flags)) {
-               spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-               tty_ldisc_put(ldisc);
-               ld = tty_ldisc_ref_wait(tty);
-               tty_ldisc_deref(ld);
-               goto restart;
+       if (tty->magic != TTY_MAGIC) {
+               printk(KERN_WARNING
+                       "bad magic number for tty struct (%d:%d) in %s\n",
+                       imajor(inode), iminor(inode), routine);
+               return 1;
        }
+#endif
+       return 0;
+}
 
-       clear_bit(TTY_LDISC, &tty->flags);
-       if (o_tty)
-               clear_bit(TTY_LDISC, &o_tty->flags);
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
-       /*
-        *      From this point on we know nobody has an ldisc
-        *      usage reference, nor can they obtain one until
-        *      we say so later on.
-        */
+static int check_tty_count(struct tty_struct *tty, const char *routine)
+{
+#ifdef CHECK_TTY_COUNT
+       struct list_head *p;
+       int count = 0;
 
-       work = cancel_delayed_work(&tty->buf.work);
-       /*
-        * Wait for ->hangup_work and ->buf.work handlers to terminate
-        */
-       flush_scheduled_work();
-       /* Shutdown the current discipline. */
-       if (tty->ldisc.close)
-               (tty->ldisc.close)(tty);
-
-       /* Now set up the new line discipline. */
-       tty_ldisc_assign(tty, ld);
-       tty_set_termios_ldisc(tty, ldisc);
-       if (tty->ldisc.open)
-               retval = (tty->ldisc.open)(tty);
-       if (retval < 0) {
-               tty_ldisc_put(ldisc);
-               /* There is an outstanding reference here so this is safe */
-               tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));
-               tty_set_termios_ldisc(tty, tty->ldisc.num);
-               if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
-                       tty_ldisc_put(o_ldisc.num);
-                       /* This driver is always present */
-                       tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-                       tty_set_termios_ldisc(tty, N_TTY);
-                       if (tty->ldisc.open) {
-                               int r = tty->ldisc.open(tty);
-
-                               if (r < 0)
-                                       panic("Couldn't open N_TTY ldisc for "
-                                             "%s --- error %d.",
-                                             tty_name(tty, buf), r);
-                       }
-               }
+       file_list_lock();
+       list_for_each(p, &tty->tty_files) {
+               count++;
        }
-       /* At this point we hold a reference to the new ldisc and a
-          a reference to the old ldisc. If we ended up flipping back
-          to the existing ldisc we have two references to it */
-
-       if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc)
-               tty->driver->set_ldisc(tty);
-
-       tty_ldisc_put(o_ldisc.num);
-
-       /*
-        *      Allow ldisc referencing to occur as soon as the driver
-        *      ldisc callback completes.
-        */
-
-       tty_ldisc_enable(tty);
-       if (o_tty)
-               tty_ldisc_enable(o_tty);
-
-       /* Restart it in case no characters kick it off. Safe if
-          already running */
-       if (work)
-               schedule_delayed_work(&tty->buf.work, 1);
-       return retval;
+       file_list_unlock();
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+           tty->driver->subtype == PTY_TYPE_SLAVE &&
+           tty->link && tty->link->count)
+               count++;
+       if (tty->count != count) {
+               printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
+                                   "!= #fd's(%d) in %s\n",
+                      tty->name, tty->count, count, routine);
+               return count;
+       }
+#endif
+       return 0;
 }
 
 /**
@@ -1150,7 +273,7 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
                if (device < base || device >= base + p->num)
                        continue;
                *index = device - base;
-               return p;
+               return tty_driver_kref_get(p);
        }
        return NULL;
 }
@@ -1170,22 +293,32 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
 {
        struct tty_driver *p, *res = NULL;
        int tty_line = 0;
-       char *str;
+       int len;
+       char *str, *stp;
+
+       for (str = name; *str; str++)
+               if ((*str >= '0' && *str <= '9') || *str == ',')
+                       break;
+       if (!*str)
+               return NULL;
+
+       len = str - name;
+       tty_line = simple_strtoul(str, &str, 10);
 
        mutex_lock(&tty_mutex);
        /* Search through the tty devices to look for a match */
        list_for_each_entry(p, &tty_drivers, tty_drivers) {
-               str = name + strlen(p->name);
-               tty_line = simple_strtoul(str, &str, 10);
-               if (*str == ',')
-                       str++;
-               if (*str == '\0')
-                       str = 0;
-
-               if (tty_line >= 0 && tty_line <= p->num && p->poll_init &&
-                               !p->poll_init(p, tty_line, str)) {
-
-                       res = p;
+               if (strncmp(name, p->name, len) != 0)
+                       continue;
+               stp = str;
+               if (*stp == ',')
+                       stp++;
+               if (*stp == '\0')
+                       stp = NULL;
+
+               if (tty_line >= 0 && tty_line <= p->num && p->ops &&
+                   p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
+                       res = tty_driver_kref_get(p);
                        *line = tty_line;
                        break;
                }
@@ -1205,26 +338,40 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver);
  *     not in the foreground, send a SIGTTOU.  If the signal is blocked or
  *     ignored, go ahead and perform the operation.  (POSIX 7.2)
  *
- *     Locking: none
+ *     Locking: ctrl_lock
  */
 
 int tty_check_change(struct tty_struct *tty)
 {
+       unsigned long flags;
+       int ret = 0;
+
        if (current->signal->tty != tty)
                return 0;
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+
        if (!tty->pgrp) {
                printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");
-               return 0;
+               goto out_unlock;
        }
        if (task_pgrp(current) == tty->pgrp)
-               return 0;
+               goto out_unlock;
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        if (is_ignored(SIGTTOU))
-               return 0;
-       if (is_current_pgrp_orphaned())
-               return -EIO;
+               goto out;
+       if (is_current_pgrp_orphaned()) {
+               ret = -EIO;
+               goto out;
+       }
        kill_pgrp(task_pgrp(current), SIGTTOU, 1);
        set_thread_flag(TIF_SIGPENDING);
-       return -ERESTARTSYS;
+       ret = -ERESTARTSYS;
+out:
+       return ret;
+out_unlock:
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+       return ret;
 }
 
 EXPORT_SYMBOL(tty_check_change);
@@ -1247,8 +394,8 @@ static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
        return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
 }
 
-static int hung_up_tty_ioctl(struct inode *inode, struct file *file,
-                            unsigned int cmd, unsigned long arg)
+static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
+               unsigned long arg)
 {
        return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
 }
@@ -1264,33 +411,19 @@ static const struct file_operations tty_fops = {
        .read           = tty_read,
        .write          = tty_write,
        .poll           = tty_poll,
-       .ioctl          = tty_ioctl,
+       .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
        .fasync         = tty_fasync,
 };
 
-#ifdef CONFIG_UNIX98_PTYS
-static const struct file_operations ptmx_fops = {
-       .llseek         = no_llseek,
-       .read           = tty_read,
-       .write          = tty_write,
-       .poll           = tty_poll,
-       .ioctl          = tty_ioctl,
-       .compat_ioctl   = tty_compat_ioctl,
-       .open           = ptmx_open,
-       .release        = tty_release,
-       .fasync         = tty_fasync,
-};
-#endif
-
 static const struct file_operations console_fops = {
        .llseek         = no_llseek,
        .read           = tty_read,
        .write          = redirected_tty_write,
        .poll           = tty_poll,
-       .ioctl          = tty_ioctl,
+       .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
@@ -1302,7 +435,7 @@ static const struct file_operations hung_up_tty_fops = {
        .read           = hung_up_tty_read,
        .write          = hung_up_tty_write,
        .poll           = hung_up_tty_poll,
-       .ioctl          = hung_up_tty_ioctl,
+       .unlocked_ioctl = hung_up_tty_ioctl,
        .compat_ioctl   = hung_up_tty_compat_ioctl,
        .release        = tty_release,
 };
@@ -1326,54 +459,17 @@ void tty_wakeup(struct tty_struct *tty)
        if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
                ld = tty_ldisc_ref(tty);
                if (ld) {
-                       if (ld->write_wakeup)
-                               ld->write_wakeup(tty);
+                       if (ld->ops->write_wakeup)
+                               ld->ops->write_wakeup(tty);
                        tty_ldisc_deref(ld);
                }
        }
-       wake_up_interruptible(&tty->write_wait);
+       wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
 }
 
 EXPORT_SYMBOL_GPL(tty_wakeup);
 
 /**
- *     tty_ldisc_flush -       flush line discipline queue
- *     @tty: tty
- *
- *     Flush the line discipline queue (if any) for this tty. If there
- *     is no line discipline active this is a no-op.
- */
-
-void tty_ldisc_flush(struct tty_struct *tty)
-{
-       struct tty_ldisc *ld = tty_ldisc_ref(tty);
-       if (ld) {
-               if (ld->flush_buffer)
-                       ld->flush_buffer(tty);
-               tty_ldisc_deref(ld);
-       }
-       tty_buffer_flush(tty);
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_flush);
-
-/**
- *     tty_reset_termios       -       reset terminal state
- *     @tty: tty to reset
- *
- *     Restore a terminal to the driver default state
- */
-
-static void tty_reset_termios(struct tty_struct *tty)
-{
-       mutex_lock(&tty->termios_mutex);
-       *tty->termios = tty->driver->init_termios;
-       tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
-       tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
-       mutex_unlock(&tty->termios_mutex);
-}
-
-/**
  *     do_tty_hangup           -       actual handler for hangup events
  *     @work: tty device
  *
@@ -1402,14 +498,13 @@ static void do_tty_hangup(struct work_struct *work)
        struct file *cons_filp = NULL;
        struct file *filp, *f = NULL;
        struct task_struct *p;
-       struct tty_ldisc *ld;
        int    closecount = 0, n;
+       unsigned long flags;
+       int refs = 0;
 
        if (!tty)
                return;
 
-       /* inuse_filps is protected by the single kernel lock */
-       lock_kernel();
 
        spin_lock(&redirect_lock);
        if (redirect && redirect->private_data == tty) {
@@ -1418,7 +513,10 @@ static void do_tty_hangup(struct work_struct *work)
        }
        spin_unlock(&redirect_lock);
 
+       /* inuse_filps is protected by the single kernel lock */
+       lock_kernel();
        check_tty_count(tty, "do_tty_hangup");
+
        file_list_lock();
        /* This breaks for file handles being sent over AF_UNIX sockets ? */
        list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
@@ -1431,48 +529,19 @@ static void do_tty_hangup(struct work_struct *work)
                filp->f_op = &hung_up_tty_fops;
        }
        file_list_unlock();
-       /*
-        * FIXME! What are the locking issues here? This may me overdoing
-        * things... This question is especially important now that we've
-        * removed the irqlock.
-        */
-       ld = tty_ldisc_ref(tty);
-       if (ld != NULL) {
-               /* We may have no line discipline at this point */
-               if (ld->flush_buffer)
-                       ld->flush_buffer(tty);
-               if (tty->driver->flush_buffer)
-                       tty->driver->flush_buffer(tty);
-               if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
-                   ld->write_wakeup)
-                       ld->write_wakeup(tty);
-               if (ld->hangup)
-                       ld->hangup(tty);
-       }
-       /*
-        * FIXME: Once we trust the LDISC code better we can wait here for
-        * ldisc completion and fix the driver call race
-        */
-       wake_up_interruptible(&tty->write_wait);
-       wake_up_interruptible(&tty->read_wait);
-       /*
-        * Shutdown the current line discipline, and reset it to
-        * N_TTY.
-        */
-       if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
-               tty_reset_termios(tty);
-       /* Defer ldisc switch */
-       /* tty_deferred_ldisc_switch(N_TTY);
 
-         This should get done automatically when the port closes and
-         tty_release is called */
+       tty_ldisc_hangup(tty);
 
        read_lock(&tasklist_lock);
        if (tty->session) {
                do_each_pid_task(tty->session, PIDTYPE_SID, p) {
                        spin_lock_irq(&p->sighand->siglock);
-                       if (p->signal->tty == tty)
+                       if (p->signal->tty == tty) {
                                p->signal->tty = NULL;
+                               /* We defer the dereferences outside fo
+                                  the tasklist lock */
+                               refs++;
+                       }
                        if (!p->signal->leader) {
                                spin_unlock_irq(&p->sighand->siglock);
                                continue;
@@ -1480,19 +549,31 @@ static void do_tty_hangup(struct work_struct *work)
                        __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
                        __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
                        put_pid(p->signal->tty_old_pgrp);  /* A noop */
+                       spin_lock_irqsave(&tty->ctrl_lock, flags);
                        if (tty->pgrp)
                                p->signal->tty_old_pgrp = get_pid(tty->pgrp);
+                       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
                        spin_unlock_irq(&p->sighand->siglock);
                } while_each_pid_task(tty->session, PIDTYPE_SID, p);
        }
        read_unlock(&tasklist_lock);
 
-       tty->flags = 0;
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       clear_bit(TTY_THROTTLED, &tty->flags);
+       clear_bit(TTY_PUSH, &tty->flags);
+       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
        put_pid(tty->session);
        put_pid(tty->pgrp);
        tty->session = NULL;
        tty->pgrp = NULL;
        tty->ctrl_status = 0;
+       set_bit(TTY_HUPPED, &tty->flags);
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+       /* Account for the p->signal references we killed */
+       while (refs--)
+               tty_kref_put(tty);
+
        /*
         * If one of the devices matches a console pointer, we
         * cannot just call hangup() because that will cause
@@ -1500,11 +581,11 @@ static void do_tty_hangup(struct work_struct *work)
         * So we just call close() the right number of times.
         */
        if (cons_filp) {
-               if (tty->driver->close)
+               if (tty->ops->close)
                        for (n = 0; n < closecount; n++)
-                               tty->driver->close(tty, cons_filp);
-       } else if (tty->driver->hangup)
-               (tty->driver->hangup)(tty);
+                               tty->ops->close(tty, cons_filp);
+       } else if (tty->ops->hangup)
+               (tty->ops->hangup)(tty);
        /*
         * We don't want to have driver/ldisc interactions beyond
         * the ones we did here. The driver layer expects no
@@ -1512,10 +593,7 @@ static void do_tty_hangup(struct work_struct *work)
         * can't yet guarantee all that.
         */
        set_bit(TTY_HUPPED, &tty->flags);
-       if (ld) {
-               tty_ldisc_enable(tty);
-               tty_ldisc_deref(ld);
-       }
+       tty_ldisc_enable(tty);
        unlock_kernel();
        if (f)
                fput(f);
@@ -1562,6 +640,23 @@ void tty_vhangup(struct tty_struct *tty)
 EXPORT_SYMBOL(tty_vhangup);
 
 /**
+ *     tty_vhangup_self        -       process vhangup for own ctty
+ *
+ *     Perform a vhangup on the current controlling tty
+ */
+
+void tty_vhangup_self(void)
+{
+       struct tty_struct *tty;
+
+       tty = get_current_tty();
+       if (tty) {
+               tty_vhangup(tty);
+               tty_kref_put(tty);
+       }
+}
+
+/**
  *     tty_hung_up_p           -       was tty hung up
  *     @filp: file pointer of tty
  *
@@ -1576,19 +671,6 @@ int tty_hung_up_p(struct file *filp)
 
 EXPORT_SYMBOL(tty_hung_up_p);
 
-/**
- *     is_tty  -       checker whether file is a TTY
- *     @filp:          file handle that may be a tty
- *
- *     Check if the file handle is a tty handle.
- */
-
-int is_tty(struct file *filp)
-{
-       return filp->f_op->read == tty_read
-               || filp->f_op->read == hung_up_tty_read;
-}
-
 static void session_clear_tty(struct pid *session)
 {
        struct task_struct *p;
@@ -1626,16 +708,17 @@ void disassociate_ctty(int on_exit)
        struct tty_struct *tty;
        struct pid *tty_pgrp = NULL;
 
-       lock_kernel();
+       if (!current->signal->leader)
+               return;
 
-       mutex_lock(&tty_mutex);
        tty = get_current_tty();
        if (tty) {
                tty_pgrp = get_pid(tty->pgrp);
-               mutex_unlock(&tty_mutex);
-               /* XXX: here we race, there is nothing protecting tty */
+               lock_kernel();
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
+               unlock_kernel();
+               tty_kref_put(tty);
        } else if (on_exit) {
                struct pid *old_pgrp;
                spin_lock_irq(&current->sighand->siglock);
@@ -1647,8 +730,6 @@ void disassociate_ctty(int on_exit)
                        kill_pgrp(old_pgrp, SIGCONT, on_exit);
                        put_pid(old_pgrp);
                }
-               mutex_unlock(&tty_mutex);
-               unlock_kernel();
                return;
        }
        if (tty_pgrp) {
@@ -1663,27 +744,27 @@ void disassociate_ctty(int on_exit)
        current->signal->tty_old_pgrp = NULL;
        spin_unlock_irq(&current->sighand->siglock);
 
-       mutex_lock(&tty_mutex);
-       /* It is possible that do_tty_hangup has free'd this tty */
        tty = get_current_tty();
        if (tty) {
+               unsigned long flags;
+               spin_lock_irqsave(&tty->ctrl_lock, flags);
                put_pid(tty->session);
                put_pid(tty->pgrp);
                tty->session = NULL;
                tty->pgrp = NULL;
+               spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+               tty_kref_put(tty);
        } else {
 #ifdef TTY_DEBUG_HANGUP
                printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
                       " = NULL", tty);
 #endif
        }
-       mutex_unlock(&tty_mutex);
 
        /* Now clear signal->tty under the lock */
        read_lock(&tasklist_lock);
        session_clear_tty(task_session(current));
        read_unlock(&tasklist_lock);
-       unlock_kernel();
 }
 
 /**
@@ -1693,8 +774,9 @@ void disassociate_ctty(int on_exit)
 void no_tty(void)
 {
        struct task_struct *tsk = current;
-       if (tsk->signal->leader)
-               disassociate_ctty(0);
+       lock_kernel();
+       disassociate_ctty(0);
+       unlock_kernel();
        proc_clear_tty(tsk);
 }
 
@@ -1714,21 +796,26 @@ void no_tty(void)
  *     but not always.
  *
  *     Locking:
- *             Broken. Relies on BKL which is unsafe here.
+ *             Uses the tty control lock internally
  */
 
 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;
+       }
        tty->stopped = 1;
        if (tty->link && tty->link->packet) {
                tty->ctrl_status &= ~TIOCPKT_START;
                tty->ctrl_status |= TIOCPKT_STOP;
-               wake_up_interruptible(&tty->link->read_wait);
+               wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
        }
-       if (tty->driver->stop)
-               (tty->driver->stop)(tty);
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+       if (tty->ops->stop)
+               (tty->ops->stop)(tty);
 }
 
 EXPORT_SYMBOL(stop_tty);
@@ -1743,21 +830,26 @@ EXPORT_SYMBOL(stop_tty);
  *     driver start method is invoked and the line discipline woken.
  *
  *     Locking:
- *             Broken. Relies on BKL which is unsafe here.
+ *             ctrl_lock
  */
 
 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;
+       }
        tty->stopped = 0;
        if (tty->link && tty->link->packet) {
                tty->ctrl_status &= ~TIOCPKT_STOP;
                tty->ctrl_status |= TIOCPKT_START;
-               wake_up_interruptible(&tty->link->read_wait);
+               wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
        }
-       if (tty->driver->start)
-               (tty->driver->start)(tty);
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+       if (tty->ops->start)
+               (tty->ops->start)(tty);
        /* If we have a running line discipline it may need kicking */
        tty_wakeup(tty);
 }
@@ -1775,10 +867,8 @@ EXPORT_SYMBOL(start_tty);
  *     for hung up devices before calling the line discipline method.
  *
  *     Locking:
- *             Locks the line discipline internally while needed
- *             For historical reasons the line discipline read method is
- *     invoked under the BKL. This will go away in time so do not rely on it
- *     in new code. Multiple read calls may be outstanding in parallel.
+ *             Locks the line discipline internally while needed. Multiple
+ *     read calls may be outstanding in parallel.
  */
 
 static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
@@ -1799,13 +889,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
           situation */
        ld = tty_ldisc_ref_wait(tty);
-       lock_kernel();
-       if (ld->read)
-               i = (ld->read)(tty, file, buf, count);
+       if (ld->ops->read)
+               i = (ld->ops->read)(tty, file, buf, count);
        else
                i = -EIO;
        tty_ldisc_deref(ld);
-       unlock_kernel();
        if (i > 0)
                inode->i_atime = current_fs_time(inode->i_sb);
        return i;
@@ -1814,7 +902,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
 void tty_write_unlock(struct tty_struct *tty)
 {
        mutex_unlock(&tty->atomic_write_lock);
-       wake_up_interruptible(&tty->write_wait);
+       wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
 }
 
 int tty_write_lock(struct tty_struct *tty, int ndelay)
@@ -1870,19 +958,19 @@ static inline ssize_t do_tty_write(
 
        /* write_buf/write_cnt is protected by the atomic_write_lock mutex */
        if (tty->write_cnt < chunk) {
-               unsigned char *buf;
+               unsigned char *buf_chunk;
 
                if (chunk < 1024)
                        chunk = 1024;
 
-               buf = kmalloc(chunk, GFP_KERNEL);
-               if (!buf) {
+               buf_chunk = kmalloc(chunk, GFP_KERNEL);
+               if (!buf_chunk) {
                        ret = -ENOMEM;
                        goto out;
                }
                kfree(tty->write_buf);
                tty->write_cnt = chunk;
-               tty->write_buf = buf;
+               tty->write_buf = buf_chunk;
        }
 
        /* Do the write .. */
@@ -1893,9 +981,7 @@ static inline ssize_t do_tty_write(
                ret = -EFAULT;
                if (copy_from_user(tty->write_buf, buf, size))
                        break;
-               lock_kernel();
                ret = write(tty, file, tty->write_buf, size);
-               unlock_kernel();
                if (ret <= 0)
                        break;
                written += ret;
@@ -1918,6 +1004,33 @@ out:
        return ret;
 }
 
+/**
+ * tty_write_message - write a message to a certain tty, not just the console.
+ * @tty: the destination tty_struct
+ * @msg: the message to write
+ *
+ * This is used for messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ *
+ * We must still hold the BKL and test the CLOSING flag for the moment.
+ */
+
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+       if (tty) {
+               mutex_lock(&tty->atomic_write_lock);
+               lock_kernel();
+               if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+                       unlock_kernel();
+                       tty->ops->write(tty, msg, strlen(msg));
+               } else
+                       unlock_kernel();
+               tty_write_unlock(tty);
+       }
+       return;
+}
+
 
 /**
  *     tty_write               -       write method for tty device file
@@ -1932,9 +1045,7 @@ out:
  *             Locks the line discipline as required
  *             Writes to the tty driver are serialized by the atomic_write_lock
  *     and are then processed in chunks to the device. The line discipline
- *     write method will not be involked in parallel for each device
- *             The line discipline write method is called under the big
- *     kernel lock for historical reasons. New code should not rely on this.
+ *     write method will not be invoked in parallel for each device.
  */
 
 static ssize_t tty_write(struct file *file, const char __user *buf,
@@ -1948,15 +1059,18 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
        tty = (struct tty_struct *)file->private_data;
        if (tty_paranoia_check(tty, inode, "tty_write"))
                return -EIO;
-       if (!tty || !tty->driver->write ||
+       if (!tty || !tty->ops->write ||
                (test_bit(TTY_IO_ERROR, &tty->flags)))
                        return -EIO;
-
+       /* Short term debug to catch buggy drivers */
+       if (tty->ops->write_room == NULL)
+               printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
+                       tty->driver->name);
        ld = tty_ldisc_ref_wait(tty);
-       if (!ld->write)
+       if (!ld->ops->write)
                ret = -EIO;
        else
-               ret = do_tty_write(ld->write, tty, file, buf, count);
+               ret = do_tty_write(ld->ops->write, tty, file, buf, count);
        tty_ldisc_deref(ld);
        return ret;
 }
@@ -2005,7 +1119,7 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p)
 }
 
 /**
- *     pty_line_name   -       generate name for a tty
+ *     tty_line_name   -       generate name for a tty
  *     @driver: the tty driver in use
  *     @index: the minor number
  *     @p: output buffer of at least 7 bytes
@@ -2021,10 +1135,158 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p)
 }
 
 /**
- *     init_dev                -       initialise a tty device
+ *     tty_driver_lookup_tty() - find an existing tty, if any
+ *     @driver: the driver for the tty
+ *     @idx:    the minor number
+ *
+ *     Return the tty, if found or ERR_PTR() otherwise.
+ *
+ *     Locking: tty_mutex must be held. If tty is found, the mutex must
+ *     be held until the 'fast-open' is also done. Will change once we
+ *     have refcounting in the driver and per driver locking
+ */
+static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
+               struct inode *inode, int idx)
+{
+       struct tty_struct *tty;
+
+       if (driver->ops->lookup)
+               return driver->ops->lookup(driver, inode, idx);
+
+       tty = driver->ttys[idx];
+       return tty;
+}
+
+/**
+ *     tty_init_termios        -  helper for termios setup
+ *     @tty: the tty to set up
+ *
+ *     Initialise the termios structures for this tty. Thus runs under
+ *     the tty_mutex currently so we can be relaxed about ordering.
+ */
+
+int tty_init_termios(struct tty_struct *tty)
+{
+       struct ktermios *tp;
+       int idx = tty->index;
+
+       tp = tty->driver->termios[idx];
+       if (tp == NULL) {
+               tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
+               if (tp == NULL)
+                       return -ENOMEM;
+               memcpy(tp, &tty->driver->init_termios,
+                                               sizeof(struct ktermios));
+               tty->driver->termios[idx] = tp;
+       }
+       tty->termios = tp;
+       tty->termios_locked = tp + 1;
+
+       /* Compatibility until drivers always set this */
+       tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
+       tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tty_init_termios);
+
+/**
+ *     tty_driver_install_tty() - install a tty entry in the driver
+ *     @driver: the driver for the tty
+ *     @tty: the tty
+ *
+ *     Install a tty object into the driver tables. The tty->index field
+ *     will be set by the time this is called. This method is responsible
+ *     for ensuring any need additional structures are allocated and
+ *     configured.
+ *
+ *     Locking: tty_mutex for now
+ */
+static int tty_driver_install_tty(struct tty_driver *driver,
+                                               struct tty_struct *tty)
+{
+       int idx = tty->index;
+       int ret;
+
+       if (driver->ops->install) {
+               lock_kernel();
+               ret = driver->ops->install(driver, tty);
+               unlock_kernel();
+               return ret;
+       }
+
+       if (tty_init_termios(tty) == 0) {
+               lock_kernel();
+               tty_driver_kref_get(driver);
+               tty->count++;
+               driver->ttys[idx] = tty;
+               unlock_kernel();
+               return 0;
+       }
+       return -ENOMEM;
+}
+
+/**
+ *     tty_driver_remove_tty() - remove a tty from the driver tables
+ *     @driver: the driver for the tty
+ *     @idx:    the minor number
+ *
+ *     Remvoe a tty object from the driver tables. The tty->index field
+ *     will be set by the time this is called.
+ *
+ *     Locking: tty_mutex for now
+ */
+static void tty_driver_remove_tty(struct tty_driver *driver,
+                                               struct tty_struct *tty)
+{
+       if (driver->ops->remove)
+               driver->ops->remove(driver, tty);
+       else
+               driver->ttys[tty->index] = NULL;
+}
+
+/*
+ *     tty_reopen()    - fast re-open of an open tty
+ *     @tty    - the tty to open
+ *
+ *     Return 0 on success, -errno on error.
+ *
+ *     Locking: tty_mutex must be held from the time the tty was found
+ *              till this open completes.
+ */
+static int tty_reopen(struct tty_struct *tty)
+{
+       struct tty_driver *driver = tty->driver;
+
+       if (test_bit(TTY_CLOSING, &tty->flags))
+               return -EIO;
+
+       if (driver->type == TTY_DRIVER_TYPE_PTY &&
+           driver->subtype == PTY_TYPE_MASTER) {
+               /*
+                * special case for PTY masters: only one open permitted,
+                * and the slave side open count is incremented as well.
+                */
+               if (tty->count)
+                       return -EIO;
+
+               tty->link->count++;
+       }
+       tty->count++;
+       tty->driver = driver; /* N.B. why do this every time?? */
+
+       mutex_lock(&tty->ldisc_mutex);
+       WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
+       mutex_unlock(&tty->ldisc_mutex);
+
+       return 0;
+}
+
+/**
+ *     tty_init_dev            -       initialise a tty device
  *     @driver: tty driver we are opening a device on
  *     @idx: device index
- *     @tty: returned tty structure
+ *     @ret_tty: returned tty structure
+ *     @first_ok: ok to open a new device (used by ptmx)
  *
  *     Prepare a tty device. This may not be a "new" clean device but
  *     could also be an active device. The pty drivers require special
@@ -2044,37 +1306,20 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p)
  * relaxed for the (most common) case of reopening a tty.
  */
 
-static int init_dev(struct tty_driver *driver, int idx,
-       struct tty_struct **ret_tty)
+struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
+                                                               int first_ok)
 {
-       struct tty_struct *tty, *o_tty;
-       struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;
-       struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
-       int retval = 0;
+       struct tty_struct *tty;
+       int retval;
 
-       /* check whether we're reopening an existing tty */
-       if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
-               tty = devpts_get_tty(idx);
-               /*
-                * If we don't have a tty here on a slave open, it's because
-                * the master already started the close process and there's
-                * no relation between devpts file and tty anymore.
-                */
-               if (!tty && driver->subtype == PTY_TYPE_SLAVE) {
-                       retval = -EIO;
-                       goto end_init;
-               }
-               /*
-                * It's safe from now on because init_dev() is called with
-                * tty_mutex held and release_dev() won't change tty->count
-                * or tty->flags without having to grab tty_mutex
-                */
-               if (tty && driver->subtype == PTY_TYPE_MASTER)
-                       tty = tty->link;
-       } else {
-               tty = driver->ttys[idx];
+       lock_kernel();
+       /* Check if pty master is being opened multiple times */
+       if (driver->subtype == PTY_TYPE_MASTER &&
+               (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+               unlock_kernel();
+               return ERR_PTR(-EIO);
        }
-       if (tty) goto fast_track;
+       unlock_kernel();
 
        /*
         * First time open is complex, especially for PTY devices.
@@ -2084,198 +1329,70 @@ static int init_dev(struct tty_driver *driver, int idx,
         * and locked termios may be retained.)
         */
 
-       if (!try_module_get(driver->owner)) {
-               retval = -ENODEV;
-               goto end_init;
-       }
-
-       o_tty = NULL;
-       tp = o_tp = NULL;
-       ltp = o_ltp = NULL;
+       if (!try_module_get(driver->owner))
+               return ERR_PTR(-ENODEV);
 
        tty = alloc_tty_struct();
        if (!tty)
                goto fail_no_mem;
-       initialize_tty_struct(tty);
-       tty->driver = driver;
-       tty->index = idx;
-       tty_line_name(driver, idx, tty->name);
-
-       if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
-               tp_loc = &tty->termios;
-               ltp_loc = &tty->termios_locked;
-       } else {
-               tp_loc = &driver->termios[idx];
-               ltp_loc = &driver->termios_locked[idx];
-       }
-
-       if (!*tp_loc) {
-               tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
-               if (!tp)
-                       goto free_mem_out;
-               *tp = driver->init_termios;
-       }
-
-       if (!*ltp_loc) {
-               ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
-               if (!ltp)
-                       goto free_mem_out;
-       }
-
-       if (driver->type == TTY_DRIVER_TYPE_PTY) {
-               o_tty = alloc_tty_struct();
-               if (!o_tty)
-                       goto free_mem_out;
-               initialize_tty_struct(o_tty);
-               o_tty->driver = driver->other;
-               o_tty->index = idx;
-               tty_line_name(driver->other, idx, o_tty->name);
-
-               if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
-                       o_tp_loc = &o_tty->termios;
-                       o_ltp_loc = &o_tty->termios_locked;
-               } else {
-                       o_tp_loc = &driver->other->termios[idx];
-                       o_ltp_loc = &driver->other->termios_locked[idx];
-               }
-
-               if (!*o_tp_loc) {
-                       o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
-                       if (!o_tp)
-                               goto free_mem_out;
-                       *o_tp = driver->other->init_termios;
-               }
-
-               if (!*o_ltp_loc) {
-                       o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
-                       if (!o_ltp)
-                               goto free_mem_out;
-               }
+       initialize_tty_struct(tty, driver, idx);
 
-               /*
-                * Everything allocated ... set up the o_tty structure.
-                */
-               if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM))
-                       driver->other->ttys[idx] = o_tty;
-               if (!*o_tp_loc)
-                       *o_tp_loc = o_tp;
-               if (!*o_ltp_loc)
-                       *o_ltp_loc = o_ltp;
-               o_tty->termios = *o_tp_loc;
-               o_tty->termios_locked = *o_ltp_loc;
-               driver->other->refcount++;
-               if (driver->subtype == PTY_TYPE_MASTER)
-                       o_tty->count++;
-
-               /* Establish the links in both directions */
-               tty->link   = o_tty;
-               o_tty->link = tty;
+       retval = tty_driver_install_tty(driver, tty);
+       if (retval < 0) {
+               free_tty_struct(tty);
+               module_put(driver->owner);
+               return ERR_PTR(retval);
        }
 
        /*
-        * All structures have been allocated, so now we install them.
-        * Failures after this point use release_tty to clean up, so
-        * there's no need to null out the local pointers.
-        */
-       if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM))
-               driver->ttys[idx] = tty;
-
-       if (!*tp_loc)
-               *tp_loc = tp;
-       if (!*ltp_loc)
-               *ltp_loc = ltp;
-       tty->termios = *tp_loc;
-       tty->termios_locked = *ltp_loc;
-       /* Compatibility until drivers always set this */
-       tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
-       tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
-       driver->refcount++;
-       tty->count++;
-
-       /*
         * Structures all installed ... call the ldisc open routines.
         * If we fail here just call release_tty to clean up.  No need
         * to decrement the use counts, as release_tty doesn't care.
         */
-
-       if (tty->ldisc.open) {
-               retval = (tty->ldisc.open)(tty);
-               if (retval)
-                       goto release_mem_out;
-       }
-       if (o_tty && o_tty->ldisc.open) {
-               retval = (o_tty->ldisc.open)(o_tty);
-               if (retval) {
-                       if (tty->ldisc.close)
-                               (tty->ldisc.close)(tty);
-                       goto release_mem_out;
-               }
-               tty_ldisc_enable(o_tty);
-       }
-       tty_ldisc_enable(tty);
-       goto success;
-
-       /*
-        * This fast open can be used if the tty is already open.
-        * No memory is allocated, and the only failures are from
-        * attempting to open a closing tty or attempting multiple
-        * opens on a pty master.
-        */
-fast_track:
-       if (test_bit(TTY_CLOSING, &tty->flags)) {
-               retval = -EIO;
-               goto end_init;
-       }
-       if (driver->type == TTY_DRIVER_TYPE_PTY &&
-           driver->subtype == PTY_TYPE_MASTER) {
-               /*
-                * special case for PTY masters: only one open permitted,
-                * and the slave side open count is incremented as well.
-                */
-               if (tty->count) {
-                       retval = -EIO;
-                       goto end_init;
-               }
-               tty->link->count++;
-       }
-       tty->count++;
-       tty->driver = driver; /* N.B. why do this every time?? */
-
-       /* FIXME */
-       if (!test_bit(TTY_LDISC, &tty->flags))
-               printk(KERN_ERR "init_dev but no ldisc\n");
-success:
-       *ret_tty = tty;
-
-       /* All paths come through here to release the mutex */
-end_init:
-       return retval;
-
-       /* Release locally allocated memory ... nothing placed in slots */
-free_mem_out:
-       kfree(o_tp);
-       if (o_tty)
-               free_tty_struct(o_tty);
-       kfree(ltp);
-       kfree(tp);
-       free_tty_struct(tty);
+       retval = tty_ldisc_setup(tty, tty->link);
+       if (retval)
+               goto release_mem_out;
+       return tty;
 
 fail_no_mem:
        module_put(driver->owner);
-       retval = -ENOMEM;
-       goto end_init;
+       return ERR_PTR(-ENOMEM);
 
        /* call the tty release_tty routine to clean out this slot */
 release_mem_out:
        if (printk_ratelimit())
-               printk(KERN_INFO "init_dev: ldisc open failed, "
+               printk(KERN_INFO "tty_init_dev: ldisc open failed, "
                                 "clearing slot %d\n", idx);
+       lock_kernel();
        release_tty(tty, idx);
-       goto end_init;
+       unlock_kernel();
+       return ERR_PTR(retval);
+}
+
+void tty_free_termios(struct tty_struct *tty)
+{
+       struct ktermios *tp;
+       int idx = tty->index;
+       /* Kill this flag and push into drivers for locking etc */
+       if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+               /* FIXME: Locking on ->termios array */
+               tp = tty->termios;
+               tty->driver->termios[idx] = NULL;
+               kfree(tp);
+       }
+}
+EXPORT_SYMBOL(tty_free_termios);
+
+void tty_shutdown(struct tty_struct *tty)
+{
+       tty_driver_remove_tty(tty->driver, tty);
+       tty_free_termios(tty);
 }
+EXPORT_SYMBOL(tty_shutdown);
 
 /**
  *     release_one_tty         -       release tty structure memory
+ *     @kref: kref of tty we are obliterating
  *
  *     Releases memory associated with a tty structure, and clears out the
  *     driver table slots. This function is called when a device is no longer
@@ -2285,39 +1402,62 @@ release_mem_out:
  *             tty_mutex - sometimes only
  *             takes the file list lock internally when working on the list
  *     of ttys that the driver keeps.
- *             FIXME: should we require tty_mutex is held here ??
+ *
+ *     This method gets called from a work queue so that the driver private
+ *     cleanup ops can sleep (needed for USB at least)
  */
-static void release_one_tty(struct tty_struct *tty, int idx)
+static void release_one_tty(struct work_struct *work)
 {
-       int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
-       struct ktermios *tp;
-
-       if (!devpts)
-               tty->driver->ttys[idx] = NULL;
-
-       if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-               tp = tty->termios;
-               if (!devpts)
-                       tty->driver->termios[idx] = NULL;
-               kfree(tp);
-
-               tp = tty->termios_locked;
-               if (!devpts)
-                       tty->driver->termios_locked[idx] = NULL;
-               kfree(tp);
-       }
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, hangup_work);
+       struct tty_driver *driver = tty->driver;
 
+       if (tty->ops->cleanup)
+               tty->ops->cleanup(tty);
 
        tty->magic = 0;
-       tty->driver->refcount--;
+       tty_driver_kref_put(driver);
+       module_put(driver->owner);
 
        file_list_lock();
        list_del_init(&tty->tty_files);
        file_list_unlock();
 
+       put_pid(tty->pgrp);
+       put_pid(tty->session);
        free_tty_struct(tty);
 }
 
+static void queue_release_one_tty(struct kref *kref)
+{
+       struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+
+       if (tty->ops->shutdown)
+               tty->ops->shutdown(tty);
+       else
+               tty_shutdown(tty);
+
+       /* The hangup queue is now free so we can reuse it rather than
+          waste a chunk of memory for each port */
+       INIT_WORK(&tty->hangup_work, release_one_tty);
+       schedule_work(&tty->hangup_work);
+}
+
+/**
+ *     tty_kref_put            -       release a tty kref
+ *     @tty: tty device
+ *
+ *     Release a reference to a tty device and if need be let the kref
+ *     layer destruct the object for us
+ */
+
+void tty_kref_put(struct tty_struct *tty)
+{
+       if (tty)
+               kref_put(&tty->kref, queue_release_one_tty);
+}
+EXPORT_SYMBOL(tty_kref_put);
+
 /**
  *     release_tty             -       release tty structure memory
  *
@@ -2329,18 +1469,29 @@ static void release_one_tty(struct tty_struct *tty, int idx)
  *             takes the file list lock internally when working on the list
  *     of ttys that the driver keeps.
  *             FIXME: should we require tty_mutex is held here ??
+ *
  */
 static void release_tty(struct tty_struct *tty, int idx)
 {
-       struct tty_driver *driver = tty->driver;
+       /* This should always be true but check for the moment */
+       WARN_ON(tty->index != idx);
 
        if (tty->link)
-               release_one_tty(tty->link, idx);
-       release_one_tty(tty, idx);
-       module_put(driver->owner);
+               tty_kref_put(tty->link);
+       tty_kref_put(tty);
 }
 
-/*
+/**
+ *     tty_release             -       vfs callback for close
+ *     @inode: inode of tty
+ *     @filp: file pointer for handle to tty
+ *
+ *     Called the last time each file handle is closed that references
+ *     this tty. There may however be several such references.
+ *
+ *     Locking:
+ *             Takes bkl. See tty_release_dev
+ *
  * Even releasing the tty structures is a tricky business.. We have
  * to be very careful that the structures are all released at the
  * same time, as interrupts might otherwise get the wrong pointers.
@@ -2348,21 +1499,21 @@ static void release_tty(struct tty_struct *tty, int idx)
  * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
  * lead to double frees or releasing memory still in use.
  */
-static void release_dev(struct file *filp)
+
+int tty_release(struct inode *inode, struct file *filp)
 {
        struct tty_struct *tty, *o_tty;
        int     pty_master, tty_closing, o_tty_closing, do_sleep;
        int     devpts;
        int     idx;
        char    buf[64];
-       unsigned long flags;
 
        tty = (struct tty_struct *)filp->private_data;
-       if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode,
-                                                       "release_dev"))
-               return;
+       if (tty_paranoia_check(tty, inode, "tty_release_dev"))
+               return 0;
 
-       check_tty_count(tty, "release_dev");
+       lock_kernel();
+       check_tty_count(tty, "tty_release_dev");
 
        tty_fasync(-1, filp, 0);
 
@@ -2374,33 +1525,30 @@ static void release_dev(struct file *filp)
 
 #ifdef TTY_PARANOIA_CHECK
        if (idx < 0 || idx >= tty->driver->num) {
-               printk(KERN_DEBUG "release_dev: bad idx when trying to "
+               printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
                                  "free (%s)\n", tty->name);
-               return;
+               unlock_kernel();
+               return 0;
        }
-       if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+       if (!devpts) {
                if (tty != tty->driver->ttys[idx]) {
-                       printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
+                       unlock_kernel();
+                       printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
                               "for (%s)\n", idx, tty->name);
-                       return;
+                       return 0;
                }
                if (tty->termios != tty->driver->termios[idx]) {
-                       printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
+                       unlock_kernel();
+                       printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
                               "for (%s)\n",
                               idx, tty->name);
-                       return;
-               }
-               if (tty->termios_locked != tty->driver->termios_locked[idx]) {
-                       printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
-                              "termios_locked for (%s)\n",
-                              idx, tty->name);
-                       return;
+                       return 0;
                }
        }
 #endif
 
 #ifdef TTY_DEBUG_HANGUP
-       printk(KERN_DEBUG "release_dev of %s (tty count=%d)...",
+       printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...",
               tty_name(tty, buf), tty->count);
 #endif
 
@@ -2408,33 +1556,30 @@ static void release_dev(struct file *filp)
        if (tty->driver->other &&
             !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
                if (o_tty != tty->driver->other->ttys[idx]) {
-                       printk(KERN_DEBUG "release_dev: other->table[%d] "
+                       unlock_kernel();
+                       printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
                                          "not o_tty for (%s)\n",
                               idx, tty->name);
-                       return;
+                       return 0 ;
                }
                if (o_tty->termios != tty->driver->other->termios[idx]) {
-                       printk(KERN_DEBUG "release_dev: other->termios[%d] "
+                       unlock_kernel();
+                       printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
                                          "not o_termios for (%s)\n",
                               idx, tty->name);
-                       return;
-               }
-               if (o_tty->termios_locked !=
-                     tty->driver->other->termios_locked[idx]) {
-                       printk(KERN_DEBUG "release_dev: other->termios_locked["
-                                         "%d] not o_termios_locked for (%s)\n",
-                              idx, tty->name);
-                       return;
+                       return 0;
                }
                if (o_tty->link != tty) {
-                       printk(KERN_DEBUG "release_dev: bad pty pointers\n");
-                       return;
+                       unlock_kernel();
+                       printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
+                       return 0;
                }
        }
 #endif
-       if (tty->driver->close)
-               tty->driver->close(tty, filp);
+       if (tty->ops->close)
+               tty->ops->close(tty, filp);
 
+       unlock_kernel();
        /*
         * Sanity check: if tty->count is going to zero, there shouldn't be
         * any waiters on tty->read_wait or tty->write_wait.  We test the
@@ -2457,6 +1602,7 @@ static void release_dev(struct file *filp)
                   opens on /dev/tty */
 
                mutex_lock(&tty_mutex);
+               lock_kernel();
                tty_closing = tty->count <= 1;
                o_tty_closing = o_tty &&
                        (o_tty->count <= (pty_master ? 1 : 0));
@@ -2464,29 +1610,30 @@ static void release_dev(struct file *filp)
 
                if (tty_closing) {
                        if (waitqueue_active(&tty->read_wait)) {
-                               wake_up(&tty->read_wait);
+                               wake_up_poll(&tty->read_wait, POLLIN);
                                do_sleep++;
                        }
                        if (waitqueue_active(&tty->write_wait)) {
-                               wake_up(&tty->write_wait);
+                               wake_up_poll(&tty->write_wait, POLLOUT);
                                do_sleep++;
                        }
                }
                if (o_tty_closing) {
                        if (waitqueue_active(&o_tty->read_wait)) {
-                               wake_up(&o_tty->read_wait);
+                               wake_up_poll(&o_tty->read_wait, POLLIN);
                                do_sleep++;
                        }
                        if (waitqueue_active(&o_tty->write_wait)) {
-                               wake_up(&o_tty->write_wait);
+                               wake_up_poll(&o_tty->write_wait, POLLOUT);
                                do_sleep++;
                        }
                }
                if (!do_sleep)
                        break;
 
-               printk(KERN_WARNING "release_dev: %s: read/write wait queue "
+               printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
                                    "active!\n", tty_name(tty, buf));
+               unlock_kernel();
                mutex_unlock(&tty_mutex);
                schedule();
        }
@@ -2498,14 +1645,14 @@ static void release_dev(struct file *filp)
         */
        if (pty_master) {
                if (--o_tty->count < 0) {
-                       printk(KERN_WARNING "release_dev: bad pty slave count "
+                       printk(KERN_WARNING "tty_release_dev: bad pty slave count "
                                            "(%d) for %s\n",
                               o_tty->count, tty_name(o_tty, buf));
                        o_tty->count = 0;
                }
        }
        if (--tty->count < 0) {
-               printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n",
+               printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n",
                       tty->count, tty_name(tty, buf));
                tty->count = 0;
        }
@@ -2550,77 +1697,29 @@ static void release_dev(struct file *filp)
        mutex_unlock(&tty_mutex);
 
        /* check whether both sides are closing ... */
-       if (!tty_closing || (o_tty && !o_tty_closing))
-               return;
+       if (!tty_closing || (o_tty && !o_tty_closing)) {
+               unlock_kernel();
+               return 0;
+       }
 
 #ifdef TTY_DEBUG_HANGUP
        printk(KERN_DEBUG "freeing tty structure...");
 #endif
        /*
-        * 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();
-
-       /*
-        * 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);
-       /*
-        * Shutdown the current line discipline, and reset it to N_TTY.
-        * N.B. why reset ldisc when we're releasing the memory??
-        *
-        * FIXME: this MUST get fixed for the new reflocking
-        */
-       if (tty->ldisc.close)
-               (tty->ldisc.close)(tty);
-       tty_ldisc_put(tty->ldisc.num);
-
-       /*
-        *      Switch the line discipline back
+        * Ask the line discipline code to release its structures
         */
-       tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-       tty_set_termios_ldisc(tty, N_TTY);
-       if (o_tty) {
-               /* FIXME: could o_tty be in setldisc here ? */
-               clear_bit(TTY_LDISC, &o_tty->flags);
-               if (o_tty->ldisc.close)
-                       (o_tty->ldisc.close)(o_tty);
-               tty_ldisc_put(o_tty->ldisc.num);
-               tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));
-               tty_set_termios_ldisc(o_tty, N_TTY);
-       }
+       tty_ldisc_release(tty, o_tty);
        /*
         * The release_tty function takes care of the details of clearing
         * the slots and preserving the termios structure.
         */
        release_tty(tty, idx);
 
-#ifdef CONFIG_UNIX98_PTYS
        /* Make this pty number available for reallocation */
-       if (devpts) {
-               mutex_lock(&allocated_ptys_lock);
-               idr_remove(&allocated_ptys, idx);
-               mutex_unlock(&allocated_ptys_lock);
-       }
-#endif
-
+       if (devpts)
+               devpts_kill_index(inode, idx);
+       unlock_kernel();
+       return 0;
 }
 
 /**
@@ -2639,19 +1738,19 @@ static void release_dev(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 init_dev work.
+ *     Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
  *              tty->count should protect the rest.
  *              ->siglock protects ->signal/->sighand
  */
 
 static int tty_open(struct inode *inode, struct file *filp)
 {
-       struct tty_struct *tty;
+       struct tty_struct *tty = NULL;
        int noctty, retval;
        struct tty_driver *driver;
        int index;
        dev_t device = inode->i_rdev;
-       unsigned short saved_flags = filp->f_flags;
+       unsigned saved_flags = filp->f_flags;
 
        nonseekable_open(inode, filp);
 
@@ -2661,50 +1760,79 @@ retry_open:
        retval = 0;
 
        mutex_lock(&tty_mutex);
+       lock_kernel();
 
        if (device == MKDEV(TTYAUX_MAJOR, 0)) {
                tty = get_current_tty();
                if (!tty) {
+                       unlock_kernel();
                        mutex_unlock(&tty_mutex);
                        return -ENXIO;
                }
-               driver = tty->driver;
+               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 = console_driver;
+               driver = tty_driver_kref_get(console_driver);
                index = fg_console;
                noctty = 1;
                goto got_driver;
        }
 #endif
        if (device == MKDEV(TTYAUX_MAJOR, 1)) {
-               driver = console_device(&index);
-               if (driver) {
-                       /* Don't let /dev/console block */
-                       filp->f_flags |= O_NONBLOCK;
-                       noctty = 1;
-                       goto got_driver;
+               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;
+                       }
                }
+               unlock_kernel();
                mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
 
        driver = get_tty_driver(device, &index);
        if (!driver) {
+               unlock_kernel();
                mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
 got_driver:
-       retval = init_dev(driver, index, &tty);
+       if (!tty) {
+               /* check whether we're reopening an existing tty */
+               tty = tty_driver_lookup_tty(driver, inode, index);
+
+               if (IS_ERR(tty)) {
+                       unlock_kernel();
+                       mutex_unlock(&tty_mutex);
+                       return PTR_ERR(tty);
+               }
+       }
+
+       if (tty) {
+               retval = tty_reopen(tty);
+               if (retval)
+                       tty = ERR_PTR(retval);
+       } else
+               tty = tty_init_dev(driver, index, 0);
+
        mutex_unlock(&tty_mutex);
-       if (retval)
-               return retval;
+       tty_driver_kref_put(driver);
+       if (IS_ERR(tty)) {
+               unlock_kernel();
+               return PTR_ERR(tty);
+       }
 
        filp->private_data = tty;
        file_move(filp, &tty->tty_files);
@@ -2716,8 +1844,8 @@ got_driver:
        printk(KERN_DEBUG "opening %s...", tty->name);
 #endif
        if (!retval) {
-               if (tty->driver->open)
-                       retval = tty->driver->open(tty, filp);
+               if (tty->ops->open)
+                       retval = tty->ops->open(tty, filp);
                else
                        retval = -ENODEV;
        }
@@ -2732,127 +1860,43 @@ got_driver:
                printk(KERN_DEBUG "error %d in opening %s...", retval,
                       tty->name);
 #endif
-               release_dev(filp);
-               if (retval != -ERESTARTSYS)
+               tty_release(inode, filp);
+               if (retval != -ERESTARTSYS) {
+                       unlock_kernel();
                        return retval;
-               if (signal_pending(current))
+               }
+               if (signal_pending(current)) {
+                       unlock_kernel();
                        return retval;
+               }
                schedule();
                /*
                 * Need to reset f_op in case a hangup happened.
                 */
                if (filp->f_op == &hung_up_tty_fops)
                        filp->f_op = &tty_fops;
+               unlock_kernel();
                goto retry_open;
        }
+       unlock_kernel();
 
-       mutex_lock(&tty_mutex);
-       spin_lock_irq(&current->sighand->siglock);
-       if (!noctty &&
-           current->signal->leader &&
-           !current->signal->tty &&
-           tty->session == NULL)
-               __proc_set_tty(current, tty);
-       spin_unlock_irq(&current->sighand->siglock);
-       mutex_unlock(&tty_mutex);
-       tty_audit_opening();
-       return 0;
-}
-
-#ifdef CONFIG_UNIX98_PTYS
-/**
- *     ptmx_open               -       open a unix 98 pty master
- *     @inode: inode of device file
- *     @filp: file pointer to tty
- *
- *     Allocate a unix98 pty master device from the ptmx driver.
- *
- *     Locking: tty_mutex protects theinit_dev work. tty->count should
- *             protect the rest.
- *             allocated_ptys_lock handles the list of free pty numbers
- */
-
-static int ptmx_open(struct inode *inode, struct file *filp)
-{
-       struct tty_struct *tty;
-       int retval;
-       int index;
-       int idr_ret;
-
-       nonseekable_open(inode, filp);
-
-       /* find a device that is not in use. */
-       mutex_lock(&allocated_ptys_lock);
-       if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
-               mutex_unlock(&allocated_ptys_lock);
-               return -ENOMEM;
-       }
-       idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
-       if (idr_ret < 0) {
-               mutex_unlock(&allocated_ptys_lock);
-               if (idr_ret == -EAGAIN)
-                       return -ENOMEM;
-               return -EIO;
-       }
-       if (index >= pty_limit) {
-               idr_remove(&allocated_ptys, index);
-               mutex_unlock(&allocated_ptys_lock);
-               return -EIO;
-       }
-       mutex_unlock(&allocated_ptys_lock);
-
-       mutex_lock(&tty_mutex);
-       retval = init_dev(ptm_driver, index, &tty);
-       mutex_unlock(&tty_mutex);
-
-       if (retval)
-               goto out;
-
-       set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
-       filp->private_data = tty;
-       file_move(filp, &tty->tty_files);
-
-       retval = -ENOMEM;
-       if (devpts_pty_new(tty->link))
-               goto out1;
-
-       check_tty_count(tty, "tty_open");
-       retval = ptm_driver->open(tty, filp);
-       if (!retval) {
-               tty_audit_opening();
-               return 0;
-       }
-out1:
-       release_dev(filp);
-       return retval;
-out:
-       mutex_lock(&allocated_ptys_lock);
-       idr_remove(&allocated_ptys, index);
-       mutex_unlock(&allocated_ptys_lock);
-       return retval;
-}
-#endif
-
-/**
- *     tty_release             -       vfs callback for close
- *     @inode: inode of tty
- *     @filp: file pointer for handle to tty
- *
- *     Called the last time each file handle is closed that references
- *     this tty. There may however be several such references.
- *
- *     Locking:
- *             Takes bkl. See release_dev
- */
 
-static int tty_release(struct inode *inode, struct file *filp)
-{
+       mutex_lock(&tty_mutex);
        lock_kernel();
-       release_dev(filp);
+       spin_lock_irq(&current->sighand->siglock);
+       if (!noctty &&
+           current->signal->leader &&
+           !current->signal->tty &&
+           tty->session == NULL)
+               __proc_set_tty(current, tty);
+       spin_unlock_irq(&current->sighand->siglock);
        unlock_kernel();
+       mutex_unlock(&tty_mutex);
        return 0;
 }
 
+
+
 /**
  *     tty_poll        -       check tty status
  *     @filp: file being polled
@@ -2876,8 +1920,8 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
                return 0;
 
        ld = tty_ldisc_ref_wait(tty);
-       if (ld->poll)
-               ret = (ld->poll)(tty, filp, wait);
+       if (ld->ops->poll)
+               ret = (ld->ops->poll)(tty, filp, wait);
        tty_ldisc_deref(ld);
        return ret;
 }
@@ -2885,21 +1929,24 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
 static int tty_fasync(int fd, struct file *filp, int on)
 {
        struct tty_struct *tty;
-       int retval;
+       unsigned long flags;
+       int retval = 0;
 
+       lock_kernel();
        tty = (struct tty_struct *)filp->private_data;
        if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
-               return 0;
+               goto out;
 
        retval = fasync_helper(fd, filp, on, &tty->fasync);
        if (retval <= 0)
-               return retval;
+               goto out;
 
        if (on) {
                enum pid_type type;
                struct pid *pid;
                if (!waitqueue_active(&tty->read_wait))
                        tty->minimum_to_wake = 1;
+               spin_lock_irqsave(&tty->ctrl_lock, flags);
                if (tty->pgrp) {
                        pid = tty->pgrp;
                        type = PIDTYPE_PGID;
@@ -2907,14 +1954,20 @@ static int tty_fasync(int fd, struct file *filp, int on)
                        pid = task_pid(current);
                        type = PIDTYPE_PID;
                }
+               get_pid(pid);
+               spin_unlock_irqrestore(&tty->ctrl_lock, flags);
                retval = __f_setown(filp, pid, type, 0);
+               put_pid(pid);
                if (retval)
-                       return retval;
+                       goto out;
        } else {
                if (!tty->fasync && !waitqueue_active(&tty->read_wait))
                        tty->minimum_to_wake = N_TTY_BUF_SIZE;
        }
-       return 0;
+       retval = 0;
+out:
+       unlock_kernel();
+       return retval;
 }
 
 /**
@@ -2943,8 +1996,9 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
                return -EPERM;
        if (get_user(ch, p))
                return -EFAULT;
+       tty_audit_tiocsti(tty, ch);
        ld = tty_ldisc_ref_wait(tty);
-       ld->receive_buf(tty, &ch, &mbz, 1);
+       ld->ops->receive_buf(tty, &ch, &mbz, 1);
        tty_ldisc_deref(ld);
        return 0;
 }
@@ -2972,8 +2026,43 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
 }
 
 /**
+ *     tty_do_resize           -       resize event
+ *     @tty: tty being resized
+ *     @rows: rows (character)
+ *     @cols: cols (character)
+ *
+ *     Update the termios variables and send the necessary signals to
+ *     peform a terminal resize correctly
+ */
+
+int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
+{
+       struct pid *pgrp;
+       unsigned long flags;
+
+       /* Lock the tty */
+       mutex_lock(&tty->termios_mutex);
+       if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
+               goto done;
+       /* Get the PID values and reference them so we can
+          avoid holding the tty ctrl lock while sending signals */
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       pgrp = get_pid(tty->pgrp);
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+       if (pgrp)
+               kill_pgrp(pgrp, SIGWINCH, 1);
+       put_pid(pgrp);
+
+       tty->winsize = *ws;
+done:
+       mutex_unlock(&tty->termios_mutex);
+       return 0;
+}
+
+/**
  *     tiocswinsz              -       implement window size set ioctl
- *     @tty; tty
+ *     @tty; tty side of tty
  *     @arg: user buffer for result
  *
  *     Copies the user idea of the window size to the kernel. Traditionally
@@ -2981,43 +2070,21 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
  *     actually has driver level meaning and triggers a VC resize.
  *
  *     Locking:
- *             Called function use the console_sem is used to ensure we do
- *     not try and resize the console twice at once.
- *             The tty->termios_mutex is used to ensure we don't double
- *     resize and get confused. Lock order - tty->termios_mutex before
- *     console sem
+ *             Driver dependant. The default do_resize method takes the
+ *     tty termios mutex and ctrl_lock. The console takes its own lock
+ *     then calls into the default method.
  */
 
-static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
-       struct winsize __user *arg)
+static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
 {
        struct winsize tmp_ws;
-
        if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
                return -EFAULT;
 
-       mutex_lock(&tty->termios_mutex);
-       if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
-               goto done;
-
-#ifdef CONFIG_VT
-       if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
-               if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col,
-                                       tmp_ws.ws_row)) {
-                       mutex_unlock(&tty->termios_mutex);
-                       return -ENXIO;
-               }
-       }
-#endif
-       if (tty->pgrp)
-               kill_pgrp(tty->pgrp, SIGWINCH, 1);
-       if ((real_tty->pgrp != tty->pgrp) && real_tty->pgrp)
-               kill_pgrp(real_tty->pgrp, SIGWINCH, 1);
-       tty->winsize = tmp_ws;
-       real_tty->winsize = tmp_ws;
-done:
-       mutex_unlock(&tty->termios_mutex);
-       return 0;
+       if (tty->ops->resize)
+               return tty->ops->resize(tty, &tmp_ws);
+       else
+               return tty_do_resize(tty, &tmp_ws);
 }
 
 /**
@@ -3063,7 +2130,7 @@ static int tioccons(struct file *file)
  *     the generic functionality existed. This piece of history is preserved
  *     in the expected tty API of posix OS's.
  *
- *     Locking: none, the open fle handle ensures it won't go away.
+ *     Locking: none, the open file handle ensures it won't go away.
  */
 
 static int fionbio(struct file *file, int __user *p)
@@ -3073,10 +2140,12 @@ static int fionbio(struct file *file, int __user *p)
        if (get_user(nonblock, p))
                return -EFAULT;
 
+       spin_lock(&file->f_lock);
        if (nonblock)
                file->f_flags |= O_NONBLOCK;
        else
                file->f_flags &= ~O_NONBLOCK;
+       spin_unlock(&file->f_lock);
        return 0;
 }
 
@@ -3134,6 +2203,27 @@ unlock:
 }
 
 /**
+ *     tty_get_pgrp    -       return a ref counted pgrp pid
+ *     @tty: tty to read
+ *
+ *     Returns a refcounted instance of the pid struct for the process
+ *     group controlling the tty.
+ */
+
+struct pid *tty_get_pgrp(struct tty_struct *tty)
+{
+       unsigned long flags;
+       struct pid *pgrp;
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       pgrp = get_pid(tty->pgrp);
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+       return pgrp;
+}
+EXPORT_SYMBOL_GPL(tty_get_pgrp);
+
+/**
  *     tiocgpgrp               -       get process group
  *     @tty: tty passed by user
  *     @real_tty: tty side of the tty pased by the user if a pty else the tty
@@ -3147,13 +2237,18 @@ unlock:
 
 static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
+       struct pid *pid;
+       int ret;
        /*
         * (tty == real_tty) is a cheap way of
         * testing if the tty is NOT a master pty.
         */
        if (tty == real_tty && current->signal->tty != real_tty)
                return -ENOTTY;
-       return put_user(pid_vnr(real_tty->pgrp), p);
+       pid = tty_get_pgrp(real_tty);
+       ret =  put_user(pid_vnr(pid), p);
+       put_pid(pid);
+       return ret;
 }
 
 /**
@@ -3165,7 +2260,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
  *     permitted where the tty session is our session.
  *
- *     Locking: None
+ *     Locking: RCU, ctrl lock
  */
 
 static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
@@ -3173,6 +2268,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
        struct pid *pgrp;
        pid_t pgrp_nr;
        int retval = tty_check_change(real_tty);
+       unsigned long flags;
 
        if (retval == -EIO)
                return -ENOTTY;
@@ -3195,8 +2291,10 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
        if (session_of_pgrp(pgrp) != task_session(current))
                goto out_unlock;
        retval = 0;
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
        put_pid(real_tty->pgrp);
        real_tty->pgrp = get_pid(pgrp);
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 out_unlock:
        rcu_read_unlock();
        return retval;
@@ -3240,10 +2338,14 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _
 static int tiocsetd(struct tty_struct *tty, int __user *p)
 {
        int ldisc;
+       int ret;
 
        if (get_user(ldisc, p))
                return -EFAULT;
-       return tty_set_ldisc(tty, ldisc);
+
+       ret = tty_set_ldisc(tty, ldisc);
+
+       return ret;
 }
 
 /**
@@ -3261,20 +2363,33 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
 
 static int send_break(struct tty_struct *tty, unsigned int duration)
 {
-       if (tty_write_lock(tty, 0) < 0)
-               return -EINTR;
-       tty->driver->break_ctl(tty, -1);
-       if (!signal_pending(current))
-               msleep_interruptible(duration);
-       tty->driver->break_ctl(tty, 0);
-       tty_write_unlock(tty);
-       if (signal_pending(current))
-               return -EINTR;
-       return 0;
+       int retval;
+
+       if (tty->ops->break_ctl == NULL)
+               return 0;
+
+       if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
+               retval = tty->ops->break_ctl(tty, duration);
+       else {
+               /* Do the work ourselves */
+               if (tty_write_lock(tty, 0) < 0)
+                       return -EINTR;
+               retval = tty->ops->break_ctl(tty, -1);
+               if (retval)
+                       goto out;
+               if (!signal_pending(current))
+                       msleep_interruptible(duration);
+               retval = tty->ops->break_ctl(tty, 0);
+out:
+               tty_write_unlock(tty);
+               if (signal_pending(current))
+                       retval = -EINTR;
+       }
+       return retval;
 }
 
 /**
- *     tiocmget                -       get modem status
+ *     tty_tiocmget            -       get modem status
  *     @tty: tty device
  *     @file: user file pointer
  *     @p: pointer to result
@@ -3289,8 +2404,8 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
 {
        int retval = -EINVAL;
 
-       if (tty->driver->tiocmget) {
-               retval = tty->driver->tiocmget(tty, file);
+       if (tty->ops->tiocmget) {
+               retval = tty->ops->tiocmget(tty, file);
 
                if (retval >= 0)
                        retval = put_user(retval, p);
@@ -3299,7 +2414,7 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
 }
 
 /**
- *     tiocmset                -       set modem status
+ *     tty_tiocmset            -       set modem status
  *     @tty: tty device
  *     @file: user file pointer
  *     @cmd: command - clear bits, set bits or set all
@@ -3314,82 +2429,67 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
 static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
             unsigned __user *p)
 {
-       int retval = -EINVAL;
-
-       if (tty->driver->tiocmset) {
-               unsigned int set, clear, val;
+       int retval;
+       unsigned int set, clear, val;
 
-               retval = get_user(val, p);
-               if (retval)
-                       return retval;
+       if (tty->ops->tiocmset == NULL)
+               return -EINVAL;
 
-               set = clear = 0;
-               switch (cmd) {
-               case TIOCMBIS:
-                       set = val;
-                       break;
-               case TIOCMBIC:
-                       clear = val;
-                       break;
-               case TIOCMSET:
-                       set = val;
-                       clear = ~val;
-                       break;
-               }
+       retval = get_user(val, p);
+       if (retval)
+               return retval;
+       set = clear = 0;
+       switch (cmd) {
+       case TIOCMBIS:
+               set = val;
+               break;
+       case TIOCMBIC:
+               clear = val;
+               break;
+       case TIOCMSET:
+               set = val;
+               clear = ~val;
+               break;
+       }
+       set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
+       clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
+       return tty->ops->tiocmset(tty, file, set, clear);
+}
 
-               set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
-               clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
+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);
 
-               retval = tty->driver->tiocmset(tty, file, set, clear);
-       }
-       return retval;
+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..
  */
-int tty_ioctl(struct inode *inode, struct file *file,
-             unsigned int cmd, unsigned long arg)
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct tty_struct *tty, *real_tty;
        void __user *p = (void __user *)arg;
        int retval;
        struct tty_ldisc *ld;
+       struct inode *inode = file->f_dentry->d_inode;
 
        tty = (struct tty_struct *)file->private_data;
        if (tty_paranoia_check(tty, inode, "tty_ioctl"))
                return -EINVAL;
 
-       /* CHECKME: is this safe as one end closes ? */
-
-       real_tty = tty;
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver->subtype == PTY_TYPE_MASTER)
-               real_tty = tty->link;
-
-       /*
-        * Break handling by driver
-        */
-       if (!tty->driver->break_ctl) {
-               switch (cmd) {
-               case TIOCSBRK:
-               case TIOCCBRK:
-                       if (tty->driver->ioctl)
-                               return tty->driver->ioctl(tty, file, cmd, arg);
-                       return -EINVAL;
-
-               /* These two ioctl's always return success; even if */
-               /* the driver doesn't support them. */
-               case TCSBRK:
-               case TCSBRKP:
-                       if (!tty->driver->ioctl)
-                               return 0;
-                       retval = tty->driver->ioctl(tty, file, cmd, arg);
-                       if (retval == -ENOIOCTLCMD)
-                               retval = 0;
-                       return retval;
-               }
-       }
+       real_tty = tty_pair_get_tty(tty);
 
        /*
         * Factor out some common prep work
@@ -3411,13 +2511,16 @@ int tty_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
+       /*
+        *      Now do the stuff.
+        */
        switch (cmd) {
        case TIOCSTI:
                return tiocsti(tty, p);
        case TIOCGWINSZ:
-               return tiocgwinsz(tty, p);
+               return tiocgwinsz(real_tty, p);
        case TIOCSWINSZ:
-               return tiocswinsz(tty, real_tty, p);
+               return tiocswinsz(real_tty, p);
        case TIOCCONS:
                return real_tty != tty ? -EINVAL : tioccons(file);
        case FIONBIO:
@@ -3442,23 +2545,19 @@ int tty_ioctl(struct inode *inode, struct file *file,
        case TIOCGSID:
                return tiocgsid(tty, real_tty, p);
        case TIOCGETD:
-               /* FIXME: check this is ok */
-               return put_user(tty->ldisc.num, (int __user *)p);
+               return put_user(tty->ldisc->ops->num, (int __user *)p);
        case TIOCSETD:
                return tiocsetd(tty, p);
-#ifdef CONFIG_VT
-       case TIOCLINUX:
-               return tioclinux(tty, arg);
-#endif
        /*
         * Break handling
         */
        case TIOCSBRK:  /* Turn break on, unconditionally */
-               tty->driver->break_ctl(tty, -1);
+               if (tty->ops->break_ctl)
+                       return tty->ops->break_ctl(tty, -1);
                return 0;
-
        case TIOCCBRK:  /* Turn break off, unconditionally */
-               tty->driver->break_ctl(tty, 0);
+               if (tty->ops->break_ctl)
+                       return tty->ops->break_ctl(tty, 0);
                return 0;
        case TCSBRK:   /* SVID version: non-zero arg --> no break */
                /* non-zero arg means wait for all output data
@@ -3487,15 +2586,15 @@ int tty_ioctl(struct inode *inode, struct file *file,
                }
                break;
        }
-       if (tty->driver->ioctl) {
-               retval = (tty->driver->ioctl)(tty, file, cmd, arg);
+       if (tty->ops->ioctl) {
+               retval = (tty->ops->ioctl)(tty, file, cmd, arg);
                if (retval != -ENOIOCTLCMD)
                        return retval;
        }
        ld = tty_ldisc_ref_wait(tty);
        retval = -EINVAL;
-       if (ld->ioctl) {
-               retval = ld->ioctl(tty, file, cmd, arg);
+       if (ld->ops->ioctl) {
+               retval = ld->ops->ioctl(tty, file, cmd, arg);
                if (retval == -ENOIOCTLCMD)
                        retval = -EINVAL;
        }
@@ -3515,15 +2614,15 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
        if (tty_paranoia_check(tty, inode, "tty_ioctl"))
                return -EINVAL;
 
-       if (tty->driver->compat_ioctl) {
-               retval = (tty->driver->compat_ioctl)(tty, file, cmd, arg);
+       if (tty->ops->compat_ioctl) {
+               retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg);
                if (retval != -ENOIOCTLCMD)
                        return retval;
        }
 
        ld = tty_ldisc_ref_wait(tty);
-       if (ld->compat_ioctl)
-               retval = ld->compat_ioctl(tty, file, cmd, arg);
+       if (ld->ops->compat_ioctl)
+               retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
        tty_ldisc_deref(ld);
 
        return retval;
@@ -3566,14 +2665,13 @@ void __do_SAK(struct tty_struct *tty)
 
        tty_ldisc_flush(tty);
 
-       if (tty->driver->flush_buffer)
-               tty->driver->flush_buffer(tty);
+       tty_driver_flush_buffer(tty);
 
        read_lock(&tasklist_lock);
        /* Kill the entire session */
        do_each_pid_task(session, PIDTYPE_SID, p) {
                printk(KERN_NOTICE "SAK: killed process %d"
-                       " (%s): task_session_nr(p)==tty->session\n",
+                       " (%s): task_session(p)==tty->session\n",
                        task_pid_nr(p), p->comm);
                send_sig(SIGKILL, p, 1);
        } while_each_pid_task(session, PIDTYPE_SID, p);
@@ -3583,7 +2681,7 @@ void __do_SAK(struct tty_struct *tty)
        do_each_thread(g, p) {
                if (p->signal->tty == tty) {
                        printk(KERN_NOTICE "SAK: killed process %d"
-                           " (%s): task_session_nr(p)==tty->session\n",
+                           " (%s): task_session(p)==tty->session\n",
                            task_pid_nr(p), p->comm);
                        send_sig(SIGKILL, p, 1);
                        continue;
@@ -3640,112 +2738,6 @@ void do_SAK(struct tty_struct *tty)
 EXPORT_SYMBOL(do_SAK);
 
 /**
- *     flush_to_ldisc
- *     @work: tty structure passed from work queue.
- *
- *     This routine is called out of the software interrupt to flush data
- *     from the buffer chain to the line discipline.
- *
- *     Locking: holds tty->buf.lock to guard buffer list. Drops the lock
- *     while invoking the line discipline receive_buf method. The
- *     receive_buf method is single threaded for each tty instance.
- */
-
-static void flush_to_ldisc(struct work_struct *work)
-{
-       struct tty_struct *tty =
-               container_of(work, struct tty_struct, buf.work.work);
-       unsigned long   flags;
-       struct tty_ldisc *disc;
-       struct tty_buffer *tbuf, *head;
-       char *char_buf;
-       unsigned char *flag_buf;
-
-       disc = tty_ldisc_ref(tty);
-       if (disc == NULL)       /*  !TTY_LDISC */
-               return;
-
-       spin_lock_irqsave(&tty->buf.lock, flags);
-       /* So we know a flush is running */
-       set_bit(TTY_FLUSHING, &tty->flags);
-       head = tty->buf.head;
-       if (head != NULL) {
-               tty->buf.head = NULL;
-               for (;;) {
-                       int count = head->commit - head->read;
-                       if (!count) {
-                               if (head->next == NULL)
-                                       break;
-                               tbuf = head;
-                               head = head->next;
-                               tty_buffer_free(tty, tbuf);
-                               continue;
-                       }
-                       /* Ldisc or user is trying to flush the buffers
-                          we are feeding to the ldisc, stop feeding the
-                          line discipline as we want to empty the queue */
-                       if (test_bit(TTY_FLUSHPENDING, &tty->flags))
-                               break;
-                       if (!tty->receive_room) {
-                               schedule_delayed_work(&tty->buf.work, 1);
-                               break;
-                       }
-                       if (count > tty->receive_room)
-                               count = tty->receive_room;
-                       char_buf = head->char_buf_ptr + head->read;
-                       flag_buf = head->flag_buf_ptr + head->read;
-                       head->read += count;
-                       spin_unlock_irqrestore(&tty->buf.lock, flags);
-                       disc->receive_buf(tty, char_buf, flag_buf, count);
-                       spin_lock_irqsave(&tty->buf.lock, flags);
-               }
-               /* Restore the queue head */
-               tty->buf.head = head;
-       }
-       /* We may have a deferred request to flush the input buffer,
-          if so pull the chain under the lock and empty the queue */
-       if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
-               __tty_buffer_flush(tty);
-               clear_bit(TTY_FLUSHPENDING, &tty->flags);
-               wake_up(&tty->read_wait);
-       }
-       clear_bit(TTY_FLUSHING, &tty->flags);
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
-
-       tty_ldisc_deref(disc);
-}
-
-/**
- *     tty_flip_buffer_push    -       terminal
- *     @tty: tty to push
- *
- *     Queue a push of the terminal flip buffers to the line discipline. This
- *     function must not be called from IRQ context if tty->low_latency is set.
- *
- *     In the event of the queue being busy for flipping the work will be
- *     held off and retried later.
- *
- *     Locking: tty buffer lock. Driver locks in low latency mode.
- */
-
-void tty_flip_buffer_push(struct tty_struct *tty)
-{
-       unsigned long flags;
-       spin_lock_irqsave(&tty->buf.lock, flags);
-       if (tty->buf.tail != NULL)
-               tty->buf.tail->commit = tty->buf.tail->used;
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
-
-       if (tty->low_latency)
-               flush_to_ldisc(&tty->buf.work.work);
-       else
-               schedule_delayed_work(&tty->buf.work, 1);
-}
-
-EXPORT_SYMBOL(tty_flip_buffer_push);
-
-
-/**
  *     initialize_tty_struct
  *     @tty: tty to initialize
  *
@@ -3755,38 +2747,59 @@ EXPORT_SYMBOL(tty_flip_buffer_push);
  *     Locking: none - tty in question must not be exposed at this point
  */
 
-static void initialize_tty_struct(struct tty_struct *tty)
+void initialize_tty_struct(struct tty_struct *tty,
+               struct tty_driver *driver, int idx)
 {
        memset(tty, 0, sizeof(struct tty_struct));
+       kref_init(&tty->kref);
        tty->magic = TTY_MAGIC;
-       tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
+       tty_ldisc_init(tty);
        tty->session = NULL;
        tty->pgrp = NULL;
        tty->overrun_time = jiffies;
        tty->buf.head = tty->buf.tail = NULL;
        tty_buffer_init(tty);
-       INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
        mutex_init(&tty->termios_mutex);
+       mutex_init(&tty->ldisc_mutex);
        init_waitqueue_head(&tty->write_wait);
        init_waitqueue_head(&tty->read_wait);
        INIT_WORK(&tty->hangup_work, do_tty_hangup);
        mutex_init(&tty->atomic_read_lock);
        mutex_init(&tty->atomic_write_lock);
+       mutex_init(&tty->output_lock);
+       mutex_init(&tty->echo_lock);
        spin_lock_init(&tty->read_lock);
+       spin_lock_init(&tty->ctrl_lock);
        INIT_LIST_HEAD(&tty->tty_files);
        INIT_WORK(&tty->SAK_work, do_SAK_work);
+
+       tty->driver = driver;
+       tty->ops = driver->ops;
+       tty->index = idx;
+       tty_line_name(driver, idx, tty->name);
 }
 
-/*
- * The default put_char routine if the driver did not define one.
+/**
+ *     tty_put_char    -       write one character to a tty
+ *     @tty: tty
+ *     @ch: character
+ *
+ *     Write one byte to the tty using the provided put_char method
+ *     if present. Returns the number of characters successfully output.
+ *
+ *     Note: the specific put_char operation in the driver layer may go
+ *     away soon. Don't call it directly, use this method
  */
 
-static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
+int tty_put_char(struct tty_struct *tty, unsigned char ch)
 {
-       tty->driver->write(tty, &ch, 1);
+       if (tty->ops->put_char)
+               return tty->ops->put_char(tty, ch);
+       return tty->ops->write(tty, &ch, 1);
 }
+EXPORT_SYMBOL_GPL(tty_put_char);
 
-static struct class *tty_class;
+struct class *tty_class;
 
 /**
  *     tty_register_device - register a tty device
@@ -3824,8 +2837,9 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index,
        else
                tty_line_name(driver, index, name);
 
-       return device_create(tty_class, device, dev, name);
+       return device_create(tty_class, device, dev, NULL, name);
 }
+EXPORT_SYMBOL(tty_register_device);
 
 /**
  *     tty_unregister_device - unregister a tty device
@@ -3843,8 +2857,6 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index)
        device_destroy(tty_class,
                MKDEV(driver->major, driver->minor_start) + index);
 }
-
-EXPORT_SYMBOL(tty_register_device);
 EXPORT_SYMBOL(tty_unregister_device);
 
 struct tty_driver *alloc_tty_driver(int lines)
@@ -3853,56 +2865,65 @@ struct tty_driver *alloc_tty_driver(int lines)
 
        driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
        if (driver) {
+               kref_init(&driver->kref);
                driver->magic = TTY_DRIVER_MAGIC;
                driver->num = lines;
                /* later we'll move allocation of tables here */
        }
        return driver;
 }
+EXPORT_SYMBOL(alloc_tty_driver);
 
-void put_tty_driver(struct tty_driver *driver)
+static void destruct_tty_driver(struct kref *kref)
 {
+       struct tty_driver *driver = container_of(kref, struct tty_driver, kref);
+       int i;
+       struct ktermios *tp;
+       void *p;
+
+       if (driver->flags & TTY_DRIVER_INSTALLED) {
+               /*
+                * Free the termios and termios_locked structures because
+                * we don't want to get memory leaks when modular tty
+                * drivers are removed from the kernel.
+                */
+               for (i = 0; i < driver->num; i++) {
+                       tp = driver->termios[i];
+                       if (tp) {
+                               driver->termios[i] = NULL;
+                               kfree(tp);
+                       }
+                       if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
+                               tty_unregister_device(driver, i);
+               }
+               p = driver->ttys;
+               proc_tty_unregister_driver(driver);
+               driver->ttys = NULL;
+               driver->termios = NULL;
+               kfree(p);
+               cdev_del(&driver->cdev);
+       }
        kfree(driver);
 }
 
-void tty_set_operations(struct tty_driver *driver,
-                       const struct tty_operations *op)
+void tty_driver_kref_put(struct tty_driver *driver)
 {
-       driver->open = op->open;
-       driver->close = op->close;
-       driver->write = op->write;
-       driver->put_char = op->put_char;
-       driver->flush_chars = op->flush_chars;
-       driver->write_room = op->write_room;
-       driver->chars_in_buffer = op->chars_in_buffer;
-       driver->ioctl = op->ioctl;
-       driver->compat_ioctl = op->compat_ioctl;
-       driver->set_termios = op->set_termios;
-       driver->throttle = op->throttle;
-       driver->unthrottle = op->unthrottle;
-       driver->stop = op->stop;
-       driver->start = op->start;
-       driver->hangup = op->hangup;
-       driver->break_ctl = op->break_ctl;
-       driver->flush_buffer = op->flush_buffer;
-       driver->set_ldisc = op->set_ldisc;
-       driver->wait_until_sent = op->wait_until_sent;
-       driver->send_xchar = op->send_xchar;
-       driver->read_proc = op->read_proc;
-       driver->write_proc = op->write_proc;
-       driver->tiocmget = op->tiocmget;
-       driver->tiocmset = op->tiocmset;
-#ifdef CONFIG_CONSOLE_POLL
-       driver->poll_init = op->poll_init;
-       driver->poll_get_char = op->poll_get_char;
-       driver->poll_put_char = op->poll_put_char;
-#endif
+       kref_put(&driver->kref, destruct_tty_driver);
 }
+EXPORT_SYMBOL(tty_driver_kref_put);
 
+void tty_set_operations(struct tty_driver *driver,
+                       const struct tty_operations *op)
+{
+       driver->ops = op;
+};
+EXPORT_SYMBOL(tty_set_operations);
 
-EXPORT_SYMBOL(alloc_tty_driver);
+void put_tty_driver(struct tty_driver *d)
+{
+       tty_driver_kref_put(d);
+}
 EXPORT_SYMBOL(put_tty_driver);
-EXPORT_SYMBOL(tty_set_operations);
 
 /*
  * Called by a tty driver to register itself.
@@ -3914,11 +2935,8 @@ int tty_register_driver(struct tty_driver *driver)
        dev_t dev;
        void **p = NULL;
 
-       if (driver->flags & TTY_DRIVER_INSTALLED)
-               return 0;
-
        if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
-               p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
+               p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
                if (!p)
                        return -ENOMEM;
        }
@@ -3942,12 +2960,9 @@ int tty_register_driver(struct tty_driver *driver)
        if (p) {
                driver->ttys = (struct tty_struct **)p;
                driver->termios = (struct ktermios **)(p + driver->num);
-               driver->termios_locked = (struct ktermios **)
-                                                       (p + driver->num * 2);
        } else {
                driver->ttys = NULL;
                driver->termios = NULL;
-               driver->termios_locked = NULL;
        }
 
        cdev_init(&driver->cdev, &tty_fops);
@@ -3956,14 +2971,11 @@ int tty_register_driver(struct tty_driver *driver)
        if (error) {
                unregister_chrdev_region(dev, driver->num);
                driver->ttys = NULL;
-               driver->termios = driver->termios_locked = NULL;
+               driver->termios = NULL;
                kfree(p);
                return error;
        }
 
-       if (!driver->put_char)
-               driver->put_char = tty_default_put_char;
-
        mutex_lock(&tty_mutex);
        list_add(&driver->tty_drivers, &tty_drivers);
        mutex_unlock(&tty_mutex);
@@ -3973,6 +2985,7 @@ int tty_register_driver(struct tty_driver *driver)
                    tty_register_device(driver, i, NULL);
        }
        proc_tty_register_driver(driver);
+       driver->flags |= TTY_DRIVER_INSTALLED;
        return 0;
 }
 
@@ -3983,46 +2996,19 @@ EXPORT_SYMBOL(tty_register_driver);
  */
 int tty_unregister_driver(struct tty_driver *driver)
 {
-       int i;
-       struct ktermios *tp;
-       void *p;
-
+#if 0
+       /* FIXME */
        if (driver->refcount)
                return -EBUSY;
-
+#endif
        unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
                                driver->num);
        mutex_lock(&tty_mutex);
        list_del(&driver->tty_drivers);
        mutex_unlock(&tty_mutex);
-
-       /*
-        * Free the termios and termios_locked structures because
-        * we don't want to get memory leaks when modular tty
-        * drivers are removed from the kernel.
-        */
-       for (i = 0; i < driver->num; i++) {
-               tp = driver->termios[i];
-               if (tp) {
-                       driver->termios[i] = NULL;
-                       kfree(tp);
-               }
-               tp = driver->termios_locked[i];
-               if (tp) {
-                       driver->termios_locked[i] = NULL;
-                       kfree(tp);
-               }
-               if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
-                       tty_unregister_device(driver, i);
-       }
-       p = driver->ttys;
-       proc_tty_unregister_driver(driver);
-       driver->ttys = NULL;
-       driver->termios = driver->termios_locked = NULL;
-       kfree(p);
-       cdev_del(&driver->cdev);
        return 0;
 }
+
 EXPORT_SYMBOL(tty_unregister_driver);
 
 dev_t tty_devnum(struct tty_struct *tty)
@@ -4033,23 +3019,35 @@ EXPORT_SYMBOL(tty_devnum);
 
 void proc_clear_tty(struct task_struct *p)
 {
-       spin_lock_irq(&p->sighand->siglock);
+       unsigned long flags;
+       struct tty_struct *tty;
+       spin_lock_irqsave(&p->sighand->siglock, flags);
+       tty = p->signal->tty;
        p->signal->tty = NULL;
-       spin_unlock_irq(&p->sighand->siglock);
+       spin_unlock_irqrestore(&p->sighand->siglock, flags);
+       tty_kref_put(tty);
 }
-EXPORT_SYMBOL(proc_clear_tty);
+
+/* Called under the sighand lock */
 
 static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
 {
        if (tty) {
-               /* We should not have a session or pgrp to here but.... */
+               unsigned long flags;
+               /* We should not have a session or pgrp to put here but.... */
+               spin_lock_irqsave(&tty->ctrl_lock, flags);
                put_pid(tty->session);
                put_pid(tty->pgrp);
-               tty->session = get_pid(task_session(tsk));
                tty->pgrp = get_pid(task_pgrp(tsk));
+               spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+               tty->session = get_pid(task_session(tsk));
+               if (tsk->signal->tty) {
+                       printk(KERN_DEBUG "tty not NULL!!\n");
+                       tty_kref_put(tsk->signal->tty);
+               }
        }
        put_pid(tsk->signal->tty_old_pgrp);
-       tsk->signal->tty = tty;
+       tsk->signal->tty = tty_kref_get(tty);
        tsk->signal->tty_old_pgrp = NULL;
 }
 
@@ -4063,18 +3061,20 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
 struct tty_struct *get_current_tty(void)
 {
        struct tty_struct *tty;
-       WARN_ON_ONCE(!mutex_is_locked(&tty_mutex));
-       tty = current->signal->tty;
-       /*
-        * session->tty can be changed/cleared from under us, make sure we
-        * issue the load. The obtained pointer, when not NULL, is valid as
-        * long as we hold tty_mutex.
-        */
-       barrier();
+       unsigned long flags;
+
+       spin_lock_irqsave(&current->sighand->siglock, flags);
+       tty = tty_kref_get(current->signal->tty);
+       spin_unlock_irqrestore(&current->sighand->siglock, flags);
        return tty;
 }
 EXPORT_SYMBOL_GPL(get_current_tty);
 
+void tty_default_fops(struct file_operations *fops)
+{
+       *fops = tty_fops;
+}
+
 /*
  * Initialize the console device. This is called *early*, so
  * we can't necessarily depend on lots of kernel help here.
@@ -4086,7 +3086,7 @@ void __init console_init(void)
        initcall_t *call;
 
        /* Setup the default TTY line discipline. */
-       (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
+       tty_ldisc_begin();
 
        /*
         * set up the console device so that later boot sequences can
@@ -4099,11 +3099,22 @@ void __init console_init(void)
        }
 }
 
+static char *tty_devnode(struct device *dev, mode_t *mode)
+{
+       if (!mode)
+               return NULL;
+       if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||
+           dev->devt == MKDEV(TTYAUX_MAJOR, 2))
+               *mode = 0666;
+       return NULL;
+}
+
 static int __init tty_class_init(void)
 {
        tty_class = class_create(THIS_MODULE, "tty");
        if (IS_ERR(tty_class))
                return PTR_ERR(tty_class);
+       tty_class->devnode = tty_devnode;
        return 0;
 }
 
@@ -4112,12 +3123,6 @@ postcore_initcall(tty_class_init);
 /* 3/2004 jmc: why do these devices exist? */
 
 static struct cdev tty_cdev, console_cdev;
-#ifdef CONFIG_UNIX98_PTYS
-static struct cdev ptmx_cdev;
-#endif
-#ifdef CONFIG_VT
-static struct cdev vc0_cdev;
-#endif
 
 /*
  * Ok, now we can initialize the rest of the tty devices and can count
@@ -4129,30 +3134,18 @@ static int __init tty_init(void)
        if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
                panic("Couldn't register /dev/tty driver\n");
-       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), "tty");
+       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
+                             "tty");
 
        cdev_init(&console_cdev, &console_fops);
        if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
                panic("Couldn't register /dev/console driver\n");
-       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), "console");
-
-#ifdef CONFIG_UNIX98_PTYS
-       cdev_init(&ptmx_cdev, &ptmx_fops);
-       if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
-           register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
-               panic("Couldn't register /dev/ptmx driver\n");
-       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), "ptmx");
-#endif
+       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
+                             "console");
 
 #ifdef CONFIG_VT
-       cdev_init(&vc0_cdev, &console_fops);
-       if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
-           register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
-               panic("Couldn't register /dev/tty0 driver\n");
-       device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), "tty0");
-
-       vty_init();
+       vty_init(&console_fops);
 #endif
        return 0;
 }