Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / char / tty_io.c
index f5e3bce..0b31cf2 100644 (file)
@@ -94,6 +94,7 @@
 #include <linux/idr.h>
 #include <linux/wait.h>
 #include <linux/bitops.h>
+#include <linux/delay.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -130,7 +131,7 @@ LIST_HEAD(tty_drivers);                     /* linked list of tty drivers */
 
 /* Semaphore to protect creating and releasing a tty. This is shared with
    vt.c for deeply disgusting hack reasons */
-DECLARE_MUTEX(tty_sem);
+DEFINE_MUTEX(tty_mutex);
 
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
@@ -153,7 +154,6 @@ static int tty_release(struct inode *, struct 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);
-extern void rs_360_init(void);
 static void release_mem(struct tty_struct *tty, int idx);
 
 
@@ -167,9 +167,12 @@ static struct tty_struct *alloc_tty_struct(void)
        return tty;
 }
 
+static void tty_buffer_free_all(struct tty_struct *);
+
 static inline void free_tty_struct(struct tty_struct *tty)
 {
        kfree(tty->write_buf);
+       tty_buffer_free_all(tty);
        kfree(tty);
 }
 
@@ -231,6 +234,232 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
        return 0;
 }
 
+/*
+ * Tty buffer allocation management
+ */
+
+static void tty_buffer_free_all(struct tty_struct *tty)
+{
+       struct tty_buffer *thead;
+       while((thead = tty->buf.head) != NULL) {
+               tty->buf.head = thead->next;
+               kfree(thead);
+       }
+       while((thead = tty->buf.free) != NULL) {
+               tty->buf.free = thead->next;
+               kfree(thead);
+       }
+       tty->buf.tail = NULL;
+}
+
+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;
+}
+
+static struct tty_buffer *tty_buffer_alloc(size_t size)
+{
+       struct tty_buffer *p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
+       if(p == NULL)
+               return NULL;
+       p->used = 0;
+       p->size = size;
+       p->next = NULL;
+       p->active = 0;
+       p->commit = 0;
+       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); */
+       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 */
+
+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)
+               kfree(b);
+       else {
+               b->next = tty->buf.free;
+               tty->buf.free = b;
+       }
+}
+
+static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
+{
+       struct tty_buffer **tbh = &tty->buf.free;
+       while((*tbh) != NULL) {
+               struct tty_buffer *t = *tbh;
+               if(t->size >= size) {
+                       *tbh = t->next;
+                       t->next = NULL;
+                       t->used = 0;
+                       t->commit = 0;
+                       t->read = 0;
+                       /* DEBUG ONLY */
+/*                     memset(t->data, '*', size); */
+/*                     printk("Flip recycle %p\n", t); */
+                       return t;
+               }
+               tbh = &((*tbh)->next);
+       }
+       /* Round the buffer size out */
+       size = (size + 0xFF) & ~ 0xFF;
+       return tty_buffer_alloc(size);
+       /* Should possibly check if this fails for the largest buffer we
+          have queued and recycle that ? */
+}
+
+int tty_buffer_request_room(struct tty_struct *tty, size_t size)
+{
+       struct tty_buffer *b, *n;
+       int left;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tty->buf.lock, flags);
+
+       /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
+          remove this conditional if its worth it. This would be invisible
+          to the callers */
+       if ((b = tty->buf.tail) != NULL) {
+               left = b->size - b->used;
+               b->active = 1;
+       } else
+               left = 0;
+
+       if (left < size) {
+               /* This is the slow path - looking for new buffers to use */
+               if ((n = tty_buffer_find(tty, size)) != NULL) {
+                       if (b != NULL) {
+                               b->next = n;
+                               b->active = 0;
+                               b->commit = b->used;
+                       } else
+                               tty->buf.head = n;
+                       tty->buf.tail = n;
+                       n->active = 1;
+               } else
+                       size = left;
+       }
+
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
+       return size;
+}
+EXPORT_SYMBOL_GPL(tty_buffer_request_room);
+
+int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
+                               size_t size)
+{
+       int copied = 0;
+       do {
+               int space = tty_buffer_request_room(tty, size - copied);
+               struct tty_buffer *tb = tty->buf.tail;
+               /* If there is no space then tb may be NULL */
+               if(unlikely(space == 0))
+                       break;
+               memcpy(tb->char_buf_ptr + tb->used, chars, space);
+               memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
+               tb->used += space;
+               copied += space;
+               chars += space;
+       }
+       /* There is a small chance that we need to split the data over
+          several buffers. If this is the case we must loop */
+       while (unlikely(size > copied));
+       return copied;
+}
+EXPORT_SYMBOL(tty_insert_flip_string);
+
+int tty_insert_flip_string_flags(struct tty_struct *tty,
+               const unsigned char *chars, const char *flags, size_t size)
+{
+       int copied = 0;
+       do {
+               int space = tty_buffer_request_room(tty, size - copied);
+               struct tty_buffer *tb = tty->buf.tail;
+               /* If there is no space then tb may be NULL */
+               if(unlikely(space == 0))
+                       break;
+               memcpy(tb->char_buf_ptr + tb->used, chars, space);
+               memcpy(tb->flag_buf_ptr + tb->used, flags, space);
+               tb->used += space;
+               copied += space;
+               chars += space;
+               flags += space;
+       }
+       /* There is a small chance that we need to split the data over
+          several buffers. If this is the case we must loop */
+       while (unlikely(size > copied));
+       return copied;
+}
+EXPORT_SYMBOL(tty_insert_flip_string_flags);
+
+void tty_schedule_flip(struct tty_struct *tty)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       if (tty->buf.tail != NULL) {
+               tty->buf.tail->active = 0;
+               tty->buf.tail->commit = tty->buf.tail->used;
+       }
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
+       schedule_delayed_work(&tty->buf.work, 1);
+}
+EXPORT_SYMBOL(tty_schedule_flip);
+
+/*
+ *     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!
+ */
+
+int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_t size)
+{
+       int space = tty_buffer_request_room(tty, size);
+       if (likely(space)) {
+               struct tty_buffer *tb = tty->buf.tail;
+               *chars = tb->char_buf_ptr + tb->used;
+               memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
+               tb->used += space;
+       }
+       return space;
+}
+
+EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
+
+/*
+ *     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!
+ */
+
+int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size)
+{
+       int space = tty_buffer_request_room(tty, size);
+       if (likely(space)) {
+               struct tty_buffer *tb = tty->buf.tail;
+               *chars = tb->char_buf_ptr + tb->used;
+               *flags = tb->flag_buf_ptr + tb->used;
+               tb->used += space;
+       }
+       return space;
+}
+
+EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
+
+
+
 /*
  *     This is probably overkill for real world processors but
  *     they are not on hot paths so a little discipline won't do 
@@ -252,7 +481,7 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
  
 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     */
