edac: add e752x parameter for sysbus_parity selection
[safe/jmp/linux-2.6] / drivers / char / tty_io.c
index 615e934..98b65a2 100644 (file)
@@ -19,7 +19,7 @@
  * Also restructured routines so that there is more of a separation
  * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
  * the low-level tty routines (serial.c, pty.c, console.c).  This
- * makes for cleaner and more compact code.  -TYT, 9/17/92 
+ * makes for cleaner and more compact code.  -TYT, 9/17/92
  *
  * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
  * which can be dynamically activated and de-activated by the line
@@ -41,7 +41,7 @@
  *
  * New TIOCLINUX variants added.
  *     -- mj@k332.feld.cvut.cz, 19-Nov-95
- * 
+ *
  * Restrict vt switching via ioctl()
  *      -- grif@cs.ucr.edu, 5-Dec-95
  *
@@ -62,7 +62,8 @@
  *      -- Russell King <rmk@arm.linux.org.uk>
  *
  * Move do_SAK() into process context.  Less stack use in devfs functions.
- * alloc_tty_struct() always uses kmalloc() -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
+ * alloc_tty_struct() always uses kmalloc()
+ *                      -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
  */
 
 #include <linux/types.h>
 #include <linux/selection.h>
 
 #include <linux/kmod.h>
+#include <linux/nsproxy.h>
 
 #undef TTY_DEBUG_HANGUP
 
 #define TTY_PARANOIA_CHECK 1
 #define CHECK_TTY_COUNT 1
 
-struct termios tty_std_termios = {     /* for the benefit of tty drivers  */
+struct ktermios tty_std_termios = {    /* for the benefit of tty drivers  */
        .c_iflag = ICRNL | IXON,
        .c_oflag = OPOST | ONLCR,
        .c_cflag = B38400 | CS8 | CREAD | HUPCL,
        .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
                   ECHOCTL | ECHOKE | IEXTEN,
-       .c_cc = INIT_C_CC
+       .c_cc = INIT_C_CC,
+       .c_ispeed = 38400,
+       .c_ospeed = 38400
 };
 
 EXPORT_SYMBOL(tty_std_termios);
@@ -123,49 +127,69 @@ EXPORT_SYMBOL(tty_std_termios);
 /* This list gets poked at by procfs and various bits of boot up code. This
    could do with some rationalisation such as pulling the tty proc function
    into this file */
-   
+
 LIST_HEAD(tty_drivers);                        /* linked list of tty drivers */
 
-/* Semaphore to protect creating and releasing a tty. This is shared with
+/* Mutex to protect creating and releasing a tty. This is shared with
    vt.c for deeply disgusting hack reasons */
 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 */
+extern int pty_limit;                  /* Config limit on Unix98 ptys */
 static DEFINE_IDR(allocated_ptys);
-static DECLARE_MUTEX(allocated_ptys_lock);
+static DEFINE_MUTEX(allocated_ptys_lock);
 static int ptmx_open(struct inode *, struct file *);
 #endif
 
-extern void disable_early_printk(void);
-
 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 *);
+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,
+int tty_ioctl(struct inode *inode, struct file *file,
              unsigned int cmd, unsigned long arg);
-static int tty_fasync(int fd, struct file * filp, int on);
-static void release_mem(struct tty_struct *tty, int idx);
+#ifdef CONFIG_COMPAT
+static long tty_compat_ioctl(struct file *file, unsigned int cmd,
+                               unsigned long arg);
+#else
+#define tty_compat_ioctl NULL
+#endif
+static int tty_fasync(int fd, struct file *filp, int on);
+static void release_tty(struct tty_struct *tty, int idx);
+static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
 
+/**
+ *     alloc_tty_struct        -       allocate a tty object
+ *
+ *     Return a new empty tty structure. The data fields have not
+ *     been initialized in any way but has been zeroed
+ *
+ *     Locking: none
+ */
 
 static struct tty_struct *alloc_tty_struct(void)
 {
-       struct tty_struct *tty;
-
-       tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
-       if (tty)
-               memset(tty, 0, sizeof(struct tty_struct));
-       return tty;
+       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
+ *
+ *     Free the write buffers, tty queue and tty memory itself.
+ *
+ *     Locking: none. Must be called after tty is definitely unused
+ */
+
 static inline void free_tty_struct(struct tty_struct *tty)
 {
        kfree(tty->write_buf);
@@ -175,6 +199,17 @@ static inline void free_tty_struct(struct tty_struct *tty)
 
 #define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)
 
+/**
+ *     tty_name        -       return tty naming
+ *     @tty: tty structure
+ *     @buf: buffer for output
+ *
+ *     Convert a tty structure into a name. The name reflects the kernel
+ *     naming policy and if udev is in use may not reflect user space
+ *
+ *     Locking: none
+ */
+
 char *tty_name(struct tty_struct *tty, char *buf)
 {
        if (!tty) /* Hmm.  NULL pointer.  That's fun. */
@@ -211,7 +246,7 @@ 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++;
@@ -226,7 +261,7 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
                                    "!= #fd's(%d) in %s\n",
                       tty->name, tty->count, count, routine);
                return count;
-       }       
+       }
 #endif
        return 0;
 }
@@ -235,32 +270,70 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
  * 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) {
+       while ((thead = tty->buf.head) != NULL) {
                tty->buf.head = thead->next;
                kfree(thead);
        }
-       while((thead = tty->buf.free) != NULL) {
+       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;
 }
 
-static struct tty_buffer *tty_buffer_alloc(size_t size)
+/**
+ *     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 = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
-       if(p == NULL)
+       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;
@@ -269,18 +342,28 @@ static struct tty_buffer *tty_buffer_alloc(size_t size)
        p->read = 0;
        p->char_buf_ptr = (char *)(p->data);
        p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
-/*     printk("Flip create %p\n", p); */
+       tty->buf.memory_used += size;
        return p;
 }
 
-/* Must be called with the tty_read lock held. This needs to acquire strategy
-   code to decide if we should kfree or relink a given expired buffer */
+/**
+ *     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 */
-/*     printk("Flip dispose %p\n", b); */
-       if(b->size >= 512)
+       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;
@@ -288,31 +371,103 @@ static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *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) {
+       while ((*tbh) != NULL) {
                struct tty_buffer *t = *tbh;
-               if(t->size >= size) {
+               if (t->size >= size) {
                        *tbh = t->next;
                        t->next = NULL;
                        t->used = 0;
                        t->commit = 0;
                        t->read = 0;
-                       /* DEBUG ONLY */
-/*                     memset(t->data, '*', size); */
-/*                     printk("Flip recycle %p\n", t); */
+                       tty->buf.memory_used += t->size;
                        return t;
                }
                tbh = &((*tbh)->next);
        }
        /* Round the buffer size out */
-       size = (size + 0xFF) & ~ 0xFF;
-       return tty_buffer_alloc(size);
+       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;
@@ -347,6 +502,18 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t 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)
 {
@@ -355,21 +522,34 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
                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))
+               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));
+               /* 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)
 {
@@ -378,7 +558,7 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
                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))
+               if (unlikely(space == 0))
                        break;
                memcpy(tb->char_buf_ptr + tb->used, chars, space);
                memcpy(tb->flag_buf_ptr + tb->used, flags, space);
@@ -386,14 +566,24 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
                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));
+               /* 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;
@@ -405,15 +595,23 @@ void tty_schedule_flip(struct tty_struct *tty)
 }
 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 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)) {
@@ -427,15 +625,24 @@ int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_
 
 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 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)) {
@@ -451,17 +658,23 @@ 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 
+ *     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)
 {
-       down(&tty->termios_sem);
+       mutex_lock(&tty->termios_mutex);
        tty->termios->c_line = num;
-       up(&tty->termios_sem);
+       mutex_unlock(&tty->termios_mutex);
 }
 
 /*
@@ -469,30 +682,56 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
  *     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);
-static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */
+/* 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;
@@ -512,6 +751,19 @@ int tty_unregister_ldisc(int disc)
 }
 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;
@@ -519,20 +771,18 @@ struct tty_ldisc *tty_ldisc_get(int disc)
 
        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 (ld->flags & LDISC_FLAG_DEFINED) {
                /* If the module is being unloaded we can't use it */
                if (!try_module_get(ld->owner))
-                       ld = NULL;
+                       ld = NULL;
                else /* lock it */
                        ld->refcount++;
-       }
-       else
+       } else
                ld = NULL;
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);
        return ld;
@@ -540,13 +790,24 @@ struct tty_ldisc *tty_ldisc_get(int disc)
 
 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);
@@ -554,9 +815,22 @@ void tty_ldisc_put(int disc)
        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;
@@ -571,6 +845,8 @@ static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
  *     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)
@@ -578,11 +854,10 @@ 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))
-       {
+       if (test_bit(TTY_LDISC, &tty->flags)) {
                ld->refcount++;
                ret = 1;
        }
@@ -594,21 +869,23 @@ static int tty_ldisc_try(struct tty_struct *tty)
  *     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 
+ *     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)
+       if (tty->ldisc.refcount == 0)
                printk(KERN_ERR "tty_ldisc_ref_wait\n");
        return &tty->ldisc;
 }
@@ -619,14 +896,16 @@ 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 
+ *     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))
+       if (tty_ldisc_try(tty))
                return &tty->ldisc;
        return NULL;
 }
@@ -639,20 +918,22 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref);
  *
  *     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)
+       if (ld->refcount == 0)
                printk(KERN_ERR "tty_ldisc_deref: no references.\n");
        else
                ld->refcount--;
-       if(ld->refcount == 0)
+       if (ld->refcount == 0)
                wake_up(&tty_ldisc_wait);
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 }
@@ -664,7 +945,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_deref);
  *     @tty: terminal to activate ldisc on
  *
  *     Set the TTY_LDISC flag when the line discipline can be called
- *     again. Do neccessary wakeups for existing sleepers.
+ *     again. Do necessary wakeups for existing sleepers.
  *
  *     Note: nobody should set this bit except via this function. Clearing
  *     directly is allowed.
@@ -675,7 +956,7 @@ 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
@@ -683,8 +964,11 @@ static void tty_ldisc_enable(struct tty_struct *tty)
  *
  *     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;
@@ -711,13 +995,6 @@ restart:
                return -EINVAL;
 
        /*
-        *      No more input please, we are switching. The new ldisc
-        *      will update this value in the ldisc open function
-        */
-
-       tty->receive_room = 0;
-
-       /*
         *      Problem: What do we do if this blocks ?
         */
 
@@ -728,6 +1005,13 @@ restart:
                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;
 
@@ -740,7 +1024,7 @@ restart:
 
        spin_lock_irqsave(&tty_ldisc_lock, flags);
        if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
-               if(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);
@@ -749,14 +1033,14 @@ restart:
                         * 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.
+                        * 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) {
+               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)
@@ -764,9 +1048,10 @@ restart:
                        goto restart;
                }
        }
-
-       /* if the TTY_LDISC bit is set, then we are racing against another ldisc change */
-
+       /*
+        *      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);
@@ -790,7 +1075,6 @@ restart:
        /*
         * Wait for ->hangup_work and ->buf.work handlers to terminate
         */
-        
        flush_scheduled_work();
        /* Shutdown the current discipline. */
        if (tty->ldisc.close)
