diff -Naur linux-2.6.7/Documentation/tty.txt linux-2.6.7-1140_CAN-2004-0814.fixed/Documentation/tty.txt --- linux-2.6.7/Documentation/tty.txt 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/Documentation/tty.txt 2004-11-23 13:23:22.000000000 -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.7/drivers/bluetooth/hci_ldisc.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/bluetooth/hci_ldisc.c --- linux-2.6.7/drivers/bluetooth/hci_ldisc.c 2004-06-15 22:19:44.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/bluetooth/hci_ldisc.c 2004-11-23 13:23:22.991451000 -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.7/drivers/char/amiserial.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/amiserial.c --- linux-2.6.7/drivers/char/amiserial.c 2004-06-15 22:20:26.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/amiserial.c 2004-11-23 13:23:22.998450000 -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.7/drivers/char/cyclades.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/cyclades.c --- linux-2.6.7/drivers/char/cyclades.c 2004-06-15 22:19:01.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/cyclades.c 2004-11-23 13:23:23.014448000 -0800 @@ -972,10 +972,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 @@ -2852,8 +2849,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; @@ -4555,10 +4551,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.7/drivers/char/epca.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/epca.c --- linux-2.6.7/drivers/char/epca.c 2004-06-15 22:19:36.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/epca.c 2004-11-23 13:23:23.026446000 -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; @@ -1129,8 +1124,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 */ @@ -2271,9 +2265,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 */ @@ -2290,9 +2282,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); @@ -3157,6 +3147,7 @@ } else { + /* ldisc lock already held in ioctl */ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); } diff -Naur linux-2.6.7/drivers/char/esp.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/esp.c --- linux-2.6.7/drivers/char/esp.c 2004-06-15 22:18:38.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/esp.c 2004-11-23 13:23:23.033445000 -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); } /* @@ -2073,8 +2067,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.7/drivers/char/generic_serial.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/generic_serial.c --- linux-2.6.7/drivers/char/generic_serial.c 2004-06-15 22:19:23.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/generic_serial.c 2004-11-23 13:23:23.037444000 -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.7/drivers/char/hvc_console.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/hvc_console.c --- linux-2.6.7/drivers/char/hvc_console.c 2004-06-15 22:19:52.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/hvc_console.c 2004-11-23 13:23:23.039444000 -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.7/drivers/char/isicom.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/isicom.c --- linux-2.6.7/drivers/char/isicom.c 2004-06-15 22:18:55.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/isicom.c 2004-11-23 13:23:23.045443000 -0800 @@ -483,10 +483,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); } @@ -1118,8 +1116,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 = 0; 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.7/drivers/char/moxa.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/moxa.c --- linux-2.6.7/drivers/char/moxa.c 2004-06-15 22:20:03.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/moxa.c 2004-11-23 13:23:23.054441000 -0800 @@ -613,8 +613,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 = 0; @@ -688,9 +688,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); } @@ -948,9 +946,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); } } @@ -1117,9 +1113,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.7/drivers/char/mxser.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/mxser.c --- linux-2.6.7/drivers/char/mxser.c 2004-06-15 22:19:42.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/mxser.c 2004-11-23 13:23:23.061440000 -0800 @@ -676,9 +676,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)) { @@ -815,8 +813,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 = 0; @@ -974,9 +972,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.7/drivers/char/n_tty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/n_tty.c --- linux-2.6.7/drivers/char/n_tty.c 2004-06-15 22:18:52.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/n_tty.c 2004-11-23 13:23:23.071439000 -0800 @@ -104,11 +104,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 && @@ -117,10 +124,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) { @@ -134,9 +144,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 */ @@ -151,9 +171,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; @@ -171,20 +196,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; @@ -244,10 +296,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) { @@ -309,13 +371,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) { @@ -336,6 +412,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; @@ -468,6 +554,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) @@ -479,6 +577,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)) @@ -495,19 +603,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) { @@ -525,6 +654,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; @@ -716,6 +855,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; @@ -734,10 +883,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) @@ -750,6 +902,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) { @@ -833,6 +998,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) @@ -848,7 +1025,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)) @@ -884,7 +1060,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 { @@ -898,6 +1073,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); @@ -907,11 +1092,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) @@ -937,14 +1133,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) @@ -975,25 +1180,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 @@ -1011,7 +1209,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) { @@ -1033,6 +1272,9 @@ } } + /* + * Internal serialization of reads. + */ if (file->f_flags & O_NONBLOCK) { if (down_trylock(&tty->atomic_read)) return -EAGAIN; @@ -1182,6 +1424,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) { @@ -1251,7 +1508,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; @@ -1292,6 +1567,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.7/drivers/char/pcmcia/synclink_cs.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/pcmcia/synclink_cs.c --- linux-2.6.7/drivers/char/pcmcia/synclink_cs.c 2004-06-15 22:19:13.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/pcmcia/synclink_cs.c 2004-11-23 13:23:23.082437000 -0800 @@ -526,6 +526,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; @@ -980,13 +1014,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); } } @@ -1871,11 +1899,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 @@ -2584,9 +2610,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); @@ -4055,11 +4080,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.7/drivers/char/pcxx.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/pcxx.c --- linux-2.6.7/drivers/char/pcxx.c 2004-06-15 22:19:36.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/pcxx.c 2004-11-23 13:23:23.090436000 -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.7/drivers/char/pty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/pty.c --- linux-2.6.7/drivers/char/pty.c 2004-06-15 22:19:10.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/pty.c 2004-11-23 13:23:23.092436000 -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.7/drivers/char/riscom8.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/riscom8.c --- linux-2.6.7/drivers/char/riscom8.c 2004-06-15 22:19:22.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/riscom8.c 2004-11-23 13:23:23.098435000 -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 = 0; @@ -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) @@ -1643,9 +1641,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.7/drivers/char/rocket.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/rocket.c --- linux-2.6.7/drivers/char/rocket.c 2004-06-15 22:19:42.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/rocket.c 2004-11-23 13:23:23.108433000 -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]); @@ -1730,8 +1734,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); @@ -1805,8 +1808,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.7/drivers/char/selection.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/selection.c --- linux-2.6.7/drivers/char/selection.c 2004-06-15 22:20:04.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/selection.c 2004-11-23 13:23:23.110433000 -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.7/drivers/char/ser_a2232.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/ser_a2232.c --- linux-2.6.7/drivers/char/ser_a2232.c 2004-06-15 22:19:03.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/ser_a2232.c 2004-11-23 13:23:23.113432000 -0800 @@ -603,10 +603,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.7/drivers/char/serial167.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/serial167.c --- linux-2.6.7/drivers/char/serial167.c 2004-06-15 22:19:22.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/serial167.c 2004-11-23 13:23:23.121431000 -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.7/drivers/char/serial_tx3912.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/serial_tx3912.c --- linux-2.6.7/drivers/char/serial_tx3912.c 2004-06-15 22:19:26.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/serial_tx3912.c 2004-11-23 13:23:23.125431000 -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.7/drivers/char/specialix.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/specialix.c --- linux-2.6.7/drivers/char/specialix.c 2004-06-15 22:19:53.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/specialix.c 2004-11-23 13:23:23.131430000 -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 = 0; @@ -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); } @@ -2064,12 +2061,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.7/drivers/char/stallion.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/stallion.c --- linux-2.6.7/drivers/char/stallion.c 2004-06-15 22:19:44.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/stallion.c 2004-11-23 13:23:23.144428000 -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; @@ -1818,10 +1817,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); } /*****************************************************************************/ @@ -2202,10 +2198,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.7/drivers/char/sx.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/sx.c --- linux-2.6.7/drivers/char/sx.c 2004-06-15 22:19:13.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/sx.c 2004-11-23 13:23:23.152427000 -0800 @@ -1044,12 +1044,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.7/drivers/char/synclink.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/synclink.c --- linux-2.6.7/drivers/char/synclink.c 2004-06-15 22:19:13.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/synclink.c 2004-11-23 13:23:23.173423000 -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.7/drivers/char/synclinkmp.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/synclinkmp.c --- linux-2.6.7/drivers/char/synclinkmp.c 2004-06-15 22:19:01.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/synclinkmp.c 2004-11-23 13:23:23.190421000 -0800 @@ -718,6 +718,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. @@ -865,8 +888,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); @@ -1271,9 +1293,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 @@ -1936,13 +1956,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); } } @@ -4864,15 +4878,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.7/drivers/char/tty_io.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/tty_io.c --- linux-2.6.7/drivers/char/tty_io.c 2004-06-15 22:19:36.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/tty_io.c 2004-11-23 13:23:23.207418000 -0800 @@ -91,6 +91,7 @@ #include #include #include +#include #include #include @@ -119,11 +120,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 */ @@ -220,65 +227,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 */ /* Cyrus Durgin */ - 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); @@ -288,12 +554,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; } @@ -402,6 +683,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 @@ -413,6 +741,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; @@ -431,6 +760,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; @@ -443,21 +773,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); @@ -466,22 +800,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; @@ -514,6 +845,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); @@ -630,9 +972,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); } @@ -644,6 +986,7 @@ int i; struct tty_struct * tty; struct inode *inode; + struct tty_ldisc *ld; /* Can't seek (pread) on ttys. */ if (ppos != &file->f_pos) @@ -656,11 +999,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; @@ -722,6 +1069,8 @@ { struct tty_struct * tty; struct inode *inode = file->f_dentry->d_inode; + ssize_t ret; + struct tty_ldisc *ld; /* Can't seek (pwrite) on ttys. */ if (ppos != &file->f_pos) @@ -732,10 +1081,16 @@ 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, @@ -924,6 +1279,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) @@ -936,7 +1292,9 @@ (tty->ldisc.close)(tty); goto release_mem_out; } + tty_ldisc_enable(o_tty); } + tty_ldisc_enable(tty); goto success; /* @@ -965,6 +1323,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; @@ -1067,6 +1428,7 @@ int pty_master, tty_closing, o_tty_closing, do_sleep; 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")) @@ -1141,7 +1503,6 @@ } } #endif - if (tty->driver->close) tty->driver->close(tty, filp); @@ -1268,8 +1629,10 @@ /* * 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); @@ -1278,21 +1641,42 @@ */ 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); } /* @@ -1454,14 +1838,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) @@ -1493,12 +1881,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; } @@ -1747,6 +2138,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")) @@ -1836,6 +2228,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); @@ -1874,16 +2267,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; } @@ -1918,14 +2314,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) { @@ -1977,24 +2380,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; @@ -2013,7 +2421,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; + } /* @@ -2035,9 +2467,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; @@ -2047,12 +2490,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); @@ -2071,6 +2522,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) @@ -2088,7 +2550,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.7/drivers/char/tty_ioctl.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/tty_ioctl.c --- linux-2.6.7/drivers/char/tty_ioctl.c 2004-06-15 22:20:26.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/tty_ioctl.c 2004-11-23 13:24:43.123269616 -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) && @@ -136,13 +149,19 @@ 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.7/drivers/char/viocons.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/viocons.c --- linux-2.6.7/drivers/char/viocons.c 2004-06-15 22:18:37.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/viocons.c 2004-11-23 13:23:23.216417000 -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.7/drivers/char/vme_scc.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/vme_scc.c --- linux-2.6.7/drivers/char/vme_scc.c 2004-06-15 22:18:50.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/vme_scc.c 2004-11-23 13:23:23.219416000 -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.7/drivers/char/vt_ioctl.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/vt_ioctl.c --- linux-2.6.7/drivers/char/vt_ioctl.c 2004-06-15 22:19:42.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/char/vt_ioctl.c 2004-11-23 13:23:23.223416000 -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.7/drivers/isdn/capi/capi.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/isdn/capi/capi.c --- linux-2.6.7/drivers/isdn/capi/capi.c 2004-06-15 22:20:04.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/isdn/capi/capi.c 2004-11-23 13:23:23.229415000 -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, 0, 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.7/drivers/isdn/i4l/isdn_tty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/isdn/i4l/isdn_tty.c --- linux-2.6.7/drivers/isdn/i4l/isdn_tty.c 2004-06-15 22:19:43.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/isdn/i4l/isdn_tty.c 2004-11-23 13:23:23.239413000 -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 @@ -1749,8 +1741,7 @@ 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 = 0; info->ncarrier = 0; tty->closing = 0; @@ -2681,8 +2672,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.7/drivers/macintosh/macserial.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/macintosh/macserial.c --- linux-2.6.7/drivers/macintosh/macserial.c 2004-06-15 22:20:04.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/macintosh/macserial.c 2004-11-23 13:23:23.247412000 -0800 @@ -717,12 +717,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) @@ -1570,10 +1566,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); } /* @@ -2000,8 +1993,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.7/drivers/net/hamradio/mkiss.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/hamradio/mkiss.c --- linux-2.6.7/drivers/net/hamradio/mkiss.c 2004-06-15 22:20:04.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/hamradio/mkiss.c 2004-11-23 13:23:23.251411000 -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.7/drivers/net/irda/irtty-sir.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/irda/irtty-sir.c --- linux-2.6.7/drivers/net/irda/irtty-sir.c 2004-06-15 22:18:37.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/irda/irtty-sir.c 2004-11-23 13:23:23.253411000 -0800 @@ -517,13 +517,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.7/drivers/net/ppp_async.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/ppp_async.c --- linux-2.6.7/drivers/net/ppp_async.c 2004-06-15 22:19:43.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/ppp_async.c 2004-11-23 13:23:23.257411000 -0800 @@ -121,6 +121,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; @@ -143,7 +146,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) @@ -254,6 +258,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) @@ -749,7 +758,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) @@ -849,7 +859,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.7/drivers/net/ppp_synctty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/ppp_synctty.c --- linux-2.6.7/drivers/net/ppp_synctty.c 2004-06-15 22:19:22.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/ppp_synctty.c 2004-11-23 13:23:23.260410000 -0800 @@ -172,6 +172,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.7/drivers/net/slip.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/slip.c --- linux-2.6.7/drivers/net/slip.c 2004-06-15 22:20:26.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/slip.c 2004-11-23 13:23:23.265409000 -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.7/drivers/net/wan/pc300_tty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/wan/pc300_tty.c --- linux-2.6.7/drivers/net/wan/pc300_tty.c 2004-06-15 22:19:22.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/wan/pc300_tty.c 2004-11-23 13:23:23.270409000 -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.7/drivers/net/wan/sdla_chdlc.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/wan/sdla_chdlc.c --- linux-2.6.7/drivers/net/wan/sdla_chdlc.c 2004-06-15 22:19:23.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/wan/sdla_chdlc.c 2004-11-23 13:23:23.281407000 -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.7/drivers/net/wireless/strip.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/wireless/strip.c --- linux-2.6.7/drivers/net/wireless/strip.c 2004-06-15 22:19:43.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/net/wireless/strip.c 2004-11-23 13:23:23.288406000 -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.7/drivers/s390/char/con3215.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/char/con3215.c --- linux-2.6.7/drivers/s390/char/con3215.c 2004-06-15 22:19:42.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/char/con3215.c 2004-11-23 13:23:23.293405000 -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.7/drivers/s390/char/sclp_tty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/char/sclp_tty.c --- linux-2.6.7/drivers/s390/char/sclp_tty.c 2004-06-15 22:18:58.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/char/sclp_tty.c 2004-11-23 13:23:23.296405000 -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.7/drivers/s390/char/sclp_vt220.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/char/sclp_vt220.c --- linux-2.6.7/drivers/s390/char/sclp_vt220.c 2004-06-15 22:19:09.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/char/sclp_vt220.c 2004-11-23 13:23:23.299404000 -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.7/drivers/s390/net/ctctty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/net/ctctty.c --- linux-2.6.7/drivers/s390/net/ctctty.c 2004-06-15 22:19:35.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/s390/net/ctctty.c 2004-11-23 13:23:23.303404000 -0800 @@ -300,10 +300,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); @@ -571,9 +568,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); } static void @@ -1034,8 +1029,7 @@ ctc_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 = 0; tty->closing = 0; if (info->blocked_open) { diff -Naur linux-2.6.7/drivers/sbus/char/aurora.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/sbus/char/aurora.c --- linux-2.6.7/drivers/sbus/char/aurora.c 2004-06-15 22:18:59.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/sbus/char/aurora.c 2004-11-23 13:23:23.310403000 -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.7/drivers/serial/68328serial.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/68328serial.c --- linux-2.6.7/drivers/serial/68328serial.c 2004-06-15 22:20:04.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/68328serial.c 2004-11-23 13:23:23.314402000 -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.7/drivers/serial/68360serial.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/68360serial.c --- linux-2.6.7/drivers/serial/68360serial.c 2004-06-15 22:20:04.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/68360serial.c 2004-11-23 13:23:23.341398000 -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.7/drivers/serial/mcfserial.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/mcfserial.c --- linux-2.6.7/drivers/serial/mcfserial.c 2004-06-15 22:19:22.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/mcfserial.c 2004-11-23 13:23:23.348397000 -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.7/drivers/serial/serial_core.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/serial_core.c --- linux-2.6.7/drivers/serial/serial_core.c 2004-06-15 22:19:01.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/serial/serial_core.c 2004-11-23 13:23:23.355396000 -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.7/drivers/tc/zs.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/tc/zs.c --- linux-2.6.7/drivers/tc/zs.c 2004-06-15 22:19:52.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/tc/zs.c 2004-11-23 13:23:23.362395000 -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.7/drivers/usb/class/bluetty.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/class/bluetty.c --- linux-2.6.7/drivers/usb/class/bluetty.c 2004-06-15 22:19:44.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/class/bluetty.c 2004-11-23 13:23:23.366394000 -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.7/drivers/usb/class/cdc-acm.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/class/cdc-acm.c --- linux-2.6.7/drivers/usb/class/cdc-acm.c 2004-06-15 22:19:37.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/class/cdc-acm.c 2004-11-23 13:23:23.369394000 -0800 @@ -321,15 +321,11 @@ static void acm_softint(void *private) { struct acm *acm = private; - struct tty_struct *tty = acm->tty; 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.7/drivers/usb/serial/digi_acceleport.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/digi_acceleport.c --- linux-2.6.7/drivers/usb/serial/digi_acceleport.c 2004-06-15 22:19:22.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/digi_acceleport.c 2004-11-23 13:23:23.376392000 -0800 @@ -630,14 +630,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); } @@ -1575,8 +1568,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.7/drivers/usb/serial/empeg.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/empeg.c --- linux-2.6.7/drivers/usb/serial/empeg.c 2004-06-15 22:20:20.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/empeg.c 2004-11-23 13:23:23.378392000 -0800 @@ -521,7 +521,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.7/drivers/usb/serial/io_edgeport.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/io_edgeport.c --- linux-2.6.7/drivers/usb/serial/io_edgeport.c 2004-06-15 22:18:57.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/io_edgeport.c 2004-11-23 13:23:23.387391000 -0800 @@ -914,12 +914,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.7/drivers/usb/serial/io_ti.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/io_ti.c --- linux-2.6.7/drivers/usb/serial/io_ti.c 2004-06-15 22:20:04.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/io_ti.c 2004-11-23 13:23:23.394390000 -0800 @@ -1810,12 +1810,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.7/drivers/usb/serial/ir-usb.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/ir-usb.c --- linux-2.6.7/drivers/usb/serial/ir-usb.c 2004-06-15 22:19:22.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/ir-usb.c 2004-11-23 13:23:23.397389000 -0800 @@ -452,6 +452,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.7/drivers/usb/serial/keyspan_pda.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/keyspan_pda.c --- linux-2.6.7/drivers/usb/serial/keyspan_pda.c 2004-06-15 22:19:37.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/keyspan_pda.c 2004-11-23 13:23:23.401389000 -0800 @@ -191,13 +191,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.7/drivers/usb/serial/mct_u232.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/mct_u232.c --- linux-2.6.7/drivers/usb/serial/mct_u232.c 2004-06-15 22:19:42.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/mct_u232.c 2004-11-23 13:23:23.404388000 -0800 @@ -585,11 +585,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.7/drivers/usb/serial/usb-serial.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/usb-serial.c --- linux-2.6.7/drivers/usb/serial/usb-serial.c 2004-06-15 22:19:17.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/usb-serial.c 2004-11-23 13:23:23.409387000 -0800 @@ -777,12 +777,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.7/drivers/usb/serial/whiteheat.c linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/whiteheat.c --- linux-2.6.7/drivers/usb/serial/whiteheat.c 2004-06-15 22:19:12.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/drivers/usb/serial/whiteheat.c 2004-11-23 13:23:23.413387000 -0800 @@ -673,8 +673,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.7/fs/proc/proc_tty.c linux-2.6.7-1140_CAN-2004-0814.fixed/fs/proc/proc_tty.c --- linux-2.6.7/fs/proc/proc_tty.c 2004-06-15 22:19:13.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/fs/proc/proc_tty.c 2004-11-23 13:23:23.415387000 -0800 @@ -15,9 +15,6 @@ #include #include -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.7/include/linux/tty.h linux-2.6.7-1140_CAN-2004-0814.fixed/include/linux/tty.h --- linux-2.6.7/include/linux/tty.h 2004-06-15 22:19:23.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/include/linux/tty.h 2004-11-23 13:23:23.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; @@ -365,6 +366,16 @@ struct semaphore; extern struct semaphore tty_sem; +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); + /* n_tty.c */ extern struct tty_ldisc tty_ldisc_N_TTY; diff -Naur linux-2.6.7/include/linux/tty_ldisc.h linux-2.6.7-1140_CAN-2004-0814.fixed/include/linux/tty_ldisc.h --- linux-2.6.7/include/linux/tty_ldisc.h 2004-06-15 22:20:26.000000000 -0700 +++ linux-2.6.7-1140_CAN-2004-0814.fixed/include/linux/tty_ldisc.h 2004-11-23 13:23:23.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 @@ -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