+static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */
 
 int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
 {
@@ -263,24 +492,35 @@ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
                return -EINVAL;
        
        spin_lock_irqsave(&tty_ldisc_lock, flags);
-       if (new_ldisc) {
-               tty_ldiscs[disc] = *new_ldisc;
-               tty_ldiscs[disc].num = disc;
-               tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
-               tty_ldiscs[disc].refcount = 0;
-       } else {
-               if(tty_ldiscs[disc].refcount)
-                       ret = -EBUSY;
-               else
-                       tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
-       }
+       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);
 
+int tty_unregister_ldisc(int disc)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       if (disc < N_TTY || disc >= NR_LDISCS)
+               return -EINVAL;
+
+       spin_lock_irqsave(&tty_ldisc_lock, flags);
+       if (tty_ldiscs[disc].refcount)
+               ret = -EBUSY;
+       else
+               tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
+       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(tty_unregister_ldisc);
+
 struct tty_ldisc *tty_ldisc_get(int disc)
 {
        unsigned long flags;
@@ -314,14 +554,12 @@ void tty_ldisc_put(int disc)
        struct tty_ldisc *ld;
        unsigned long flags;
        
-       if (disc < N_TTY || disc >= NR_LDISCS)
-               BUG();
+       BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
                
        spin_lock_irqsave(&tty_ldisc_lock, flags);
        ld = &tty_ldiscs[disc];
-       if(ld->refcount == 0)
-               BUG();
-       ld->refcount --;
+       BUG_ON(ld->refcount == 0);
+       ld->refcount--;
        module_put(ld->owner);
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 }
@@ -416,8 +654,7 @@ void tty_ldisc_deref(struct tty_ldisc *ld)
 {
        unsigned long flags;
 
-       if(ld == NULL)
-               BUG();
+       BUG_ON(ld == NULL);
                
        spin_lock_irqsave(&tty_ldisc_lock, flags);
        if(ld->refcount == 0)
@@ -459,21 +696,19 @@ static void tty_ldisc_enable(struct tty_struct *tty)
  
 static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 {
-       int     retval = 0;
-       struct  tty_ldisc o_ldisc;
+       int retval = 0;
+       struct tty_ldisc o_ldisc;
        char buf[64];
        int work;
        unsigned long flags;
        struct tty_ldisc *ld;
+       struct tty_struct *o_tty;
 
        if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
                return -EINVAL;
 
 restart:
 
-       if (tty->ldisc.num == ldisc)
-               return 0;       /* We are already in the desired discipline */
-       
        ld = tty_ldisc_get(ldisc);
        /* Eduardo Blanco <ejbs@cs.cs.com.uy> */
        /* Cyrus Durgin <cider@speakeasy.org> */
@@ -484,48 +719,88 @@ restart:
        if (ld == NULL)
                return -EINVAL;
 
-       o_ldisc = tty->ldisc;
+       /*
+        *      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 ?
+        */
 
        tty_wait_until_sent(tty, 0);
 
+       if (tty->ldisc.num == ldisc) {
+               tty_ldisc_put(ldisc);
+               return 0;
+       }
+
+       o_ldisc = tty->ldisc;
+       o_tty = tty->link;
+
        /*
         *      Make sure we don't change while someone holds a
         *      reference to the line discipline. The TTY_LDISC bit
         *      prevents anyone taking a reference once it is clear.
         *      We need the lock to avoid racing reference takers.
         */
-        
+
        spin_lock_irqsave(&tty_ldisc_lock, flags);
-       if(tty->ldisc.refcount)
-       {
-               /* Free the new ldisc we grabbed. Must drop the lock
-                  first. */
+       if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
+               if(tty->ldisc.refcount) {
+                       /* Free the new ldisc we grabbed. Must drop the lock
+                          first. */
+                       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+                       tty_ldisc_put(ldisc);
+                       /*
+                        * There are several reasons we may be busy, including
+                        * random momentary I/O traffic. We must therefore
+                        * retry. We could distinguish between blocking ops
+                        * and retries if we made tty_ldisc_wait() smarter. That
+                        * is up for discussion.
+                        */
+                       if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
+                               return -ERESTARTSYS;
+                       goto restart;
+               }
+               if(o_tty && o_tty->ldisc.refcount) {
+                       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+                       tty_ldisc_put(ldisc);
+                       if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
+                               return -ERESTARTSYS;
+                       goto restart;
+               }
+       }
+
+       /* if 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);
-               /*
-                * There are several reasons we may be busy, including
-                * random momentary I/O traffic. We must therefore
-                * retry. We could distinguish between blocking ops
-                * and retries if we made tty_ldisc_wait() smarter. That
-                * is up for discussion.
-                */
-               if(wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
-                       return -ERESTARTSYS;                    
+               ld = tty_ldisc_ref_wait(tty);
+               tty_ldisc_deref(ld);
                goto restart;
        }
-       clear_bit(TTY_LDISC, &tty->flags);      
+
+       clear_bit(TTY_LDISC, &tty->flags);
        clear_bit(TTY_DONT_FLIP, &tty->flags);
+       if (o_tty) {
+               clear_bit(TTY_LDISC, &o_tty->flags);
+               clear_bit(TTY_DONT_FLIP, &o_tty->flags);
+       }
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-       
+
        /*
         *      From this point on we know nobody has an ldisc
         *      usage reference, nor can they obtain one until
         *      we say so later on.
         */
-        
-       work = cancel_delayed_work(&tty->flip.work);
+
+       work = cancel_delayed_work(&tty->buf.work);
        /*
-        * Wait for ->hangup_work and ->flip.work handlers to terminate
+        * Wait for ->hangup_work and ->buf.work handlers to terminate
         */
         
        flush_scheduled_work();
@@ -573,11 +848,13 @@ restart:
         */
         
        tty_ldisc_enable(tty);
+       if (o_tty)
+               tty_ldisc_enable(o_tty);
        
        /* Restart it in case no characters kick it off. Safe if
           already running */
-       if(work)
-               schedule_delayed_work(&tty->flip.work, 1);
+       if (work)
+               schedule_delayed_work(&tty->buf.work, 1);
        return retval;
 }
 
@@ -770,7 +1047,7 @@ static void do_tty_hangup(void *data)
        check_tty_count(tty, "do_tty_hangup");
        file_list_lock();
        /* This breaks for file handles being sent over AF_UNIX sockets ? */
-       list_for_each_entry(filp, &tty->tty_files, f_list) {
+       list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
                if (filp->f_op->write == redirected_tty_write)
                        cons_filp = filp;
                if (filp->f_op->write != tty_write)
@@ -828,8 +1105,8 @@ static void do_tty_hangup(void *data)
                                p->signal->tty = NULL;
                        if (!p->signal->leader)
                                continue;
-                       send_group_sig_info(SIGHUP, SEND_SIG_PRIV, p);
-                       send_group_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+                       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);
@@ -919,11 +1196,11 @@ void disassociate_ctty(int on_exit)
 
        lock_kernel();
 
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
        tty = current->signal->tty;
        if (tty) {
                tty_pgrp = tty->pgrp;
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
        } else {
@@ -931,7 +1208,7 @@ void disassociate_ctty(int on_exit)
                        kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
                        kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
                }
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                unlock_kernel();        
                return;
        }
