drivers/edac: drivers to use new PCI operation
[safe/jmp/linux-2.6] / drivers / char / tty_io.c
index 2a1e95b..de37ebc 100644 (file)
 #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);
@@ -126,9 +128,10 @@ EXPORT_SYMBOL(tty_std_termios);
    
 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 */
@@ -138,8 +141,6 @@ static DECLARE_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 *);
@@ -150,8 +151,16 @@ static int tty_open(struct inode *, struct file *);
 static int tty_release(struct inode *, struct file *);
 int tty_ioctl(struct inode * inode, struct file * file,
              unsigned int cmd, unsigned long arg);
+#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_mem(struct tty_struct *tty, int idx);
+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
@@ -160,17 +169,11 @@ static void release_mem(struct tty_struct *tty, int idx);
  *     been initialized in any way but has been zeroed
  *
  *     Locking: none
- *     FIXME: use kzalloc
  */
 
 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 *);
@@ -255,7 +258,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;
 }
@@ -264,18 +267,6 @@ 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
- */
-
-
 /**
  *     tty_buffer_free_all             -       free buffers used by a tty
  *     @tty: tty to free from
@@ -378,6 +369,29 @@ 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
+ *
+ *     Locking: none
+ */
+
+static void tty_buffer_flush(struct tty_struct *tty)
+{
+       struct tty_buffer *thead;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       while((thead = tty->buf.head) != NULL) {
+               tty->buf.head = thead->next;
+               tty_buffer_free(tty, thead);
+       }
+       tty->buf.tail = NULL;
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
+}
+
+/**
  *     tty_buffer_find         -       find a free tty buffer
  *     @tty: tty owning the buffer
  *     @size: characters wanted
@@ -619,14 +633,14 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
  *     they are not on hot paths so a little discipline won't do 
  *     any harm.
  *
- *     Locking: takes termios_sem
+ *     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);
 }
 
 /*
@@ -920,7 +934,7 @@ static void tty_ldisc_enable(struct tty_struct *tty)
  *     context.
  *
  *     Locking: takes tty_ldisc_lock.
- *             called functions take termios_sem
+ *              called functions take termios_mutex
  */
  
 static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
@@ -949,13 +963,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 ?
         */
 
@@ -966,6 +973,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;
 
@@ -1124,17 +1138,18 @@ 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;
 }
 
@@ -1164,12 +1179,19 @@ static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
        return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
 }
 
+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,
@@ -1182,6 +1204,7 @@ static const struct file_operations ptmx_fops = {
        .write          = tty_write,
        .poll           = tty_poll,
        .ioctl          = tty_ioctl,
+       .compat_ioctl   = tty_compat_ioctl,
        .open           = ptmx_open,
        .release        = tty_release,
        .fasync         = tty_fasync,
@@ -1194,6 +1217,7 @@ static const struct file_operations console_fops = {
        .write          = redirected_tty_write,
        .poll           = tty_poll,
        .ioctl          = tty_ioctl,
+       .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
        .fasync         = tty_fasync,
@@ -1205,6 +1229,7 @@ static const struct file_operations hung_up_tty_fops = {
        .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,
 };
 
@@ -1253,13 +1278,30 @@ void tty_ldisc_flush(struct tty_struct *tty)
                        ld->flush_buffer(tty);
                tty_ldisc_deref(ld);
        }
+       tty_buffer_flush(tty);
 }
 
 EXPORT_SYMBOL_GPL(tty_ldisc_flush);