@@ -824,21 +1108,21 @@ restart:
        /* 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)
@@ -846,9 +1130,17 @@ restart:
        return retval;
 }
 
-/*
- * This routine returns a tty driver structure, given a device number
+/**
+ *     get_tty_driver          -       find device of a tty
+ *     @dev_t: device identifier
+ *     @index: returns the index of the tty
+ *
+ *     This routine returns a tty driver structure, given a device number
+ *     and also passes back the index number.
+ *
+ *     Locking: caller must hold tty_mutex
  */
+
 static struct tty_driver *get_tty_driver(dev_t device, int *index)
 {
        struct tty_driver *p;
@@ -863,96 +1155,155 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
        return NULL;
 }
 
-/*
- * If we try to write to, or set the state of, a terminal and we're
- * not in the foreground, send a SIGTTOU.  If the signal is blocked or
- * ignored, go ahead and perform the operation.  (POSIX 7.2)
+#ifdef CONFIG_CONSOLE_POLL
+
+/**
+ *     tty_find_polling_driver -       find device of a polled tty
+ *     @name: name string to match
+ *     @line: pointer to resulting tty line nr
+ *
+ *     This routine returns a tty driver structure, given a name
+ *     and the condition that the tty driver is capable of polled
+ *     operation.
+ */
+struct tty_driver *tty_find_polling_driver(char *name, int *line)
+{
+       struct tty_driver *p, *res = NULL;
+       int tty_line = 0;
+       char *str;
+
+       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 = NULL;
+
+               if (tty_line >= 0 && tty_line <= p->num && p->poll_init &&
+                               !p->poll_init(p, tty_line, str)) {
+
+                       res = p;
+                       *line = tty_line;
+                       break;
+               }
+       }
+       mutex_unlock(&tty_mutex);
+
+       return res;
+}
+EXPORT_SYMBOL_GPL(tty_find_polling_driver);
+#endif
+
+/**
+ *     tty_check_change        -       check for POSIX terminal changes
+ *     @tty: tty to check
+ *
+ *     If we try to write to, or set the state of, a terminal and we're
+ *     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
  */