@@ -942,7 +1219,7 @@ void disassociate_ctty(int on_exit)
        }
 
        /* Must lock changes to tty_old_pgrp */
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
        current->signal->tty_old_pgrp = 0;
        tty->session = 0;
        tty->pgrp = -1;
@@ -953,7 +1230,7 @@ void disassociate_ctty(int on_exit)
                p->signal->tty = NULL;
        } while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
        read_unlock(&tasklist_lock);
-       up(&tty_sem);
+       mutex_unlock(&tty_mutex);
        unlock_kernel();
 }
 
@@ -1037,7 +1314,7 @@ static inline ssize_t do_tty_write(
        ssize_t ret = 0, written = 0;
        unsigned int chunk;
        
-       if (down_interruptible(&tty->atomic_write)) {
+       if (mutex_lock_interruptible(&tty->atomic_write_lock)) {
                return -ERESTARTSYS;
        }
 
@@ -1060,7 +1337,7 @@ static inline ssize_t do_tty_write(
        if (count < chunk)
                chunk = count;
 
-       /* write_buf/write_cnt is protected by the atomic_write semaphore */
+       /* write_buf/write_cnt is protected by the atomic_write_lock mutex */
        if (tty->write_cnt < chunk) {
                unsigned char *buf;
 
@@ -1069,7 +1346,7 @@ static inline ssize_t do_tty_write(
 
                buf = kmalloc(chunk, GFP_KERNEL);
                if (!buf) {
-                       up(&tty->atomic_write);
+                       mutex_unlock(&tty->atomic_write_lock);
                        return -ENOMEM;
                }
                kfree(tty->write_buf);
@@ -1105,7 +1382,7 @@ static inline ssize_t do_tty_write(
                inode->i_mtime = current_fs_time(inode->i_sb);
                ret = written;
        }
-       up(&tty->atomic_write);
+       mutex_unlock(&tty->atomic_write_lock);
        return ret;
 }
 
@@ -1173,8 +1450,8 @@ static inline void tty_line_name(struct tty_driver *driver, int index, char *p)
 
 /*
  * WSH 06/09/97: Rewritten to remove races and properly clean up after a
- * failed open.  The new code protects the open with a semaphore, so it's
- * really quite straightforward.  The semaphore locking can probably be
+ * 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,
@@ -1371,20 +1648,17 @@ fast_track:
 success:
        *ret_tty = tty;
        
-       /* All paths come through here to release the semaphore */
+       /* All paths come through here to release the mutex */
 end_init:
        return retval;
 
        /* Release locally allocated memory ... nothing placed in slots */
 free_mem_out:
-       if (o_tp)
-               kfree(o_tp);
+       kfree(o_tp);
        if (o_tty)
                free_tty_struct(o_tty);
-       if (ltp)
-               kfree(ltp);
-       if (tp)
-               kfree(tp);
+       kfree(ltp);
+       kfree(tp);
        free_tty_struct(tty);
 
 fail_no_mem:
@@ -1400,6 +1674,19 @@ release_mem_out:
        goto end_init;
 }
 
+/*
+ * Get a copy of the termios structure for the driver/index
+ */
+void tty_get_termios(struct tty_driver *driver, int idx, struct termios *tio)
+{
+       lock_kernel();
+       if (driver->termios[idx])
+               *tio = *driver->termios[idx];
+       else
+               *tio = driver->init_termios;
+       unlock_kernel();
+}
+
 /*
  * Releases memory associated with a tty structure, and clears out the
  * driver table slots.
@@ -1467,7 +1754,7 @@ static void release_dev(struct file * filp)
 {
        struct tty_struct *tty, *o_tty;
        int     pty_master, tty_closing, o_tty_closing, do_sleep;
-       int     devpts_master, devpts;
+       int     devpts;
        int     idx;
        char    buf[64];
        unsigned long flags;
@@ -1484,7 +1771,6 @@ static void release_dev(struct file * filp)
        pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
                      tty->driver->subtype == PTY_TYPE_MASTER);
        devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
-       devpts_master = pty_master && devpts;
        o_tty = tty->link;
 
 #ifdef TTY_PARANOIA_CHECK
@@ -1571,11 +1857,10 @@ static void release_dev(struct file * filp)
                /* Guard against races with tty->count changes elsewhere and
                   opens on /dev/tty */
                   
-               down(&tty_sem);
+               mutex_lock(&tty_mutex);
                tty_closing = tty->count <= 1;
                o_tty_closing = o_tty &&
                        (o_tty->count <= (pty_master ? 1 : 0));
-               up(&tty_sem);
                do_sleep = 0;
 
                if (tty_closing) {
@@ -1603,6 +1888,7 @@ static void release_dev(struct file * filp)
 
                printk(KERN_WARNING "release_dev: %s: read/write wait queue "
                                    "active!\n", tty_name(tty, buf));
+               mutex_unlock(&tty_mutex);
                schedule();
        }       
 
@@ -1611,8 +1897,6 @@ static void release_dev(struct file * filp)
         * both sides, and we've completed the last operation that could 
         * block, so it's safe to proceed with closing.
         */
-        
-       down(&tty_sem);
        if (pty_master) {
                if (--o_tty->count < 0) {
                        printk(KERN_WARNING "release_dev: bad pty slave count "
@@ -1626,7 +1910,6 @@ static void release_dev(struct file * filp)
                       tty->count, tty_name(tty, buf));
                tty->count = 0;
        }
-       up(&tty_sem);
        
        /*
         * We've decremented tty->count, so we need to remove this file
@@ -1671,6 +1954,8 @@ static void release_dev(struct file * filp)
                read_unlock(&tasklist_lock);
        }
 
+       mutex_unlock(&tty_mutex);
+
        /* check whether both sides are closing ... */
        if (!tty_closing || (o_tty && !o_tty_closing))
                return;
@@ -1685,10 +1970,10 @@ static void release_dev(struct file * filp)
         */
        clear_bit(TTY_LDISC, &tty->flags);
        clear_bit(TTY_DONT_FLIP, &tty->flags);
-       cancel_delayed_work(&tty->flip.work);
+       cancel_delayed_work(&tty->buf.work);
 
        /*
-        * Wait for ->hangup_work and ->flip.work handlers to terminate
+        * Wait for ->hangup_work and ->buf.work handlers to terminate
         */
         
        flush_scheduled_work();
@@ -1775,11 +2060,11 @@ retry_open:
        index  = -1;
        retval = 0;
        
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
 
        if (device == MKDEV(TTYAUX_MAJOR,0)) {
                if (!current->signal->tty) {
-                       up(&tty_sem);
+                       mutex_unlock(&tty_mutex);
                        return -ENXIO;
                }
                driver = current->signal->tty->driver;
@@ -1805,18 +2090,18 @@ retry_open:
                        noctty = 1;
                        goto got_driver;
                }
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
 
        driver = get_tty_driver(device, &index);
        if (!driver) {
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
 got_driver:
        retval = init_dev(driver, index, &tty);
-       up(&tty_sem);
+       mutex_unlock(&tty_mutex);
        if (retval)
                return retval;
 
@@ -1902,9 +2187,9 @@ static int ptmx_open(struct inode * inode, struct file * filp)
        }
        up(&allocated_ptys_lock);
 
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
        retval = init_dev(ptm_driver, index, &tty);
-       up(&tty_sem);
+       mutex_unlock(&tty_mutex);
        
        if (retval)
                goto out;
@@ -1923,6 +2208,7 @@ static int ptmx_open(struct inode * inode, struct file * filp)
                return 0;
 out1:
        release_dev(filp);
+       return retval;
 out:
        down(&allocated_ptys_lock);
        idr_remove(&allocated_ptys, index);
@@ -2175,12 +2461,11 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
        return tty_set_ldisc(tty, ldisc);
 }
 
-static int send_break(struct tty_struct *tty, int duration)
+static int send_break(struct tty_struct *tty, unsigned int duration)
 {
        tty->driver->break_ctl(tty, -1);
        if (!signal_pending(current)) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(duration);
+               msleep_interruptible(duration);
        }
        tty->driver->break_ctl(tty, 0);
        if (signal_pending(current))
@@ -2361,10 +2646,10 @@ int tty_ioctl(struct inode * inode, struct file * file,
                         * all by anyone?
                         */
                        if (!arg)
-                               return send_break(tty, HZ/4);
+                               return send_break(tty, 250);
                        return 0;
                case TCSBRKP:   /* support for POSIX tcsendbreak() */   
-                       return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
+                       return send_break(tty, arg ? arg*100 : 250);
 
                case TIOCMGET:
                        return tty_tiocmget(tty, file, p);
@@ -2416,11 +2701,12 @@ static void __do_SAK(void *arg)
        tty_hangup(tty);
 #else
        struct tty_struct *tty = arg;
-       struct task_struct *p;
+       struct task_struct *g, *p;
        int session;
        int             i;
        struct file     *filp;
        struct tty_ldisc *disc;
+       struct fdtable *fdt;
        
        if (!tty)
                return;
@@ -2436,8 +2722,18 @@ static void __do_SAK(void *arg)
                tty->driver->flush_buffer(tty);
        
        read_lock(&tasklist_lock);
+       /* Kill the entire session */
        do_each_task_pid(session, PIDTYPE_SID, p) {
-               if (p->signal->tty == tty || session > 0) {
+               printk(KERN_NOTICE "SAK: killed process %d"
+                       " (%s): p->signal->session==tty->session\n",
+                       p->pid, p->comm);
+               send_sig(SIGKILL, p, 1);
+       } while_each_task_pid(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);
@@ -2446,8 +2742,13 @@ static void __do_SAK(void *arg)
                }
                task_lock(p);
                if (p->files) {
+                       /*
+                        * We don't take a ref to the file, so we must
+                        * hold ->file_lock instead.
+                        */
                        spin_lock(&p->files->file_lock);
-                       for (i=0; i < p->files->max_fds; i++) {
+                       fdt = files_fdtable(p->files);
+                       for (i=0; i < fdt->max_fds; i++) {
                                filp = fcheck_files(p->files, i);
                                if (!filp)
                                        continue;
@@ -2456,14 +2757,14 @@ static void __do_SAK(void *arg)
                                        printk(KERN_NOTICE "SAK: killed process %d"
                                            " (%s): fd#%d opened to the tty\n",
                                            p->pid, p->comm, i);
-                                       send_sig(SIGKILL, p, 1);
+                                       force_sig(SIGKILL, p);
                                        break;
                                }
                        }
                        spin_unlock(&p->files->file_lock);
                }
                task_unlock(p);
-       } while_each_task_pid(session, PIDTYPE_SID, p);
+       } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 #endif
 }
@@ -2486,17 +2787,18 @@ EXPORT_SYMBOL(do_SAK);
 
 /*
  * This routine is called out of the software interrupt to flush data
- * from the flip buffer to the line discipline. 
+ * from the buffer chain to the line discipline.
  */
  
 static void flush_to_ldisc(void *private_)
 {
        struct tty_struct *tty = (struct tty_struct *) private_;
-       unsigned char   *cp;
-       char            *fp;
-       int             count;
        unsigned long   flags;
        struct tty_ldisc *disc;
+       struct tty_buffer *tbuf;
+       int count;
+       char *char_buf;
+       unsigned char *flag_buf;
 
        disc = tty_ldisc_ref(tty);
        if (disc == NULL)       /*  !TTY_LDISC */
@@ -2506,28 +2808,27 @@ static void flush_to_ldisc(void *private_)
                /*
                 * Do it after the next timer tick:
                 */
-               schedule_delayed_work(&tty->flip.work, 1);
+               schedule_delayed_work(&tty->buf.work, 1);
                goto out;
        }
-       spin_lock_irqsave(&tty->read_lock, flags);
-       if (tty->flip.buf_num) {
-               cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-               fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
-               tty->flip.buf_num = 0;
-               tty->flip.char_buf_ptr = tty->flip.char_buf;
-               tty->flip.flag_buf_ptr = tty->flip.flag_buf;
-       } else {
-               cp = tty->flip.char_buf;
-               fp = tty->flip.flag_buf;
-               tty->flip.buf_num = 1;
-               tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-               tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       while((tbuf = tty->buf.head) != NULL) {
+               while ((count = tbuf->commit - tbuf->read) != 0) {
+                       char_buf = tbuf->char_buf_ptr + tbuf->read;
+                       flag_buf = tbuf->flag_buf_ptr + tbuf->read;
+                       tbuf->read += count;
+                       spin_unlock_irqrestore(&tty->buf.lock, flags);
+                       disc->receive_buf(tty, char_buf, flag_buf, count);
+                       spin_lock_irqsave(&tty->buf.lock, flags);
+               }
+               if (tbuf->active)
+                       break;
+               tty->buf.head = tbuf->next;
+               if (tty->buf.head == NULL)
+                       tty->buf.tail = NULL;
+               tty_buffer_free(tty, tbuf);
        }
-       count = tty->flip.count;
-       tty->flip.count = 0;
-       spin_unlock_irqrestore(&tty->read_lock, flags);
-
-       disc->receive_buf(tty, cp, fp, count);
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
 out:
        tty_ldisc_deref(disc);
 }
@@ -2619,14 +2920,23 @@ EXPORT_SYMBOL(tty_get_baud_rate);
 
 void tty_flip_buffer_push(struct tty_struct *tty)
 {
+       unsigned long flags;
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       if (tty->buf.tail != NULL) {
+               tty->buf.tail->active = 0;
+               tty->buf.tail->commit = tty->buf.tail->used;
+       }
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
+
        if (tty->low_latency)
                flush_to_ldisc((void *) tty);
        else
-               schedule_delayed_work(&tty->flip.work, 1);
+               schedule_delayed_work(&tty->buf.work, 1);
 }
 
 EXPORT_SYMBOL(tty_flip_buffer_push);
 
+
 /*
  * This subroutine initializes a tty structure.
  */
@@ -2637,16 +2947,16 @@ static void initialize_tty_struct(struct tty_struct *tty)
        tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
        tty->pgrp = -1;
        tty->overrun_time = jiffies;
-       tty->flip.char_buf_ptr = tty->flip.char_buf;
-       tty->flip.flag_buf_ptr = tty->flip.flag_buf;
-       INIT_WORK(&tty->flip.work, flush_to_ldisc, tty);
-       init_MUTEX(&tty->flip.pty_sem);
+       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_waitqueue_head(&tty->write_wait);
        init_waitqueue_head(&tty->read_wait);
        INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
-       sema_init(&tty->atomic_read, 1);
-       sema_init(&tty->atomic_write, 1);
+       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);
@@ -2660,7 +2970,7 @@ static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
        tty->driver->write(tty, &ch, 1);
 }
 