+
+/**
+ *     tty_reset_termios       -       reset terminal state
+ *     @tty: tty to reset
+ *
+ *     Restore a terminal to the driver default state
+ */
+
+static void tty_reset_termios(struct tty_struct *tty)
+{
+       mutex_lock(&tty->termios_mutex);
+       *tty->termios = tty->driver->init_termios;
+       tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
+       tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+       mutex_unlock(&tty->termios_mutex);
+}
        
 /**
  *     do_tty_hangup           -       actual handler for hangup events
- *     @data: tty device
+ *     @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
@@ -1272,16 +1314,17 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush);
  *
  *     Locking:
  *             BKL
- *             redirect lock for undoing redirection
- *             file list lock for manipulating list of ttys
- *             tty_ldisc_lock from called functions
- *             termios_sem resetting termios data
- *             tasklist_lock to walk task list for hangup event
- *
+ *               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 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;
@@ -1343,11 +1386,7 @@ static void do_tty_hangup(void *data)
         * 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);
@@ -1356,23 +1395,30 @@ static void do_tty_hangup(void *data)
          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
@@ -1438,7 +1484,7 @@ void tty_vhangup(struct tty_struct * tty)
 
        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);
 
@@ -1458,6 +1504,23 @@ int tty_hung_up_p(struct file * filp)
 EXPORT_SYMBOL(tty_hung_up_p);
 
 /**
+ * is_tty      -       checker whether file is a TTY
+ */
+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
  *
@@ -1473,65 +1536,98 @@ EXPORT_SYMBOL(tty_hung_up_p);
  *     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: tty_mutex is taken to protect current->signal->tty
+ *     Locking:
  *             BKL is taken for hysterical raisins
- *             Tasklist lock is taken (under tty_mutex) to walk process
- *             lists for the session.
+ *               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();        
                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        -       propogate flow control
+ *     stop_tty        -       propagate flow control
  *     @tty: tty to stop
  *
  *     Perform flow control to the driver. For PTY/TTY pairs we
- *     must also propogate the TIOCKPKT status. May be called
+ *     must also propagate the TIOCKPKT status. May be called
  *     on an already stopped device and will not re-call the driver
  *     method.
  *
@@ -1561,11 +1657,11 @@ void stop_tty(struct tty_struct *tty)
 EXPORT_SYMBOL(stop_tty);
 
 /**
- *     start_tty       -       propogate flow control
+ *     start_tty       -       propagate flow control
  *     @tty: tty to start
  *
  *     Start a tty that has been stopped if at all possible. Perform
- *     any neccessary wakeups and propogate the TIOCPKT status. If this
+ *     any neccessary 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.
  *
@@ -1588,7 +1684,6 @@ void start_tty(struct tty_struct *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);
@@ -1619,7 +1714,7 @@ static ssize_t tty_read(struct file * file, char __user * buf, size_t count,
        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)))
@@ -1640,6 +1735,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
@@ -1651,13 +1763,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;
        
-       /* FIXME: O_NDELAY ... */
-       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
@@ -1690,8 +1801,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;
@@ -1722,11 +1833,12 @@ 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;
 }
 
@@ -1753,7 +1865,7 @@ static ssize_t tty_write(struct file * file, const char __user * buf, size_t cou
                         loff_t *ppos)
 {
        struct tty_struct * tty;
-       struct inode *inode = file->f_dentry->d_inode;
+       struct inode *inode = file->f_path.dentry->d_inode;
        ssize_t ret;
        struct tty_ldisc *ld;
        
@@ -1860,13 +1972,27 @@ 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;
+       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 {
@@ -1908,7 +2034,7 @@ static int init_dev(struct tty_driver *driver, int idx,
        }
 
        if (!*tp_loc) {
-               tp = (struct termios *) kmalloc(sizeof(struct termios),
+               tp = (struct ktermios *) kmalloc(sizeof(struct ktermios),
                                                GFP_KERNEL);
                if (!tp)
                        goto free_mem_out;
@@ -1916,11 +2042,9 @@ static int init_dev(struct tty_driver *driver, int idx,
        }
 
        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) {
@@ -1941,19 +2065,17 @@ 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 = (struct ktermios *)
+                               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));
                }
 
                /*
@@ -1979,7 +2101,7 @@ static int init_dev(struct tty_driver *driver, int idx,
 
        /* 
         * 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)) {
@@ -1992,13 +2114,16 @@ static int init_dev(struct tty_driver *driver, int idx,
                *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) {
@@ -2068,16 +2193,17 @@ 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;
 }
 
 /**
- *     release_mem             -       release tty structure memory
+ *     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
@@ -2089,37 +2215,14 @@ release_mem_out:
  *     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)
@@ -2132,15 +2235,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
@@ -2159,7 +2286,7 @@ static void release_dev(struct file * filp)
        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");
@@ -2340,16 +2467,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);
        }
 
@@ -2414,10 +2535,10 @@ static void release_dev(struct file * filp)
                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 */
@@ -2446,9 +2567,9 @@ static void release_dev(struct file * filp)
  *     The termios state of a pty is reset on first open so that
  *     settings don't persist across reuse.
  *
- *     Locking: tty_mutex protects current->signal->tty, get_tty_driver and
- *             init_dev work. tty->count should protect the rest.
- *             task_lock is held to update task details for sessions
+ *     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)
@@ -2470,12 +2591,13 @@ retry_open:
        mutex_lock(&tty_mutex);
 
        if (device == MKDEV(TTYAUX_MAJOR,0)) {
-               if (!current->signal->tty) {
+               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;
@@ -2550,17 +2672,17 @@ 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;
 }
 
@@ -2623,8 +2745,10 @@ 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;
@@ -2675,7 +2799,7 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait)
        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);