-int tty_check_change(struct tty_struct * tty)
+
+int tty_check_change(struct tty_struct *tty)
 {
        if (current->signal->tty != tty)
                return 0;
-       if (tty->pgrp <= 0) {
-               printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n");
+       if (!tty->pgrp) {
+               printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");
                return 0;
        }
-       if (process_group(current) == tty->pgrp)
+       if (task_pgrp(current) == tty->pgrp)
                return 0;
        if (is_ignored(SIGTTOU))
                return 0;
-       if (is_orphaned_pgrp(process_group(current)))
+       if (is_current_pgrp_orphaned())
                return -EIO;
-       (void) kill_pg(process_group(current), SIGTTOU, 1);
+       kill_pgrp(task_pgrp(current), SIGTTOU, 1);
+       set_thread_flag(TIF_SIGPENDING);
        return -ERESTARTSYS;
 }
 
 EXPORT_SYMBOL(tty_check_change);
 
-static ssize_t hung_up_tty_read(struct file * file, char __user * buf,
+static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
 {
        return 0;
 }
 
-static ssize_t hung_up_tty_write(struct file * file, const char __user * buf,
+static ssize_t hung_up_tty_write(struct file *file, const char __user *buf,
                                 size_t count, loff_t *ppos)
 {
        return -EIO;
 }
 
 /* No kernel lock held - none needed ;) */
-static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait)
+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,
+static int hung_up_tty_ioctl(struct inode *inode, struct file *file,
                             unsigned int cmd, unsigned long arg)
 {
        return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
 }
 
-static struct file_operations tty_fops = {
+static long hung_up_tty_compat_ioctl(struct file *file,
+                                    unsigned int cmd, unsigned long arg)
+{
+       return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
+}
+
+static const struct file_operations tty_fops = {
        .llseek         = no_llseek,
        .read           = tty_read,
        .write          = tty_write,
        .poll           = tty_poll,
        .ioctl          = tty_ioctl,
+       .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
        .fasync         = tty_fasync,
 };
 
 #ifdef CONFIG_UNIX98_PTYS
-static struct file_operations ptmx_fops = {
+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 struct file_operations console_fops = {
+static const struct file_operations console_fops = {
        .llseek         = no_llseek,
        .read           = tty_read,
        .write          = redirected_tty_write,
        .poll           = tty_poll,
        .ioctl          = tty_ioctl,
+       .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
        .fasync         = tty_fasync,
 };
 
-static struct file_operations hung_up_tty_fops = {
+static const struct file_operations hung_up_tty_fops = {
        .llseek         = no_llseek,
        .read           = hung_up_tty_read,
        .write          = hung_up_tty_write,
        .poll           = hung_up_tty_poll,
        .ioctl          = hung_up_tty_ioctl,
+       .compat_ioctl   = hung_up_tty_compat_ioctl,
        .release        = tty_release,
 };
 
@@ -967,15 +1318,15 @@ static struct file *redirect;
  *     informs the line discipline if present that the driver is ready
  *     to receive more output data.
  */
+
 void tty_wakeup(struct tty_struct *tty)
 {
        struct tty_ldisc *ld;
-       
+
        if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
                ld = tty_ldisc_ref(tty);
-               if(ld) {
-                       if(ld->write_wakeup)
+               if (ld) {
+                       if (ld->write_wakeup)
                                ld->write_wakeup(tty);
                        tty_ldisc_deref(ld);
                }
@@ -992,28 +1343,63 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
  *     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)
+       if (ld) {
+               if (ld->flush_buffer)
                        ld->flush_buffer(tty);
                tty_ldisc_deref(ld);
        }
+       tty_buffer_flush(tty);
 }
 
 EXPORT_SYMBOL_GPL(tty_ldisc_flush);
-       
-/*
- * This can be called by the "eventd" kernel thread.  That is process synchronous,
- * but doesn't hold any locks, so we need to make sure we have the appropriate
- * locks for what we're doing..
+
+/**
+ *     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
+ *
+ *     This can be called by the "eventd" kernel thread.  That is process
+ *     synchronous but doesn't hold any locks, so we need to make sure we
+ *     have the appropriate locks for what we're doing.
+ *
+ *     The hangup event clears any pending redirections onto the hung up
+ *     device. It ensures future writes will error and it does the needed
+ *     line discipline hangup and signal delivery. The tty object itself
+ *     remains intact.
+ *
+ *     Locking:
+ *             BKL
+ *               redirect lock for undoing redirection
+ *               file list lock for manipulating list of ttys
+ *               tty_ldisc_lock from called functions
+ *               termios_mutex resetting termios data
+ *               tasklist_lock to walk task list for hangup event
+ *                 ->siglock to protect ->signal/->sighand
  */
-static void do_tty_hangup(void *data)
+static void do_tty_hangup(struct work_struct *work)
 {
-       struct tty_struct *tty = (struct tty_struct *) data;
-       struct file * cons_filp = NULL;
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, hangup_work);
+       struct file *cons_filp = NULL;
        struct file *filp, *f = NULL;
        struct task_struct *p;
        struct tty_ldisc *ld;
@@ -1031,7 +1417,7 @@ static void do_tty_hangup(void *data)
                redirect = NULL;
        }
        spin_unlock(&redirect_lock);
-       
+
        check_tty_count(tty, "do_tty_hangup");
        file_list_lock();
        /* This breaks for file handles being sent over AF_UNIX sockets ? */
@@ -1045,13 +1431,14 @@ static void do_tty_hangup(void *data)
                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. */
-
+       /*
+        * 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 != NULL) {
+               /* We may have no line discipline at this point */
                if (ld->flush_buffer)
                        ld->flush_buffer(tty);
                if (tty->driver->flush_buffer)
@@ -1062,54 +1449,55 @@ static void do_tty_hangup(void *data)
                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 */
-          
+       /*
+        * 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)
-       {
-               down(&tty->termios_sem);
-               *tty->termios = tty->driver->init_termios;
-               up(&tty->termios_sem);
-       }
-       
+               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 */
-       
+
        read_lock(&tasklist_lock);
-       if (tty->session > 0) {
-               do_each_task_pid(tty->session, PIDTYPE_SID, p) {
+       if (tty->session) {
+               do_each_pid_task(tty->session, PIDTYPE_SID, p) {
+                       spin_lock_irq(&p->sighand->siglock);
                        if (p->signal->tty == tty)
                                p->signal->tty = NULL;
-                       if (!p->signal->leader)
+                       if (!p->signal->leader) {
+                               spin_unlock_irq(&p->sighand->siglock);
                                continue;
-                       group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
-                       group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
-                       if (tty->pgrp > 0)
-                               p->signal->tty_old_pgrp = tty->pgrp;
-               } while_each_task_pid(tty->session, PIDTYPE_SID, p);
+                       }
+                       __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 */
+                       if (tty->pgrp)
+                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
+                       spin_unlock_irq(&p->sighand->siglock);
+               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
        }
        read_unlock(&tasklist_lock);
 
        tty->flags = 0;
-       tty->session = 0;
-       tty->pgrp = -1;
+       put_pid(tty->session);
+       put_pid(tty->pgrp);
+       tty->session = NULL;
+       tty->pgrp = NULL;
        tty->ctrl_status = 0;
        /*
-        *      If one of the devices matches a console pointer, we
-        *      cannot just call hangup() because that will cause
-        *      tty->count and state->count to go out of sync.
-        *      So we just call close() the right number of times.
+        * If one of the devices matches a console pointer, we
+        * cannot just call hangup() because that will cause
+        * tty->count and state->count to go out of sync.
+        * So we just call close() the right number of times.
         */
        if (cons_filp) {
                if (tty->driver->close)
@@ -1117,12 +1505,12 @@ static void do_tty_hangup(void *data)
                                tty->driver->close(tty, cons_filp);
        } else if (tty->driver->hangup)
                (tty->driver->hangup)(tty);
-               
-       /* We don't want to have driver/ldisc interactions beyond
-          the ones we did here. The driver layer expects no
-          calls after ->hangup() from the ldisc side. However we
-          can't yet guarantee all that */
-
+       /*
+        * We don't want to have driver/ldisc interactions beyond
+        * the ones we did here. The driver layer expects no
+        * calls after ->hangup() from the ldisc side. However we
+        * can't yet guarantee all that.
+        */
        set_bit(TTY_HUPPED, &tty->flags);
        if (ld) {
                tty_ldisc_enable(tty);
@@ -1133,11 +1521,18 @@ static void do_tty_hangup(void *data)
                fput(f);
 }
 
-void tty_hangup(struct tty_struct * tty)
+/**
+ *     tty_hangup              -       trigger a hangup event
+ *     @tty: tty to hangup
+ *
+ *     A carrier loss (virtual or otherwise) has occurred on this like
+ *     schedule a hangup sequence to run after this event.
+ */
+
+void tty_hangup(struct tty_struct *tty)
 {
 #ifdef TTY_DEBUG_HANGUP
        char    buf[64];
-       
        printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
 #endif
        schedule_work(&tty->hangup_work);
@@ -1145,83 +1540,183 @@ void tty_hangup(struct tty_struct * tty)
 
 EXPORT_SYMBOL(tty_hangup);
 
-void tty_vhangup(struct tty_struct * tty)
+/**
+ *     tty_vhangup             -       process vhangup
+ *     @tty: tty to hangup
+ *
+ *     The user has asked via system call for the terminal to be hung up.
+ *     We do this synchronously so that when the syscall returns the process
+ *     is complete. That guarantee is necessary for security reasons.
+ */
+
+void tty_vhangup(struct tty_struct *tty)
 {
 #ifdef TTY_DEBUG_HANGUP
        char    buf[64];
 
        printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
 #endif
-       do_tty_hangup((void *) tty);
+       do_tty_hangup(&tty->hangup_work);
 }
+
 EXPORT_SYMBOL(tty_vhangup);
 
-int tty_hung_up_p(struct file * filp)
+/**
+ *     tty_hung_up_p           -       was tty hung up
+ *     @filp: file pointer of tty
+ *
+ *     Return true if the tty has been subject to a vhangup or a carrier
+ *     loss
+ */
+
+int tty_hung_up_p(struct file *filp)
 {
        return (filp->f_op == &hung_up_tty_fops);
 }
 
 EXPORT_SYMBOL(tty_hung_up_p);
 
-/*
- * This function is typically called only by the session leader, when
- * it wants to disassociate itself from its controlling tty.
+/**
+ *     is_tty  -       checker whether file is a TTY
+ *     @filp:          file handle that may be a tty
  *
- * It performs the following functions:
+ *     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;
+       do_each_pid_task(session, PIDTYPE_SID, p) {
+               proc_clear_tty(p);
+       } while_each_pid_task(session, PIDTYPE_SID, p);
+}
+
+/**
+ *     disassociate_ctty       -       disconnect controlling tty
+ *     @on_exit: true if exiting so need to "hang up" the session
+ *
+ *     This function is typically called only by the session leader, when
+ *     it wants to disassociate itself from its controlling tty.
+ *
+ *     It performs the following functions:
  *     (1)  Sends a SIGHUP and SIGCONT to the foreground process group
  *     (2)  Clears the tty from being controlling the session
  *     (3)  Clears the controlling tty for all processes in the
  *             session group.
  *
- * The argument on_exit is set to 1 if called when a process is
- * exiting; it is 0 if called by the ioctl TIOCNOTTY.
+ *     The argument on_exit is set to 1 if called when a process is
+ *     exiting; it is 0 if called by the ioctl TIOCNOTTY.
+ *
+ *     Locking:
+ *             BKL is taken for hysterical raisins
+ *               tty_mutex is taken to protect tty
+ *               ->siglock is taken to protect ->signal/->sighand
+ *               tasklist_lock is taken to walk process list for sessions
+ *                 ->siglock is taken to protect ->signal/->sighand
  */
+
 void disassociate_ctty(int on_exit)
 {
        struct tty_struct *tty;
-       struct task_struct *p;
-       int tty_pgrp = -1;
+       struct pid *tty_pgrp = NULL;
 
        lock_kernel();
 
        mutex_lock(&tty_mutex);
-       tty = current->signal->tty;
+       tty = get_current_tty();
        if (tty) {
-               tty_pgrp = tty->pgrp;
+               tty_pgrp = get_pid(tty->pgrp);
                mutex_unlock(&tty_mutex);
+               /* XXX: here we race, there is nothing protecting tty */
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
-       } else {
-               if (current->signal->tty_old_pgrp) {
-                       kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
-                       kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
+       } else if (on_exit) {
+               struct pid *old_pgrp;
+               spin_lock_irq(&current->sighand->siglock);
+               old_pgrp = current->signal->tty_old_pgrp;
+               current->signal->tty_old_pgrp = NULL;
+               spin_unlock_irq(&current->sighand->siglock);
+               if (old_pgrp) {
+                       kill_pgrp(old_pgrp, SIGHUP, on_exit);
+                       kill_pgrp(old_pgrp, SIGCONT, on_exit);
+                       put_pid(old_pgrp);
                }
                mutex_unlock(&tty_mutex);
-               unlock_kernel();        
+               unlock_kernel();
                return;
        }
-       if (tty_pgrp > 0) {
-               kill_pg(tty_pgrp, SIGHUP, on_exit);
+       if (tty_pgrp) {
+               kill_pgrp(tty_pgrp, SIGHUP, on_exit);
                if (!on_exit)
-                       kill_pg(tty_pgrp, SIGCONT, on_exit);
+                       kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+               put_pid(tty_pgrp);
        }
 
-       /* Must lock changes to tty_old_pgrp */
+       spin_lock_irq(&current->sighand->siglock);
+       put_pid(current->signal->tty_old_pgrp);
+       current->signal->tty_old_pgrp = NULL;
+       spin_unlock_irq(&current->sighand->siglock);
+
        mutex_lock(&tty_mutex);
-       current->signal->tty_old_pgrp = 0;
-       tty->session = 0;
-       tty->pgrp = -1;
+       /* It is possible that do_tty_hangup has free'd this tty */
+       tty = get_current_tty();
+       if (tty) {
+               put_pid(tty->session);
+               put_pid(tty->pgrp);
+               tty->session = NULL;
+               tty->pgrp = NULL;
+       } 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);
-       do_each_task_pid(current->signal->session, PIDTYPE_SID, p) {
-               p->signal->tty = NULL;
-       } while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
+       session_clear_tty(task_session(current));
        read_unlock(&tasklist_lock);
-       mutex_unlock(&tty_mutex);
        unlock_kernel();
 }
 
+/**
+ *
+ *     no_tty  - Ensure the current process does not have a controlling tty
+ */
+void no_tty(void)
+{
+       struct task_struct *tsk = current;
+       if (tsk->signal->leader)
+               disassociate_ctty(0);
+       proc_clear_tty(tsk);
+}
+
+
+/**
+ *     stop_tty        -       propagate flow control
+ *     @tty: tty to stop
+ *
+ *     Perform flow control to the driver. For PTY/TTY pairs we
+ *     must also propagate the TIOCKPKT status. May be called
+ *     on an already stopped device and will not re-call the driver
+ *     method.
+ *
+ *     This functionality is used by both the line disciplines for
+ *     halting incoming flow and by the driver. It may therefore be
+ *     called from any context, may be under the tty atomic_write_lock
+ *     but not always.
+ *
+ *     Locking:
+ *             Broken. Relies on BKL which is unsafe here.
+ */
+
 void stop_tty(struct tty_struct *tty)
 {
        if (tty->stopped)
@@ -1238,6 +1733,19 @@ void stop_tty(struct tty_struct *tty)
 
 EXPORT_SYMBOL(stop_tty);
 
+/**
+ *     start_tty       -       propagate flow control
+ *     @tty: tty to start
+ *
+ *     Start a tty that has been stopped if at all possible. Perform
+ *     any necessary wakeups and propagate the TIOCPKT status. If this
+ *     is the tty was previous stopped and is being started then the
+ *     driver start method is invoked and the line discipline woken.
+ *
+ *     Locking:
+ *             Broken. Relies on BKL which is unsafe here.
+ */
+
 void start_tty(struct tty_struct *tty)
 {
        if (!tty->stopped || tty->flow_stopped)
@@ -1250,24 +1758,39 @@ void start_tty(struct tty_struct *tty)
        }
        if (tty->driver->start)
                (tty->driver->start)(tty);
-
        /* If we have a running line discipline it may need kicking */
        tty_wakeup(tty);
-       wake_up_interruptible(&tty->write_wait);
 }
 
 EXPORT_SYMBOL(start_tty);
 
-static ssize_t tty_read(struct file * file, char __user * buf, size_t count, 
+/**
+ *     tty_read        -       read method for tty device files
+ *     @file: pointer to tty file
+ *     @buf: user buffer
+ *     @count: size of user buffer
+ *     @ppos: unused
+ *
+ *     Perform the read system call function on this terminal device. Checks
+ *     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.
+ */
+
+static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
                        loff_t *ppos)
 {
        int i;
-       struct tty_struct * tty;
+       struct tty_struct *tty;
        struct inode *inode;
        struct tty_ldisc *ld;
 
        tty = (struct tty_struct *)file->private_data;
-       inode = file->f_dentry->d_inode;
+       inode = file->f_path.dentry->d_inode;
        if (tty_paranoia_check(tty, inode, "tty_read"))
                return -EIO;
        if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
@@ -1278,7 +1801,7 @@ static ssize_t tty_read(struct file * file, char __user * buf, size_t count,
        ld = tty_ldisc_ref_wait(tty);
        lock_kernel();
        if (ld->read)
-               i = (ld->read)(tty,file,buf,count);
+               i = (ld->read)(tty, file, buf, count);
        else
                i = -EIO;
        tty_ldisc_deref(ld);
@@ -1288,6 +1811,23 @@ static ssize_t tty_read(struct file * file, char __user * buf, size_t count,
        return i;
 }
 
+void tty_write_unlock(struct tty_struct *tty)
+{
+       mutex_unlock(&tty->atomic_write_lock);
+       wake_up_interruptible(&tty->write_wait);
+}
+
+int tty_write_lock(struct tty_struct *tty, int ndelay)
+{
+       if (!mutex_trylock(&tty->atomic_write_lock)) {
+               if (ndelay)
+                       return -EAGAIN;
+               if (mutex_lock_interruptible(&tty->atomic_write_lock))
+                       return -ERESTARTSYS;
+       }
+       return 0;
+}
+
 /*
  * Split writes up in sane blocksizes to avoid
  * denial-of-service type attacks
@@ -1299,12 +1839,12 @@ static inline ssize_t do_tty_write(
        const char __user *buf,
        size_t count)
 {
-       ssize_t ret = 0, written = 0;
+       ssize_t ret, written = 0;
        unsigned int chunk;
-       
-       if (mutex_lock_interruptible(&tty->atomic_write_lock)) {
-               return -ERESTARTSYS;
-       }
+
+       ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
+       if (ret < 0)
+               return ret;
 
        /*
         * We chunk up writes into a temporary buffer. This
@@ -1318,6 +1858,9 @@ static inline ssize_t do_tty_write(
         * layer has problems with bigger chunks. It will
         * claim to be able to handle more characters than
         * it actually does.
+        *
+        * FIXME: This can probably go away now except that 64K chunks
+        * are too likely to fail unless switched to vmalloc...
         */
        chunk = 2048;
        if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
@@ -1334,8 +1877,8 @@ static inline ssize_t do_tty_write(
 
                buf = kmalloc(chunk, GFP_KERNEL);
                if (!buf) {
-                       mutex_unlock(&tty->atomic_write_lock);
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto out;
                }
                kfree(tty->write_buf);
                tty->write_cnt = chunk;
@@ -1366,30 +1909,50 @@ static inline ssize_t do_tty_write(
                cond_resched();
        }
        if (written) {
-               struct inode *inode = file->f_dentry->d_inode;
+               struct inode *inode = file->f_path.dentry->d_inode;
                inode->i_mtime = current_fs_time(inode->i_sb);
                ret = written;
        }
-       mutex_unlock(&tty->atomic_write_lock);
+out:
+       tty_write_unlock(tty);
        return ret;
 }
 
 
-static ssize_t tty_write(struct file * file, const char __user * buf, size_t count,
-                        loff_t *ppos)
+/**
+ *     tty_write               -       write method for tty device file
+ *     @file: tty file pointer
+ *     @buf: user data to write
+ *     @count: bytes to write
+ *     @ppos: unused
+ *
+ *     Write data to a tty device via the line discipline.
+ *
+ *     Locking:
+ *             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.
+ */
+
+static ssize_t tty_write(struct file *file, const char __user *buf,
+                                               size_t count, loff_t *ppos)
 {
-       struct tty_struct * tty;
-       struct inode *inode = file->f_dentry->d_inode;
+       struct tty_struct *tty;
+       struct inode *inode = file->f_path.dentry->d_inode;
        ssize_t ret;
        struct tty_ldisc *ld;
-       
+
        tty = (struct tty_struct *)file->private_data;
        if (tty_paranoia_check(tty, inode, "tty_write"))
                return -EIO;
-       if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags)))
-               return -EIO;
+       if (!tty || !tty->driver->write ||
+               (test_bit(TTY_IO_ERROR, &tty->flags)))
+                       return -EIO;
 
-       ld = tty_ldisc_ref_wait(tty);           
+       ld = tty_ldisc_ref_wait(tty);
        if (!ld->write)
                ret = -EIO;
        else
@@ -1398,8 +1961,8 @@ static ssize_t tty_write(struct file * file, const char __user * buf, size_t cou
        return ret;
 }
 
-ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count,
-                        loff_t *ppos)
+ssize_t redirected_tty_write(struct file *file, const char __user *buf,
+                                               size_t count, loff_t *ppos)
 {
        struct file *p = NULL;
 
@@ -1416,43 +1979,96 @@ ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t
                fput(p);
                return res;
        }
-
        return tty_write(file, buf, count, ppos);
 }
 
 static char ptychar[] = "pqrstuvwxyzabcde";
 
-static inline void pty_line_name(struct tty_driver *driver, int index, char *p)
+/**
+ *     pty_line_name   -       generate name for a pty
+ *     @driver: the tty driver in use
+ *     @index: the minor number
+ *     @p: output buffer of at least 6 bytes
+ *
+ *     Generate a name from a driver reference and write it to the output
+ *     buffer.
+ *
+ *     Locking: None
+ */
+static void pty_line_name(struct tty_driver *driver, int index, char *p)
 {
        int i = index + driver->name_base;
        /* ->name is initialized to "ttyp", but "tty" is expected */
        sprintf(p, "%s%c%x",
-                       driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
-                       ptychar[i >> 4 & 0xf], i & 0xf);
+               driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
+               ptychar[i >> 4 & 0xf], i & 0xf);
 }
 
-static inline void tty_line_name(struct tty_driver *driver, int index, char *p)
+/**
+ *     pty_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
+ *
+ *     Generate a name from a driver reference and write it to the output
+ *     buffer.
+ *
+ *     Locking: None
+ */
+static void tty_line_name(struct tty_driver *driver, int index, char *p)
 {
        sprintf(p, "%s%d", driver->name, index + driver->name_base);
 }
 
-/*
+/**
+ *     init_dev                -       initialise a tty device
+ *     @driver: tty driver we are opening a device on
+ *     @idx: device index
+ *     @tty: returned tty structure
+ *
+ *     Prepare a tty device. This may not be a "new" clean device but
+ *     could also be an active device. The pty drivers require special
+ *     handling because of this.
+ *
+ *     Locking:
+ *             The function is called under the tty_mutex, which
+ *     protects us from the tty struct or driver itself going away.
+ *
+ *     On exit the tty device has the line discipline attached and
+ *     a reference count of 1. If a pair was created for pty/tty use
+ *     and the other was a pty master then it too has a reference count of 1.
+ *
  * WSH 06/09/97: Rewritten to remove races and properly clean up after a
  * failed open.  The new code protects the open with a mutex, so it's
  * really quite straightforward.  The mutex locking can probably be
  * 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, *o_tty;
-       struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
-       struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
-       int retval=0;
+       struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;
+       struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
+       int retval = 0;
 
        /* 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 {
@@ -1464,7 +2080,7 @@ static int init_dev(struct tty_driver *driver, int idx,
         * First time open is complex, especially for PTY devices.
         * This code guarantees that either everything succeeds and the
         * TTY is ready for operation, or else the table slots are vacated
-        * and the allocated memory released.  (Except that the termios 
+        * and the allocated memory released.  (Except that the termios
         * and locked termios may be retained.)
         */
 
@@ -1478,7 +2094,7 @@ static int init_dev(struct tty_driver *driver, int idx,
        ltp = o_ltp = NULL;
 
        tty = alloc_tty_struct();
-       if(!tty)
+       if (!tty)
                goto fail_no_mem;
        initialize_tty_struct(tty);
        tty->driver = driver;
@@ -1494,19 +2110,16 @@ static int init_dev(struct tty_driver *driver, int idx,
        }
 
        if (!*tp_loc) {
-               tp = (struct termios *) kmalloc(sizeof(struct termios),
-                                               GFP_KERNEL);
+               tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
                if (!tp)
                        goto free_mem_out;
                *tp = driver->init_termios;
        }
 
        if (!*ltp_loc) {
-               ltp = (struct termios *) kmalloc(sizeof(struct termios),
-                                                GFP_KERNEL);
+               ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
                if (!ltp)
                        goto free_mem_out;
-               memset(ltp, 0, sizeof(struct termios));
        }
 
        if (driver->type == TTY_DRIVER_TYPE_PTY) {
@@ -1527,27 +2140,23 @@ static int init_dev(struct tty_driver *driver, int idx,
                }
 
                if (!*o_tp_loc) {
-                       o_tp = (struct termios *)
-                               kmalloc(sizeof(struct termios), GFP_KERNEL);
+                       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 = (struct termios *)
-                               kmalloc(sizeof(struct termios), GFP_KERNEL);
+                       o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
                        if (!o_ltp)
                                goto free_mem_out;
-                       memset(o_ltp, 0, sizeof(struct termios));
                }
 
                /*
                 * Everything allocated ... set up the o_tty structure.
                 */
-               if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) {
+               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)
@@ -1563,28 +2172,30 @@ static int init_dev(struct tty_driver *driver, int idx,
                o_tty->link = tty;
        }
 
-       /* 
+       /*
         * All structures have been allocated, so now we install them.
-        * Failures after this point use release_mem to clean up, so 
+        * 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)) {
+       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_mem to clean up.  No need
-        * to decrement the use counts, as release_mem doesn't care.
+        * 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) {
@@ -1618,7 +2229,7 @@ fast_track:
        if (driver->type == TTY_DRIVER_TYPE_PTY &&
            driver->subtype == PTY_TYPE_MASTER) {
                /*
-                * special case for PTY masters: only one open permitted, 
+                * special case for PTY masters: only one open permitted,
                 * and the slave side open count is incremented as well.
                 */
                if (tty->count) {
@@ -1631,11 +2242,11 @@ fast_track:
        tty->driver = driver; /* N.B. why do this every time?? */
 
        /* FIXME */
-       if(!test_bit(TTY_LDISC, &tty->flags))
+       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;
@@ -1654,48 +2265,36 @@ fail_no_mem:
        retval = -ENOMEM;
        goto end_init;
 
-       /* call the tty release_mem routine to clean out this slot */
+       /* call the tty release_tty routine to clean out this slot */
 release_mem_out:
-       printk(KERN_INFO "init_dev: ldisc open failed, "
-                        "clearing slot %d\n", idx);
-       release_mem(tty, idx);
+       if (printk_ratelimit())
+               printk(KERN_INFO "init_dev: ldisc open failed, "
+                                "clearing slot %d\n", idx);
+       release_tty(tty, idx);
        goto end_init;
 }
 
-/*
- * Releases memory associated with a tty structure, and clears out the
- * driver table slots.
+/**
+ *     release_one_tty         -       release tty structure memory
+ *
+ *     Releases memory associated with a tty structure, and clears out the
+ *     driver table slots. This function is called when a device is no longer
+ *     in use. It also gets called when setup of a device fails.
+ *
+ *     Locking:
+ *             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 ??
  */
-static void release_mem(struct tty_struct *tty, int idx)
+static void release_one_tty(struct tty_struct *tty, int idx)
 {
-       struct tty_struct *o_tty;
-       struct termios *tp;
        int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
-
-       if ((o_tty = tty->link) != NULL) {
-               if (!devpts)
-                       o_tty->driver->ttys[idx] = NULL;
-               if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-                       tp = o_tty->termios;
-                       if (!devpts)
-                               o_tty->driver->termios[idx] = NULL;
-                       kfree(tp);
-
-                       tp = o_tty->termios_locked;
-                       if (!devpts)
-                               o_tty->driver->termios_locked[idx] = NULL;
-                       kfree(tp);
-               }
-               o_tty->magic = 0;
-               o_tty->driver->refcount--;
-               file_list_lock();
-               list_del_init(&o_tty->tty_files);
-               file_list_unlock();
-               free_tty_struct(o_tty);
-       }
+       struct ktermios *tp;
 
        if (!devpts)
                tty->driver->ttys[idx] = NULL;
+
        if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
                tp = tty->termios;
                if (!devpts)
@@ -1708,15 +2307,39 @@ static void release_mem(struct tty_struct *tty, int idx)
                kfree(tp);
        }
 
+
        tty->magic = 0;
        tty->driver->refcount--;
+
        file_list_lock();
        list_del_init(&tty->tty_files);
        file_list_unlock();
-       module_put(tty->driver->owner);
+
        free_tty_struct(tty);
 }
 
+/**
+ *     release_tty             -       release tty structure memory
+ *
+ *     Release both @tty and a possible linked partner (think pty pair),
+ *     and decrement the refcount of the backing module.
+ *
+ *     Locking:
+ *             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 ??
+ */
+static void release_tty(struct tty_struct *tty, int idx)
+{
+       struct tty_driver *driver = tty->driver;
+
+       if (tty->link)
+               release_one_tty(tty->link, idx);
+       release_one_tty(tty, idx);
+       module_put(driver->owner);
+}
+
 /*
  * Even releasing the tty structures is a tricky business.. We have
  * to be very careful that the structures are all released at the
@@ -1725,7 +2348,7 @@ static void release_mem(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)
+static void release_dev(struct file *filp)
 {
        struct tty_struct *tty, *o_tty;
        int     pty_master, tty_closing, o_tty_closing, do_sleep;
@@ -1733,9 +2356,10 @@ static void release_dev(struct file * filp)
        int     idx;
        char    buf[64];
        unsigned long flags;
-       
+
        tty = (struct tty_struct *)filp->private_data;
-       if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "release_dev"))
+       if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode,
+                                                       "release_dev"))
                return;
 
        check_tty_count(tty, "release_dev");
@@ -1795,7 +2419,7 @@ static void release_dev(struct file * filp)
                               idx, tty->name);
                        return;
                }
-               if (o_tty->termios_locked != 
+               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",
@@ -1831,7 +2455,7 @@ static void release_dev(struct file * filp)
        while (1) {
                /* Guard against races with tty->count changes elsewhere and
                   opens on /dev/tty */
-                  
+
                mutex_lock(&tty_mutex);
                tty_closing = tty->count <= 1;
                o_tty_closing = o_tty &&
@@ -1865,11 +2489,11 @@ static void release_dev(struct file * filp)
                                    "active!\n", tty_name(tty, buf));
                mutex_unlock(&tty_mutex);
                schedule();
-       }       
+       }
 
        /*
-        * The closing flags are now consistent with the open counts on 
-        * both sides, and we've completed the last operation that could 
+        * The closing flags are now consistent with the open counts on
+        * both sides, and we've completed the last operation that could
         * block, so it's safe to proceed with closing.
         */
        if (pty_master) {
@@ -1885,7 +2509,7 @@ static void release_dev(struct file * filp)
                       tty->count, tty_name(tty, buf));
                tty->count = 0;
        }
-       
+
        /*
         * We've decremented tty->count, so we need to remove this file
         * descriptor off the tty->tty_files list; this serves two
@@ -1905,9 +2529,9 @@ static void release_dev(struct file * filp)
         * case of a pty we may have to wait around for the other side
         * to close, and TTY_CLOSING makes sure we can't be reopened.
         */
-       if(tty_closing)
+       if (tty_closing)
                set_bit(TTY_CLOSING, &tty->flags);
-       if(o_tty_closing)
+       if (o_tty_closing)
                set_bit(TTY_CLOSING, &o_tty->flags);
 
        /*
@@ -1916,16 +2540,10 @@ static void release_dev(struct file * filp)
         * tty.
         */
        if (tty_closing || o_tty_closing) {
-               struct task_struct *p;
-
                read_lock(&tasklist_lock);
-               do_each_task_pid(tty->session, PIDTYPE_SID, p) {
-                       p->signal->tty = NULL;
-               } while_each_task_pid(tty->session, PIDTYPE_SID, p);
+               session_clear_tty(tty->session);
                if (o_tty)
-                       do_each_task_pid(o_tty->session, PIDTYPE_SID, p) {
-                               p->signal->tty = NULL;
-                       } while_each_task_pid(o_tty->session, PIDTYPE_SID, p);
+                       session_clear_tty(o_tty->session);
                read_unlock(&tasklist_lock);
        }
 
@@ -1934,7 +2552,7 @@ static void release_dev(struct file * filp)
        /* check whether both sides are closing ... */
        if (!tty_closing || (o_tty && !o_tty_closing))
                return;
-       
+
 #ifdef TTY_DEBUG_HANGUP
        printk(KERN_DEBUG "freeing tty structure...");
 #endif
@@ -1949,17 +2567,16 @@ static void release_dev(struct file * filp)
        /*
         * 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)
-       {
+       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);
@@ -1974,12 +2591,12 @@ static void release_dev(struct file * filp)
        if (tty->ldisc.close)
                (tty->ldisc.close)(tty);
        tty_ldisc_put(tty->ldisc.num);
-       
+
        /*
         *      Switch the line discipline back
         */
        tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-       tty_set_termios_ldisc(tty,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);
@@ -1987,38 +2604,47 @@ static void release_dev(struct file * filp)
                        (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_set_termios_ldisc(o_tty, N_TTY);
        }
        /*
-        * The release_mem function takes care of the details of clearing
+        * The release_tty function takes care of the details of clearing
         * the slots and preserving the termios structure.
         */
-       release_mem(tty, idx);
+       release_tty(tty, idx);
 
 #ifdef CONFIG_UNIX98_PTYS
        /* Make this pty number available for reallocation */
        if (devpts) {
-               down(&allocated_ptys_lock);
+               mutex_lock(&allocated_ptys_lock);
                idr_remove(&allocated_ptys, idx);
-               up(&allocated_ptys_lock);
+               mutex_unlock(&allocated_ptys_lock);
        }
 #endif
 
 }
 