-static struct class_simple *tty_class;
+static struct class *tty_class;
 
 /**
  * tty_register_device - register a tty device
@@ -2693,7 +3003,7 @@ void tty_register_device(struct tty_driver *driver, unsigned index,
                pty_line_name(driver, index, name);
        else
                tty_line_name(driver, index, name);
-       class_simple_device_add(tty_class, dev, device, name);
+       class_device_create(tty_class, NULL, dev, device, "%s", name);
 }
 
 /**
@@ -2707,7 +3017,7 @@ void tty_register_device(struct tty_driver *driver, unsigned index,
 void tty_unregister_device(struct tty_driver *driver, unsigned index)
 {
        devfs_remove("%s%d", driver->devfs_name, index + driver->name_base);
-       class_simple_device_remove(MKDEV(driver->major, driver->minor_start) + index);
+       class_device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index);
 }
 
 EXPORT_SYMBOL(tty_register_device);
@@ -2905,11 +3215,6 @@ void __init console_init(void)
         */
 #ifdef CONFIG_EARLY_PRINTK
        disable_early_printk();
-#endif
-#ifdef CONFIG_SERIAL_68360
-       /* This is not a console initcall. I know not what it's doing here.
-          So I haven't moved it. dwmw2 */
-        rs_360_init();
 #endif
        call = __con_initcall_start;
        while (call < __con_initcall_end) {
@@ -2924,7 +3229,7 @@ extern int vty_init(void);
 
 static int __init tty_class_init(void)
 {
-       tty_class = class_simple_create(THIS_MODULE, "tty");
+       tty_class = class_create(THIS_MODULE, "tty");
        if (IS_ERR(tty_class))
                return PTR_ERR(tty_class);
        return 0;
@@ -2953,14 +3258,14 @@ static int __init tty_init(void)
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
                panic("Couldn't register /dev/tty driver\n");
        devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 0), S_IFCHR|S_IRUGO|S_IWUGO, "tty");
-       class_simple_device_add(tty_class, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
+       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
 
        cdev_init(&console_cdev, &console_fops);
        if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
                panic("Couldn't register /dev/console driver\n");
        devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 1), S_IFCHR|S_IRUSR|S_IWUSR, "console");
-       class_simple_device_add(tty_class, MKDEV(TTYAUX_MAJOR, 1), NULL, "console");
+       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console");
 
 #ifdef CONFIG_UNIX98_PTYS
        cdev_init(&ptmx_cdev, &ptmx_fops);
@@ -2968,7 +3273,7 @@ static int __init tty_init(void)
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
                panic("Couldn't register /dev/ptmx driver\n");
        devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 2), S_IFCHR|S_IRUGO|S_IWUGO, "ptmx");
-       class_simple_device_add(tty_class, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
+       class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
 #endif
 
 #ifdef CONFIG_VT
@@ -2977,7 +3282,7 @@ static int __init tty_init(void)
            register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
                panic("Couldn't register /dev/tty0 driver\n");
        devfs_mk_cdev(MKDEV(TTY_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vc/0");
-       class_simple_device_add(tty_class, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
+       class_device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
 
        vty_init();
 #endif