@@ -2691,7 +2815,7 @@ static int tty_fasync(int fd, struct file * filp, int on)
        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);
@@ -2699,9 +2823,18 @@ static int tty_fasync(int fd, struct file * filp, int on)
                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 {
@@ -2724,6 +2857,8 @@ static int tty_fasync(int fd, struct file * filp, int on)
  *     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)
@@ -2746,18 +2881,21 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
  *     @tty; tty
  *     @arg: user buffer for result
  *
- *     Copies the kernel idea of the window size into the user buffer. No
- *     locking is done.
+ *     Copies the kernel idea of the window size into the user buffer.
  *
- *     FIXME: Returning random values racing a window size set is wrong
- *     should lock here against that
+ *     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;
 }
 
 /**
@@ -2772,8 +2910,8 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
  *     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_sem is used to ensure we don't double
- *     resize and get confused. Lock order - tty->termios.sem before
+ *             The tty->termios_mutex is used to ensure we don't double
+ *     resize and get confused. Lock order - tty->termios_mutex before
  *     console sem
  */
 
@@ -2785,26 +2923,27 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
        if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
                return -EFAULT;
 
-       down(&tty->termios_sem);
+       mutex_lock(&tty->termios_mutex);
        if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
                goto done;
 
 #ifdef CONFIG_VT
        if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
-               if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row)) {
-                       up(&tty->termios_sem);
+               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:
-       up(&tty->termios_sem);
+       mutex_unlock(&tty->termios_mutex);
        return 0;
 }
 
@@ -2877,28 +3016,28 @@ static int fionbio(struct file *file, int __user *p)
  *     leader to set this tty as the controlling tty for the session.
  *
  *     Locking:
- *             Takes tasklist lock internally to walk sessions
- *             Takes task_lock() when updating signal->tty
- *
- *     FIXME: tty_mutex is needed to protect signal->tty references.
- *     FIXME: why task_lock on the signal->tty reference ??
- *
+ *             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)
 {
-       struct task_struct *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!
@@ -2907,22 +3046,18 @@ static int tiocsctty(struct tty_struct *tty, int arg)
                        /*
                         * 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;
 }
 
 /**
@@ -2934,7 +3069,7 @@ static int tiocsctty(struct tty_struct *tty, int arg)
  *     Obtain the process group of the tty. If there is no process group
  *     return an error.
  *
- *     Locking: none. Reference to ->signal->tty is safe.
+ *     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)
@@ -2945,7 +3080,7 @@ 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_nr(real_tty->pgrp), p);
 }
 
 /**
@@ -2958,13 +3093,12 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
  *     permitted where the tty session is our session.
  *
  *     Locking: None
- *
- *     FIXME: current->signal->tty referencing is unsafe.
  */
 
 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)
