Security fixes (#68421, #72317, #72452).
authorGuy Martin <gmsoft@gentoo.org>
Wed, 8 Dec 2004 17:10:46 +0000 (17:10 +0000)
committerGuy Martin <gmsoft@gentoo.org>
Wed, 8 Dec 2004 17:10:46 +0000 (17:10 +0000)
sys-kernel/hppa-dev-sources/ChangeLog
sys-kernel/hppa-dev-sources/Manifest
sys-kernel/hppa-dev-sources/files/AF_UNIX-security.patch [new file with mode: 0644]
sys-kernel/hppa-dev-sources/files/CAN-2004-0814-r1.patch [new file with mode: 0644]
sys-kernel/hppa-dev-sources/files/CAN-2004-1074.patch [new file with mode: 0644]
sys-kernel/hppa-dev-sources/files/digest-hppa-dev-sources-2.6.8.1_p11-r2 [new file with mode: 0644]
sys-kernel/hppa-dev-sources/hppa-dev-sources-2.6.8.1_p11-r2.ebuild [new file with mode: 0644]

index 45ff614793d48400f11113e5726f71a4cd11206d..b6e030314f51fa89ae76581e15531f51ceb622d6 100644 (file)
@@ -1,6 +1,13 @@
 # ChangeLog for sys-kernel/hppa-dev-sources
 # Copyright 2002-2004 Gentoo Foundation; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/sys-kernel/hppa-dev-sources/ChangeLog,v 1.24 2004/11/24 17:35:47 gmsoft Exp $
+# $Header: /var/cvsroot/gentoo-x86/sys-kernel/hppa-dev-sources/ChangeLog,v 1.25 2004/12/08 17:10:46 gmsoft Exp $
+
+*hppa-dev-sources-2.6.8.1_p11-r2 (08 Dec 2004)
+
+  08 Dec 2004; Guy Martin <gmsoft@gentoo.org> +files/AF_UNIX-security.patch,
+  +files/CAN-2004-0814-r1.patch, +files/CAN-2004-1074.patch,
+  +hppa-dev-sources-2.6.8.1_p11-r2.ebuild:
+  Security fixes (#68421, #72317, #72452).
 
 *hppa-dev-sources-2.6.8.1_p11-r1 (24 Nov 2004)
 
index ee22d371ab036312a4a279560b1e08a60a4377b8..90066c72146160e89aa6c32d860aadccec3aa2b6 100644 (file)
@@ -1,10 +1,11 @@
-MD5 faf9dd76877c34d04b7dcebb187fcd54 ChangeLog 4692
+MD5 d767460db9650d878b6165e4c4acad72 ChangeLog 4965
 MD5 ae5609d9bd55ed7d9792b7b86da4df6b hppa-dev-sources-2.6.7_p1-r2.ebuild 1180
 MD5 73864d24b030876a0f811d73fdbf556c hppa-dev-sources-2.6.7_p14-r1.ebuild 1449
 MD5 05bef7ac2c9a5cdf016c4be6f71d034b hppa-dev-sources-2.6.7_p14.ebuild 1176
 MD5 109cb26368a56cea9cf1d1210cb3e857 hppa-dev-sources-2.6.8.1_p11-r1.ebuild 1274
 MD5 f1b3ae515c1f32812ecbf25193148a90 metadata.xml 223
 MD5 e659a5ccd8cb4f1630eb94a992053474 hppa-dev-sources-2.6.8.1_p11.ebuild 1098
+MD5 db64518ef707a3ac3558971110dc3e7f hppa-dev-sources-2.6.8.1_p11-r2.ebuild 1361
 MD5 b6e38b41c8a79943df2ab2642149d06f files/CAN-2004-0497.patch 2214
 MD5 f0e12ba218f53c2694a91259bdc2fdc7 files/CAN-2004-0596.patch 494
 MD5 c91330cc5b4044b6f59696095c2dc0fb files/CAN-2004-0626-death_packet.patch 423
@@ -20,3 +21,7 @@ MD5 222e890035f4ad3152f0c2a625a9ea67 files/security-proc-cmdline.patch 693
 MD5 00d36aabec0074afff01d252833fdcd0 files/digest-hppa-dev-sources-2.6.8.1_p11 133
 MD5 1ee8ba8362089c31fdd7d88b32eaf63e files/binfmt_elf-loader-security.patch 1938
 MD5 00d36aabec0074afff01d252833fdcd0 files/digest-hppa-dev-sources-2.6.8.1_p11-r1 133
+MD5 ef54ffd1bb8629f1a948afa9ff83d8a0 files/CAN-2004-0814-r1.patch 130187
+MD5 b0a1f80aff51d6601e8924329023b241 files/AF_UNIX-security.patch 515
+MD5 22192366443458dc8815827df35b63a7 files/CAN-2004-1074.patch 8034
+MD5 00d36aabec0074afff01d252833fdcd0 files/digest-hppa-dev-sources-2.6.8.1_p11-r2 133
diff --git a/sys-kernel/hppa-dev-sources/files/AF_UNIX-security.patch b/sys-kernel/hppa-dev-sources/files/AF_UNIX-security.patch
new file mode 100644 (file)
index 0000000..6ced784
--- /dev/null
@@ -0,0 +1,24 @@
+--- linux-2.4.27/net/unix/af_unix.c    2004-11-24 08:23:21 -08:00
++++ linux-2.4.28/net/unix/af_unix.c    2004-11-24 08:23:21 -08:00
+@@ -1403,9 +1403,11 @@
+ 
+       msg->msg_namelen = 0;
+ 
++      down(&sk->protinfo.af_unix.readsem);
++
+       skb = skb_recv_datagram(sk, flags, noblock, &err);
+       if (!skb)
+-              goto out;
++              goto out_unlock;
+ 
+       wake_up_interruptible(&sk->protinfo.af_unix.peer_wait);
+ 
+@@ -1449,6 +1451,8 @@
+ 
+ out_free:
+       skb_free_datagram(sk,skb);
++out_unlock:
++      up(&sk->protinfo.af_unix.readsem);
+ out:
+       return err;
+ }
diff --git a/sys-kernel/hppa-dev-sources/files/CAN-2004-0814-r1.patch b/sys-kernel/hppa-dev-sources/files/CAN-2004-0814-r1.patch
new file mode 100644 (file)
index 0000000..496654d
--- /dev/null
@@ -0,0 +1,4370 @@
+diff -Naur linux-2.6.8.orig/Documentation/tty.txt linux-2.6.8/Documentation/tty.txt
+--- linux-2.6.8.orig/Documentation/tty.txt     1969-12-31 16:00:00.000000000 -0800
++++ linux-2.6.8/Documentation/tty.txt  2004-11-28 04:49:42.182497442 -0800
+@@ -0,0 +1,194 @@
++
++                      The Lockronomicon
++
++Your guide to the ancient and twisted locking policies of the tty layer and
++the warped logic behind them. Beware all ye who read on.
++
++FIXME: still need to work out the full set of BKL assumptions and document
++them so they can eventually be killed off.
++
++
++Line Discipline
++---------------
++
++Line disciplines are registered with tty_register_ldisc() passing the
++discipline number and the ldisc structure. At the point of registration the 
++discipline must be ready to use and it is possible it will get used before
++the call returns success. If the call returns an error then it won't get
++called. Do not re-use ldisc numbers as they are part of the userspace ABI
++and writing over an existing ldisc will cause demons to eat your computer.
++After the return the ldisc data has been copied so you may free your own 
++copy of the structure. You must not re-register over the top of the line
++discipline even with the same data or your computer again will be eaten by
++demons.
++
++In order to remove a line discipline call tty_register_ldisc passing NULL.
++In ancient times this always worked. In modern times the function will
++return -EBUSY if the ldisc is currently in use. Since the ldisc referencing
++code manages the module counts this should not usually be a concern.
++
++Heed this warning: the reference count field of the registered copies of the
++tty_ldisc structure in the ldisc table counts the number of lines using this
++discipline. The reference count of the tty_ldisc structure within a tty 
++counts the number of active users of the ldisc at this instant. In effect it
++counts the number of threads of execution within an ldisc method (plus those
++about to enter and exit although this detail matters not).
++
++Line Discipline Methods
++-----------------------
++
++TTY side interfaces:
++
++close()               -       This is called on a terminal when the line
++                      discipline is being unplugged. At the point of
++                      execution no further users will enter the
++                      ldisc code for this tty. Can sleep.
++
++open()                -       Called when the line discipline is attached to
++                      the terminal. No other call into the line
++                      discipline for this tty will occur until it
++                      completes successfully. Can sleep.
++
++write()               -       A process is writing data from user space
++                      through the line discipline. Multiple write calls
++                      are serialized by the tty layer for the ldisc. May
++                      sleep.
++
++flush_buffer()        -       May be called at any point between open and close.
++
++chars_in_buffer() -   Report the number of bytes in the buffer.
++
++set_termios() -       Called on termios structure changes. The caller
++                      passes the old termios data and the current data
++                      is in the tty. Called under the termios lock so
++                      may not sleep. Serialized against itself only.
++
++read()                -       Move data from the line discipline to the user.
++                      Multiple read calls may occur in parallel and the
++                      ldisc must deal with serialization issues. May 
++                      sleep.
++
++poll()                -       Check the status for the poll/select calls. Multiple
++                      poll calls may occur in parallel. May sleep.
++
++ioctl()               -       Called when an ioctl is handed to the tty layer
++                      that might be for the ldisc. Multiple ioctl calls
++                      may occur in parallel. May sleep. 
++
++Driver Side Interfaces:
++
++receive_buf() -       Hand buffers of bytes from the driver to the ldisc
++                      for processing. Semantics currently rather
++                      mysterious 8(
++
++receive_room()        -       Can be called by the driver layer at any time when
++                      the ldisc is opened. The ldisc must be able to
++                      handle the reported amount of data at that instant.
++                      Synchronization between active receive_buf and
++                      receive_room calls is down to the driver not the
++                      ldisc. Must not sleep.
++
++write_wakeup()        -       May be called at any point between open and close.
++                      The TTY_DO_WRITE_WAKEUP flag indicates if a call
++                      is needed but always races versus calls. Thus the
++                      ldisc must be careful about setting order and to
++                      handle unexpected calls. Must not sleep.
++
++
++Locking
++
++Callers to the line discipline functions from the tty layer are required to
++take line discipline locks. The same is true of calls from the driver side
++but not yet enforced.
++
++Three calls are now provided
++
++      ldisc = tty_ldisc_ref(tty);
++
++takes a handle to the line discipline in the tty and returns it. If no ldisc
++is currently attached or the ldisc is being closed and re-opened at this
++point then NULL is returned. While this handle is held the ldisc will not
++change or go away.
++
++      tty_ldisc_deref(ldisc)
++
++Returns the ldisc reference and allows the ldisc to be closed. Returning the
++reference takes away your right to call the ldisc functions until you take
++a new reference.
++
++      ldisc = tty_ldisc_ref_wait(tty);
++
++Performs the same function as tty_ldisc_ref except that it will wait for an
++ldisc change to complete and then return a reference to the new ldisc. 
++
++While these functions are slightly slower than the old code they should have
++minimal impact as most receive logic uses the flip buffers and they only
++need to take a reference when they push bits up through the driver.
++
++A caution: The ldisc->open(), ldisc->close() and driver->set_ldisc 
++functions are called with the ldisc unavailable. Thus tty_ldisc_ref will
++fail in this situation if used within these functions. Ldisc and driver
++code calling its own functions must be careful in this case. 
++
++
++Driver Interface
++----------------
++
++open()                -       Called when a device is opened. May sleep
++
++close()               -       Called when a device is closed. At the point of
++                      return from this call the driver must make no 
++                      further ldisc calls of any kind. May sleep
++
++write()               -       Called to write bytes to the device. May not
++                      sleep. May occur in parallel in special cases. 
++                      Because this includes panic paths drivers generally
++                      shouldn't try and do clever locking here.
++
++put_char()    -       Stuff a single character onto the queue. The
++                      driver is guaranteed following up calls to
++                      flush_chars.
++
++flush_chars() -       Ask the kernel to write put_char queue
++
++write_room()  -       Return the number of characters tht can be stuffed
++                      into the port buffers without overflow (or less).
++                      The ldisc is responsible for being intelligent
++                      about multi-threading of write_room/write calls
++
++ioctl()               -       Called when an ioctl may be for the driver
++
++set_termios() -       Called on termios change, may get parallel calls,
++                      may block for now (may change that)
++
++set_ldisc()   -       Notifier for discipline change. At the point this 
++                      is done the discipline is not yet usable. Can now
++                      sleep (I think)
++
++throttle()    -       Called by the ldisc to ask the driver to do flow
++                      control.  Serialization including with unthrottle
++                      is the job of the ldisc layer.
++
++unthrottle()  -       Called by the ldisc to ask the driver to stop flow
++                      control.
++
++stop()                -       Ldisc notifier to the driver to stop output. As with
++                      throttle the serializations with start() are down
++                      to the ldisc layer.
++
++start()               -       Ldisc notifier to the driver to start output.
++
++hangup()      -       Ask the tty driver to cause a hangup initiated
++                      from the host side. [Can sleep ??]
++
++break_ctl()   -       Send RS232 break. Can sleep. Can get called in
++                      parallel, driver must serialize (for now), and
++                      with write calls.
++
++wait_until_sent() -   Wait for characters to exit the hardware queue
++                      of the driver. Can sleep
++
++send_xchar()    -     Send XON/XOFF and if possible jump the queue with
++                      it in order to get fast flow control responses.
++                      Cannot sleep ??
++
+diff -Naur linux-2.6.8.orig/drivers/bluetooth/hci_ldisc.c linux-2.6.8/drivers/bluetooth/hci_ldisc.c
+--- linux-2.6.8.orig/drivers/bluetooth/hci_ldisc.c     2004-08-13 22:37:38.000000000 -0700
++++ linux-2.6.8/drivers/bluetooth/hci_ldisc.c  2004-11-28 04:49:42.192495818 -0800
+@@ -188,9 +188,7 @@
+       }
+ 
+       /* Flush any pending characters in the driver and discipline. */
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
+-
++      tty_ldisc_flush(tty);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+ 
+@@ -280,7 +278,9 @@
+ 
+       spin_lock_init(&hu->rx_lock);
+ 
+-      /* Flush any pending characters in the driver and line discipline */
++      /* Flush any pending characters in the driver and line discipline. */
++      /* FIXME: why is this needed. Note don't use ldisc_ref here as the
++         open path is before the ldisc is referencable */
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/amiserial.c linux-2.6.8/drivers/char/amiserial.c
+--- linux-2.6.8.orig/drivers/char/amiserial.c  2004-08-13 22:38:10.000000000 -0700
++++ linux-2.6.8/drivers/char/amiserial.c       2004-11-28 04:49:42.210492894 -0800
+@@ -557,9 +557,7 @@
+               return;
+ 
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+ }
+@@ -1023,9 +1021,7 @@
+       info->xmit.head = info->xmit.tail = 0;
+       local_irq_restore(flags);
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1564,8 +1560,8 @@
+       shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++              
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+diff -Naur linux-2.6.8.orig/drivers/char/cyclades.c linux-2.6.8/drivers/char/cyclades.c
+--- linux-2.6.8.orig/drivers/char/cyclades.c   2004-08-13 22:36:32.000000000 -0700
++++ linux-2.6.8/drivers/char/cyclades.c        2004-11-28 04:49:42.227490132 -0800
+@@ -970,10 +970,7 @@
+       wake_up_interruptible(&info->delta_msr_wait);
+     }
+     if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+-        if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+-        && tty->ldisc.write_wakeup){
+-            (tty->ldisc.write_wakeup)(tty);
+-        }
++        tty_wakeup(tty);
+         wake_up_interruptible(&tty->write_wait);
+     }
+ #ifdef Z_WAKE
+@@ -2850,8 +2847,7 @@
+     shutdown(info);
+     if (tty->driver->flush_buffer)
+         tty->driver->flush_buffer(tty);
+-    if (tty->ldisc.flush_buffer)
+-        tty->ldisc.flush_buffer(tty);
++    tty_ldisc_flush(tty);        
+     CY_LOCK(info, flags);
+ 
+     tty->closing = 0;
+@@ -4554,10 +4550,8 @@
+       }
+       CY_UNLOCK(info, flags);
+     }
++    tty_wakeup(tty);
+     wake_up_interruptible(&tty->write_wait);
+-    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-      && tty->ldisc.write_wakeup)
+-          (tty->ldisc.write_wakeup)(tty);
+ } /* cy_flush_buffer */
+ 
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/epca.c linux-2.6.8/drivers/char/epca.c
+--- linux-2.6.8.orig/drivers/char/epca.c       2004-08-13 22:37:14.000000000 -0700
++++ linux-2.6.8/drivers/char/epca.c    2004-11-28 04:49:42.279481685 -0800
+@@ -551,9 +551,7 @@
+               if (tty->driver->flush_buffer)
+                       tty->driver->flush_buffer(tty);
+ 
+-              if (tty->ldisc.flush_buffer)
+-                      tty->ldisc.flush_buffer(tty);
+-
++              tty_ldisc_flush(tty);
+               shutdown(ch);
+               tty->closing = 0;
+               ch->event = 0;
+@@ -657,10 +655,7 @@
+               cli();
+               if (tty->driver->flush_buffer)
+                       tty->driver->flush_buffer(tty);
+-
+-              if (tty->ldisc.flush_buffer)
+-                      tty->ldisc.flush_buffer(tty);
+-
++              tty_ldisc_flush(tty);
+               shutdown(ch);
+ 
+               ch->tty   = NULL;
+@@ -1120,8 +1115,7 @@
+       restore_flags(flags);
+ 
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ 
+ } /* End pc_flush_buffer */
+ 
+@@ -2262,9 +2256,7 @@
+                               { /* Begin if LOWWAIT */
+ 
+                                       ch->statusflags &= ~LOWWAIT;
+-                                      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                                                tty->ldisc.write_wakeup)
+-                                              (tty->ldisc.write_wakeup)(tty);
++                                      tty_wakeup(tty);
+                                       wake_up_interruptible(&tty->write_wait);
+ 
+                               } /* End if LOWWAIT */
+@@ -2281,9 +2273,7 @@
+                               { /* Begin if EMPTYWAIT */
+ 
+                                       ch->statusflags &= ~EMPTYWAIT;
+-                                      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                                                tty->ldisc.write_wakeup)
+-                                              (tty->ldisc.write_wakeup)(tty);
++                                      tty_wakeup(tty);
+ 
+                                       wake_up_interruptible(&tty->write_wait);
+ 
+@@ -3136,6 +3126,7 @@
+                       }
+                       else 
+                       {
++                              /* ldisc lock already held in ioctl */
+                               if (tty->ldisc.flush_buffer)
+                                       tty->ldisc.flush_buffer(tty);
+                       }
+diff -Naur linux-2.6.8.orig/drivers/char/esp.c linux-2.6.8/drivers/char/esp.c
+--- linux-2.6.8.orig/drivers/char/esp.c        2004-08-13 22:36:11.000000000 -0700
++++ linux-2.6.8/drivers/char/esp.c     2004-11-28 04:49:42.304477623 -0800
+@@ -762,10 +762,7 @@
+               return;
+ 
+       if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+ }
+ 
+@@ -1370,10 +1367,7 @@
+       cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       sti();
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -2069,8 +2063,7 @@
+       shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = NULL;
+diff -Naur linux-2.6.8.orig/drivers/char/generic_serial.c linux-2.6.8/drivers/char/generic_serial.c
+--- linux-2.6.8.orig/drivers/char/generic_serial.c     2004-08-13 22:36:58.000000000 -0700
++++ linux-2.6.8/drivers/char/generic_serial.c  2004-11-28 04:49:42.350470151 -0800
+@@ -436,9 +436,7 @@
+       restore_flags(flags);
+ 
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+       func_exit ();
+ }
+ 
+@@ -578,9 +576,7 @@
+       if (!tty) return;
+ 
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+       func_exit ();
+@@ -694,8 +690,8 @@
+ {
+       unsigned long flags;
+       struct gs_port *port;
+-
+-      func_enter ();
++      
++      func_enter ()
+ 
+       if (!tty) return;
+ 
+@@ -760,8 +756,8 @@
+ 
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++              
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+ 
+       port->event = 0;
+diff -Naur linux-2.6.8.orig/drivers/char/hvc_console.c linux-2.6.8/drivers/char/hvc_console.c
+--- linux-2.6.8.orig/drivers/char/hvc_console.c        2004-08-13 22:37:39.000000000 -0700
++++ linux-2.6.8/drivers/char/hvc_console.c     2004-11-28 04:49:42.381465115 -0800
+@@ -248,10 +248,7 @@
+ 
+               if (hp->do_wakeup) {
+                       hp->do_wakeup = 0;
+-                      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-                          && tty->ldisc.write_wakeup)
+-                              (tty->ldisc.write_wakeup)(tty);
+-                      wake_up_interruptible(&tty->write_wait);
++                      tty_wakeup(tty);
+               }
+       }
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/hvcs.c linux-2.6.8/drivers/char/hvcs.c
+--- linux-2.6.8.orig/drivers/char/hvcs.c       2004-08-13 22:37:14.000000000 -0700
++++ linux-2.6.8/drivers/char/hvcs.c    2004-11-28 04:49:42.385464465 -0800
+@@ -393,10 +393,7 @@
+                        * a non-existent tty.
+                        */
+                       if (tty) {
+-                              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-                                              && tty->ldisc.write_wakeup)
+-                                      (tty->ldisc.write_wakeup) (tty);
+-                              wake_up_interruptible(&tty->write_wait);
++                              tty_wakeup(tty);
+                       }
+               }
+       }
+diff -Naur linux-2.6.8.orig/drivers/char/isicom.c linux-2.6.8/drivers/char/isicom.c
+--- linux-2.6.8.orig/drivers/char/isicom.c     2004-08-13 22:36:12.000000000 -0700
++++ linux-2.6.8/drivers/char/isicom.c  2004-11-28 04:49:42.424458129 -0800
+@@ -484,10 +484,8 @@
+       
+       if (!tty)
+               return;
+-      
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++
++      tty_wakeup(tty);        
+       wake_up_interruptible(&tty->write_wait);
+ }             
+               
+@@ -1119,8 +1117,8 @@
+       isicom_shutdown_port(port);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++              
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       port->tty = NULL;
+       if (port->blocked_open) {
+@@ -1563,9 +1561,7 @@
+       restore_flags(flags);
+       
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/moxa.c linux-2.6.8/drivers/char/moxa.c
+--- linux-2.6.8.orig/drivers/char/moxa.c       2004-08-13 22:38:04.000000000 -0700
++++ linux-2.6.8/drivers/char/moxa.c    2004-11-28 04:49:42.445454718 -0800
+@@ -618,8 +618,8 @@
+ 
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
++                      
+       tty->closing = 0;
+       ch->event = 0;
+       ch->tty = NULL;
+@@ -693,9 +693,7 @@
+       if (ch == NULL)
+               return;
+       MoxaPortFlushData(ch->port, 1);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup) (tty);
++      tty_wakeup(tty);
+       wake_up_interruptible(&tty->write_wait);
+ }
+ 
+@@ -954,9 +952,7 @@
+                               if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) {
+                                       if (!tp->stopped) {
+                                               ch->statusflags &= ~LOWWAIT;
+-                                              if ((tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                                                tp->ldisc.write_wakeup)
+-                                                      (tp->ldisc.write_wakeup) (tp);
++                                              tty_wakeup(tty);
+                                               wake_up_interruptible(&tp->write_wait);
+                                       }
+                               }
+@@ -1123,9 +1119,7 @@
+       if (ch->tty && (ch->statusflags & EMPTYWAIT)) {
+               if (MoxaPortTxQueue(ch->port) == 0) {
+                       ch->statusflags &= ~EMPTYWAIT;
+-                      if ((ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                          ch->tty->ldisc.write_wakeup)
+-                              (ch->tty->ldisc.write_wakeup) (ch->tty);
++                      tty_wakeup(tty);
+                       wake_up_interruptible(&ch->tty->write_wait);
+                       return;
+               }
+diff -Naur linux-2.6.8.orig/drivers/char/mxser.c linux-2.6.8/drivers/char/mxser.c
+--- linux-2.6.8.orig/drivers/char/mxser.c      2004-08-13 22:37:26.000000000 -0700
++++ linux-2.6.8/drivers/char/mxser.c   2004-11-28 04:49:42.456452931 -0800
+@@ -678,9 +678,7 @@
+       tty = info->tty;
+       if (tty) {
+               if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) {
+-                      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                          tty->ldisc.write_wakeup)
+-                              (tty->ldisc.write_wakeup) (tty);
++                      tty_wakeup(tty);
+                       wake_up_interruptible(&tty->write_wait);
+               }
+               if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) {
+@@ -817,8 +815,8 @@
+       mxser_shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
++
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = NULL;
+@@ -976,9 +974,7 @@
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       restore_flags(flags);
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup) (tty);
++      tty_wakeup(tty);
+ }
+ 
+ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
+diff -Naur linux-2.6.8.orig/drivers/char/n_tty.c linux-2.6.8/drivers/char/n_tty.c
+--- linux-2.6.8.orig/drivers/char/n_tty.c      2004-08-13 22:36:11.000000000 -0700
++++ linux-2.6.8/drivers/char/n_tty.c   2004-11-28 04:49:42.469450819 -0800
+@@ -99,11 +99,18 @@
+       spin_unlock_irqrestore(&tty->read_lock, flags);
+ }
+ 
+-/* 
+- * Check whether to call the driver.unthrottle function.
+- * We test the TTY_THROTTLED bit first so that it always
+- * indicates the current state.
++/**
++ *    check_unthrottle        -       allow new receive data
++ *    @tty; tty device
++ *
++ *    Check whether to call the driver.unthrottle function.
++ *    We test the TTY_THROTTLED bit first so that it always
++ *    indicates the current state. The decision about whether
++ *    it is worth allowing more input has been taken by the caller.
++ *    Can sleep, may be called under the atomic_read semaphore but
++ *    this is not guaranteed.
+  */
++ 
+ static void check_unthrottle(struct tty_struct * tty)
+ {
+       if (tty->count &&
+@@ -112,10 +119,13 @@
+               tty->driver->unthrottle(tty);
+ }
+ 
+-/*
+- * Reset the read buffer counters, clear the flags, 
+- * and make sure the driver is unthrottled. Called
+- * from n_tty_open() and n_tty_flush_buffer().
++/**
++ *    reset_buffer_flags      -       reset buffer state
++ *    @tty: terminal to reset
++ *
++ *    Reset the read buffer counters, clear the flags, 
++ *    and make sure the driver is unthrottled. Called
++ *    from n_tty_open() and n_tty_flush_buffer().
+  */
+ static void reset_buffer_flags(struct tty_struct *tty)
+ {
+@@ -129,9 +139,19 @@
+       check_unthrottle(tty);
+ }
+ 
+-/*
+- * Flush the input buffer
++/**
++ *    n_tty_flush_buffer      -       clean input queue
++ *    @tty:   terminal device
++ *
++ *    Flush the input buffer. Called when the line discipline is
++ *    being closed, when the tty layer wants the buffer flushed (eg
++ *    at hangup) or when the N_TTY line discipline internally has to
++ *    clean the pending queue (for example some signals).
++ *
++ *    FIXME: tty->ctrl_status is not spinlocked and relies on
++ *    lock_kernel() still.
+  */
++ 
+ void n_tty_flush_buffer(struct tty_struct * tty)
+ {
+       /* clear everything and unthrottle the driver */
+@@ -146,9 +166,14 @@
+       }
+ }
+ 
+-/*
+- * Return number of characters buffered to be delivered to user
++/**
++ *    n_tty_chars_in_buffer   -       report available bytes
++ *    @tty: tty device
++ *
++ *    Report the number of characters buffered to be delivered to user
++ *    at this instant in time. 
+  */
++ 
+ ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
+ {
+       unsigned long flags;
+@@ -166,20 +191,47 @@
+       return n;
+ }
+ 
++/**
++ *    is_utf8_continuation    -       utf8 multibyte check
++ *    @c: byte to check
++ *
++ *    Returns true if the utf8 character 'c' is a multibyte continuation
++ *    character. We use this to correctly compute the on screen size
++ *    of the character when printing
++ */
++ 
+ static inline int is_utf8_continuation(unsigned char c)
+ {
+       return (c & 0xc0) == 0x80;
+ }
+ 
++/**
++ *    is_continuation         -       multibyte check
++ *    @c: byte to check
++ *
++ *    Returns true if the utf8 character 'c' is a multibyte continuation
++ *    character and the terminal is in unicode mode.
++ */
++ 
+ static inline int is_continuation(unsigned char c, struct tty_struct *tty)
+ {
+       return I_IUTF8(tty) && is_utf8_continuation(c);
+ }
+ 
+-/*
+- * Perform OPOST processing.  Returns -1 when the output device is
+- * full and the character must be retried.
++/**
++ *    opost                   -       output post processor
++ *    @c: character (or partial unicode symbol)
++ *    @tty: terminal device
++ *
++ *    Perform OPOST processing.  Returns -1 when the output device is
++ *    full and the character must be retried. Note that Linux currently
++ *    ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY. They simply aren't
++ *    relevant in the world today. If you ever need them, add them here.
++ *
++ *    Called from both the receive and transmit sides and can be called
++ *    re-entrantly. Relies on lock_kernel() still.
+  */
++ 
+ static int opost(unsigned char c, struct tty_struct *tty)
+ {
+       int     space, spaces;
+@@ -239,10 +291,20 @@
+       return 0;
+ }
+ 
+-/*
+- * opost_block --- to speed up block console writes, among other
+- * things.
++/**
++ *    opost_block             -       block postprocess
++ *    @tty: terminal device
++ *    @inbuf: user buffer
++ *    @nr: number of bytes
++ *
++ *    This path is used to speed up block console writes, among other
++ *    things when processing blocks of output data. It handles only
++ *    the simple cases normally found and helps to generate blocks of
++ *    symbols for the console driver and thus improve performance.
++ *
++ *    Called from write_chan under the tty layer write lock.
+  */
++ 
+ static ssize_t opost_block(struct tty_struct * tty,
+                      const unsigned char __user * inbuf, unsigned int nr)
+ {
+@@ -304,13 +366,27 @@
+ }
+ 
+ 
+-
++/**
++ *    put_char        -       write character to driver
++ *    @c: character (or part of unicode symbol)
++ *    @tty: terminal device
++ *
++ *    Queue a byte to the driver layer for output
++ */
++ 
+ static inline void put_char(unsigned char c, struct tty_struct *tty)
+ {
+       tty->driver->put_char(tty, c);
+ }
+ 
+-/* Must be called only when L_ECHO(tty) is true. */
++/**
++ *    echo_char       -       echo characters
++ *    @c: unicode byte to echo
++ *    @tty: terminal device
++ *
++ *    Echo user input back onto the screen. This must be called only when 
++ *    L_ECHO(tty) is true. Called from the driver receive_buf path.
++ */
+ 
+ static void echo_char(unsigned char c, struct tty_struct *tty)
+ {
+@@ -331,6 +407,16 @@
+       }
+ }
+ 
++/**
++ *    eraser          -       handle erase function
++ *    @c: character input
++ *    @tty: terminal device
++ *
++ *    Perform erase and neccessary output when an erase character is
++ *    present in the stream from the driver layer. Handles the complexities
++ *    of UTF-8 multibyte symbols.
++ */
++ 
+ static void eraser(unsigned char c, struct tty_struct *tty)
+ {
+       enum { ERASE, WERASE, KILL } kill_type;
+@@ -463,6 +549,18 @@
+               finish_erasing(tty);
+ }
+ 
++/**
++ *    isig            -       handle the ISIG optio
++ *    @sig: signal
++ *    @tty: terminal
++ *    @flush: force flush
++ *
++ *    Called when a signal is being sent due to terminal input. This
++ *    may caus terminal flushing to take place according to the termios
++ *    settings and character used. Called from the driver receive_buf
++ *    path so serialized.
++ */
++ 
+ static inline void isig(int sig, struct tty_struct *tty, int flush)
+ {
+       if (tty->pgrp > 0)
+@@ -474,6 +572,16 @@
+       }
+ }
+ 
++/**
++ *    n_tty_receive_break     -       handle break
++ *    @tty: terminal
++ *
++ *    An RS232 break event has been hit in the incoming bitstream. This
++ *    can cause a variety of events depending upon the termios settings.
++ *
++ *    Called from the receive_buf path so single threaded.
++ */
++ 
+ static inline void n_tty_receive_break(struct tty_struct *tty)
+ {
+       if (I_IGNBRK(tty))
+@@ -490,19 +598,40 @@
+       wake_up_interruptible(&tty->read_wait);
+ }
+ 
++/**
++ *    n_tty_receive_overrun   -       handle overrun reporting
++ *    @tty: terminal
++ *
++ *    Data arrived faster than we could process it. While the tty
++ *    driver has flagged this the bits that were missed are gone
++ *    forever.
++ *
++ *    Called from the receive_buf path so single threaded. Does not
++ *    need locking as num_overrun and overrun_time are function
++ *    private.
++ */
++ 
+ static inline void n_tty_receive_overrun(struct tty_struct *tty)
+ {
+       char buf[64];
+ 
+       tty->num_overrun++;
+       if (time_before(tty->overrun_time, jiffies - HZ)) {
+-              printk("%s: %d input overrun(s)\n", tty_name(tty, buf),
++              printk(KERN_WARNING "%s: %d input overrun(s)\n", tty_name(tty, buf),
+                      tty->num_overrun);
+               tty->overrun_time = jiffies;
+               tty->num_overrun = 0;
+       }
+ }
+ 
++/**
++ *    n_tty_receive_parity_error      -       error notifier
++ *    @tty: terminal device
++ *    @c: character
++ *
++ *    Process a parity error and queue the right data to indicate
++ *    the error case if neccessary. Locking as per n_tty_receive_buf.
++ */
+ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
+                                             unsigned char c)
+ {
+@@ -520,6 +649,16 @@
+       wake_up_interruptible(&tty->read_wait);
+ }
+ 
++/**
++ *    n_tty_receive_char      -       perform processing
++ *    @tty: terminal device
++ *    @c: character
++ *
++ *    Process an individual character of input received from the driver.
++ *    This is serialized with respect to itself by the rules for the 
++ *    driver above.
++ */
++
+ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+ {
+       unsigned long flags;
+@@ -711,6 +850,16 @@
+       put_tty_queue(c, tty);
+ }     
+ 
++/**
++ *    n_tty_receive_room      -       receive space
++ *    @tty: terminal
++ *
++ *    Called by the driver to find out how much data it is
++ *    permitted to feed to the line discipline without any being lost
++ *    and thus to manage flow control. Not serialized. Answers for the
++ *    "instant".
++ */
++ 
+ static int n_tty_receive_room(struct tty_struct *tty)
+ {
+       int     left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+@@ -729,10 +878,13 @@
+       return 0;
+ }
+ 
+-/*
+- * Required for the ptys, serial driver etc. since processes
+- * that attach themselves to the master and rely on ASYNC
+- * IO must be woken up
++/**
++ *    n_tty_write_wakeup      -       asynchronous I/O notifier
++ *    @tty: tty device
++ *
++ *    Required for the ptys, serial driver etc. since processes
++ *    that attach themselves to the master and rely on ASYNC
++ *    IO must be woken up
+  */
+ 
+ static void n_tty_write_wakeup(struct tty_struct *tty)
+@@ -745,6 +897,19 @@
+       return;
+ }
+ 
++/**
++ *    n_tty_receive_buf       -       data receive
++ *    @tty: terminal device
++ *    @cp: buffer
++ *    @fp: flag buffer
++ *    @count: characters
++ *
++ *    Called by the terminal driver when a block of characters has
++ *    been received. This function must be called from soft contexts
++ *    not from interrupt context. The driver is responsible for making
++ *    calls one at a time and in order (or using flush_to_ldisc)
++ */
++ 
+ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+                             char *fp, int count)
+ {
+@@ -828,6 +993,18 @@
+               current->sighand->action[sig-1].sa.sa_handler == SIG_IGN);
+ }
+ 
++/**
++ *    n_tty_set_termios       -       termios data changed
++ *    @tty: terminal
++ *    @old: previous data
++ *
++ *    Called by the tty layer when the user changes termios flags so
++ *    that the line discipline can plan ahead. This function cannot sleep
++ *    and is protected from re-entry by the tty layer. The user is 
++ *    guaranteed that this function will not be re-entered or in progress
++ *    when the ldisc is closed.
++ */
++ 
+ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
+ {
+       if (!tty)
+@@ -843,7 +1020,6 @@
+           I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
+           I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
+           I_PARMRK(tty)) {
+-              local_irq_disable(); // FIXME: is this safe?
+               memset(tty->process_char_map, 0, 256/8);
+ 
+               if (I_IGNCR(tty) || I_ICRNL(tty))
+@@ -879,7 +1055,6 @@
+                       set_bit(SUSP_CHAR(tty), tty->process_char_map);
+               }
+               clear_bit(__DISABLED_CHAR, tty->process_char_map);
+-              local_irq_enable(); // FIXME: is this safe?
+               tty->raw = 0;
+               tty->real_raw = 0;
+       } else {
+@@ -893,6 +1068,16 @@
+       }
+ }
+ 
++/**
++ *    n_tty_close             -       close the ldisc for this tty
++ *    @tty: device
++ *
++ *    Called from the terminal layer when this line discipline is 
++ *    being shut down, either because of a close or becsuse of a 
++ *    discipline change. The function will not be called while other
++ *    ldisc methods are in progress.
++ */
++ 
+ static void n_tty_close(struct tty_struct *tty)
+ {
+       n_tty_flush_buffer(tty);
+@@ -902,11 +1087,22 @@
+       }
+ }
+ 
++/**
++ *    n_tty_open              -       open an ldisc
++ *    @tty: terminal to open
++ *
++ *    Called when this line discipline is being attached to the 
++ *    terminal device. Can sleep. Called serialized so that no
++ *    other events will occur in parallel. No further open will occur
++ *    until a close.
++ */
++
+ static int n_tty_open(struct tty_struct *tty)
+ {
+       if (!tty)
+               return -EINVAL;
+ 
++      /* This one is ugly. Currently a malloc failure here can panic */
+       if (!tty->read_buf) {
+               tty->read_buf = alloc_buf();
+               if (!tty->read_buf)
+@@ -932,14 +1128,23 @@
+       return 0;
+ }
+ 
+-/*
+- * Helper function to speed up read_chan.  It is only called when
+- * ICANON is off; it copies characters straight from the tty queue to
+- * user space directly.  It can be profitably called twice; once to
+- * drain the space from the tail pointer to the (physical) end of the
+- * buffer, and once to drain the space from the (physical) beginning of
+- * the buffer to head pointer.
++/**
++ *    copy_from_read_buf      -       copy read data directly
++ *    @tty: terminal device
++ *    @b: user data
++ *    @nr: size of data
++ *
++ *    Helper function to speed up read_chan.  It is only called when
++ *    ICANON is off; it copies characters straight from the tty queue to
++ *    user space directly.  It can be profitably called twice; once to
++ *    drain the space from the tail pointer to the (physical) end of the
++ *    buffer, and once to drain the space from the (physical) beginning of
++ *    the buffer to head pointer.
++ *
++ *    Called under the tty->atomic_read sem and with TTY_DONT_FLIP set
++ *
+  */
++ 
+ static inline int copy_from_read_buf(struct tty_struct *tty,
+                                     unsigned char __user **b,
+                                     size_t *nr)
+@@ -970,25 +1175,18 @@
+ 
+ extern ssize_t redirected_tty_write(struct file *,const char *,size_t,loff_t *);
+ 
+-static ssize_t read_chan(struct tty_struct *tty, struct file *file,
+-                       unsigned char __user *buf, size_t nr)
++/**
++ *    job_control             -       check job control
++ *    @tty: tty
++ *    @file: file handle
++ *
++ *    Perform job control management checks on this file/tty descriptor
++ *    and if appropriate send any needed signals and return a negative 
++ *    error code if action should be taken.
++ */
++ 
++static int job_control(struct tty_struct *tty, struct file *file)
+ {
+-      unsigned char __user *b = buf;
+-      DECLARE_WAITQUEUE(wait, current);
+-      int c;
+-      int minimum, time;
+-      ssize_t retval = 0;
+-      ssize_t size;
+-      long timeout;
+-      unsigned long flags;
+-
+-do_it_again:
+-
+-      if (!tty->read_buf) {
+-              printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
+-              return -EIO;
+-      }
+-
+       /* Job control check -- must be done at start and after
+          every sleep (POSIX.1 7.1.1.4). */
+       /* NOTE: not yet done after every sleep pending a thorough
+@@ -1006,7 +1204,48 @@
+                       return -ERESTARTSYS;
+               }
+       }
++      return 0;
++}
++ 
++
++/**
++ *    read_chan               -       read function for tty
++ *    @tty: tty device
++ *    @file: file object
++ *    @buf: userspace buffer pointer
++ *    @nr: size of I/O
++ *
++ *    Perform reads for the line discipline. We are guaranteed that the
++ *    line discipline will not be closed under us but we may get multiple
++ *    parallel readers and must handle this ourselves. We may also get
++ *    a hangup. Always called in user context, may sleep.
++ *
++ *    This code must be sure never to sleep through a hangup.
++ */
++ 
++static ssize_t read_chan(struct tty_struct *tty, struct file *file,
++                       unsigned char __user *buf, size_t nr)
++{
++      unsigned char __user *b = buf;
++      DECLARE_WAITQUEUE(wait, current);
++      int c;
++      int minimum, time;
++      ssize_t retval = 0;
++      ssize_t size;
++      long timeout;
++      unsigned long flags;
++
++do_it_again:
++
++      if (!tty->read_buf) {
++              printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
++              return -EIO;
++      }
+ 
++      c = job_control(tty, file);
++      if(c < 0)
++              return c;
++      
+       minimum = time = 0;
+       timeout = MAX_SCHEDULE_TIMEOUT;
+       if (!tty->icanon) {
+@@ -1028,6 +1267,9 @@
+               }
+       }
+ 
++      /*
++       *      Internal serialization of reads.
++       */
+       if (file->f_flags & O_NONBLOCK) {
+               if (down_trylock(&tty->atomic_read))
+                       return -EAGAIN;
+@@ -1177,6 +1419,21 @@
+       return retval;
+ }
+ 
++/**
++ *    write_chan              -       write function for tty
++ *    @tty: tty device
++ *    @file: file object
++ *    @buf: userspace buffer pointer
++ *    @nr: size of I/O
++ *
++ *    Write function of the terminal device. This is serialized with
++ *    respect to other write callers but not to termios changes, reads
++ *    and other such events. We must be careful with N_TTY as the receive
++ *    code will echo characters, thus calling driver write methods.
++ *
++ *    This code must be sure never to sleep through a hangup.
++ */
++ 
+ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
+                         const unsigned char __user * buf, size_t nr)
+ {
+@@ -1246,7 +1503,25 @@
+       return (b - buf) ? b - buf : retval;
+ }
+ 
+-/* Called without the kernel lock held - fine */
++/**
++ *    normal_poll             -       poll method for N_TTY
++ *    @tty: terminal device
++ *    @file: file accessing it
++ *    @wait: poll table
++ *
++ *    Called when the line discipline is asked to poll() for data or
++ *    for special events. This code is not serialized with respect to
++ *    other events save open/close.
++ *
++ *    This code must be sure never to sleep through a hangup.
++ *    Called without the kernel lock held - fine
++ *
++ *    FIXME: if someone changes the VMIN or discipline settings for the
++ *    terminal while another process is in poll() the poll does not
++ *    recompute the new limits. Possibly set_termios should issue
++ *    a read wakeup to fix this bug.
++ */
++ 
+ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, poll_table *wait)
+ {
+       unsigned int mask = 0;
+@@ -1287,6 +1562,7 @@
+       n_tty_ioctl,            /* ioctl */
+       n_tty_set_termios,      /* set_termios */
+       normal_poll,            /* poll */
++      NULL,                   /* hangup */
+       n_tty_receive_buf,      /* receive_buf */
+       n_tty_receive_room,     /* receive_room */
+       n_tty_write_wakeup      /* write_wakeup */
+diff -Naur linux-2.6.8.orig/drivers/char/pcmcia/synclink_cs.c linux-2.6.8/drivers/char/pcmcia/synclink_cs.c
+--- linux-2.6.8.orig/drivers/char/pcmcia/synclink_cs.c 2004-08-13 22:36:45.000000000 -0700
++++ linux-2.6.8/drivers/char/pcmcia/synclink_cs.c      2004-11-28 04:49:42.509444321 -0800
+@@ -531,6 +531,40 @@
+       return mgslpc_get_text_ptr;
+ }
+ 
++/**
++ * line discipline callback wrappers
++ *
++ * The wrappers maintain line discipline references
++ * while calling into the line discipline.
++ *
++ * ldisc_flush_buffer - flush line discipline receive buffers
++ * ldisc_receive_buf  - pass receive data to line discipline
++ */
++
++static void ldisc_flush_buffer(struct tty_struct *tty)
++{
++      struct tty_ldisc *ld = tty_ldisc_ref(tty);
++      if (ld) {
++              if (ld->flush_buffer)
++                      ld->flush_buffer(tty);
++              tty_ldisc_deref(ld);
++      }
++}
++
++static void ldisc_receive_buf(struct tty_struct *tty,
++                            const __u8 *data, char *flags, int count)
++{
++      struct tty_ldisc *ld;
++      if (!tty)
++              return;
++      ld = tty_ldisc_ref(tty);
++      if (ld) {
++              if (ld->receive_buf)
++                      ld->receive_buf(tty, data, flags, count);
++              tty_ldisc_deref(ld);
++      }
++}
++
+ static dev_link_t *mgslpc_attach(void)
+ {
+     MGSLPC_INFO *info;
+@@ -984,13 +1018,7 @@
+               printk("bh_transmit() entry on %s\n", info->device_name);
+ 
+       if (tty) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup) {
+-                      if ( debug_level >= DEBUG_LEVEL_BH )
+-                              printk( "%s(%d):calling ldisc.write_wakeup on %s\n",
+-                                      __FILE__,__LINE__,info->device_name);
+-                      (tty->ldisc.write_wakeup)(tty);
+-              }
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+ }
+@@ -1875,11 +1903,9 @@
+       info->tx_count = info->tx_put = info->tx_get = 0;
+       del_timer(&info->tx_timer);     
+       spin_unlock_irqrestore(&info->lock,flags);
+-      
++
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /* Send a high-priority XON/XOFF character
+@@ -2588,9 +2614,8 @@
+ 
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-              
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++
++      ldisc_flush_buffer(tty);
+               
+       shutdown(info);
+       
+@@ -4059,11 +4084,7 @@
+                       } 
+                       else
+ #endif
+-                      {
+-                              /* Call the line discipline receive callback directly. */
+-                              if (tty && tty->ldisc.receive_buf)
+-                                      tty->ldisc.receive_buf(tty, buf->data, info->flag_buf, framesize);
+-                      }
++                              ldisc_receive_buf(tty, buf->data, info->flag_buf, framesize);
+               }
+       }
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/pcxx.c linux-2.6.8/drivers/char/pcxx.c
+--- linux-2.6.8.orig/drivers/char/pcxx.c       2004-08-13 22:37:15.000000000 -0700
++++ linux-2.6.8/drivers/char/pcxx.c    2004-11-28 04:49:42.532440584 -0800
+@@ -538,28 +538,11 @@
+       
+               if(tty->driver->flush_buffer)
+                       tty->driver->flush_buffer(tty);
+-              if(tty->ldisc.flush_buffer)
+-                      tty->ldisc.flush_buffer(tty);
++              tty_ldisc_flush(tty);
+               shutdown(info);
+               tty->closing = 0;
+               info->event = 0;
+               info->tty = NULL;
+-#ifndef MODULE
+-/* ldiscs[] is not available in a MODULE
+-** worth noting that while I'm not sure what this hunk of code is supposed
+-** to do, it is not present in the serial.c driver.  Hmmm.  If you know,
+-** please send me a note.  brian@ilinx.com
+-** Don't know either what this is supposed to do christoph@lameter.com.
+-*/
+-              if(tty->ldisc.num != ldiscs[N_TTY].num) {
+-                      if(tty->ldisc.close)
+-                              (tty->ldisc.close)(tty);
+-                      tty->ldisc = ldiscs[N_TTY];
+-                      tty->termios->c_line = N_TTY;
+-                      if(tty->ldisc.open)
+-                              (tty->ldisc.open)(tty);
+-              }
+-#endif
+               if(info->blocked_open) {
+                       if(info->close_delay) {
+                               current->state = TASK_INTERRUPTIBLE;
+@@ -800,9 +783,7 @@
+       memoff(ch);
+       restore_flags(flags);
+ 
+-      wake_up_interruptible(&tty->write_wait);
+-      if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ static void pcxe_flush_chars(struct tty_struct *tty)
+@@ -1675,10 +1656,7 @@
+                       if (event & LOWTX_IND) {
+                               if (ch->statusflags & LOWWAIT) {
+                                       ch->statusflags &= ~LOWWAIT;
+-                                      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                                              tty->ldisc.write_wakeup)
+-                                              (tty->ldisc.write_wakeup)(tty);
+-                                      wake_up_interruptible(&tty->write_wait);
++                                      tty_wakeup(tty);
+                               }
+                       }
+ 
+@@ -1686,10 +1664,7 @@
+                               ch->statusflags &= ~TXBUSY;
+                               if (ch->statusflags & EMPTYWAIT) {
+                                       ch->statusflags &= ~EMPTYWAIT;
+-                                      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                                              tty->ldisc.write_wakeup)
+-                                              (tty->ldisc.write_wakeup)(tty);
+-                                      wake_up_interruptible(&tty->write_wait);
++                                      tty_wakeup(tty);
+                               }
+                       }
+               }
+@@ -2165,8 +2140,7 @@
+                               tty_wait_until_sent(tty, 0);
+                       }
+                       else {
+-                              if(tty->ldisc.flush_buffer)
+-                                      tty->ldisc.flush_buffer(tty);
++                              tty_ldisc_flush(tty);
+                       }
+ 
+                       /* Fall Thru */
+diff -Naur linux-2.6.8.orig/drivers/char/pty.c linux-2.6.8/drivers/char/pty.c
+--- linux-2.6.8.orig/drivers/char/pty.c        2004-08-13 22:36:44.000000000 -0700
++++ linux-2.6.8/drivers/char/pty.c     2004-11-28 04:49:42.546438310 -0800
+@@ -91,10 +91,7 @@
+       if (!o_tty)
+               return;
+ 
+-      if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          o_tty->ldisc.write_wakeup)
+-              (o_tty->ldisc.write_wakeup)(o_tty);
+-      wake_up_interruptible(&o_tty->write_wait);
++      tty_wakeup(o_tty);
+       set_bit(TTY_THROTTLED, &tty->flags);
+ }
+ 
+@@ -107,6 +104,10 @@
+  *   (2) avoid redundant copying for cases where count >> receive_room
+  * N.B. Calls from user space may now return an error code instead of
+  * a count.
++ *
++ * FIXME: Our pty_write method is called with our ldisc lock held but
++ * not our partners. We can't just take the other one blindly without
++ * risking deadlocks.  There is also the small matter of TTY_DONT_FLIP
+  */
+ static int pty_write(struct tty_struct * tty, int from_user,
+                      const unsigned char *buf, int count)
+diff -Naur linux-2.6.8.orig/drivers/char/riscom8.c linux-2.6.8/drivers/char/riscom8.c
+--- linux-2.6.8.orig/drivers/char/riscom8.c    2004-08-13 22:36:57.000000000 -0700
++++ linux-2.6.8/drivers/char/riscom8.c 2004-11-28 04:49:42.562435711 -0800
+@@ -1127,8 +1127,8 @@
+       rc_shutdown_port(bp, port);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
++
+       tty->closing = 0;
+       port->event = 0;
+       port->tty = NULL;
+@@ -1301,9 +1301,7 @@
+       restore_flags(flags);
+       
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ static int rc_tiocmget(struct tty_struct *tty, struct file *file)
+@@ -1644,9 +1642,7 @@
+               return;
+ 
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+ }
+diff -Naur linux-2.6.8.orig/drivers/char/rocket.c linux-2.6.8/drivers/char/rocket.c
+--- linux-2.6.8.orig/drivers/char/rocket.c     2004-08-13 22:37:26.000000000 -0700
++++ linux-2.6.8/drivers/char/rocket.c  2004-11-28 04:49:42.577433274 -0800
+@@ -250,12 +250,16 @@
+                         CHANNEL_t * cp, unsigned int ChanStatus)
+ {
+       unsigned int CharNStat;
+-      int ToRecv, wRecv, space, count;
++      int ToRecv, wRecv, space = 0, count;
+       unsigned char *cbuf;
+       char *fbuf;
++      struct tty_ldisc *ld;
++
++      ld = tty_ldisc_ref(tty);
+ 
+       ToRecv = sGetRxCnt(cp);
+-      space = tty->ldisc.receive_room(tty);
++      if (ld)
++              space = ld->receive_room(tty);
+       if (space > 2 * TTY_FLIPBUF_SIZE)
+               space = 2 * TTY_FLIPBUF_SIZE;
+       cbuf = tty->flip.char_buf;
+@@ -354,7 +358,8 @@
+               count += ToRecv;
+       }
+       /*  Push the data up to the tty layer */
+-      tty->ldisc.receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, count);
++      ld->receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, count);
++      tty_ldisc_deref(ld);
+ }
+ 
+ /*
+@@ -408,8 +413,7 @@
+               clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+ 
+       if (info->xmit_cnt < WAKEUP_CHARS) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup) (tty);
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+ #ifdef ROCKETPORT_HAVE_POLL_WAIT
+               wake_up_interruptible(&tty->poll_wait);
+@@ -1022,7 +1026,7 @@
+       unsigned long flags;
+       int timeout;
+       CHANNEL_t *cp;
+-
++      
+       if (rocket_paranoia_check(info, "rp_close"))
+               return;
+ 
+@@ -1101,8 +1105,8 @@
+ 
+       if (TTY_DRIVER_FLUSH_BUFFER_EXISTS(tty))
+               TTY_DRIVER_FLUSH_BUFFER(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++              
++      tty_ldisc_flush(tty);
+ 
+       clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+ 
+@@ -1731,8 +1735,7 @@
+       
+ end:
+       if (info->xmit_cnt < WAKEUP_CHARS) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))  && tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup) (tty);
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+ #ifdef ROCKETPORT_HAVE_POLL_WAIT
+               wake_up_interruptible(&tty->poll_wait);
+@@ -1806,8 +1809,7 @@
+ #ifdef ROCKETPORT_HAVE_POLL_WAIT
+       wake_up_interruptible(&tty->poll_wait);
+ #endif
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup) (tty);
++      tty_wakeup(tty);
+ 
+       cp = &info->channel;
+       sFlushTxFIFO(cp);
+diff -Naur linux-2.6.8.orig/drivers/char/selection.c linux-2.6.8/drivers/char/selection.c
+--- linux-2.6.8.orig/drivers/char/selection.c  2004-08-13 22:38:04.000000000 -0700
++++ linux-2.6.8/drivers/char/selection.c       2004-11-28 04:49:42.584432137 -0800
+@@ -281,12 +281,15 @@
+ {
+       struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
+       int     pasted = 0, count;
++      struct  tty_ldisc *ld;
+       DECLARE_WAITQUEUE(wait, current);
+ 
+       acquire_console_sem();
+       poke_blanked_console();
+       release_console_sem();
+ 
++      ld = tty_ldisc_ref_wait(tty);
++      
+       add_wait_queue(&vt->paste_wait, &wait);
+       while (sel_buffer && sel_buffer_lth > pasted) {
+               set_current_state(TASK_INTERRUPTIBLE);
+@@ -301,6 +304,8 @@
+       }
+       remove_wait_queue(&vt->paste_wait, &wait);
+       current->state = TASK_RUNNING;
++
++      tty_ldisc_deref(ld);
+       return 0;
+ }
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/ser_a2232.c linux-2.6.8/drivers/char/ser_a2232.c
+--- linux-2.6.8.orig/drivers/char/ser_a2232.c  2004-08-13 22:36:33.000000000 -0700
++++ linux-2.6.8/drivers/char/ser_a2232.c       2004-11-28 04:49:42.605428725 -0800
+@@ -599,10 +599,7 @@
+                                       
+                               /* WakeUp if output buffer runs low */
+                               if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) {
+-                                      if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup){
+-                                              (port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
+-                                      }
+-                                      wake_up_interruptible(&port->gs.tty->write_wait);
++                                      tty_wakeup(port->gs.tty);
+                               }
+                       } // if the port is used
+               } // for every port on the board
+diff -Naur linux-2.6.8.orig/drivers/char/serial167.c linux-2.6.8/drivers/char/serial167.c
+--- linux-2.6.8.orig/drivers/char/serial167.c  2004-08-13 22:36:56.000000000 -0700
++++ linux-2.6.8/drivers/char/serial167.c       2004-11-28 04:49:42.611427751 -0800
+@@ -760,11 +760,7 @@
+       wake_up_interruptible(&info->open_wait);
+     }
+     if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+-      if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+-      && tty->ldisc.write_wakeup){
+-          (tty->ldisc.write_wakeup)(tty);
+-      }
+-      wake_up_interruptible(&tty->write_wait);
++      tty_wakeup(tty);
+     }
+ } /* do_softint */
+ 
+@@ -1343,10 +1339,7 @@
+     local_irq_save(flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+     local_irq_restore(flags);
+-    wake_up_interruptible(&tty->write_wait);
+-    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-    && tty->ldisc.write_wakeup)
+-      (tty->ldisc.write_wakeup)(tty);
++    tty_wakeup(tty);
+ } /* cy_flush_buffer */
+ 
+ 
+@@ -1846,18 +1839,9 @@
+     shutdown(info);
+     if (tty->driver->flush_buffer)
+       tty->driver->flush_buffer(tty);
+-    if (tty->ldisc.flush_buffer)
+-      tty->ldisc.flush_buffer(tty);
++    tty_ldisc_flush(tty);
+     info->event = 0;
+     info->tty = 0;
+-    if (tty->ldisc.num != ldiscs[N_TTY].num) {
+-      if (tty->ldisc.close)
+-          (tty->ldisc.close)(tty);
+-      tty->ldisc = ldiscs[N_TTY];
+-      tty->termios->c_line = N_TTY;
+-      if (tty->ldisc.open)
+-          (tty->ldisc.open)(tty);
+-    }
+     if (info->blocked_open) {
+       if (info->close_delay) {
+           current->state = TASK_INTERRUPTIBLE;
+diff -Naur linux-2.6.8.orig/drivers/char/serial_tx3912.c linux-2.6.8/drivers/char/serial_tx3912.c
+--- linux-2.6.8.orig/drivers/char/serial_tx3912.c      2004-08-13 22:36:58.000000000 -0700
++++ linux-2.6.8/drivers/char/serial_tx3912.c   2004-11-28 04:49:42.621426126 -0800
+@@ -191,12 +191,9 @@
+       }
+       
+         if (port->gs.xmit_cnt <= port->gs.wakeup_chars) {
+-                if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                    port->gs.tty->ldisc.write_wakeup)
+-                        (port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
++              tty_wakeup(port->gs.tty);
+                 rs_dprintk (TX3912_UART_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n",
+                             port->gs.wakeup_chars); 
+-                wake_up_interruptible(&port->gs.tty->write_wait);
+               }       
+ }
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/specialix.c linux-2.6.8/drivers/char/specialix.c
+--- linux-2.6.8.orig/drivers/char/specialix.c  2004-08-13 22:37:40.000000000 -0700
++++ linux-2.6.8/drivers/char/specialix.c       2004-11-28 04:49:42.625425476 -0800
+@@ -1468,8 +1468,7 @@
+       sx_shutdown_port(bp, port);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       port->event = 0;
+       port->tty = NULL;
+@@ -1646,10 +1645,8 @@
+       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+       restore_flags(flags);
+       
++      tty_wakeup(tty);
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
+ }
+ 
+ 
+@@ -2052,12 +2049,8 @@
+       if(!(tty = port->tty)) 
+               return;
+ 
+-      if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
+-      }
++      if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event))
++              tty_wakeup(tty);
+ }
+ 
+ static struct tty_operations sx_ops = {
+diff -Naur linux-2.6.8.orig/drivers/char/stallion.c linux-2.6.8/drivers/char/stallion.c
+--- linux-2.6.8.orig/drivers/char/stallion.c   2004-08-13 22:37:37.000000000 -0700
++++ linux-2.6.8/drivers/char/stallion.c        2004-11-28 04:49:42.657420278 -0800
+@@ -1197,8 +1197,7 @@
+               portp->tx.tail = (char *) NULL;
+       }
+       set_bit(TTY_IO_ERROR, &tty->flags);
+-      if (tty->ldisc.flush_buffer)
+-              (tty->ldisc.flush_buffer)(tty);
++      tty_ldisc_flush(tty);
+ 
+       tty->closing = 0;
+       portp->tty = (struct tty_struct *) NULL;
+@@ -1809,10 +1808,7 @@
+               return;
+ 
+       stl_flush(portp);
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*****************************************************************************/
+@@ -2193,10 +2189,7 @@
+ 
+       lock_kernel();
+       if (test_bit(ASYI_TXLOW, &portp->istate)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+       if (test_bit(ASYI_DCDCHANGE, &portp->istate)) {
+               clear_bit(ASYI_DCDCHANGE, &portp->istate);
+diff -Naur linux-2.6.8.orig/drivers/char/sx.c linux-2.6.8/drivers/char/sx.c
+--- linux-2.6.8.orig/drivers/char/sx.c 2004-08-13 22:36:56.000000000 -0700
++++ linux-2.6.8/drivers/char/sx.c      2004-11-28 04:49:42.661419628 -0800
+@@ -1046,12 +1046,9 @@
+       }
+ 
+       if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) {
+-              if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  port->gs.tty->ldisc.write_wakeup)
+-                      (port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
++              tty_wakeup(port->gs.tty);
+               sx_dprintk (SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n",
+                           port->gs.wakeup_chars); 
+-              wake_up_interruptible(&port->gs.tty->write_wait);
+       }
+ 
+       clear_bit (SX_PORT_TRANSMIT_LOCK, &port->locks);
+diff -Naur linux-2.6.8.orig/drivers/char/synclink.c linux-2.6.8/drivers/char/synclink.c
+--- linux-2.6.8.orig/drivers/char/synclink.c   2004-08-13 22:36:46.000000000 -0700
++++ linux-2.6.8/drivers/char/synclink.c        2004-11-28 04:49:42.677417029 -0800
+@@ -993,6 +993,29 @@
+       return 0;
+ }
+ 
++/**
++ * line discipline callback wrappers
++ *
++ * The wrappers maintain line discipline references
++ * while calling into the line discipline.
++ *
++ * ldisc_receive_buf  - pass receive data to line discipline
++ */
++
++static void ldisc_receive_buf(struct tty_struct *tty,
++                            const __u8 *data, char *flags, int count)
++{
++      struct tty_ldisc *ld;
++      if (!tty)
++              return;
++      ld = tty_ldisc_ref(tty);
++      if (ld) {
++              if (ld->receive_buf)
++                      ld->receive_buf(tty, data, flags, count);
++              tty_ldisc_deref(ld);
++      }
++}
++
+ /* mgsl_stop()                throttle (stop) transmitter
+  *    
+  * Arguments:         tty     pointer to tty info structure
+@@ -1153,13 +1176,7 @@
+                       __FILE__,__LINE__,info->device_name);
+ 
+       if (tty) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup) {
+-                      if ( debug_level >= DEBUG_LEVEL_BH )
+-                              printk( "%s(%d):calling ldisc.write_wakeup on %s\n",
+-                                      __FILE__,__LINE__,info->device_name);
+-                      (tty->ldisc.write_wakeup)(tty);
+-              }
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+ 
+@@ -2415,11 +2432,8 @@
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
+-              
+-}     /* end of mgsl_flush_buffer() */
++      tty_wakeup(tty);
++}
+ 
+ /* mgsl_send_xchar()
+  *
+@@ -3253,9 +3267,8 @@
+ 
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-              
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++
++      tty_ldisc_flush(tty);
+               
+       shutdown(info);
+       
+@@ -6830,11 +6843,7 @@
+                       } 
+                       else
+ #endif
+-                      {
+-                              /* Call the line discipline receive callback directly. */
+-                              if ( tty && tty->ldisc.receive_buf )
+-                              tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+-                      }
++                              ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+               }
+       }
+       /* Free the buffers used by this frame. */
+@@ -7006,9 +7015,7 @@
+                       memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize);
+                       info->icount.rxok++;
+ 
+-                      /* Call the line discipline receive callback directly. */
+-                      if ( tty && tty->ldisc.receive_buf )
+-                              tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
++                      ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+               }
+ 
+               /* Free the buffers used by this frame. */
+diff -Naur linux-2.6.8.orig/drivers/char/synclinkmp.c linux-2.6.8/drivers/char/synclinkmp.c
+--- linux-2.6.8.orig/drivers/char/synclinkmp.c 2004-08-13 22:36:32.000000000 -0700
++++ linux-2.6.8/drivers/char/synclinkmp.c      2004-11-28 04:49:42.693414430 -0800
+@@ -722,6 +722,29 @@
+       return 0;
+ }
+ 
++/**
++ * line discipline callback wrappers
++ *
++ * The wrappers maintain line discipline references
++ * while calling into the line discipline.
++ *
++ * ldisc_receive_buf  - pass receive data to line discipline
++ */
++
++static void ldisc_receive_buf(struct tty_struct *tty,
++                            const __u8 *data, char *flags, int count)
++{
++      struct tty_ldisc *ld;
++      if (!tty)
++              return;
++      ld = tty_ldisc_ref(tty);
++      if (ld) {
++              if (ld->receive_buf)
++                      ld->receive_buf(tty, data, flags, count);
++              tty_ldisc_deref(ld);
++      }
++}
++
+ /* tty callbacks */
+ 
+ /* Called when a port is opened.  Init and enable port.
+@@ -869,8 +892,7 @@
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+ 
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+ 
+       shutdown(info);
+ 
+@@ -1275,9 +1297,7 @@
+       spin_unlock_irqrestore(&info->lock,flags);
+ 
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /* throttle (stop) transmitter
+@@ -1941,13 +1961,7 @@
+                       __FILE__,__LINE__,info->device_name);
+ 
+       if (tty) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup) {
+-                      if ( debug_level >= DEBUG_LEVEL_BH )
+-                              printk( "%s(%d):%s calling ldisc.write_wakeup\n",
+-                                      __FILE__,__LINE__,info->device_name);
+-                      (tty->ldisc.write_wakeup)(tty);
+-              }
++              tty_wakeup(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+ }
+@@ -4869,15 +4883,8 @@
+                       }
+                       else
+ #endif
+-                      {
+-                              if ( tty && tty->ldisc.receive_buf ) {
+-                                      /* Call the line discipline receive callback directly. */
+-                                      tty->ldisc.receive_buf(tty,
+-                                              info->tmp_rx_buf,
+-                                              info->flag_buf,
+-                                              framesize);
+-                              }
+-                      }
++                              ldisc_receive_buf(tty,info->tmp_rx_buf,
++                                                info->flag_buf, framesize);
+               }
+       }
+       /* Free the buffers used by this frame. */
+diff -Naur linux-2.6.8.orig/drivers/char/tty_io.c linux-2.6.8/drivers/char/tty_io.c
+--- linux-2.6.8.orig/drivers/char/tty_io.c     2004-08-13 22:37:15.000000000 -0700
++++ linux-2.6.8/drivers/char/tty_io.c  2004-11-28 04:49:42.733407931 -0800
+@@ -92,6 +92,7 @@
+ #include <linux/smp_lock.h>
+ #include <linux/device.h>
+ #include <linux/idr.h>
++#include <linux/wait.h>
+ 
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+@@ -120,11 +121,17 @@
+ 
+ EXPORT_SYMBOL(tty_std_termios);
+ 
++/* This list gets poked at by procfs and various bits of boot up code. This
++   could do with some rationalisation such as pulling the tty proc function
++   into this file */
++   
+ LIST_HEAD(tty_drivers);                       /* linked list of tty drivers */
+-struct tty_ldisc ldiscs[NR_LDISCS];   /* line disc dispatch table     */
+ 
+-/* Semaphore to protect creating and releasing a tty */
++/* Semaphore to protect creating and releasing a tty. This is shared with
++   vt.c for deeply disgusting hack reasons */
+ DECLARE_MUTEX(tty_sem);
++/* Lock for tty_termios changes - private to tty_io/tty_ioctl */
++spinlock_t tty_termios_lock = SPIN_LOCK_UNLOCKED;
+ 
+ #ifdef CONFIG_UNIX98_PTYS
+ extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
+@@ -223,65 +230,324 @@
+       return 0;
+ }
+ 
++/*
++ *    This is probably overkill for real world processors but
++ *    they are not on hot paths so a little discipline won't do 
++ *    any harm.
++ */
++ 
++static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
++{
++      unsigned long flags;
++      spin_lock_irqsave(&tty_termios_lock, flags);
++      tty->termios->c_line = num;
++      spin_unlock_irqrestore(&tty_termios_lock, flags);
++}
++
++/*
++ *    This guards the refcounted line discipline lists. The lock
++ *    must be taken with irqs off because there are hangup path
++ *    callers who will do ldisc lookups and cannot sleep.
++ */
++ 
++static spinlock_t tty_ldisc_lock = SPIN_LOCK_UNLOCKED;
++static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
++static struct tty_ldisc tty_ldiscs[NR_LDISCS];        /* line disc dispatch table     */
++
+ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
+ {
++      unsigned long flags;
++      int ret = 0;
++      
+       if (disc < N_TTY || disc >= NR_LDISCS)
+               return -EINVAL;
+       
++      spin_lock_irqsave(&tty_ldisc_lock, flags);
+       if (new_ldisc) {
+-              ldiscs[disc] = *new_ldisc;
+-              ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
+-              ldiscs[disc].num = disc;
+-      } else
+-              memset(&ldiscs[disc], 0, sizeof(struct tty_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;
++      }
++      spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+       
+-      return 0;
++      return ret;
+ }
+ 
+ EXPORT_SYMBOL(tty_register_ldisc);
+ 
+-/* Set the discipline of a tty line. */
++struct tty_ldisc *tty_ldisc_get(int disc)
++{
++      unsigned long flags;
++      struct tty_ldisc *ld;
++
++      if (disc < N_TTY || disc >= NR_LDISCS)
++              return NULL;
++      
++      spin_lock_irqsave(&tty_ldisc_lock, flags);
++
++      ld = &tty_ldiscs[disc];
++      /* Check the entry is defined */
++      if(ld->flags & LDISC_FLAG_DEFINED)
++      {
++              /* If the module is being unloaded we can't use it */
++              if (!try_module_get(ld->owner))
++                      ld = NULL;
++              else /* lock it */
++                      ld->refcount++;
++      }
++      else
++              ld = NULL;
++      spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++      return ld;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_get);
++
++void tty_ldisc_put(int disc)
++{
++      struct tty_ldisc *ld;
++      unsigned long flags;
++      
++      if (disc < N_TTY || disc >= NR_LDISCS)
++              BUG();
++              
++      spin_lock_irqsave(&tty_ldisc_lock, flags);
++      ld = &tty_ldiscs[disc];
++      if(ld->refcount == 0)
++              BUG();
++      ld->refcount --;
++      module_put(ld->owner);
++      spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++}
++      
++EXPORT_SYMBOL_GPL(tty_ldisc_put);
++
++void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
++{
++      tty->ldisc = *ld;
++      tty->ldisc.refcount = 0;
++}
++
++/**
++ *    tty_ldisc_try           -       internal helper
++ *    @tty: the tty
++ *
++ *    Make a single attempt to grab and bump the refcount on
++ *    the tty ldisc. Return 0 on failure or 1 on success. This is
++ *    used to implement both the waiting and non waiting versions
++ *    of tty_ldisc_ref
++ */
++
++static int tty_ldisc_try(struct tty_struct *tty)
++{
++      unsigned long flags;
++      struct tty_ldisc *ld;
++      int ret = 0;
++      
++      spin_lock_irqsave(&tty_ldisc_lock, flags);
++      ld = &tty->ldisc;
++      if(test_bit(TTY_LDISC, &tty->flags))
++      {
++              ld->refcount++;
++              ret = 1;
++      }
++      spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++      return ret;
++}
++
++/**
++ *    tty_ldisc_ref_wait      -       wait for the tty ldisc
++ *    @tty: tty device
++ *
++ *    Dereference the line discipline for the terminal and take a 
++ *    reference to it. If the line discipline is in flux then 
++ *    wait patiently until it changes.
++ *
++ *    Note: Must not be called from an IRQ/timer context. The caller
++ *    must also be careful not to hold other locks that will deadlock
++ *    against a discipline change, such as an existing ldisc reference
++ *    (which we check for)
++ */
++ 
++struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
++{
++      /* wait_event is a macro */
++      wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
++      if(tty->ldisc.refcount == 0)
++              printk(KERN_ERR "tty_ldisc_ref_wait\n");
++      return &tty->ldisc;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
++
++/**
++ *    tty_ldisc_ref           -       get the tty ldisc
++ *    @tty: tty device
++ *
++ *    Dereference the line discipline for the terminal and take a 
++ *    reference to it. If the line discipline is in flux then 
++ *    return NULL. Can be called from IRQ and timer functions.
++ */
++ 
++struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
++{
++      if(tty_ldisc_try(tty))
++              return &tty->ldisc;
++      return NULL;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_ref);
++
++/**
++ *    tty_ldisc_deref         -       free a tty ldisc reference
++ *    @ld: reference to free up
++ *
++ *    Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
++ *    be called in IRQ context.
++ */
++ 
++void tty_ldisc_deref(struct tty_ldisc *ld)
++{
++      unsigned long flags;
++
++      if(ld == NULL)
++              BUG();
++              
++      spin_lock_irqsave(&tty_ldisc_lock, flags);
++      if(ld->refcount == 0)
++              printk(KERN_ERR "tty_ldisc_deref: no references.\n");
++      else
++              ld->refcount--;
++      if(ld->refcount == 0)
++              wake_up(&tty_ldisc_wait);
++      spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_deref);
++
++/**
++ *    tty_ldisc_enable        -       allow ldisc use
++ *    @tty: terminal to activate ldisc on
++ *
++ *    Set the TTY_LDISC flag when the line discipline can be called
++ *    again. Do neccessary wakeups for existing sleepers.
++ *
++ *    Note: nobody should set this bit except via this function. Clearing
++ *    directly is allowed.
++ */
++
++static void tty_ldisc_enable(struct tty_struct *tty)
++{
++      set_bit(TTY_LDISC, &tty->flags);
++      wake_up(&tty_ldisc_wait);
++}
++      
++/**
++ *    tty_set_ldisc           -       set line discipline
++ *    @tty: the terminal to set
++ *    @ldisc: the line discipline
++ *
++ *    Set the discipline of a tty line. Must be called from a process
++ *    context.
++ */
++ 
+ static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
+ {
+       int     retval = 0;
+       struct  tty_ldisc o_ldisc;
+       char buf[64];
++      int work;
++      unsigned long flags;
++      struct tty_ldisc *ld;
+ 
+       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> */
+-      if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {
++      if (ld == NULL) {
+               request_module("tty-ldisc-%d", ldisc);
++              ld = tty_ldisc_get(ldisc);
+       }
+-      if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
++      if (ld == NULL)
+               return -EINVAL;
+ 
+-      if (tty->ldisc.num == ldisc)
+-              return 0;       /* We are already in the desired discipline */
+-
+-      if (!try_module_get(ldiscs[ldisc].owner))
+-              return -EINVAL;
+-      
+       o_ldisc = tty->ldisc;
+ 
+       tty_wait_until_sent(tty, 0);
++
++      /*
++       *      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. */
++              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;
++      }
++      clear_bit(TTY_LDISC, &tty->flags);      
++      clear_bit(TTY_DONT_FLIP, &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);
++      /*
++       * Wait for ->hangup_work and ->flip.work handlers to terminate
++       */
++       
++      flush_scheduled_work();
+       /* Shutdown the current discipline. */
+       if (tty->ldisc.close)
+               (tty->ldisc.close)(tty);
+ 
+       /* Now set up the new line discipline. */
+-      tty->ldisc = ldiscs[ldisc];
+-      tty->termios->c_line = ldisc;
++      tty_ldisc_assign(tty, ld);
++      tty_set_termios_ldisc(tty, ldisc);
+       if (tty->ldisc.open)
+               retval = (tty->ldisc.open)(tty);
+       if (retval < 0) {
+-              tty->ldisc = o_ldisc;
+-              tty->termios->c_line = tty->ldisc.num;
++              tty_ldisc_put(ldisc);
++              /* There is an outstanding reference here so this is safe */
++              tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));
++              tty_set_termios_ldisc(tty, tty->ldisc.num);
+               if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
+-                      tty->ldisc = ldiscs[N_TTY];
+-                      tty->termios->c_line = N_TTY;
++                      tty_ldisc_put(o_ldisc.num);
++                      /* This driver is always present */
++                      tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
++                      tty_set_termios_ldisc(tty, N_TTY);
+                       if (tty->ldisc.open) {
+                               int r = tty->ldisc.open(tty);
+ 
+@@ -291,12 +557,27 @@
+                                             tty_name(tty, buf), r);
+                       }
+               }
+-      } else {
+-              module_put(o_ldisc.owner);
+       }
++      /* At this point we hold a reference to the new ldisc and a
++         a reference to the old ldisc. If we ended up flipping back
++         to the existing ldisc we have two references to it */
+       
+       if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc)
+               tty->driver->set_ldisc(tty);
++              
++      tty_ldisc_put(o_ldisc.num);
++      
++      /*
++       *      Allow ldisc referencing to occur as soon as the driver
++       *      ldisc callback completes.
++       */
++       
++      tty_ldisc_enable(tty);
++      
++      /* Restart it in case no characters kick it off. Safe if
++         already running */
++      if(work)
++              schedule_delayed_work(&tty->flip.work, 1);
+       return retval;
+ }
+ 
+@@ -399,6 +680,53 @@
+ 
+ static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED;
+ static struct file *redirect;
++
++/**
++ *    tty_wakeup      -       request more data
++ *    @tty: terminal
++ *
++ *    Internal and external helper for wakeups of tty. This function
++ *    informs the line discipline if present that the driver is ready
++ *    to receive more output data.
++ */
++ 
++void tty_wakeup(struct tty_struct *tty)
++{
++      struct tty_ldisc *ld;
++      
++      if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
++              ld = tty_ldisc_ref(tty);
++              if(ld) {
++                      if(ld->write_wakeup)
++                              ld->write_wakeup(tty);
++                      tty_ldisc_deref(ld);
++              }
++      }
++      wake_up_interruptible(&tty->write_wait);
++}
++
++EXPORT_SYMBOL_GPL(tty_wakeup);
++
++/**
++ *    tty_ldisc_flush -       flush line discipline queue
++ *    @tty: tty
++ *
++ *    Flush the line discipline queue (if any) for this tty. If there
++ *    is no line discipline active this is a no-op.
++ */
++ 
++void tty_ldisc_flush(struct tty_struct *tty)
++{
++      struct tty_ldisc *ld = tty_ldisc_ref(tty);
++      if(ld) {
++              if(ld->flush_buffer)
++                      ld->flush_buffer(tty);
++              tty_ldisc_deref(ld);
++      }
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_flush);
++      
+ /*
+  * This can be called by the "eventd" kernel thread.  That is process synchronous,
+  * but doesn't hold any locks, so we need to make sure we have the appropriate
+@@ -410,6 +738,7 @@
+       struct file * cons_filp = NULL;
+       struct file *filp, *f = NULL;
+       struct task_struct *p;
++      struct tty_ldisc *ld;
+       struct pid *pid;
+       int    closecount = 0, n;
+ 
+@@ -428,6 +757,7 @@
+       
+       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) {
+               if (filp->f_op->write == redirected_tty_write)
+                       cons_filp = filp;
+@@ -440,21 +770,25 @@
+       file_list_unlock();
+       
+       /* FIXME! What are the locking issues here? This may me overdoing things..
+-      * this question is especially important now that we've removed the irqlock. */
+-      {
+-              unsigned long flags;
++       * this question is especially important now that we've removed the irqlock. */
+ 
+-              local_irq_save(flags); // FIXME: is this safe?
+-              if (tty->ldisc.flush_buffer)
+-                      tty->ldisc.flush_buffer(tty);
++      ld = tty_ldisc_ref(tty);
++      if(ld != NULL)  /* We may have no line discipline at this point */
++      {
++              if (ld->flush_buffer)
++                      ld->flush_buffer(tty);
+               if (tty->driver->flush_buffer)
+                       tty->driver->flush_buffer(tty);
+               if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              local_irq_restore(flags); // FIXME: is this safe?
++                  ld->write_wakeup)
++                      ld->write_wakeup(tty);
++              if (ld->hangup)
++                      ld->hangup(tty);
+       }
+ 
++      /* FIXME: Once we trust the LDISC code better we can wait here for
++         ldisc completion and fix the driver call race */
++         
+       wake_up_interruptible(&tty->write_wait);
+       wake_up_interruptible(&tty->read_wait);
+ 
+@@ -463,22 +797,19 @@
+        * N_TTY.
+        */
+       if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
++      {
++              unsigned long flags;
++              spin_lock_irqsave(&tty_termios_lock, flags);
+               *tty->termios = tty->driver->init_termios;
+-      if (tty->ldisc.num != ldiscs[N_TTY].num) {
+-              if (tty->ldisc.close)
+-                      (tty->ldisc.close)(tty);
+-              module_put(tty->ldisc.owner);
+-              
+-              tty->ldisc = ldiscs[N_TTY];
+-              tty->termios->c_line = N_TTY;
+-              if (tty->ldisc.open) {
+-                      int i = (tty->ldisc.open)(tty);
+-                      if (i < 0)
+-                              printk(KERN_ERR "do_tty_hangup: N_TTY open: "
+-                                              "error %d\n", -i);
+-              }
++              spin_unlock_irqrestore(&tty_termios_lock, flags);
+       }
+       
++      /* Defer ldisc switch */
++      /* tty_deferred_ldisc_switch(N_TTY);
++      
++        This should get done automatically when the port closes and
++        tty_release is called */
++      
+       read_lock(&tasklist_lock);
+       if (tty->session > 0) {
+               struct list_head *l;
+@@ -511,6 +842,17 @@
+                               tty->driver->close(tty, cons_filp);
+       } else if (tty->driver->hangup)
+               (tty->driver->hangup)(tty);
++              
++      /* We don't want to have driver/ldisc interactions beyond
++         the ones we did here. The driver layer expects no
++         calls after ->hangup() from the ldisc side. However we
++         can't yet guarantee all that */
++
++      set_bit(TTY_HUPPED, &tty->flags);
++      if (ld) {
++              tty_ldisc_enable(tty);
++              tty_ldisc_deref(ld);
++      }
+       unlock_kernel();
+       if (f)
+               fput(f);
+@@ -627,9 +969,9 @@
+       }
+       if (tty->driver->start)
+               (tty->driver->start)(tty);
+-      if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++
++      /* If we have a running line discipline it may need kicking */
++      tty_wakeup(tty);
+       wake_up_interruptible(&tty->write_wait);
+ }
+ 
+@@ -641,6 +983,7 @@
+       int i;
+       struct tty_struct * tty;
+       struct inode *inode;
++      struct tty_ldisc *ld;
+ 
+       tty = (struct tty_struct *)file->private_data;
+       inode = file->f_dentry->d_inode;
+@@ -649,11 +992,15 @@
+       if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
+               return -EIO;
+ 
++      /* We want to wait for the line discipline to sort out in this
++         situation */
++      ld = tty_ldisc_ref_wait(tty);
+       lock_kernel();
+-      if (tty->ldisc.read)
+-              i = (tty->ldisc.read)(tty,file,buf,count);
++      if (ld->read)
++              i = (ld->read)(tty,file,buf,count);
+       else
+               i = -EIO;
++      tty_ldisc_deref(ld);
+       unlock_kernel();
+       if (i > 0)
+               inode->i_atime = CURRENT_TIME;
+@@ -715,16 +1062,23 @@
+ {
+       struct tty_struct * tty;
+       struct inode *inode = file->f_dentry->d_inode;
+-
++      ssize_t ret;
++      struct tty_ldisc *ld;
++      
+       tty = (struct tty_struct *)file->private_data;
+       if (tty_paranoia_check(tty, inode, "tty_write"))
+               return -EIO;
+       if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags)))
+               return -EIO;
+-      if (!tty->ldisc.write)
+-              return -EIO;
+-      return do_tty_write(tty->ldisc.write, tty, file,
++
++      ld = tty_ldisc_ref_wait(tty);           
++      if (!ld->write)
++              ret = -EIO;
++      else
++              ret = do_tty_write(ld->write, tty, file,
+                           (const unsigned char __user *)buf, count);
++      tty_ldisc_deref(ld);
++      return ret;
+ }
+ 
+ ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count,
+@@ -910,6 +1264,7 @@
+        * 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 (tty->ldisc.open) {
+               retval = (tty->ldisc.open)(tty);
+               if (retval)
+@@ -922,7 +1277,9 @@
+                               (tty->ldisc.close)(tty);
+                       goto release_mem_out;
+               }
++              tty_ldisc_enable(o_tty);
+       }
++      tty_ldisc_enable(tty);
+       goto success;
+ 
+       /*
+@@ -951,6 +1308,9 @@
+       tty->count++;
+       tty->driver = driver; /* N.B. why do this every time?? */
+ 
++      /* FIXME */
++      if(!test_bit(TTY_LDISC, &tty->flags))
++              printk(KERN_ERR "init_dev but no ldisc\n");
+ success:
+       *ret_tty = tty;
+       
+@@ -1054,6 +1414,7 @@
+       int     devpts_master, devpts;
+       int     idx;
+       char    buf[64];
++      unsigned long flags;
+       
+       tty = (struct tty_struct *)filp->private_data;
+       if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "release_dev"))
+@@ -1130,7 +1491,6 @@
+               }
+       }
+ #endif
+-
+       if (tty->driver->close)
+               tty->driver->close(tty, filp);
+ 
+@@ -1254,36 +1614,58 @@
+ #ifdef TTY_DEBUG_HANGUP
+       printk(KERN_DEBUG "freeing tty structure...");
+ #endif
+-
+       /*
+        * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
+-       * kill any delayed work.
++       * kill any delayed work. As this is the final close it does not
++       * race with the set_ldisc code path.
+        */
++      clear_bit(TTY_LDISC, &tty->flags);
+       clear_bit(TTY_DONT_FLIP, &tty->flags);
+       cancel_delayed_work(&tty->flip.work);
+ 
+       /*
+        * Wait for ->hangup_work and ->flip.work handlers to terminate
+        */
++       
+       flush_scheduled_work();
+-
++      
++      /*
++       * Wait for any short term users (we know they are just driver
++       * side waiters as the file is closing so user count on the file
++       * side is zero.
++       */
++      spin_lock_irqsave(&tty_ldisc_lock, flags);
++      while(tty->ldisc.refcount)
++      {
++              spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++              wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
++              spin_lock_irqsave(&tty_ldisc_lock, flags);
++      }
++      spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+       /*
+        * Shutdown the current line discipline, and reset it to N_TTY.
+        * N.B. why reset ldisc when we're releasing the memory??
++       *
++       * FIXME: this MUST get fixed for the new reflocking
+        */
+       if (tty->ldisc.close)
+               (tty->ldisc.close)(tty);
+-      module_put(tty->ldisc.owner);
++      tty_ldisc_put(tty->ldisc.num);
+       
+-      tty->ldisc = ldiscs[N_TTY];
+-      tty->termios->c_line = N_TTY;
++      /*
++       *      Switch the line discipline back
++       */
++      tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
++      tty_set_termios_ldisc(tty,N_TTY); 
+       if (o_tty) {
++              /* FIXME: could o_tty be in setldisc here ? */
++              clear_bit(TTY_LDISC, &o_tty->flags);
+               if (o_tty->ldisc.close)
+                       (o_tty->ldisc.close)(o_tty);
+-              module_put(o_tty->ldisc.owner);
+-              o_tty->ldisc = ldiscs[N_TTY];
++              tty_ldisc_put(o_tty->ldisc.num);
++              tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));
++              tty_set_termios_ldisc(o_tty,N_TTY); 
+       }
+-
+       /*
+        * The release_mem function takes care of the details of clearing
+        * the slots and preserving the termios structure.
+@@ -1323,6 +1705,7 @@
+       unsigned short saved_flags = filp->f_flags;
+ 
+       nonseekable_open(inode, filp);
++      
+ retry_open:
+       noctty = filp->f_flags & O_NOCTTY;
+       index  = -1;
+@@ -1479,14 +1862,18 @@
+ static unsigned int tty_poll(struct file * filp, poll_table * wait)
+ {
+       struct tty_struct * tty;
++      struct tty_ldisc *ld;
++      int ret = 0;
+ 
+       tty = (struct tty_struct *)filp->private_data;
+       if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_poll"))
+               return 0;
+-
+-      if (tty->ldisc.poll)
+-              return (tty->ldisc.poll)(tty, filp, wait);
+-      return 0;
++              
++      ld = tty_ldisc_ref_wait(tty);
++      if (ld->poll)
++              ret = (ld->poll)(tty, filp, wait);
++      tty_ldisc_deref(ld);
++      return ret;
+ }
+ 
+ static int tty_fasync(int fd, struct file * filp, int on)
+@@ -1518,12 +1905,15 @@
+ static int tiocsti(struct tty_struct *tty, char __user *p)
+ {
+       char ch, mbz = 0;
+-
++      struct tty_ldisc *ld;
++      
+       if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (get_user(ch, p))
+               return -EFAULT;
+-      tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
++      ld = tty_ldisc_ref_wait(tty);
++      ld->receive_buf(tty, &ch, &mbz, 1);
++      tty_ldisc_deref(ld);
+       return 0;
+ }
+ 
+@@ -1772,6 +2162,7 @@
+       struct tty_struct *tty, *real_tty;
+       void __user *p = (void __user *)arg;
+       int retval;
++      struct tty_ldisc *ld;
+       
+       tty = (struct tty_struct *)file->private_data;
+       if (tty_paranoia_check(tty, inode, "tty_ioctl"))
+@@ -1861,6 +2252,7 @@
+               case TIOCGSID:
+                       return tiocgsid(tty, real_tty, p);
+               case TIOCGETD:
++                      /* FIXME: check this is ok */
+                       return put_user(tty->ldisc.num, (int __user *)p);
+               case TIOCSETD:
+                       return tiocsetd(tty, p);
+@@ -1899,16 +2291,19 @@
+                       return tty_tiocmset(tty, file, cmd, p);
+       }
+       if (tty->driver->ioctl) {
+-              int retval = (tty->driver->ioctl)(tty, file, cmd, arg);
++              retval = (tty->driver->ioctl)(tty, file, cmd, arg);
+               if (retval != -ENOIOCTLCMD)
+                       return retval;
+       }
+-      if (tty->ldisc.ioctl) {
+-              int retval = (tty->ldisc.ioctl)(tty, file, cmd, arg);
+-              if (retval != -ENOIOCTLCMD)
+-                      return retval;
++      ld = tty_ldisc_ref_wait(tty);
++      retval = -EINVAL;
++      if (ld->ioctl) {
++              retval = ld->ioctl(tty, file, cmd, arg);
++              if (retval == -ENOIOCTLCMD)
++                      retval = -EINVAL;
+       }
+-      return -EINVAL;
++      tty_ldisc_deref(ld);
++      return retval;
+ }
+ 
+ 
+@@ -1943,14 +2338,21 @@
+       int session;
+       int             i;
+       struct file     *filp;
++      struct tty_ldisc *disc;
+       
+       if (!tty)
+               return;
+       session  = tty->session;
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      
++      /* 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);
++
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
++      
+       read_lock(&tasklist_lock);
+       for_each_task_pid(session, PIDTYPE_SID, p, l, pid) {
+               if (p->signal->tty == tty || session > 0) {
+@@ -2002,24 +2404,29 @@
+ 
+ /*
+  * This routine is called out of the software interrupt to flush data
+- * from the flip buffer to the line discipline.
++ * from the flip buffer 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;
++      unsigned long   flags;
++      struct tty_ldisc *disc;
++
++      disc = tty_ldisc_ref(tty);
++      if (disc == NULL)       /*  !TTY_LDISC */
++              return;
+ 
+       if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+               /*
+                * Do it after the next timer tick:
+                */
+               schedule_delayed_work(&tty->flip.work, 1);
+-              return;
++              goto out;
+       }
+-
+       spin_lock_irqsave(&tty->read_lock, flags);
+       if (tty->flip.buf_num) {
+               cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
+@@ -2038,7 +2445,31 @@
+       tty->flip.count = 0;
+       spin_unlock_irqrestore(&tty->read_lock, flags);
+ 
+-      tty->ldisc.receive_buf(tty, cp, fp, count);
++      disc->receive_buf(tty, cp, fp, count);
++out:
++      tty_ldisc_deref(disc);
++}
++
++/*
++ *    Call the ldisc flush directly from a driver. This function may
++ *    return an error and need retrying by the user.
++ */
++
++int tty_push_data(struct tty_struct *tty, unsigned char *cp, unsigned char *fp, int count)
++{
++      int ret = 0;
++      struct tty_ldisc *disc;
++      
++      disc = tty_ldisc_ref(tty);
++      if(test_bit(TTY_DONT_FLIP, &tty->flags))
++              ret = -EAGAIN;
++      else if(disc == NULL)
++              ret = -EIO;
++      else
++              disc->receive_buf(tty, cp, fp, count);
++      tty_ldisc_deref(disc);
++      return ret;
++      
+ }
+ 
+ /*
+@@ -2060,9 +2491,20 @@
+ 
+ static int n_baud_table = ARRAY_SIZE(baud_table);
+ 
++/**
++ *    tty_termios_baud_rate
++ *    @termios: termios structure
++ *
++ *    Convert termios baud rate data into a speed. This should be called
++ *    with the termios lock held if this termios is a terminal termios
++ *    structure. May change the termios data.
++ */
++ 
+ int tty_termios_baud_rate(struct termios *termios)
+ {
+-      unsigned int cbaud = termios->c_cflag & CBAUD;
++      unsigned int cbaud;
++      
++      cbaud = termios->c_cflag & CBAUD;
+ 
+       if (cbaud & CBAUDEX) {
+               cbaud &= ~CBAUDEX;
+@@ -2072,12 +2514,20 @@
+               else
+                       cbaud += 15;
+       }
+-
+       return baud_table[cbaud];
+ }
+ 
+ EXPORT_SYMBOL(tty_termios_baud_rate);
+ 
++/**
++ *    tty_get_baud_rate       -       get tty bit rates
++ *    @tty: tty to query
++ *
++ *    Returns the baud rate as an integer for this terminal. The
++ *    termios lock must be held by the caller and the terminal bit
++ *    flags may be updated.
++ */
++ 
+ int tty_get_baud_rate(struct tty_struct *tty)
+ {
+       int baud = tty_termios_baud_rate(tty->termios);
+@@ -2096,6 +2546,17 @@
+ 
+ EXPORT_SYMBOL(tty_get_baud_rate);
+ 
++/**
++ *    tty_flip_buffer_push    -       terminal
++ *    @tty: tty to push
++ *
++ *    Queue a push of the terminal flip buffers to the line discipline. This
++ *    function must not be called from IRQ context if tty->low_latency is set.
++ *
++ *    In the event of the queue being busy for flipping the work will be
++ *    held off and retried later.
++ */
++
+ void tty_flip_buffer_push(struct tty_struct *tty)
+ {
+       if (tty->low_latency)
+@@ -2113,7 +2574,7 @@
+ {
+       memset(tty, 0, sizeof(struct tty_struct));
+       tty->magic = TTY_MAGIC;
+-      tty->ldisc = ldiscs[N_TTY];
++      tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
+       tty->pgrp = -1;
+       tty->flip.char_buf_ptr = tty->flip.char_buf;
+       tty->flip.flag_buf_ptr = tty->flip.flag_buf;
+diff -Naur linux-2.6.8.orig/drivers/char/tty_ioctl.c linux-2.6.8/drivers/char/tty_ioctl.c
+--- linux-2.6.8.orig/drivers/char/tty_ioctl.c  2004-08-13 22:38:10.000000000 -0700
++++ linux-2.6.8/drivers/char/tty_ioctl.c       2004-11-28 04:49:54.591481233 -0800
+@@ -29,6 +29,8 @@
+ 
+ #undef        DEBUG
+ 
++extern spinlock_t tty_termios_lock;
++
+ /*
+  * Internal flag options for termios setting behavior
+  */
+@@ -98,8 +100,18 @@
+ {
+       int canon_change;
+       struct termios old_termios = *tty->termios;
++      struct tty_ldisc *ld;
++      unsigned long flags;
++      
++      /*
++       *      Perform the actual termios internal changes under lock.
++       */
++       
++
++      /* FIXME: we need to decide on some locking/ordering semantics
++         for the set_termios notification eventually */
++      spin_lock_irqsave(&tty_termios_lock, flags);
+ 
+-      local_irq_disable(); // FIXME: is this safe?
+       *tty->termios = *new_termios;
+       unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
+       canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
+@@ -109,12 +121,13 @@
+               tty->canon_data = 0;
+               tty->erasing = 0;
+       }
+-      local_irq_enable(); // FIXME: is this safe?
++      
++      
+       if (canon_change && !L_ICANON(tty) && tty->read_cnt)
+               /* Get characters left over from canonical mode. */
+               wake_up_interruptible(&tty->read_wait);
+ 
+-      /* see if packet mode change of state */
++      /* See if packet mode change of state. */
+ 
+       if (tty->link && tty->link->packet) {
+               int old_flow = ((old_termios.c_iflag & IXON) &&
+@@ -132,17 +145,23 @@
+                       wake_up_interruptible(&tty->link->read_wait);
+               }
+       }
+-
++         
+       if (tty->driver->set_termios)
+               (*tty->driver->set_termios)(tty, &old_termios);
+ 
+-      if (tty->ldisc.set_termios)
+-              (*tty->ldisc.set_termios)(tty, &old_termios);
++      ld = tty_ldisc_ref(tty);
++      if (ld != NULL) {
++              if (ld->set_termios)
++                      (ld->set_termios)(tty, &old_termios);
++              tty_ldisc_deref(ld);
++      }
++      spin_unlock_irqrestore(&tty_termios_lock, flags);
+ }
+ 
+ static int set_termios(struct tty_struct * tty, void __user *arg, int opt)
+ {
+       struct termios tmp_termios;
++      struct tty_ldisc *ld;
+       int retval = tty_check_change(tty);
+ 
+       if (retval)
+@@ -159,9 +178,14 @@
+                       return -EFAULT;
+       }
+ 
+-      if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
+-
++      ld = tty_ldisc_ref(tty);
++      
++      if (ld != NULL) {
++              if ((opt & TERMIOS_FLUSH) && ld->flush_buffer)
++                      ld->flush_buffer(tty);
++              tty_ldisc_deref(ld);
++      }
++      
+       if (opt & TERMIOS_WAIT) {
+               tty_wait_until_sent(tty, 0);
+               if (signal_pending(current))
+@@ -225,12 +249,16 @@
+ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
+ {
+       struct sgttyb tmp;
++      unsigned long flags;
+ 
++      spin_lock_irqsave(&tty_termios_lock, flags);
+       tmp.sg_ispeed = 0;
+       tmp.sg_ospeed = 0;
+       tmp.sg_erase = tty->termios->c_cc[VERASE];
+       tmp.sg_kill = tty->termios->c_cc[VKILL];
+       tmp.sg_flags = get_sgflags(tty);
++      spin_unlock_irqrestore(&tty_termios_lock, flags);
++      
+       return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
+ }
+ 
+@@ -265,16 +293,21 @@
+       int retval;
+       struct sgttyb tmp;
+       struct termios termios;
++      unsigned long flags;
+ 
+       retval = tty_check_change(tty);
+       if (retval)
+               return retval;
+-      termios =  *tty->termios;
++      
+       if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
+               return -EFAULT;
++              
++      spin_lock_irqsave(&tty_termios_lock, flags);
++      termios =  *tty->termios;
+       termios.c_cc[VERASE] = tmp.sg_erase;
+       termios.c_cc[VKILL] = tmp.sg_kill;
+       set_sgflags(&termios, tmp.sg_flags);
++      spin_unlock_irqrestore(&tty_termios_lock, flags);
+       change_termios(tty, &termios);
+       return 0;
+ }
+@@ -365,6 +398,8 @@
+       struct tty_struct * real_tty;
+       void __user *p = (void __user *)arg;
+       int retval;
++      struct tty_ldisc *ld;
++      unsigned long flags;
+ 
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+           tty->driver->subtype == PTY_TYPE_MASTER)
+@@ -443,22 +478,26 @@
+                       retval = tty_check_change(tty);
+                       if (retval)
+                               return retval;
++                              
++                      ld = tty_ldisc_ref(tty);
+                       switch (arg) {
+                       case TCIFLUSH:
+-                              if (tty->ldisc.flush_buffer)
+-                                      tty->ldisc.flush_buffer(tty);
++                              if (ld->flush_buffer)
++                                      ld->flush_buffer(tty);
+                               break;
+                       case TCIOFLUSH:
+-                              if (tty->ldisc.flush_buffer)
+-                                      tty->ldisc.flush_buffer(tty);
++                              if (ld->flush_buffer)
++                                      ld->flush_buffer(tty);
+                               /* fall through */
+                       case TCOFLUSH:
+                               if (tty->driver->flush_buffer)
+                                       tty->driver->flush_buffer(tty);
+                               break;
+                       default:
++                              tty_ldisc_deref(ld);
+                               return -EINVAL;
+                       }
++                      tty_ldisc_deref(ld);
+                       return 0;
+               case TIOCOUTQ:
+                       return put_user(tty->driver->chars_in_buffer ?
+@@ -504,9 +543,11 @@
+               case TIOCSSOFTCAR:
+                       if (get_user(arg, (unsigned int __user *) arg))
+                               return -EFAULT;
++                      spin_lock_irqsave(&tty_termios_lock, flags);
+                       tty->termios->c_cflag =
+                               ((tty->termios->c_cflag & ~CLOCAL) |
+                                (arg ? CLOCAL : 0));
++                      spin_unlock_irqrestore(&tty_termios_lock, flags);
+                       return 0;
+               default:
+                       return -ENOIOCTLCMD;
+diff -Naur linux-2.6.8.orig/drivers/char/viocons.c linux-2.6.8/drivers/char/viocons.c
+--- linux-2.6.8.orig/drivers/char/viocons.c    2004-08-13 22:36:10.000000000 -0700
++++ linux-2.6.8/drivers/char/viocons.c 2004-11-28 04:49:42.767402408 -0800
+@@ -422,10 +422,7 @@
+                       pi->overflowMessage = 0;
+ 
+               if (pi->tty) {
+-                      if ((pi->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                          (pi->tty->ldisc.write_wakeup))
+-                              (pi->tty->ldisc.write_wakeup)(pi->tty);
+-                      wake_up_interruptible(&pi->tty->write_wait);
++                      tty_wakeup(pi->tty);
+               }
+       }
+ 
+diff -Naur linux-2.6.8.orig/drivers/char/vme_scc.c linux-2.6.8/drivers/char/vme_scc.c
+--- linux-2.6.8.orig/drivers/char/vme_scc.c    2004-08-13 22:36:11.000000000 -0700
++++ linux-2.6.8/drivers/char/vme_scc.c 2004-11-28 04:49:42.779400459 -0800
+@@ -544,12 +544,8 @@
+               SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);   /* disable tx_int on next tx underrun? */
+               port->gs.flags &= ~GS_TX_INTEN;
+       }
+-      if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) {
+-              if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                              port->gs.tty->ldisc.write_wakeup)
+-                      (port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
+-              wake_up_interruptible(&port->gs.tty->write_wait);
+-      }
++      if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars)
++              tty_wakeup(port->gs.tty);
+ 
+       SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+       return IRQ_HANDLED;
+diff -Naur linux-2.6.8.orig/drivers/char/vt_ioctl.c linux-2.6.8/drivers/char/vt_ioctl.c
+--- linux-2.6.8.orig/drivers/char/vt_ioctl.c   2004-08-13 22:37:26.000000000 -0700
++++ linux-2.6.8/drivers/char/vt_ioctl.c        2004-11-28 04:49:42.785399484 -0800
+@@ -536,8 +536,7 @@
+                 default:
+                       return -EINVAL;
+               }
+-              if (tty->ldisc.flush_buffer)
+-                      tty->ldisc.flush_buffer(tty);
++              tty_ldisc_flush(tty);
+               return 0;
+ 
+       case KDGKBMODE:
+diff -Naur linux-2.6.8.orig/drivers/isdn/capi/capi.c linux-2.6.8/drivers/isdn/capi/capi.c
+--- linux-2.6.8.orig/drivers/isdn/capi/capi.c  2004-08-13 22:38:04.000000000 -0700
++++ linux-2.6.8/drivers/isdn/capi/capi.c       2004-11-28 04:49:42.797397535 -0800
+@@ -436,51 +436,62 @@
+       struct sk_buff *nskb;
+       int datalen;
+       u16 errcode, datahandle;
+-
++      struct tty_ldisc *ld;
++      
+       datalen = skb->len - CAPIMSG_LEN(skb->data);
+-      if (mp->tty) {
+-              if (mp->tty->ldisc.receive_buf == 0) {
+-                      printk(KERN_ERR "capi: ldisc has no receive_buf function\n");
+-                      return -1;
+-              }
+-              if (mp->ttyinstop) {
++      if (mp->tty == NULL)
++      {
++#ifdef _DEBUG_DATAFLOW
++              printk(KERN_DEBUG "capi: currently no receiver\n");
++#endif
++              return -1;
++      }
++      
++      ld = tty_ldisc_ref(mp->tty);
++      if (ld == NULL)
++              return -1;
++      if (ld->receive_buf == NULL) {
+ #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+-                      printk(KERN_DEBUG "capi: recv tty throttled\n");
++              printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n");
+ #endif
+-                      return -1;
+-              }
+-              if (mp->tty->ldisc.receive_room &&
+-                  mp->tty->ldisc.receive_room(mp->tty) < datalen) {
++              goto bad;
++      }
++      if (mp->ttyinstop) {
+ #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+-                      printk(KERN_DEBUG "capi: no room in tty\n");
++              printk(KERN_DEBUG "capi: recv tty throttled\n");
+ #endif
+-                      return -1;
+-              }
+-              if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) {
+-                      printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
+-                      return -1;
+-              }
+-              datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
+-              errcode = capi20_put_message(mp->ap, nskb);
+-              if (errcode != CAPI_NOERROR) {
+-                      printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
+-                                      errcode);
+-                      kfree_skb(nskb);
+-                      return -1;
+-              }
+-              (void)skb_pull(skb, CAPIMSG_LEN(skb->data));
+-#ifdef _DEBUG_DATAFLOW
+-              printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
+-                                      datahandle, skb->len);
++              goto bad;
++      }
++      if (ld->receive_room &&
++          ld->receive_room(mp->tty) < datalen) {
++#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
++              printk(KERN_DEBUG "capi: no room in tty\n");
+ #endif
+-              mp->tty->ldisc.receive_buf(mp->tty, skb->data, NULL, skb->len);
+-              kfree_skb(skb);
+-              return 0;
+-
++              goto bad;
++      }
++      if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) {
++              printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
++              goto bad;
++      }
++      datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
++      errcode = capi20_put_message(mp->ap, nskb);
++      if (errcode != CAPI_NOERROR) {
++              printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
++                              errcode);
++              kfree_skb(nskb);
++              goto bad;
+       }
++      (void)skb_pull(skb, CAPIMSG_LEN(skb->data));
+ #ifdef _DEBUG_DATAFLOW
+-      printk(KERN_DEBUG "capi: currently no receiver\n");
++      printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
++                              datahandle, skb->len);
+ #endif
++      ld->receive_buf(mp->tty, skb->data, NULL, skb->len);
++      kfree_skb(skb);
++      tty_ldisc_deref(ld);
++      return 0;
++bad:
++      tty_ldisc_deref(ld);
+       return -1;
+ }
+ 
+@@ -614,6 +625,7 @@
+ 
+ 
+       if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
++              
+               datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2);
+ #ifdef _DEBUG_DATAFLOW
+               printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n",
+@@ -633,10 +645,8 @@
+ #endif
+               kfree_skb(skb);
+               (void)capiminor_del_ack(mp, datahandle);
+-              if (mp->tty) {
+-                      if (mp->tty->ldisc.write_wakeup)
+-                              mp->tty->ldisc.write_wakeup(mp->tty);
+-              }
++              if (mp->tty)
++                      tty_wakeup(tty);
+               (void)handle_minor_send(mp);
+ 
+       } else {
+diff -Naur linux-2.6.8.orig/drivers/isdn/i4l/isdn_tty.c linux-2.6.8/drivers/isdn/i4l/isdn_tty.c
+--- linux-2.6.8.orig/drivers/isdn/i4l/isdn_tty.c       2004-08-13 22:37:27.000000000 -0700
++++ linux-2.6.8/drivers/isdn/i4l/isdn_tty.c    2004-11-28 04:49:42.814394773 -0800
+@@ -296,10 +296,7 @@
+               info->send_outstanding++;
+               info->msr &= ~UART_MSR_CTS;
+               info->lsr &= ~UART_LSR_TEMT;
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup) (tty);
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+               return;
+       }
+       if (slen < 0) {
+@@ -1174,10 +1171,7 @@
+                                               /* If DLE decoding results in zero-transmit, but
+                                                * c originally was non-zero, do a wakeup.
+                                                */
+-                                              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                                               tty->ldisc.write_wakeup)
+-                                                      (tty->ldisc.write_wakeup) (tty);
+-                                              wake_up_interruptible(&tty->write_wait);
++                                              tty_wakeup(tty);
+                                               info->msr |= UART_MSR_CTS;
+                                               info->lsr |= UART_LSR_TEMT;
+                                       }
+@@ -1290,9 +1284,7 @@
+       isdn_tty_cleanup_xmit(info);
+       info->xmit_count = 0;
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup) (tty);
++      tty_wakeup(tty);
+ }
+ 
+ static void
+@@ -1747,10 +1739,10 @@
+       }
+       dev->modempoll--;
+       isdn_tty_shutdown(info);
++      
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+       info->tty = NULL;
+       info->ncarrier = 0;
+       tty->closing = 0;
+@@ -2681,8 +2673,7 @@
+               if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+                       return;
+               }
+-              if (info->tty->ldisc.flush_buffer)
+-                      info->tty->ldisc.flush_buffer(info->tty);
++              tty_ldisc_flush(tty);
+               if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+                   (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+                      (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
+diff -Naur linux-2.6.8.orig/drivers/macintosh/macserial.c linux-2.6.8/drivers/macintosh/macserial.c
+--- linux-2.6.8.orig/drivers/macintosh/macserial.c     2004-08-13 22:38:08.000000000 -0700
++++ linux-2.6.8/drivers/macintosh/macserial.c  2004-11-28 04:49:42.000000000 -0800
+@@ -713,12 +713,8 @@
+       if (!tty)
+               return;
+ 
+-      if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
+-      }
++      if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event))
++              tty_wakeup(tty);
+ }
+ 
+ static int startup(struct mac_serial * info)
+@@ -1564,10 +1560,7 @@
+       spin_lock_irqsave(&info->lock, flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       spin_unlock_irqrestore(&info->lock, flags);
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1994,8 +1987,7 @@
+ 
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+diff -Naur linux-2.6.8.orig/drivers/net/hamradio/mkiss.c linux-2.6.8/drivers/net/hamradio/mkiss.c
+--- linux-2.6.8.orig/drivers/net/hamradio/mkiss.c      2004-08-13 22:38:08.000000000 -0700
++++ linux-2.6.8/drivers/net/hamradio/mkiss.c   2004-11-28 04:49:42.000000000 -0800
+@@ -602,8 +602,6 @@
+ 
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
+ 
+       /* Restore default settings */
+       ax->dev->type = ARPHRD_AX25;
+diff -Naur linux-2.6.8.orig/drivers/net/irda/irtty-sir.c linux-2.6.8/drivers/net/irda/irtty-sir.c
+--- linux-2.6.8.orig/drivers/net/irda/irtty-sir.c      2004-08-13 22:36:10.000000000 -0700
++++ linux-2.6.8/drivers/net/irda/irtty-sir.c   2004-11-28 04:49:42.000000000 -0800
+@@ -509,13 +509,6 @@
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+       
+-/* from old irtty - but what is it good for?
+- * we _are_ the ldisc and we _don't_ implement flush_buffer!
+- *
+- *    if (tty->ldisc.flush_buffer)
+- *            tty->ldisc.flush_buffer(tty);
+- */
+-
+       /* apply mtt override */
+       sir_tty_drv.qos_mtt_bits = qos_mtt_bits;
+ 
+diff -Naur linux-2.6.8.orig/drivers/net/ppp_async.c linux-2.6.8/drivers/net/ppp_async.c
+--- linux-2.6.8.orig/drivers/net/ppp_async.c   2004-08-13 22:37:37.000000000 -0700
++++ linux-2.6.8/drivers/net/ppp_async.c        2004-11-28 04:49:42.000000000 -0800
+@@ -122,6 +122,9 @@
+  * frees the memory that ppp_asynctty_receive is using.  The best
+  * way to fix this is to use a rwlock in the tty struct, but for now
+  * we use a single global rwlock for all ttys in ppp line discipline.
++ *
++ * FIXME: this is no longer true. The _close path for the ldisc is 
++ * now guaranteed to be sane. 
+  */
+ static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED;
+ 
+@@ -144,7 +147,8 @@
+ }
+ 
+ /*
+- * Called when a tty is put into PPP line discipline.
++ * Called when a tty is put into PPP line discipline. Called in process
++ * context.
+  */
+ static int
+ ppp_asynctty_open(struct tty_struct *tty)
+@@ -255,6 +259,11 @@
+       return -EAGAIN;
+ }
+ 
++/*
++ * Called in process context only. May be re-entered by multiple
++ * ioctl calling threads.
++ */
++ 
+ static int
+ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
+                  unsigned int cmd, unsigned long arg)
+@@ -716,7 +725,8 @@
+ 
+ /*
+  * Flush output from our internal buffers.
+- * Called for the TCFLSH ioctl.
++ * Called for the TCFLSH ioctl. Can be entered in parallel
++ * but this is covered by the xmit_lock.
+  */
+ static void
+ ppp_async_flush_output(struct asyncppp *ap)
+@@ -816,7 +826,9 @@
+               skb_trim(skb, 0);
+ }
+ 
+-/* called when the tty driver has data for us. */
++/* Called when the tty driver has data for us. Runs parallel with the
++   other ldisc functions but will not be re-entered */
++
+ static void
+ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
+               char *flags, int count)
+diff -Naur linux-2.6.8.orig/drivers/net/ppp_synctty.c linux-2.6.8/drivers/net/ppp_synctty.c
+--- linux-2.6.8.orig/drivers/net/ppp_synctty.c 2004-08-13 22:36:56.000000000 -0700
++++ linux-2.6.8/drivers/net/ppp_synctty.c      2004-11-28 04:49:42.000000000 -0800
+@@ -175,6 +175,8 @@
+  * frees the memory that ppp_synctty_receive is using.  The best
+  * way to fix this is to use a rwlock in the tty struct, but for now
+  * we use a single global rwlock for all ttys in ppp line discipline.
++ *
++ * FIXME: Fixed in tty_io nowdays.
+  */
+ static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED;
+ 
+diff -Naur linux-2.6.8.orig/drivers/net/slip.c linux-2.6.8/drivers/net/slip.c
+--- linux-2.6.8.orig/drivers/net/slip.c        2004-08-13 22:38:10.000000000 -0700
++++ linux-2.6.8/drivers/net/slip.c     2004-11-28 04:49:42.000000000 -0800
+@@ -672,7 +672,9 @@
+  * Handle the 'receiver data ready' interrupt.
+  * This function is called by the 'tty_io' module in the kernel when
+  * a block of SLIP data has been received, which can now be decapsulated
+- * and sent on to some IP layer for further processing.
++ * and sent on to some IP layer for further processing. This will not
++ * be re-entered while running but other ldisc functions may be called
++ * in parallel
+  */
+  
+ static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+@@ -841,9 +843,11 @@
+  * SLIP line discipline is called for.  Because we are
+  * sure the tty line exists, we only have to link it to
+  * a free SLIP channel...
++ *
++ * Called in process context serialized from other ldisc calls.
+  */
+-static int
+-slip_open(struct tty_struct *tty)
++
++static int slip_open(struct tty_struct *tty)
+ {
+       struct slip *sl;
+       int err;
+@@ -876,11 +880,11 @@
+       tty->disc_data = sl;
+       sl->line = tty_devnum(tty);
+       sl->pid = current->pid;
++      
++      /* FIXME: already done before we were called - seems this can go */
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
+-
++              
+       if (!test_bit(SLF_INUSE, &sl->flags)) {
+               /* Perform the low-level SLIP initialization. */
+               if ((err = sl_alloc_bufs(sl, SL_MTU)) != 0)
+@@ -923,6 +927,9 @@
+ }
+ 
+ /*
++
++  FIXME: 1,2 are fixed 3 was never true anyway.
++  
+    Let me to blame a bit.
+    1. TTY module calls this funstion on soft interrupt.
+    2. TTY module calls this function WITH MASKED INTERRUPTS!
+@@ -941,9 +948,8 @@
+ 
+ /*
+  * Close down a SLIP channel.
+- * This means flushing out any pending queues, and then restoring the
+- * TTY line discipline to what it was before it got hooked to SLIP
+- * (which usually is TTY again).
++ * This means flushing out any pending queues, and then returning. This
++ * call is serialized against other ldisc functions.
+  */
+ static void
+ slip_close(struct tty_struct *tty)
+diff -Naur linux-2.6.8.orig/drivers/net/wan/pc300_tty.c linux-2.6.8/drivers/net/wan/pc300_tty.c
+--- linux-2.6.8.orig/drivers/net/wan/pc300_tty.c       2004-08-13 22:36:57.000000000 -0700
++++ linux-2.6.8/drivers/net/wan/pc300_tty.c    2004-11-28 04:49:42.000000000 -0800
+@@ -634,14 +634,8 @@
+       }
+ 
+       CPC_TTY_DBG("%s: call wake_up_interruptible\n",cpc_tty->name);
+-      
+-      wake_up_interruptible(&tty->write_wait); 
+-
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){
+-              CPC_TTY_DBG("%s: call line disc. wake up\n",cpc_tty->name);
+-              tty->ldisc.write_wakeup(tty); 
+-      } 
+ 
++      tty_wakeup(tty);        
+       return; 
+ } 
+ 
+@@ -692,9 +686,11 @@
+       st_cpc_tty_area *cpc_tty; 
+       volatile st_cpc_rx_buf * buf;
+       char flags=0,flg_rx=1; 
++      struct tty_ldisc *ld;
+ 
+       if (cpc_tty_cnt == 0) return;
+ 
++      
+       for (i=0; (i < 4) && flg_rx ; i++) {
+               flg_rx = 0;
+               port = (int) data;
+@@ -702,11 +698,18 @@
+                       cpc_tty = &cpc_tty_area[port];
+               
+                       if ((buf=cpc_tty->buf_rx.first) != 0) {
+-                                                                                                                      
+-                              if (cpc_tty->tty && (cpc_tty->tty->ldisc.receive_buf)) { 
+-                                      CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name);
+-                                      cpc_tty->tty->ldisc.receive_buf(cpc_tty->tty, (char *)(buf->data), 
+-                                      &flags, buf->size);
++                              
++                              if(cpc_tty->tty)
++                              {                                                                                       
++                                      ld = tty_ldisc_ref(cpc_tty);
++                                      if(ld)
++                                      {
++                                              if (ld->receive_buf)) {
++                                                      CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name);
++                                                      ld->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size);
++                                              }
++                                              tty_ldisc_deref(ld);
++                                      }
+                               }       
+                               cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next;
+                               kfree((unsigned char *)buf);
+@@ -910,13 +913,7 @@
+               CPC_TTY_DBG("%s: the interface is not opened\n",cpc_tty->name);
+               return; 
+       }
+-
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){
+-              CPC_TTY_DBG("%s:call line disc. wakeup\n",cpc_tty->name);
+-              tty->ldisc.write_wakeup (tty); 
+-      }
+-
+-      wake_up_interruptible(&tty->write_wait); 
++      tty_wakeup(tty);
+ }
+ 
+ /*
+diff -Naur linux-2.6.8.orig/drivers/net/wan/sdla_chdlc.c linux-2.6.8/drivers/net/wan/sdla_chdlc.c
+--- linux-2.6.8.orig/drivers/net/wan/sdla_chdlc.c      2004-08-13 22:36:58.000000000 -0700
++++ linux-2.6.8/drivers/net/wan/sdla_chdlc.c   2004-11-28 04:49:42.000000000 -0800
+@@ -3628,11 +3628,7 @@
+       if ((tty=card->tty)==NULL)
+               return;
+       
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup){
+-              (tty->ldisc.write_wakeup)(tty);
+-      }
+-      wake_up_interruptible(&tty->write_wait);
++      tty_wakeup(tty);
+ #if defined(SERIAL_HAVE_POLL_WAIT)
+       wake_up_interruptible(&tty->poll_wait);
+ #endif        
+@@ -3857,6 +3853,7 @@
+       char fp=0;
+       struct tty_struct *tty;
+       int i;
++      struct tty_ldisc *ld;
+       
+       if (!card->tty_open){
+               dbg_printk(KERN_INFO "%s: TTY not open during receive\n",
+@@ -3944,8 +3941,11 @@
+                       len -= offset;
+               }
+               sdla_peek(&card->hw, addr, card->tty_rx+offset, len);
+-              if (tty->ldisc.receive_buf){
+-                      tty->ldisc.receive_buf(tty,card->tty_rx,&fp,olen);
++              ld = tty_ldisc_ref(tty);
++              if (ld) {
++                      if (ld->receive_buf)
++                              ld->receive_buf(tty,card->tty_rx,&fp,olen);
++                      tty_ldisc_deref(ld);
+               }else{
+                       if (net_ratelimit()){
+                               printk(KERN_INFO 
+@@ -4252,14 +4252,10 @@
+       if (!tty)
+               return;
+       
+-      wake_up_interruptible(&tty->write_wait);
+ #if defined(SERIAL_HAVE_POLL_WAIT)
+       wake_up_interruptible(&tty->poll_wait);
+ #endif
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
+-
++      tty_wakeup(tty);
+       return;
+ }
+ 
+diff -Naur linux-2.6.8.orig/drivers/net/wireless/strip.c linux-2.6.8/drivers/net/wireless/strip.c
+--- linux-2.6.8.orig/drivers/net/wireless/strip.c      2004-08-13 22:37:26.000000000 -0700
++++ linux-2.6.8/drivers/net/wireless/strip.c   2004-11-28 04:49:42.000000000 -0800
+@@ -2661,8 +2661,6 @@
+       tty->disc_data = strip_info;
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
+ 
+       /*
+        * Restore default settings
+diff -Naur linux-2.6.8.orig/drivers/s390/char/con3215.c linux-2.6.8/drivers/s390/char/con3215.c
+--- linux-2.6.8.orig/drivers/s390/char/con3215.c       2004-08-13 22:37:26.000000000 -0700
++++ linux-2.6.8/drivers/s390/char/con3215.c    2004-11-28 04:49:43.000000000 -0800
+@@ -366,10 +366,7 @@
+       tty = raw->tty;
+       if (tty != NULL &&
+           RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+ }
+ 
+@@ -1055,10 +1052,7 @@
+ 
+       raw = (struct raw3215_info *) tty->driver_data;
+       raw3215_flush_buffer(raw);
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+diff -Naur linux-2.6.8.orig/drivers/s390/char/sclp_tty.c linux-2.6.8/drivers/s390/char/sclp_tty.c
+--- linux-2.6.8.orig/drivers/s390/char/sclp_tty.c      2004-08-13 22:38:11.000000000 -0700
++++ linux-2.6.8/drivers/s390/char/sclp_tty.c   2004-11-28 04:49:43.000000000 -0800
+@@ -277,10 +277,7 @@
+       wake_up(&sclp_tty_waitq);
+       /* check if the tty needs a wake up call */
+       if (sclp_tty != NULL) {
+-              if ((sclp_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  sclp_tty->ldisc.write_wakeup)
+-                      (sclp_tty->ldisc.write_wakeup)(sclp_tty);
+-              wake_up_interruptible(&sclp_tty->write_wait);
++              tty_wakeup(tty);
+       }
+ }
+ 
+diff -Naur linux-2.6.8.orig/drivers/s390/char/sclp_vt220.c linux-2.6.8/drivers/s390/char/sclp_vt220.c
+--- linux-2.6.8.orig/drivers/s390/char/sclp_vt220.c    2004-08-13 22:36:44.000000000 -0700
++++ linux-2.6.8/drivers/s390/char/sclp_vt220.c 2004-11-28 04:49:43.000000000 -0800
+@@ -139,10 +139,7 @@
+       wake_up(&sclp_vt220_waitq);
+       /* Check if the tty needs a wake up call */
+       if (sclp_vt220_tty != NULL) {
+-              if ((sclp_vt220_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  (sclp_vt220_tty->ldisc.write_wakeup != NULL))
+-                      (sclp_vt220_tty->ldisc.write_wakeup)(sclp_vt220_tty);
+-              wake_up_interruptible(&sclp_vt220_tty->write_wait);
++              tty_wakeup(tty);
+       }
+ }
+ 
+diff -Naur linux-2.6.8.orig/drivers/s390/net/ctctty.c linux-2.6.8/drivers/s390/net/ctctty.c
+--- linux-2.6.8.orig/drivers/s390/net/ctctty.c 2004-08-13 22:37:14.000000000 -0700
++++ linux-2.6.8/drivers/s390/net/ctctty.c      2004-11-28 04:49:43.000000000 -0800
+@@ -307,10 +307,7 @@
+ 
+               info->flags &= ~CTC_ASYNC_TX_LINESTAT;
+               if (tty) {
+-                      if (wake && (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                          tty->ldisc.write_wakeup)
+-                              (tty->ldisc.write_wakeup)(tty);
+-                      wake_up_interruptible(&tty->write_wait);
++                      tty_wakeup(tty);
+               }
+       }
+       return (skb_queue_empty(&info->tx_queue) ? 0 : 1);
+@@ -589,9 +586,7 @@
+       info->lsr |= UART_LSR_TEMT;
+       spin_unlock_irqrestore(&ctc_tty_lock, flags);
+       wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup) (tty);
++      tty_wakeup(tty);
+ ex:
+       DBF_TEXT_(trace, 2, "ex: %s ", __FUNCTION__);
+       return;
+@@ -1066,8 +1061,7 @@
+               skb_queue_purge(&info->tx_queue);
+               info->lsr |= UART_LSR_TEMT;
+       }
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+       info->tty = 0;
+       tty->closing = 0;
+       if (info->blocked_open) {
+diff -Naur linux-2.6.8.orig/drivers/sbus/char/aurora.c linux-2.6.8/drivers/sbus/char/aurora.c
+--- linux-2.6.8.orig/drivers/sbus/char/aurora.c        2004-08-13 22:36:17.000000000 -0700
++++ linux-2.6.8/drivers/sbus/char/aurora.c     2004-11-28 04:49:43.000000000 -0800
+@@ -1531,8 +1531,7 @@
+       aurora_shutdown_port(bp, port);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       port->event = 0;
+       port->tty = 0;
+@@ -1743,10 +1742,7 @@
+       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+       restore_flags(flags);
+       
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ #ifdef AURORA_DEBUG
+       printk("aurora_flush_buffer: end\n");
+ #endif
+@@ -2223,10 +2219,7 @@
+               return;
+ 
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+ #ifdef AURORA_DEBUG
+       printk("do_softint: end\n");
+diff -Naur linux-2.6.8.orig/drivers/serial/68328serial.c linux-2.6.8/drivers/serial/68328serial.c
+--- linux-2.6.8.orig/drivers/serial/68328serial.c      2004-08-13 22:38:08.000000000 -0700
++++ linux-2.6.8/drivers/serial/68328serial.c   2004-11-28 04:49:43.000000000 -0800
+@@ -435,10 +435,7 @@
+               return;
+ #if 0
+       if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+ #endif   
+ }
+@@ -858,10 +855,7 @@
+       cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       sti();
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1185,11 +1179,13 @@
+       shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++              
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
++#warning "This is not and has never been valid so fix it"     
++#if 0
+       if (tty->ldisc.num != ldiscs[N_TTY].num) {
+               if (tty->ldisc.close)
+                       (tty->ldisc.close)(tty);
+@@ -1198,6 +1194,7 @@
+               if (tty->ldisc.open)
+                       (tty->ldisc.open)(tty);
+       }
++#endif        
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+diff -Naur linux-2.6.8.orig/drivers/serial/68360serial.c linux-2.6.8/drivers/serial/68360serial.c
+--- linux-2.6.8.orig/drivers/serial/68360serial.c      2004-08-13 22:38:04.000000000 -0700
++++ linux-2.6.8/drivers/serial/68360serial.c   2004-11-28 04:49:43.000000000 -0800
+@@ -700,12 +700,8 @@
+       if (!tty)
+               return;
+ 
+-      if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
+-      }
++      if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event))
++              tty_wakeup(tty);
+ }
+ 
+ 
+@@ -1152,10 +1148,7 @@
+       /* There is nothing to "flush", whatever we gave the CPM
+        * is on its way out.
+        */
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+       info->flags &= ~TX_WAKEUP;
+ }
+ 
+@@ -1716,8 +1709,7 @@
+       shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);           
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+diff -Naur linux-2.6.8.orig/drivers/serial/mcfserial.c linux-2.6.8/drivers/serial/mcfserial.c
+--- linux-2.6.8.orig/drivers/serial/mcfserial.c        2004-08-13 22:36:56.000000000 -0700
++++ linux-2.6.8/drivers/serial/mcfserial.c     2004-11-28 04:49:43.000000000 -0800
+@@ -424,11 +424,7 @@
+       tty = info->tty;
+       if (!tty)
+               return;
+-
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
+-      wake_up_interruptible(&tty->write_wait);
++      tty_wakeup(tty);
+ }
+ 
+ 
+@@ -835,10 +831,7 @@
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       local_irq_restore(flags);
+ 
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1232,11 +1225,12 @@
+       shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
++      
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
++#if 0 
+       if (tty->ldisc.num != ldiscs[N_TTY].num) {
+               if (tty->ldisc.close)
+                       (tty->ldisc.close)(tty);
+@@ -1245,6 +1239,7 @@
+               if (tty->ldisc.open)
+                       (tty->ldisc.open)(tty);
+       }
++#endif        
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+diff -Naur linux-2.6.8.orig/drivers/serial/serial_core.c linux-2.6.8/drivers/serial/serial_core.c
+--- linux-2.6.8.orig/drivers/serial/serial_core.c      2004-08-13 22:36:32.000000000 -0700
++++ linux-2.6.8/drivers/serial/serial_core.c   2004-11-28 04:49:43.000000000 -0800
+@@ -107,15 +107,7 @@
+ static void uart_tasklet_action(unsigned long data)
+ {
+       struct uart_state *state = (struct uart_state *)data;
+-      struct tty_struct *tty;
+-
+-      tty = state->info->tty;
+-      if (tty) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      tty->ldisc.write_wakeup(tty);
+-              wake_up_interruptible(&tty->write_wait);
+-      }
++      tty_wakeup(state->info->tty);
+ }
+ 
+ static inline void
+@@ -581,10 +573,7 @@
+       spin_lock_irqsave(&port->lock, flags);
+       uart_circ_clear(&state->info->xmit);
+       spin_unlock_irqrestore(&port->lock, flags);
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1216,7 +1205,7 @@
+ {
+       struct uart_state *state = tty->driver_data;
+       struct uart_port *port;
+-
++      
+       BUG_ON(!kernel_locked());
+ 
+       if (!state || !state->port)
+@@ -1239,12 +1228,12 @@
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+-              printk("uart_close: bad serial port count; tty->count is 1, "
++              printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, "
+                      "state->count is %d\n", state->count);
+               state->count = 1;
+       }
+       if (--state->count < 0) {
+-              printk("rs_close: bad serial port count for %s: %d\n",
++              printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n",
+                      tty->name, state->count);
+               state->count = 0;
+       }
+@@ -1280,8 +1269,9 @@
+ 
+       uart_shutdown(state);
+       uart_flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++
++      tty_ldisc_flush(tty);   
++      
+       tty->closing = 0;
+       state->info->tty = NULL;
+ 
+diff -Naur linux-2.6.8.orig/drivers/tc/zs.c linux-2.6.8/drivers/tc/zs.c
+--- linux-2.6.8.orig/drivers/tc/zs.c   2004-08-13 22:37:39.000000000 -0700
++++ linux-2.6.8/drivers/tc/zs.c        2004-11-28 04:49:43.000000000 -0800
+@@ -683,10 +683,7 @@
+               return;
+ 
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+ }
+ 
+@@ -1010,10 +1007,7 @@
+       cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       sti();
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
++      tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1407,8 +1401,7 @@
+       shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
++      tty_ldisc_flush(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+diff -Naur linux-2.6.8.orig/drivers/usb/class/bluetty.c linux-2.6.8/drivers/usb/class/bluetty.c
+--- linux-2.6.8.orig/drivers/usb/class/bluetty.c       2004-08-13 22:37:37.000000000 -0700
++++ linux-2.6.8/drivers/usb/class/bluetty.c    2004-11-28 04:49:43.000000000 -0800
+@@ -992,17 +992,10 @@
+ 
+       dbg("%s", __FUNCTION__);
+ 
+-      if (!bluetooth) {
++      if (!bluetooth)
+               return;
+-      }
+-
+-      tty = bluetooth->tty;
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-              dbg("%s - write wakeup call.", __FUNCTION__);
+-              (tty->ldisc.write_wakeup)(tty);
+-      }
+ 
+-      wake_up_interruptible(&tty->write_wait);
++      tty_wakeup(&bluetooth->tty);
+ }
+ 
+ 
+diff -Naur linux-2.6.8.orig/drivers/usb/class/cdc-acm.c linux-2.6.8/drivers/usb/class/cdc-acm.c
+--- linux-2.6.8.orig/drivers/usb/class/cdc-acm.c       2004-08-13 22:37:15.000000000 -0700
++++ linux-2.6.8/drivers/usb/class/cdc-acm.c    2004-11-28 04:49:43.000000000 -0800
+@@ -248,16 +248,11 @@
+ static void acm_softint(void *private)
+ {
+       struct acm *acm = private;
+-      struct tty_struct *tty = acm->tty;
+       dbg("Entering acm_softint.\n");
+       
+       if (!ACM_READY(acm))
+               return;
+-
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
+-
+-      wake_up_interruptible(&tty->write_wait);
++      tty_wakeup(acm->tty);
+ }
+ 
+ /*
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/digi_acceleport.c linux-2.6.8/drivers/usb/serial/digi_acceleport.c
+--- linux-2.6.8.orig/drivers/usb/serial/digi_acceleport.c      2004-08-13 22:36:57.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/digi_acceleport.c   2004-11-28 04:49:43.000000000 -0800
+@@ -624,14 +624,7 @@
+       wake_up_interruptible( &port->write_wait );
+ 
+       /* wake up line discipline */
+-      if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-      && tty->ldisc.write_wakeup )
+-              (tty->ldisc.write_wakeup)(tty);
+-
+-      /* wake up other tty processes */
+-      wake_up_interruptible( &tty->write_wait );
+-      /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
+-
++      tty_wakeup(tty);
+ }
+ 
+ 
+@@ -1569,8 +1562,7 @@
+       /* flush driver and line discipline buffers */
+       if( tty->driver->flush_buffer )
+               tty->driver->flush_buffer( tty );
+-      if( tty->ldisc.flush_buffer )
+-              tty->ldisc.flush_buffer( tty );
++      tty_ldisc_flush(tty);
+ 
+       if (port->serial->dev) {
+               /* wait for transmit idle */
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/empeg.c linux-2.6.8/drivers/usb/serial/empeg.c
+--- linux-2.6.8.orig/drivers/usb/serial/empeg.c        2004-08-13 22:38:08.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/empeg.c     2004-11-28 04:49:43.000000000 -0800
+@@ -516,7 +516,9 @@
+        */
+       port->tty->low_latency = 1;
+ 
+-      /* Notify the tty driver that the termios have changed. */
++      /* Notify the tty driver that the termios have changed.
++         FIXME: Why - the ldisc will do this anyway and NULL is not
++         a valid previous state */
+       port->tty->ldisc.set_termios(port->tty, NULL);
+ 
+       return;
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/io_edgeport.c linux-2.6.8/drivers/usb/serial/io_edgeport.c
+--- linux-2.6.8.orig/drivers/usb/serial/io_edgeport.c  2004-08-13 22:36:14.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/io_edgeport.c       2004-11-28 04:49:43.000000000 -0800
+@@ -900,12 +900,7 @@
+ 
+       if (tty && edge_port->open) {
+               /* let the tty driver wakeup if it has a special write_wakeup function */
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-                      (tty->ldisc.write_wakeup)(tty);
+-              }
+-
+-              /* tell the tty driver that something has changed */
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+ 
+       // Release the Write URB
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/io_ti.c linux-2.6.8/drivers/usb/serial/io_ti.c
+--- linux-2.6.8.orig/drivers/usb/serial/io_ti.c        2004-08-13 22:38:08.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/io_ti.c     2004-11-28 04:49:43.000000000 -0800
+@@ -1804,12 +1804,7 @@
+       tty = port->tty;
+       if (tty) {
+               /* let the tty driver wakeup if it has a special write_wakeup function */
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-                      (tty->ldisc.write_wakeup)(tty);
+-              }
+-
+-              /* tell the tty driver that something has changed */
+-              wake_up_interruptible(&tty->write_wait);
++              tty_wakeup(tty);
+       }
+ }
+ 
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/ir-usb.c linux-2.6.8/drivers/usb/serial/ir-usb.c
+--- linux-2.6.8.orig/drivers/usb/serial/ir-usb.c       2004-08-13 22:36:57.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/ir-usb.c    2004-11-28 04:49:43.000000000 -0800
+@@ -449,6 +449,10 @@
+                        */
+                       tty = port->tty;
+ 
++                      /*
++                       *      FIXME: must not do this in IRQ context,
++                       *      must honour TTY_DONT_FLIP
++                       */
+                       tty->ldisc.receive_buf(
+                               tty,
+                               data+1,
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/keyspan_pda.c linux-2.6.8/drivers/usb/serial/keyspan_pda.c
+--- linux-2.6.8.orig/drivers/usb/serial/keyspan_pda.c  2004-08-13 22:37:25.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/keyspan_pda.c       2004-11-28 04:49:43.000000000 -0800
+@@ -186,13 +186,7 @@
+       wake_up_interruptible( &port->write_wait );
+ 
+       /* wake up line discipline */
+-      if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-      && tty->ldisc.write_wakeup )
+-              (tty->ldisc.write_wakeup)(tty);
+-
+-      /* wake up other tty processes */
+-      wake_up_interruptible( &tty->write_wait );
+-      /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
++      tty_wakeup(tty);
+ }
+ 
+ static void keyspan_pda_request_unthrottle( struct usb_serial *serial )
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/mct_u232.c linux-2.6.8/drivers/usb/serial/mct_u232.c
+--- linux-2.6.8.orig/drivers/usb/serial/mct_u232.c     2004-08-13 22:37:25.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/mct_u232.c  2004-11-28 04:49:43.000000000 -0800
+@@ -579,11 +579,7 @@
+ 
+       if (write_blocking) {
+               wake_up_interruptible(&port->write_wait);
+-              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                  tty->ldisc.write_wakeup)
+-                      (tty->ldisc.write_wakeup)(tty);
+-              wake_up_interruptible(&tty->write_wait);
+-              
++              tty_wakeup(tty);
+       } else {
+               /* from generic_write_bulk_callback */
+               schedule_work(&port->work);
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/usb-serial.c linux-2.6.8/drivers/usb/serial/usb-serial.c
+--- linux-2.6.8.orig/drivers/usb/serial/usb-serial.c   2004-08-13 22:38:11.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/usb-serial.c        2004-11-28 04:49:43.000000000 -0800
+@@ -755,12 +755,7 @@
+       if (!tty)
+               return;
+ 
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-              dbg("%s - write wakeup call.", __FUNCTION__);
+-              (tty->ldisc.write_wakeup)(tty);
+-      }
+-
+-      wake_up_interruptible(&tty->write_wait);
++      tty_wakeup(tty);
+ }
+ 
+ static void destroy_serial(struct kref *kref)
+diff -Naur linux-2.6.8.orig/drivers/usb/serial/whiteheat.c linux-2.6.8/drivers/usb/serial/whiteheat.c
+--- linux-2.6.8.orig/drivers/usb/serial/whiteheat.c    2004-08-13 22:36:45.000000000 -0700
++++ linux-2.6.8/drivers/usb/serial/whiteheat.c 2004-11-28 04:49:43.000000000 -0800
+@@ -668,8 +668,7 @@
+ 
+       if (port->tty->driver->flush_buffer)
+               port->tty->driver->flush_buffer(port->tty);
+-      if (port->tty->ldisc.flush_buffer)
+-              port->tty->ldisc.flush_buffer(port->tty);
++      tty_ldisc_flush(port->tty);
+ 
+       firm_report_tx_done(port);
+ 
+diff -Naur linux-2.6.8.orig/fs/proc/proc_tty.c linux-2.6.8/fs/proc/proc_tty.c
+--- linux-2.6.8.orig/fs/proc/proc_tty.c        2004-08-13 22:36:56.000000000 -0700
++++ linux-2.6.8/fs/proc/proc_tty.c     2004-11-28 04:49:43.000000000 -0800
+@@ -15,9 +15,6 @@
+ #include <linux/seq_file.h>
+ #include <asm/bitops.h>
+ 
+-extern struct tty_ldisc ldiscs[];
+-
+-
+ static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
+                               int count, int *eof, void *data);
+ 
+@@ -159,12 +156,15 @@
+       int     i;
+       int     len = 0;
+       off_t   begin = 0;
+-
++      struct tty_ldisc *ld;
++      
+       for (i=0; i < NR_LDISCS; i++) {
+-              if (!(ldiscs[i].flags & LDISC_FLAG_DEFINED))
++              ld = tty_ldisc_get(i);
++              if (ld == NULL)
+                       continue;
+               len += sprintf(page+len, "%-10s %2d\n",
+-                             ldiscs[i].name ? ldiscs[i].name : "???", i);
++                             ld->name ? ld->name : "???", i);
++              tty_ldisc_put(i);
+               if (len+begin > off+count)
+                       break;
+               if (len+begin < off) {
+diff -Naur linux-2.6.8.orig/include/linux/tty.h linux-2.6.8/include/linux/tty.h
+--- linux-2.6.8.orig/include/linux/tty.h       2004-08-13 22:36:58.000000000 -0700
++++ linux-2.6.8/include/linux/tty.h    2004-11-28 04:49:43.000000000 -0800
+@@ -306,26 +306,27 @@
+  * tty->write.  Thus, you must use the inline functions set_bit() and
+  * clear_bit() to make things atomic.
+  */
+-#define TTY_THROTTLED 0
+-#define TTY_IO_ERROR 1
+-#define TTY_OTHER_CLOSED 2
+-#define TTY_EXCLUSIVE 3
+-#define TTY_DEBUG 4
+-#define TTY_DO_WRITE_WAKEUP 5
+-#define TTY_PUSH 6
+-#define TTY_CLOSING 7
+-#define TTY_DONT_FLIP 8
+-#define TTY_HW_COOK_OUT 14
+-#define TTY_HW_COOK_IN 15
+-#define TTY_PTY_LOCK 16
+-#define TTY_NO_WRITE_SPLIT 17
++#define TTY_THROTTLED                 0       /* Call unthrottle() at threshold min */
++#define TTY_IO_ERROR          1       /* Canse an I/O error (may be no ldisc too) */
++#define TTY_OTHER_CLOSED      2       /* Other side (if any) has closed */
++#define TTY_EXCLUSIVE                 3       /* Exclusive open mode */
++#define TTY_DEBUG             4       /* Debugging */
++#define TTY_DO_WRITE_WAKEUP   5       /* Call write_wakeup after queuing new */
++#define TTY_PUSH              6       /* n_tty private */
++#define TTY_CLOSING           7       /* ->close() in progress */
++#define TTY_DONT_FLIP                 8       /* Defer buffer flip */
++#define TTY_LDISC             9       /* Line discipline attached */
++#define TTY_HW_COOK_OUT       14      /* Hardware can do output cooking */
++#define TTY_HW_COOK_IN                15      /* Hardware can do input cooking */
++#define TTY_PTY_LOCK          16      /* pty private */
++#define TTY_NO_WRITE_SPLIT    17      /* Preserve write boundaries to driver */
++#define TTY_HUPPED            18      /* Post driver->hangup() */
+ 
+ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
+ 
+ extern void tty_write_flush(struct tty_struct *);
+ 
+ extern struct termios tty_std_termios;
+-extern struct tty_ldisc ldiscs[];
+ extern int fg_console, last_console, want_console;
+ 
+ extern int kmsg_redirect;
+@@ -362,6 +363,16 @@
+ extern int tty_get_baud_rate(struct tty_struct *tty);
+ extern int tty_termios_baud_rate(struct termios *termios);
+ 
++extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *);
++extern void tty_ldisc_deref(struct tty_ldisc *);
++extern struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *);
++
++extern struct tty_ldisc *tty_ldisc_get(int);
++extern void tty_ldisc_put(int);
++
++extern void tty_wakeup(struct tty_struct *tty);
++extern void tty_ldisc_flush(struct tty_struct *tty);
++
+ struct semaphore;
+ extern struct semaphore tty_sem;
+ 
+diff -Naur linux-2.6.8.orig/include/linux/tty_ldisc.h linux-2.6.8/include/linux/tty_ldisc.h
+--- linux-2.6.8.orig/include/linux/tty_ldisc.h 2004-08-13 22:38:09.000000000 -0700
++++ linux-2.6.8/include/linux/tty_ldisc.h      2004-11-28 04:49:43.000000000 -0800
+@@ -95,6 +95,13 @@
+  *    that line discpline should try to send more characters to the
+  *    low-level driver for transmission.  If the line discpline does
+  *    not have any more data to send, it can just return.
++ *
++ * int (*hangup)(struct tty_struct *)
++ *
++ *    Called on a hangup. Tells the discipline that it should
++ *    cease I/O to the tty driver. Can sleep. The driver should
++ *    seek to perform this action quickly but should wait until
++ *    any pending driver I/O is completed.
+  */
+ 
+ #include <linux/fs.h>
+@@ -122,6 +129,7 @@
+       void    (*set_termios)(struct tty_struct *tty, struct termios * old);
+       unsigned int (*poll)(struct tty_struct *, struct file *,
+                            struct poll_table_struct *);
++      int     (*hangup)(struct tty_struct *tty);
+       
+       /*
+        * The following routines are called from below.
+@@ -132,6 +140,8 @@
+       void    (*write_wakeup)(struct tty_struct *);
+ 
+       struct  module *owner;
++      
++      int refcount;
+ };
+ 
+ #define TTY_LDISC_MAGIC       0x5403
diff --git a/sys-kernel/hppa-dev-sources/files/CAN-2004-1074.patch b/sys-kernel/hppa-dev-sources/files/CAN-2004-1074.patch
new file mode 100644 (file)
index 0000000..53ca070
--- /dev/null
@@ -0,0 +1,205 @@
+diff -urNp -X /usr/src/dontdiff linux-2.6.8-gentoo-r11/arch/ia64/ia32/binfmt_elf32.c linux-dsd/arch/ia64/ia32/binfmt_elf32.c
+--- linux-2.6.8-gentoo-r11/arch/ia64/ia32/binfmt_elf32.c       2004-08-14 06:37:42.000000000 +0100
++++ linux-dsd/arch/ia64/ia32/binfmt_elf32.c    2004-12-03 01:22:18.416099008 +0000
+@@ -84,7 +84,11 @@ ia64_elf32_init (struct pt_regs *regs)
+               vma->vm_ops = &ia32_shared_page_vm_ops;
+               down_write(&current->mm->mmap_sem);
+               {
+-                      insert_vm_struct(current->mm, vma);
++                      if (insert_vm_struct(current->mm, vma)) {
++                              kmem_cache_free(vm_area_cachep, vma);
++                              up_write(&current->mm->mmap_sem);
++                              return;
++                      }
+               }
+               up_write(&current->mm->mmap_sem);
+       }
+@@ -103,7 +107,11 @@ ia64_elf32_init (struct pt_regs *regs)
+               vma->vm_flags = VM_READ|VM_WRITE|VM_MAYREAD|VM_MAYWRITE;
+               down_write(&current->mm->mmap_sem);
+               {
+-                      insert_vm_struct(current->mm, vma);
++                      if (insert_vm_struct(current->mm, vma)) {
++                              kmem_cache_free(vm_area_cachep, vma);
++                              up_write(&current->mm->mmap_sem);
++                              return;
++                      }
+               }
+               up_write(&current->mm->mmap_sem);
+       }
+@@ -151,7 +159,7 @@ ia32_setup_arg_pages (struct linux_binpr
+       unsigned long stack_base;
+       struct vm_area_struct *mpnt;
+       struct mm_struct *mm = current->mm;
+-      int i;
++      int i, ret;
+ 
+       stack_base = IA32_STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
+       mm->arg_start = bprm->p + stack_base;
+@@ -186,7 +194,11 @@ ia32_setup_arg_pages (struct linux_binpr
+                       mpnt->vm_flags = VM_STACK_FLAGS;
+               mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC)?
+                                       PAGE_COPY_EXEC: PAGE_COPY;
+-              insert_vm_struct(current->mm, mpnt);
++              if ((ret = insert_vm_struct(current->mm, mpnt))) {
++                      up_write(&current->mm->mmap_sem);
++                      kmem_cache_free(vm_area_cachep, mpnt);
++                      return ret;
++              }
+               current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+       }
+ 
+diff -urNp -X /usr/src/dontdiff linux-2.6.8-gentoo-r11/arch/ia64/mm/init.c linux-dsd/arch/ia64/mm/init.c
+--- linux-2.6.8-gentoo-r11/arch/ia64/mm/init.c 2004-08-14 06:36:56.000000000 +0100
++++ linux-dsd/arch/ia64/mm/init.c      2004-12-03 01:20:32.714168144 +0000
+@@ -131,7 +131,13 @@ ia64_init_addr_space (void)
+               vma->vm_end = vma->vm_start + PAGE_SIZE;
+               vma->vm_page_prot = protection_map[VM_DATA_DEFAULT_FLAGS & 0x7];
+               vma->vm_flags = VM_DATA_DEFAULT_FLAGS | VM_GROWSUP;
+-              insert_vm_struct(current->mm, vma);
++              down_write(&current->mm->mmap_sem);
++              if (insert_vm_struct(current->mm, vma)) {
++                      up_write(&current->mm->mmap_sem);
++                      kmem_cache_free(vm_area_cachep, vma);
++                      return;
++              }
++              up_write(&current->mm->mmap_sem);
+       }
+ 
+       /* map NaT-page at address zero to speed up speculative dereferencing of NULL: */
+@@ -143,7 +149,13 @@ ia64_init_addr_space (void)
+                       vma->vm_end = PAGE_SIZE;
+                       vma->vm_page_prot = __pgprot(pgprot_val(PAGE_READONLY) | _PAGE_MA_NAT);
+                       vma->vm_flags = VM_READ | VM_MAYREAD | VM_IO | VM_RESERVED;
+-                      insert_vm_struct(current->mm, vma);
++                      down_write(&current->mm->mmap_sem);
++                      if (insert_vm_struct(current->mm, vma)) {
++                              up_write(&current->mm->mmap_sem);
++                              kmem_cache_free(vm_area_cachep, vma);
++                              return;
++                      }
++                      up_write(&current->mm->mmap_sem);
+               }
+       }
+ }
+diff -urNp -X /usr/src/dontdiff linux-2.6.8-gentoo-r11/arch/s390/kernel/compat_exec.c linux-dsd/arch/s390/kernel/compat_exec.c
+--- linux-2.6.8-gentoo-r11/arch/s390/kernel/compat_exec.c      2004-08-14 06:37:40.000000000 +0100
++++ linux-dsd/arch/s390/kernel/compat_exec.c   2004-12-03 01:23:39.196818472 +0000
+@@ -39,7 +39,7 @@ int setup_arg_pages32(struct linux_binpr
+       unsigned long stack_base;
+       struct vm_area_struct *mpnt;
+       struct mm_struct *mm = current->mm;
+-      int i;
++      int i, ret;
+ 
+       stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
+       mm->arg_start = bprm->p + stack_base;
+@@ -68,7 +68,11 @@ int setup_arg_pages32(struct linux_binpr
+               /* executable stack setting would be applied here */
+               mpnt->vm_page_prot = PAGE_COPY;
+               mpnt->vm_flags = VM_STACK_FLAGS;
+-              insert_vm_struct(mm, mpnt);
++              if ((ret = insert_vm_struct(mm, mpnt))) {
++                      up_write(&mm->mmap_sem);
++                      kmem_cache_free(vm_area_cachep, mpnt);
++                      return ret;
++              }
+               mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+       } 
+ 
+diff -urNp -X /usr/src/dontdiff linux-2.6.8-gentoo-r11/arch/x86_64/ia32/ia32_binfmt.c linux-dsd/arch/x86_64/ia32/ia32_binfmt.c
+--- linux-2.6.8-gentoo-r11/arch/x86_64/ia32/ia32_binfmt.c      2004-08-14 06:36:12.000000000 +0100
++++ linux-dsd/arch/x86_64/ia32/ia32_binfmt.c   2004-12-03 01:25:24.771768640 +0000
+@@ -330,7 +330,7 @@ int setup_arg_pages(struct linux_binprm 
+       unsigned long stack_base;
+       struct vm_area_struct *mpnt;
+       struct mm_struct *mm = current->mm;
+-      int i;
++      int i, ret;
+ 
+       stack_base = IA32_STACK_TOP - MAX_ARG_PAGES * PAGE_SIZE;
+       mm->arg_start = bprm->p + stack_base;
+@@ -364,7 +364,11 @@ int setup_arg_pages(struct linux_binprm 
+                       mpnt->vm_flags = vm_stack_flags32;
+               mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? 
+                       PAGE_COPY_EXEC : PAGE_COPY;
+-              insert_vm_struct(mm, mpnt);
++              if ((ret = insert_vm_struct(mm, mpnt))) {
++                      up_write(&mm->mmap_sem);
++                      kmem_cache_free(vm_area_cachep, mpnt);
++                      return ret;
++              }
+               mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+       } 
+ 
+diff -urNp -X /usr/src/dontdiff linux-2.6.8-gentoo-r11/fs/exec.c linux-dsd/fs/exec.c
+--- linux-2.6.8-gentoo-r11/fs/exec.c   2004-12-03 01:13:58.502097488 +0000
++++ linux-dsd/fs/exec.c        2004-12-03 01:26:47.749154160 +0000
+@@ -341,7 +341,7 @@ int setup_arg_pages(struct linux_binprm 
+       unsigned long stack_base;
+       struct vm_area_struct *mpnt;
+       struct mm_struct *mm = current->mm;
+-      int i;
++      int i, ret;
+       long arg_size;
+ 
+ #ifdef CONFIG_STACK_GROWSUP
+@@ -412,7 +412,6 @@ int setup_arg_pages(struct linux_binprm 
+ 
+       down_write(&mm->mmap_sem);
+       {
+-              struct vm_area_struct *vma;
+               mpnt->vm_mm = mm;
+ #ifdef CONFIG_STACK_GROWSUP
+               mpnt->vm_start = stack_base;
+@@ -433,13 +432,11 @@ int setup_arg_pages(struct linux_binprm 
+                       mpnt->vm_flags = VM_STACK_FLAGS;
+               mpnt->vm_flags |= mm->def_flags;
+               mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7];
+-              vma = find_vma(mm, mpnt->vm_start);
+-              if (vma) {
++              if ((ret = insert_vm_struct(mm, mpnt))) {
+                       up_write(&mm->mmap_sem);
+                       kmem_cache_free(vm_area_cachep, mpnt);
+-                      return -ENOMEM;
++                      return ret;
+               }
+-              insert_vm_struct(mm, mpnt);
+               mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+       }
+ 
+diff -urNp -X /usr/src/dontdiff linux-2.6.8-gentoo-r11/include/linux/mm.h linux-dsd/include/linux/mm.h
+--- linux-2.6.8-gentoo-r11/include/linux/mm.h  2004-08-14 06:36:13.000000000 +0100
++++ linux-dsd/include/linux/mm.h       2004-12-03 01:20:32.718167536 +0000
+@@ -624,7 +624,7 @@ extern struct vm_area_struct *vma_merge(
+ extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
+ extern int split_vma(struct mm_struct *,
+       struct vm_area_struct *, unsigned long addr, int new_below);
+-extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
++extern int insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
+ extern void __vma_link_rb(struct mm_struct *, struct vm_area_struct *,
+       struct rb_node **, struct rb_node *);
+ extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
+diff -urNp -X /usr/src/dontdiff linux-2.6.8-gentoo-r11/mm/mmap.c linux-dsd/mm/mmap.c
+--- linux-2.6.8-gentoo-r11/mm/mmap.c   2004-08-14 06:37:15.000000000 +0100
++++ linux-dsd/mm/mmap.c        2004-12-03 01:20:32.720167232 +0000
+@@ -1740,7 +1740,7 @@ void exit_mmap(struct mm_struct *mm)
+  * and into the inode's i_mmap tree.  If vm_file is non-NULL
+  * then i_mmap_lock is taken here.
+  */
+-void insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
++int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
+ {
+       struct vm_area_struct * __vma, * prev;
+       struct rb_node ** rb_link, * rb_parent;
+@@ -1763,8 +1763,9 @@ void insert_vm_struct(struct mm_struct *
+       }
+       __vma = find_vma_prepare(mm,vma->vm_start,&prev,&rb_link,&rb_parent);
+       if (__vma && __vma->vm_start < vma->vm_end)
+-              BUG();
++              return -ENOMEM;
+       vma_link(mm, vma, prev, rb_link, rb_parent);
++      return 0;
+ }
+ 
+ /*
diff --git a/sys-kernel/hppa-dev-sources/files/digest-hppa-dev-sources-2.6.8.1_p11-r2 b/sys-kernel/hppa-dev-sources/files/digest-hppa-dev-sources-2.6.8.1_p11-r2
new file mode 100644 (file)
index 0000000..4571cc1
--- /dev/null
@@ -0,0 +1,2 @@
+MD5 9517ca999e822b898fbdc7e72796b1aa linux-2.6.8.1.tar.bz2 35628066
+MD5 7d2bce70b2d231234bef64dbebb33385 patch-2.6.8.1-pa11.gz 89416
diff --git a/sys-kernel/hppa-dev-sources/hppa-dev-sources-2.6.8.1_p11-r2.ebuild b/sys-kernel/hppa-dev-sources/hppa-dev-sources-2.6.8.1_p11-r2.ebuild
new file mode 100644 (file)
index 0000000..1f397bb
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright 1999-2004 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-x86/sys-kernel/hppa-dev-sources/hppa-dev-sources-2.6.8.1_p11-r2.ebuild,v 1.1 2004/12/08 17:10:46 gmsoft Exp $
+#OKV=original kernel version, KV=patched kernel version.  They can be the same.
+
+ETYPE="sources"
+inherit kernel-2 eutils
+OKV="${PV/_p*/}"
+PATCH_LEVEL="${PV/${OKV}_p/}"
+EXTRAVERSION="-pa${PATCH_LEVEL}"
+[ ! "${PR}" = "r0" ] && EXTRAVERSION="${EXTRAVERSION}-${PR}"
+KV=${OKV}${EXTRAVERSION}
+S=${WORKDIR}/linux-${KV}
+
+
+DESCRIPTION="Full sources for the Linux kernel with patch for hppa"
+SRC_URI="mirror://kernel/linux/kernel/v2.6/linux-${OKV}.tar.bz2 http://ftp.parisc-linux.org/cvs/linux-2.6/patch-${OKV}-pa${PATCH_LEVEL}.gz"
+HOMEPAGE="http://www.kernel.org/ http://www.gentoo.org/ http://parisc-linux.org"
+KEYWORDS="hppa -*"
+IUSE=""
+SLOT="${KV}"
+
+
+src_unpack() {
+       unpack linux-${OKV}.tar.bz2
+       mv ${WORKDIR}/linux-${OKV} ${WORKDIR}/linux-${KV}
+       cd ${S}
+
+       epatch ${DISTDIR}/patch-${OKV}-pa${PATCH_LEVEL}.gz
+       epatch ${FILESDIR}/security-proc-cmdline.patch
+       epatch ${FILESDIR}/binfmt_elf-loader-security.patch
+       epatch ${FILESDIR}/CAN-2004-0883.patch
+       epatch ${FILESDIR}/ptmx-security.patch
+       epatch ${FILESDIR}/CAN-2004-0814-r1.patch
+       epatch ${FILESDIR}/AF_UNIX-security.patch
+       epatch ${FILESDIR}/CAN-2004-1074.patch
+
+
+}