-/*
- * tty_open and tty_release keep up the tty count that contains the
- * number of opens done on a tty. We cannot use the inode-count, as
- * different inodes might point to the same tty.
+/**
+ *     tty_open                -       open a tty device
+ *     @inode: inode of device file
+ *     @filp: file pointer to tty
+ *
+ *     tty_open and tty_release keep up the tty count that contains the
+ *     number of opens done on a tty. We cannot use the inode-count, as
+ *     different inodes might point to the same tty.
+ *
+ *     Open-counting is needed for pty masters, as well as for keeping
+ *     track of serial lines: DTR is dropped when the last close happens.
+ *     (This is not done solely through tty->count, now.  - Ted 1/27/92)
  *
- * Open-counting is needed for pty masters, as well as for keeping
- * track of serial lines: DTR is dropped when the last close happens.
- * (This is not done solely through tty->count, now.  - Ted 1/27/92)
+ *     The termios state of a pty is reset on first open so that
+ *     settings don't persist across reuse.
  *
- * 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.
+ *              tty->count should protect the rest.
+ *              ->siglock protects ->signal/->sighand
  */
-static int tty_open(struct inode * inode, struct file * filp)
+
+static int tty_open(struct inode *inode, struct file *filp)
 {
        struct tty_struct *tty;
        int noctty, retval;
@@ -2028,27 +2654,28 @@ static int tty_open(struct inode * inode, struct file * filp)
        unsigned short saved_flags = filp->f_flags;
 
        nonseekable_open(inode, filp);
-       
+
 retry_open:
        noctty = filp->f_flags & O_NOCTTY;
        index  = -1;
        retval = 0;
-       
+
        mutex_lock(&tty_mutex);
 
-       if (device == MKDEV(TTYAUX_MAJOR,0)) {
-               if (!current->signal->tty) {
+       if (device == MKDEV(TTYAUX_MAJOR, 0)) {
+               tty = get_current_tty();
+               if (!tty) {
                        mutex_unlock(&tty_mutex);
                        return -ENXIO;
                }
-               driver = current->signal->tty->driver;
-               index = current->signal->tty->index;
+               driver = tty->driver;
+               index = tty->index;
                filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
                /* noctty = 1; */
                goto got_driver;
        }
 #ifdef CONFIG_VT
-       if (device == MKDEV(TTY_MAJOR,0)) {
+       if (device == MKDEV(TTY_MAJOR, 0)) {
                extern struct tty_driver *console_driver;
                driver = console_driver;
                index = fg_console;
@@ -2056,7 +2683,7 @@ retry_open:
                goto got_driver;
        }
 #endif
-       if (device == MKDEV(TTYAUX_MAJOR,1)) {
+       if (device == MKDEV(TTYAUX_MAJOR, 1)) {
                driver = console_device(&index);
                if (driver) {
                        /* Don't let /dev/console block */
@@ -2096,7 +2723,8 @@ got_driver:
        }
        filp->f_flags = saved_flags;
 
-       if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
+       if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
+                                               !capable(CAP_SYS_ADMIN))
                retval = -EBUSY;
 
        if (retval) {
@@ -2117,22 +2745,34 @@ got_driver:
                        filp->f_op = &tty_fops;
                goto retry_open;
        }
+
+       mutex_lock(&tty_mutex);
+       spin_lock_irq(&current->sighand->siglock);
        if (!noctty &&
            current->signal->leader &&
            !current->signal->tty &&
-           tty->session == 0) {
-               task_lock(current);
-               current->signal->tty = tty;
-               task_unlock(current);
-               current->signal->tty_old_pgrp = 0;
-               tty->session = current->signal->session;
-               tty->pgrp = process_group(current);
-       }
+           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
-static int ptmx_open(struct inode * inode, struct file * filp)
+/**
+ *     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;
@@ -2142,29 +2782,29 @@ static int ptmx_open(struct inode * inode, struct file * filp)
        nonseekable_open(inode, filp);
 
        /* find a device that is not in use. */
-       down(&allocated_ptys_lock);
+       mutex_lock(&allocated_ptys_lock);
        if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
-               up(&allocated_ptys_lock);
+               mutex_unlock(&allocated_ptys_lock);
                return -ENOMEM;
        }
        idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
        if (idr_ret < 0) {
-               up(&allocated_ptys_lock);
+               mutex_unlock(&allocated_ptys_lock);
                if (idr_ret == -EAGAIN)
                        return -ENOMEM;
                return -EIO;
        }
        if (index >= pty_limit) {
                idr_remove(&allocated_ptys, index);
-               up(&allocated_ptys_lock);
+               mutex_unlock(&allocated_ptys_lock);
                return -EIO;
        }
-       up(&allocated_ptys_lock);
+       mutex_unlock(&allocated_ptys_lock);
 
        mutex_lock(&tty_mutex);
        retval = init_dev(ptm_driver, index, &tty);
        mutex_unlock(&tty_mutex);
-       
+
        if (retval)
                goto out;
 
@@ -2178,20 +2818,34 @@ static int ptmx_open(struct inode * inode, struct file * filp)
 
        check_tty_count(tty, "tty_open");
        retval = ptm_driver->open(tty, filp);
-       if (!retval)
+       if (!retval) {
+               tty_audit_opening();
                return 0;
+       }
 out1:
        release_dev(filp);
        return retval;
 out:
-       down(&allocated_ptys_lock);
+       mutex_lock(&allocated_ptys_lock);
        idr_remove(&allocated_ptys, index);
-       up(&allocated_ptys_lock);
+       mutex_unlock(&allocated_ptys_lock);
        return retval;
 }
 #endif
 
-static int tty_release(struct inode * inode, struct file * filp)
+/**
+ *     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)
 {
        lock_kernel();
        release_dev(filp);
@@ -2199,17 +2853,28 @@ static int tty_release(struct inode * inode, struct file * filp)
        return 0;
 }
 
-/* No kernel lock held - fine */
-static unsigned int tty_poll(struct file * filp, poll_table * wait)
+/**
+ *     tty_poll        -       check tty status
+ *     @filp: file being polled
+ *     @wait: poll wait structures to update
+ *
+ *     Call the line discipline polling method to obtain the poll
+ *     status of the device.
+ *
+ *     Locking: locks called line discipline but ldisc poll method
+ *     may be re-entered freely by other callers.
+ */
+
+static unsigned int tty_poll(struct file *filp, poll_table *wait)
 {
-       struct tty_struct * tty;
+       struct tty_struct *tty;
        struct tty_ldisc *ld;
        int ret = 0;
 
        tty = (struct tty_struct *)filp->private_data;
-       if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_poll"))
+       if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
                return 0;
-               
+
        ld = tty_ldisc_ref_wait(tty);
        if (ld->poll)
                ret = (ld->poll)(tty, filp, wait);
@@ -2217,23 +2882,32 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait)
        return ret;
 }
 
-static int tty_fasync(int fd, struct file * filp, int on)
+static int tty_fasync(int fd, struct file *filp, int on)
 {
-       struct tty_struct * tty;
+       struct tty_struct *tty;
        int retval;
 
        tty = (struct tty_struct *)filp->private_data;
-       if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_fasync"))
+       if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
                return 0;
-       
+
        retval = fasync_helper(fd, filp, on, &tty->fasync);
        if (retval <= 0)
                return retval;
 
        if (on) {
+               enum pid_type type;
+               struct pid *pid;
                if (!waitqueue_active(&tty->read_wait))
                        tty->minimum_to_wake = 1;
-               retval = f_setown(filp, (-tty->pgrp) ? : current->pid, 0);
+               if (tty->pgrp) {
+                       pid = tty->pgrp;
+                       type = PIDTYPE_PGID;
+               } else {
+                       pid = task_pid(current);
+                       type = PIDTYPE_PID;
+               }
+               retval = __f_setown(filp, pid, type, 0);
                if (retval)
                        return retval;
        } else {
@@ -2243,11 +2917,28 @@ static int tty_fasync(int fd, struct file * filp, int on)
        return 0;
 }
 
+/**
+ *     tiocsti                 -       fake input character
+ *     @tty: tty to fake input into
+ *     @p: pointer to character
+ *
+ *     Fake input to a tty device. Does the necessary locking and
+ *     input management.
+ *
+ *     FIXME: does not honour flow control ??
+ *
+ *     Locking:
+ *             Called functions take tty_ldisc_lock
+ *             current->signal->tty check is safe without locks
+ *
+ *     FIXME: may race normal receive processing
+ */
+
 static int tiocsti(struct tty_struct *tty, char __user *p)
 {
        char ch, mbz = 0;
        struct tty_ldisc *ld;
-       
+
        if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
                return -EPERM;
        if (get_user(ch, p))
@@ -2258,42 +2949,86 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
        return 0;
 }
 
-static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
+/**
+ *     tiocgwinsz              -       implement window query ioctl
+ *     @tty; tty
+ *     @arg: user buffer for result
+ *
+ *     Copies the kernel idea of the window size into the user buffer.
+ *
+ *     Locking: tty->termios_mutex is taken to ensure the winsize data
+ *             is consistent.
+ */
+
+static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
 {
-       if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
-               return -EFAULT;
-       return 0;
+       int err;
+
+       mutex_lock(&tty->termios_mutex);
+       err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
+       mutex_unlock(&tty->termios_mutex);
+
+       return err ? -EFAULT: 0;
 }
 
+/**
+ *     tiocswinsz              -       implement window size set ioctl
+ *     @tty; tty
+ *     @arg: user buffer for result
+ *
+ *     Copies the user idea of the window size to the kernel. Traditionally
+ *     this is just advisory information but for the Linux console it
+ *     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
+ */
+
 static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
-       struct winsize __user * arg)
+       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)))
-               return 0;
+               goto done;
+
 #ifdef CONFIG_VT
        if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