@@ -2973,16 +3107,26 @@ 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_pid(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;
 }
 
 /**
@@ -2994,7 +3138,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
  *     Obtain the session id of the tty. If there is no session
  *     return an error.
  *
- *     Locking: none. Reference to ->signal->tty is safe.
+ *     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)
@@ -3005,9 +3149,9 @@ 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_nr(real_tty->session), p);
 }
 
 /**
@@ -3038,19 +3182,19 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
  *     timed break functionality.
  *
  *     Locking:
- *             None
+ *             atomic_write_lock serializes
  *
- *     FIXME:
- *             What if two overlap
  */
 
 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;
@@ -3143,6 +3287,8 @@ int tty_ioctl(struct inode * inode, struct file * file,
        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)
@@ -3210,14 +3356,9 @@ int tty_ioctl(struct inode * inode, struct file * file,
                        clear_bit(TTY_EXCLUSIVE, &tty->flags);
                        return 0;
                case TIOCNOTTY:
-                       /* FIXME: taks lock or tty_mutex ? */
                        if (current->signal->tty != tty)
                                return -ENOTTY;
-                       if (current->signal->leader)
-                               disassociate_ctty(0);
-                       task_lock(current);
-                       current->signal->tty = NULL;
-                       task_unlock(current);
+                       no_tty();
                        return 0;
                case TIOCSCTTY:
                        return tiocsctty(tty, arg);
@@ -3264,6 +3405,15 @@ int tty_ioctl(struct inode * inode, struct file * file,
                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);
@@ -3281,6 +3431,32 @@ 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
@@ -3301,47 +3477,41 @@ 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;
+       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);
+       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",
+                       " (%s): process_session(p)==tty->session\n",
                        p->pid, 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",
+                           " (%s): process_session(p)==tty->session\n",
                            p->pid, p->comm);
                        send_sig(SIGKILL, p, 1);
                        continue;
@@ -3375,6 +3545,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,
@@ -3385,7 +3562,6 @@ void do_SAK(struct tty_struct *tty)
 {
        if (!tty)
                return;
-       PREPARE_WORK(&tty->SAK_work, __do_SAK, tty);
        schedule_work(&tty->SAK_work);
 }
 
@@ -3393,7 +3569,7 @@ EXPORT_SYMBOL(do_SAK);
 
 /**
  *     flush_to_ldisc
- *     @private_: tty structure passed from work queue.
+ *     @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.
@@ -3403,9 +3579,10 @@ EXPORT_SYMBOL(do_SAK);
  *     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;
@@ -3450,84 +3627,6 @@ static void flush_to_ldisc(void *private_)
        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.
- *
- *     Locking: none
- */
-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.
- *
- *     Locking: none
- */
-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
@@ -3550,7 +3649,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);
 }
@@ -3573,21 +3672,22 @@ 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_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
        init_MUTEX(&tty->buf.pty_sem);
-       init_MUTEX(&tty->termios_sem);
+       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);
 }
 
 /*
@@ -3609,7 +3709,8 @@ static struct class *tty_class;
  *             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
@@ -3619,8 +3720,8 @@ static struct class *tty_class;
  *     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;
@@ -3636,7 +3737,7 @@ 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);
 }
 
 /**
@@ -3652,7 +3753,7 @@ struct class_device *tty_register_device(struct tty_driver *driver,
 
 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);
@@ -3662,9 +3763,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 */
@@ -3677,7 +3777,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;
@@ -3687,6 +3788,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;
@@ -3722,24 +3824,22 @@ int tty_register_driver(struct tty_driver *driver)
        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);
+                                               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);
@@ -3748,8 +3848,8 @@ 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;
@@ -3770,7 +3870,9 @@ 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);
+       mutex_unlock(&tty_mutex);
        
        if ( !(driver->flags & TTY_DRIVER_DYNAMIC_DEV) ) {
                for(i = 0; i < driver->num; i++)
@@ -3788,7 +3890,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)
@@ -3796,8 +3898,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
@@ -3826,9 +3929,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
@@ -3847,9 +3998,6 @@ void __init console_init(void)
         * 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)();
@@ -3891,20 +4039,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
@@ -3912,7 +4060,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