-               int rc;
-
-               acquire_console_sem();
-               rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row);
-               release_console_sem();
-               if (rc)
+               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 > 0)
-               kill_pg(tty->pgrp, SIGWINCH, 1);
-       if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0))
-               kill_pg(real_tty->pgrp, SIGWINCH, 1);
+       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;
 }
 
+/**
+ *     tioccons        -       allow admin to move logical console
+ *     @file: the file to become console
+ *
+ *     Allow the adminstrator to move the redirected console device
+ *
+ *     Locking: uses redirect_lock to guard the redirect information
+ */
+
 static int tioccons(struct file *file)
 {
        if (!capable(CAP_SYS_ADMIN))
@@ -2319,6 +3054,17 @@ static int tioccons(struct file *file)
        return 0;
 }
 
+/**
+ *     fionbio         -       non blocking ioctl
+ *     @file: file to set blocking value
+ *     @p: user parameter
+ *
+ *     Historical tty interfaces had a blocking control ioctl before
+ *     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.
+ */
 
 static int fionbio(struct file *file, int __user *p)
 {
@@ -2334,46 +3080,71 @@ static int fionbio(struct file *file, int __user *p)
        return 0;
 }
 
+/**
+ *     tiocsctty       -       set controlling tty
+ *     @tty: tty structure
+ *     @arg: user argument
+ *
+ *     This ioctl is used to manage job control. It permits a session
+ *     leader to set this tty as the controlling tty for the session.
+ *
+ *     Locking:
+ *             Takes tty_mutex() to protect tty instance
+ *             Takes tasklist_lock internally to walk sessions
+ *             Takes ->siglock() when updating signal->tty
+ */
+
 static int tiocsctty(struct tty_struct *tty, int arg)
 {
-       task_t *p;
+       int ret = 0;
+       if (current->signal->leader && (task_session(current) == tty->session))
+               return ret;
 
-       if (current->signal->leader &&
-           (current->signal->session == tty->session))
-               return 0;
+       mutex_lock(&tty_mutex);
        /*
         * The process must be a session leader and
         * not have a controlling tty already.
         */
-       if (!current->signal->leader || current->signal->tty)
-               return -EPERM;
-       if (tty->session > 0) {
+       if (!current->signal->leader || current->signal->tty) {
+               ret = -EPERM;
+               goto unlock;
+       }
+
+       if (tty->session) {
                /*
                 * This tty is already the controlling
                 * tty for another session group!
                 */
-               if ((arg == 1) && capable(CAP_SYS_ADMIN)) {
+               if (arg == 1 && capable(CAP_SYS_ADMIN)) {
                        /*
                         * Steal it away
                         */
-
                        read_lock(&tasklist_lock);
-                       do_each_task_pid(tty->session, PIDTYPE_SID, p) {
-                               p->signal->tty = NULL;
-                       } while_each_task_pid(tty->session, PIDTYPE_SID, p);
+                       session_clear_tty(tty->session);
                        read_unlock(&tasklist_lock);
-               } else
-                       return -EPERM;
-       }
-       task_lock(current);
-       current->signal->tty = tty;
-       task_unlock(current);
-       current->signal->tty_old_pgrp = 0;
-       tty->session = current->signal->session;
-       tty->pgrp = process_group(current);
-       return 0;
+               } else {
+                       ret = -EPERM;
+                       goto unlock;
+               }
+       }
+       proc_set_tty(current, tty);
+unlock:
+       mutex_unlock(&tty_mutex);
+       return ret;
 }
 
+/**
+ *     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
+ *     @p: returned pid
+ *
+ *     Obtain the process group of the tty. If there is no process group
+ *     return an error.
+ *
+ *     Locking: none. Reference to current->signal->tty is safe.
+ */
+
 static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
        /*
@@ -2382,12 +3153,25 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
         */
        if (tty == real_tty && current->signal->tty != real_tty)
                return -ENOTTY;
-       return put_user(real_tty->pgrp, p);
+       return put_user(pid_vnr(real_tty->pgrp), p);
 }
 
+/**
+ *     tiocspgrp               -       attempt to set process group
+ *     @tty: tty passed by user
+ *     @real_tty: tty side device matching tty passed by user
+ *     @p: pid pointer
+ *
+ *     Set the process group of the tty to the session passed. Only
+ *     permitted where the tty session is our session.
+ *
+ *     Locking: None
+ */
+
 static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
-       pid_t pgrp;
+       struct pid *pgrp;
+       pid_t pgrp_nr;
        int retval = tty_check_change(real_tty);
 
        if (retval == -EIO)
@@ -2396,18 +3180,40 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
                return retval;
        if (!current->signal->tty ||
            (current->signal->tty != real_tty) ||
-           (real_tty->session != current->signal->session))
+           (real_tty->session != task_session(current)))
                return -ENOTTY;
-       if (get_user(pgrp, p))
+       if (get_user(pgrp_nr, p))
                return -EFAULT;
-       if (pgrp < 0)
+       if (pgrp_nr < 0)
                return -EINVAL;
-       if (session_of_pgrp(pgrp) != current->signal->session)
-               return -EPERM;
-       real_tty->pgrp = pgrp;
-       return 0;
+       rcu_read_lock();
+       pgrp = find_vpid(pgrp_nr);
+       retval = -ESRCH;
+       if (!pgrp)
+               goto out_unlock;
+       retval = -EPERM;
+       if (session_of_pgrp(pgrp) != task_session(current))
+               goto out_unlock;
+       retval = 0;
+       put_pid(real_tty->pgrp);
+       real_tty->pgrp = get_pid(pgrp);
+out_unlock:
+       rcu_read_unlock();
+       return retval;
 }
 
+/**
+ *     tiocgsid                -       get session id
+ *     @tty: tty passed by user
+ *     @real_tty: tty side of the tty pased by the user if a pty else the tty
+ *     @p: pointer to returned session id
+ *
+ *     Obtain the session id of the tty. If there is no session
+ *     return an error.
+ *
+ *     Locking: none. Reference to current->signal->tty is safe.
+ */
+
 static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
        /*
@@ -2416,11 +3222,21 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _
        */
        if (tty == real_tty && current->signal->tty != real_tty)
                return -ENOTTY;
-       if (real_tty->session <= 0)
+       if (!real_tty->session)
                return -ENOTTY;
-       return put_user(real_tty->session, p);
+       return put_user(pid_vnr(real_tty->session), p);
 }
 
+/**
+ *     tiocsetd        -       set line discipline
+ *     @tty: tty device
+ *     @p: pointer to user data
+ *
+ *     Set the line discipline according to user request.
+ *
+ *     Locking: see tty_set_ldisc, this function is just a helper
+ */
+
 static int tiocsetd(struct tty_struct *tty, int __user *p)
 {
        int ldisc;
@@ -2430,20 +3246,46 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
        return tty_set_ldisc(tty, ldisc);
 }
 
+/**
+ *     send_break      -       performed time break
+ *     @tty: device to break on
+ *     @duration: timeout in mS
+ *
+ *     Perform a timed break on hardware that lacks its own driver level
+ *     timed break functionality.
+ *
+ *     Locking:
+ *             atomic_write_lock serializes
+ *
+ */
+
 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)) {
+       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;
 }
 
-static int
-tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
+/**
+ *     tiocmget                -       get modem status
+ *     @tty: tty device
+ *     @file: user file pointer
+ *     @p: pointer to result
+ *
+ *     Obtain the modem status bits from the tty driver if the feature
+ *     is supported. Return -EINVAL if it is not available.
+ *
+ *     Locking: none (up to the driver)
+ */
+
+static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
 {
        int retval = -EINVAL;
 
@@ -2456,8 +3298,20 @@ tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
        return retval;
 }
 
-static int
-tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
+/**
+ *     tiocmset                -       set modem status
+ *     @tty: tty device
+ *     @file: user file pointer
+ *     @cmd: command - clear bits, set bits or set all
+ *     @p: pointer to desired bits
+ *
+ *     Set the modem status bits from the tty driver if the feature
+ *     is supported. Return -EINVAL if it is not available.
+ *
+ *     Locking: none (up to the driver)
+ */
+
+static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
             unsigned __user *p)
 {
        int retval = -EINVAL;
@@ -2494,18 +3348,20 @@ tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
 /*
  * Split this up, as gcc can choke on it otherwise..
  */
-int tty_ioctl(struct inode * inode, struct file * file,
+int tty_ioctl(struct inode *inode, 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;
-       
+
        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)
@@ -2515,13 +3371,13 @@ int tty_ioctl(struct inode * inode, struct file * file,
         * Break handling by driver
         */
        if (!tty->driver->break_ctl) {
-               switch(cmd) {
+               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:
@@ -2543,7 +3399,7 @@ int tty_ioctl(struct inode * inode, struct file * file,
        case TIOCSBRK:
        case TIOCCBRK:
        case TCSBRK:
-       case TCSBRKP:                   
+       case TCSBRKP:
                retval = tty_check_change(tty);
                if (retval)
                        return retval;
@@ -2556,76 +3412,80 @@ int tty_ioctl(struct inode * inode, struct file * file,
        }
 
        switch (cmd) {
-               case TIOCSTI:
-                       return tiocsti(tty, p);
-               case TIOCGWINSZ:
-                       return tiocgwinsz(tty, p);
-               case TIOCSWINSZ:
-                       return tiocswinsz(tty, real_tty, p);
-               case TIOCCONS:
-                       return real_tty!=tty ? -EINVAL : tioccons(file);
-               case FIONBIO:
-                       return fionbio(file, p);
-               case TIOCEXCL:
-                       set_bit(TTY_EXCLUSIVE, &tty->flags);
-                       return 0;
-               case TIOCNXCL:
-                       clear_bit(TTY_EXCLUSIVE, &tty->flags);
-                       return 0;
-               case TIOCNOTTY:
-                       if (current->signal->tty != tty)
-                               return -ENOTTY;
-                       if (current->signal->leader)
-                               disassociate_ctty(0);
-                       task_lock(current);
-                       current->signal->tty = NULL;
-                       task_unlock(current);
-                       return 0;
-               case TIOCSCTTY:
-                       return tiocsctty(tty, arg);
-               case TIOCGPGRP:
-                       return tiocgpgrp(tty, real_tty, p);
-               case TIOCSPGRP:
-                       return tiocspgrp(tty, real_tty, p);
-               case TIOCGSID:
-                       return tiocgsid(tty, real_tty, p);
-               case TIOCGETD:
-                       /* FIXME: check this is ok */
-                       return put_user(tty->ldisc.num, (int __user *)p);
-               case TIOCSETD:
-                       return tiocsetd(tty, p);
+       case TIOCSTI:
+               return tiocsti(tty, p);
+       case TIOCGWINSZ:
+               return tiocgwinsz(tty, p);
+       case TIOCSWINSZ:
+               return tiocswinsz(tty, real_tty, p);
+       case TIOCCONS:
+               return real_tty != tty ? -EINVAL : tioccons(file);
+       case FIONBIO:
+               return fionbio(file, p);
+       case TIOCEXCL:
+               set_bit(TTY_EXCLUSIVE, &tty->flags);
+               return 0;
+       case TIOCNXCL:
+               clear_bit(TTY_EXCLUSIVE, &tty->flags);
+               return 0;
+       case TIOCNOTTY:
+               if (current->signal->tty != tty)
+                       return -ENOTTY;
+               no_tty();
+               return 0;
+       case TIOCSCTTY:
+               return tiocsctty(tty, arg);
+       case TIOCGPGRP:
+               return tiocgpgrp(tty, real_tty, p);
+       case TIOCSPGRP:
+               return tiocspgrp(tty, real_tty, p);
+       case TIOCGSID:
+               return tiocgsid(tty, real_tty, p);
+       case TIOCGETD:
+               /* FIXME: check this is ok */
+               return put_user(tty->ldisc.num, (int __user *)p);
+       case TIOCSETD:
+               return tiocsetd(tty, p);
 #ifdef CONFIG_VT
-               case TIOCLINUX:
-                       return tioclinux(tty, arg);
+       case TIOCLINUX:
+               return tioclinux(tty, arg);
 #endif
-               /*
-                * Break handling
-                */
-               case TIOCSBRK:  /* Turn break on, unconditionally */
-                       tty->driver->break_ctl(tty, -1);
-                       return 0;
-                       
-               case TIOCCBRK:  /* Turn break off, unconditionally */
-                       tty->driver->break_ctl(tty, 0);
-                       return 0;
-               case TCSBRK:   /* SVID version: non-zero arg --> no break */
-                       /* non-zero arg means wait for all output data
-                        * to be sent (performed above) but don't send break.
-                        * This is used by the tcdrain() termios function.
-                        */
-                       if (!arg)
-                               return send_break(tty, 250);
-                       return 0;
-               case TCSBRKP:   /* support for POSIX tcsendbreak() */   
-                       return send_break(tty, arg ? arg*100 : 250);
-
-               case TIOCMGET:
-                       return tty_tiocmget(tty, file, p);
+       /*
+        * Break handling
+        */
+       case TIOCSBRK:  /* Turn break on, unconditionally */
+               tty->driver->break_ctl(tty, -1);
+               return 0;
 
-               case TIOCMSET:
-               case TIOCMBIC:
-               case TIOCMBIS:
-                       return tty_tiocmset(tty, file, cmd, p);
+       case TIOCCBRK:  /* Turn break off, unconditionally */
+               tty->driver->break_ctl(tty, 0);
+               return 0;
+       case TCSBRK:   /* SVID version: non-zero arg --> no break */
+               /* non-zero arg means wait for all output data
+                * to be sent (performed above) but don't send break.
+                * This is used by the tcdrain() termios function.
+                */
+               if (!arg)
+                       return send_break(tty, 250);
+               return 0;
+       case TCSBRKP:   /* support for POSIX tcsendbreak() */
+               return send_break(tty, arg ? arg*100 : 250);
+
+       case TIOCMGET:
+               return tty_tiocmget(tty, file, p);
+       case TIOCMSET:
+       case TIOCMBIC:
+       case TIOCMBIS:
+               return tty_tiocmset(tty, file, cmd, p);
+       case TCFLSH:
+               switch (arg) {
+               case TCIFLUSH:
+               case TCIOFLUSH:
+               /* flush tty buffer and allow ldisc to process ioctl */
+                       tty_buffer_flush(tty);
+                       break;
+               }
+               break;
        }
        if (tty->driver->ioctl) {
                retval = (tty->driver->ioctl)(tty, file, cmd, arg);
@@ -2643,13 +3503,39 @@ int tty_ioctl(struct inode * inode, struct file * file,
        return retval;
 }
 
+#ifdef CONFIG_COMPAT
+static long tty_compat_ioctl(struct file *file, unsigned int cmd,
+                               unsigned long arg)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct tty_struct *tty = file->private_data;
+       struct tty_ldisc *ld;
+       int retval = -ENOIOCTLCMD;
+
+       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 (retval != -ENOIOCTLCMD)
+                       return retval;
+       }
+
+       ld = tty_ldisc_ref_wait(tty);
+       if (ld->compat_ioctl)
+               retval = ld->compat_ioctl(tty, file, cmd, arg);
+       tty_ldisc_deref(ld);
+
+       return retval;
+}
+#endif
 
 /*
  * This implements the "Secure Attention Key" ---  the idea is to
  * prevent trojan horses by killing all processes associated with this
  * tty when the user hits the "Secure Attention Key".  Required for
  * super-paranoid applications --- see the Orange Book for more details.
- * 
+ *
  * This code could be nicer; ideally it should send a HUP, wait a few
  * seconds, then send a INT, and then a KILL signal.  But you then
  * have to coordinate with the init process, since all processes associated
@@ -2663,48 +3549,42 @@ int tty_ioctl(struct inode * inode, struct file * file,
  * Nasty bug: do_SAK is being called in interrupt context.  This can
  * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
  */
-static void __do_SAK(void *arg)
+void __do_SAK(struct tty_struct *tty)
 {
 #ifdef TTY_SOFT_SAK
        tty_hangup(tty);
 #else
-       struct tty_struct *tty = arg;
        struct task_struct *g, *p;
-       int session;
+       struct pid *session;
        int             i;
        struct file     *filp;
-       struct tty_ldisc *disc;
        struct fdtable *fdt;
-       
+
        if (!tty)
                return;
-       session  = tty->session;
-       
-       /* We don't want an ldisc switch during this */
-       disc = tty_ldisc_ref(tty);
-       if (disc && disc->flush_buffer)
-               disc->flush_buffer(tty);
-       tty_ldisc_deref(disc);
+       session = tty->session;
+
+       tty_ldisc_flush(tty);
 
        if (tty->driver->flush_buffer)
                tty->driver->flush_buffer(tty);
-       
+
        read_lock(&tasklist_lock);
        /* Kill the entire session */
-       do_each_task_pid(session, PIDTYPE_SID, p) {
+       do_each_pid_task(session, PIDTYPE_SID, p) {
                printk(KERN_NOTICE "SAK: killed process %d"
-                       " (%s): p->signal->session==tty->session\n",
-                       p->pid, p->comm);
+                       " (%s): task_session_nr(p)==tty->session\n",
+                       task_pid_nr(p), p->comm);
                send_sig(SIGKILL, p, 1);
-       } while_each_task_pid(session, PIDTYPE_SID, p);
+       } while_each_pid_task(session, PIDTYPE_SID, p);
        /* Now kill any processes that happen to have the
         * tty open.
         */
        do_each_thread(g, p) {
                if (p->signal->tty == tty) {
                        printk(KERN_NOTICE "SAK: killed process %d"
-                           " (%s): p->signal->session==tty->session\n",
-                           p->pid, p->comm);
+                           " (%s): task_session_nr(p)==tty->session\n",
+                           task_pid_nr(p), p->comm);
                        send_sig(SIGKILL, p, 1);
                        continue;
                }
@@ -2716,7 +3596,7 @@ static void __do_SAK(void *arg)
                         */
                        spin_lock(&p->files->file_lock);
                        fdt = files_fdtable(p->files);
-                       for (i=0; i < fdt->max_fds; i++) {
+                       for (i = 0; i < fdt->max_fds; i++) {
                                filp = fcheck_files(p->files, i);
                                if (!filp)
                                        continue;
@@ -2724,7 +3604,7 @@ static void __do_SAK(void *arg)
                                    filp->private_data == tty) {
                                        printk(KERN_NOTICE "SAK: killed process %d"
                                            " (%s): fd#%d opened to the tty\n",
-                                           p->pid, p->comm, i);
+                                           task_pid_nr(p), p->comm, i);
                                        force_sig(SIGKILL, p);
                                        break;
                                }
@@ -2737,6 +3617,13 @@ static void __do_SAK(void *arg)
 #endif
 }
 
+static void do_SAK_work(struct work_struct *work)
+{
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, SAK_work);
+       __do_SAK(tty);
+}
+
 /*
  * The tq handling here is a little racy - tty->SAK_work may already be queued.
  * Fortunately we don't need to worry, because if ->SAK_work is already queued,
@@ -2747,20 +3634,27 @@ void do_SAK(struct tty_struct *tty)
 {
        if (!tty)
                return;
-       PREPARE_WORK(&tty->SAK_work, __do_SAK, tty);
        schedule_work(&tty->SAK_work);
 }
 
 EXPORT_SYMBOL(do_SAK);
 
-/*
- * This routine is called out of the software interrupt to flush data
- * from the buffer chain to the line discipline.
+/**
+ *     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(void *private_)
+
+static void flush_to_ldisc(struct work_struct *work)
 {
-       struct tty_struct *tty = (struct tty_struct *) private_;
+       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;
@@ -2772,6 +3666,8 @@ static void flush_to_ldisc(void *private_)
                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;
@@ -2785,6 +3681,11 @@ static void flush_to_ldisc(void *private_)
                                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;
@@ -2798,87 +3699,22 @@ static void flush_to_ldisc(void *private_)
                        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);
 }
 
-/*
- * Routine which returns the baud rate of the tty
- *
- * Note that the baud_table needs to be kept in sync with the
- * include/asm/termbits.h file.
- */
-static int baud_table[] = {
-       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-       9600, 19200, 38400, 57600, 115200, 230400, 460800,
-#ifdef __sparc__
-       76800, 153600, 307200, 614400, 921600
-#else
-       500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
-       2500000, 3000000, 3500000, 4000000
-#endif
-};
-
-static int n_baud_table = ARRAY_SIZE(baud_table);
-
-/**
- *     tty_termios_baud_rate
- *     @termios: termios structure
- *
- *     Convert termios baud rate data into a speed. This should be called
- *     with the termios lock held if this termios is a terminal termios
- *     structure. May change the termios data.
- */
-int tty_termios_baud_rate(struct termios *termios)
-{
-       unsigned int cbaud;
-       
-       cbaud = termios->c_cflag & CBAUD;
-
-       if (cbaud & CBAUDEX) {
-               cbaud &= ~CBAUDEX;
-
-               if (cbaud < 1 || cbaud + 15 > n_baud_table)
-                       termios->c_cflag &= ~CBAUDEX;
-               else
-                       cbaud += 15;
-       }
-       return baud_table[cbaud];
-}
-
-EXPORT_SYMBOL(tty_termios_baud_rate);
-
-/**
- *     tty_get_baud_rate       -       get tty bit rates
- *     @tty: tty to query
- *
- *     Returns the baud rate as an integer for this terminal. The
- *     termios lock must be held by the caller and the terminal bit
- *     flags may be updated.
- */
-int tty_get_baud_rate(struct tty_struct *tty)
-{
-       int baud = tty_termios_baud_rate(tty->termios);
-
-       if (baud == 38400 && tty->alt_speed) {
-               if (!tty->warned) {
-                       printk(KERN_WARNING "Use of setserial/setrocket to "
-                                           "set SPD_* flags is deprecated\n");
-                       tty->warned = 1;
-               }
-               baud = tty->alt_speed;
-       }
-       
-       return baud;
-}
-
-EXPORT_SYMBOL(tty_get_baud_rate);
-
 /**
  *     tty_flip_buffer_push    -       terminal
  *     @tty: tty to push
@@ -2888,6 +3724,8 @@ EXPORT_SYMBOL(tty_get_baud_rate);
  *
  *     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)
@@ -2899,7 +3737,7 @@ void tty_flip_buffer_push(struct tty_struct *tty)
        spin_unlock_irqrestore(&tty->buf.lock, flags);
 
        if (tty->low_latency)
-               flush_to_ldisc((void *) tty);
+               flush_to_ldisc(&tty->buf.work.work);
        else
                schedule_delayed_work(&tty->buf.work, 1);
 }
@@ -2907,34 +3745,42 @@ void tty_flip_buffer_push(struct tty_struct *tty)
 EXPORT_SYMBOL(tty_flip_buffer_push);
 
 
-/*
- * This subroutine initializes a tty structure.
+/**
+ *     initialize_tty_struct
+ *     @tty: tty to initialize
+ *
+ *     This subroutine initializes a tty structure that has been newly
+ *     allocated.
+ *
+ *     Locking: none - tty in question must not be exposed at this point
  */
+
 static void initialize_tty_struct(struct tty_struct *tty)
 {
        memset(tty, 0, sizeof(struct tty_struct));
        tty->magic = TTY_MAGIC;
        tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-       tty->pgrp = -1;
+       tty->session = NULL;
+       tty->pgrp = NULL;
        tty->overrun_time = jiffies;
        tty->buf.head = tty->buf.tail = NULL;
        tty_buffer_init(tty);
-       INIT_WORK(&tty->buf.work, flush_to_ldisc, tty);
-       init_MUTEX(&tty->buf.pty_sem);
-       init_MUTEX(&tty->termios_sem);
+       INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
+       mutex_init(&tty->termios_mutex);
        init_waitqueue_head(&tty->write_wait);
        init_waitqueue_head(&tty->read_wait);
-       INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
+       INIT_WORK(&tty->hangup_work, do_tty_hangup);
        mutex_init(&tty->atomic_read_lock);
        mutex_init(&tty->atomic_write_lock);
        spin_lock_init(&tty->read_lock);
        INIT_LIST_HEAD(&tty->tty_files);
-       INIT_WORK(&tty->SAK_work, NULL, NULL);
+       INIT_WORK(&tty->SAK_work, do_SAK_work);
 }
 
 /*
  * The default put_char routine if the driver did not define one.
  */
+
 static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
 {
        tty->driver->write(tty, &ch, 1);
@@ -2943,21 +3789,26 @@ static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
 static struct class *tty_class;
 
 /**
- * tty_register_device - register a tty device
- * @driver: the tty driver that describes the tty device
- * @index: the index in the tty driver for this tty device
- * @device: a struct device that is associated with this tty device.
- *     This field is optional, if there is no known struct device for this
- *     tty device it can be set to NULL safely.
+ *     tty_register_device - register a tty device
+ *     @driver: the tty driver that describes the tty device
+ *     @index: the index in the tty driver for this tty device
+ *     @device: a struct device that is associated with this tty device.
+ *             This field is optional, if there is no known struct device
+ *             for this tty device it can be set to NULL safely.
  *
- * Returns a pointer to the class device (or ERR_PTR(-EFOO) on error).
+ *     Returns a pointer to the struct device for this tty device
+ *     (or ERR_PTR(-EFOO) on error).
  *
- * This call is required to be made to register an individual tty device if
- * the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set.  If that
- * bit is not set, this function should not be called by a tty driver.
+ *     This call is required to be made to register an individual tty device
+ *     if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set.  If
+ *     that bit is not set, this function should not be called by a tty
+ *     driver.
+ *
+ *     Locking: ??
  */
-struct class_device *tty_register_device(struct tty_driver *driver,
-                                        unsigned index, struct device *device)
+
+struct device *tty_register_device(struct tty_driver *driver, unsigned index,
+                                  struct device *device)
 {
        char name[64];
        dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
@@ -2973,20 +3824,24 @@ struct class_device *tty_register_device(struct tty_driver *driver,
        else
                tty_line_name(driver, index, name);
 
-       return class_device_create(tty_class, NULL, dev, device, "%s", name);
+       return device_create(tty_class, device, dev, name);
 }
 
 /**
- * tty_unregister_device - unregister a tty device
- * @driver: the tty driver that describes the tty device
- * @index: the index in the tty driver for this tty device
+ *     tty_unregister_device - unregister a tty device
+ *     @driver: the tty driver that describes the tty device
+ *     @index: the index in the tty driver for this tty device
  *
- * If a tty device is registered with a call to tty_register_device() then
- * this function must be made when the tty device is gone.
+ *     If a tty device is registered with a call to tty_register_device() then
+ *     this function must be called when the tty device is gone.
+ *
+ *     Locking: ??
  */
+
 void tty_unregister_device(struct tty_driver *driver, unsigned index)
 {
-       class_device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index);
+       device_destroy(tty_class,
+               MKDEV(driver->major, driver->minor_start) + index);
 }
 
 EXPORT_SYMBOL(tty_register_device);
@@ -2996,9 +3851,8 @@ struct tty_driver *alloc_tty_driver(int lines)
 {
        struct tty_driver *driver;
 
-       driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
+       driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
        if (driver) {
-               memset(driver, 0, sizeof(struct tty_driver));
                driver->magic = TTY_DRIVER_MAGIC;
                driver->num = lines;
                /* later we'll move allocation of tables here */
@@ -3011,7 +3865,8 @@ void put_tty_driver(struct tty_driver *driver)
        kfree(driver);
 }
 
-void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
+void tty_set_operations(struct tty_driver *driver,
+                       const struct tty_operations *op)
 {
        driver->open = op->open;
        driver->close = op->close;
@@ -3021,6 +3876,7 @@ void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
        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;
@@ -3036,6 +3892,11 @@ void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
        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
 }
 
 
@@ -3049,31 +3910,29 @@ EXPORT_SYMBOL(tty_set_operations);
 int tty_register_driver(struct tty_driver *driver)
 {
        int error;
-        int i;
+       int i;
        dev_t dev;
        void **p = NULL;
 
        if (driver->flags & TTY_DRIVER_INSTALLED)
                return 0;
 
-       if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
-               p = kmalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
+       if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
+               p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
                if (!p)
                        return -ENOMEM;
-               memset(p, 0, driver->num * 3 * sizeof(void *));
        }
 
        if (!driver->major) {
-               error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
-                                               (char*)driver->name);
+               error = alloc_chrdev_region(&dev, driver->minor_start,
+                                               driver->num, driver->name);
                if (!error) {
                        driver->major = MAJOR(dev);
                        driver->minor_start = MINOR(dev);
                }
        } else {
                dev = MKDEV(driver->major, driver->minor_start);
-               error = register_chrdev_region(dev, driver->num,
-                                               (char*)driver->name);
+               error = register_chrdev_region(dev, driver->num, driver->name);
        }
        if (error < 0) {
                kfree(p);
@@ -3082,8 +3941,9 @@ int tty_register_driver(struct tty_driver *driver)
 
        if (p) {
                driver->ttys = (struct tty_struct **)p;
-               driver->termios = (struct termios **)(p + driver->num);
-               driver->termios_locked = (struct termios **)(p + driver->num * 2);
+               driver->termios = (struct ktermios **)(p + driver->num);
+               driver->termios_locked = (struct ktermios **)
+                                                       (p + driver->num * 2);
        } else {
                driver->ttys = NULL;
                driver->termios = NULL;
@@ -3094,7 +3954,6 @@ int tty_register_driver(struct tty_driver *driver)
        driver->cdev.owner = driver->owner;
        error = cdev_add(&driver->cdev, dev, driver->num);
        if (error) {
-               cdev_del(&driver->cdev);
                unregister_chrdev_region(dev, driver->num);
                driver->ttys = NULL;
                driver->termios = driver->termios_locked = NULL;
@@ -3104,11 +3963,13 @@ int tty_register_driver(struct tty_driver *driver)
 
        if (!driver->put_char)
                driver->put_char = tty_default_put_char;
-       
+
+       mutex_lock(&tty_mutex);
        list_add(&driver->tty_drivers, &tty_drivers);
-       
-       if ( !(driver->flags & TTY_DRIVER_DYNAMIC_DEV) ) {
-               for(i = 0; i < driver->num; i++)
+       mutex_unlock(&tty_mutex);
+
+       if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
+               for (i = 0; i < driver->num; i++)
                    tty_register_device(driver, i, NULL);
        }
        proc_tty_register_driver(driver);
@@ -3123,7 +3984,7 @@ EXPORT_SYMBOL(tty_register_driver);
 int tty_unregister_driver(struct tty_driver *driver)
 {
        int i;
-       struct termios *tp;
+       struct ktermios *tp;
        void *p;
 
        if (driver->refcount)
@@ -3131,8 +3992,9 @@ int tty_unregister_driver(struct tty_driver *driver)
 
        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
@@ -3161,9 +4023,57 @@ int tty_unregister_driver(struct tty_driver *driver)
        cdev_del(&driver->cdev);
        return 0;
 }
-
 EXPORT_SYMBOL(tty_unregister_driver);
 
+dev_t tty_devnum(struct tty_struct *tty)
+{
+       return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
+}
+EXPORT_SYMBOL(tty_devnum);
+
+void proc_clear_tty(struct task_struct *p)
+{
+       spin_lock_irq(&p->sighand->siglock);
+       p->signal->tty = NULL;
+       spin_unlock_irq(&p->sighand->siglock);
+}
+EXPORT_SYMBOL(proc_clear_tty);
+
+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.... */
+               put_pid(tty->session);
+               put_pid(tty->pgrp);
+               tty->session = get_pid(task_session(tsk));
+               tty->pgrp = get_pid(task_pgrp(tsk));
+       }
+       put_pid(tsk->signal->tty_old_pgrp);
+       tsk->signal->tty = tty;
+       tsk->signal->tty_old_pgrp = NULL;
+}
+
+static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+{
+       spin_lock_irq(&tsk->sighand->siglock);
+       __proc_set_tty(tsk, tty);
+       spin_unlock_irq(&tsk->sighand->siglock);
+}
+
+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();
+       return tty;
+}
+EXPORT_SYMBOL_GPL(get_current_tty);
 
 /*
  * Initialize the console device. This is called *early*, so
@@ -3179,12 +4089,9 @@ void __init console_init(void)
        (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
 
        /*
-        * set up the console device so that later boot sequences can 
+        * set up the console device so that later boot sequences can
         * inform about problems etc..
         */
-#ifdef CONFIG_EARLY_PRINTK
-       disable_early_printk();
-#endif
        call = __con_initcall_start;
        while (call < __con_initcall_end) {
                (*call)();
@@ -3192,10 +4099,6 @@ void __init console_init(void)
        }
 }
 
-#ifdef CONFIG_VT
-extern int vty_init(void);
-#endif
-
 static int __init tty_class_init(void)
 {
        tty_class = class_create(THIS_MODULE, "tty");
@@ -3226,20 +4129,20 @@ 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");
-       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
+       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), "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");
-       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console");
+       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");
-       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
+       device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), "ptmx");
 #endif
 
 #ifdef CONFIG_VT
@@ -3247,7 +4150,7 @@ static int __init tty_init(void)
        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");
-       class_device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
+       device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), "tty0");
 
        vty_init();
 #endif