tty->closing is exclusively used to cause the N_TTY line discipline to drop further input on final tty close (except XON/XOFF output flow control changes). In turn, this prevents the line discipline from generating new tty driver i/o requests (eg., when echoing) after the tty driver has performed h/w shutdown. Abstract this notification with new ldisc api function, tty_ldisc_closing(), which invokes the new line discipline method, ops->closing(). Define this method for N_TTY line discipline and localize closing state to n_tty private data. Remove tty->closing. Note that resetting tty->closing to 0 (and thus allowing the line discipline to resume normal input processing) is unnecessary and undesirable: since the tty is in final close, both the line discipline instance and the tty are being destroyed, so resuming normal input processing after h/w shutdown is counter-productive. NB: ipwireless_tty_free() is completely bogus; freeing the tty (?!) with open, in-use file descriptors is laughable. Signed-off-by: Peter Hurley <peter@xxxxxxxxxxxxxxxxxx> --- v2: Fixed tty_ldisc_closing() ld use found by Johannes Stezenbach <js@xxxxxxxxx> drivers/isdn/i4l/isdn_tty.c | 2 +- drivers/s390/char/con3215.c | 3 +-- drivers/staging/dgap/dgap.c | 4 +--- drivers/staging/dgnc/dgnc_tty.c | 4 +--- drivers/tty/hvc/hvsi.c | 2 +- drivers/tty/ipwireless/tty.c | 1 - drivers/tty/n_tty.c | 13 +++++++++++-- drivers/tty/rocket.c | 1 - drivers/tty/serial/68328serial.c | 3 +-- drivers/tty/serial/crisv10.c | 3 +-- drivers/tty/serial/serial_core.c | 1 - drivers/tty/tty_ldisc.c | 20 ++++++++++++++++++++ drivers/tty/tty_port.c | 3 +-- include/linux/tty.h | 2 +- include/linux/tty_ldisc.h | 9 +++++++++ 15 files changed, 49 insertions(+), 22 deletions(-) diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 2175225..cddba25 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1574,7 +1574,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) } port->flags |= ASYNC_CLOSING; - tty->closing = 1; + tty_ldisc_closing(tty); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 0fc3fe5..715251d 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -1006,11 +1006,10 @@ static void tty3215_close(struct tty_struct *tty, struct file * filp) raw = (struct raw3215_info *) tty->driver_data; if (raw == NULL || tty->count > 1) return; - tty->closing = 1; + tty_ldisc_closing(tty); /* Shutdown the terminal */ raw3215_shutdown(raw); tasklet_kill(&raw->tlet); - tty->closing = 0; tty_port_tty_set(&raw->port, NULL); } diff --git a/drivers/staging/dgap/dgap.c b/drivers/staging/dgap/dgap.c index 9112dd2..0456e28 100644 --- a/drivers/staging/dgap/dgap.c +++ b/drivers/staging/dgap/dgap.c @@ -4622,7 +4622,7 @@ static void dgap_tty_close(struct tty_struct *tty, struct file *file) un->un_flags |= UN_CLOSING; - tty->closing = 1; + tty_ldisc_closing(tty); /* * Only officially close channel if count is 0 and @@ -4645,8 +4645,6 @@ static void dgap_tty_close(struct tty_struct *tty, struct file *file) spin_lock_irqsave(&ch->ch_lock, lock_flags); - tty->closing = 0; - /* * If we have HUPCL set, lower DTR and RTS */ diff --git a/drivers/staging/dgnc/dgnc_tty.c b/drivers/staging/dgnc/dgnc_tty.c index fbfe79a..96960d8 100644 --- a/drivers/staging/dgnc/dgnc_tty.c +++ b/drivers/staging/dgnc/dgnc_tty.c @@ -1410,7 +1410,7 @@ static void dgnc_tty_close(struct tty_struct *tty, struct file *file) /* OK, its the last close on the unit */ un->un_flags |= UN_CLOSING; - tty->closing = 1; + tty_ldisc_closing(tty); /* @@ -1441,8 +1441,6 @@ static void dgnc_tty_close(struct tty_struct *tty, struct file *file) spin_lock_irqsave(&ch->ch_lock, flags); - tty->closing = 0; - /* * If we have HUPCL set, lower DTR and RTS */ diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index a75146f..fbaa6ab 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -796,7 +796,7 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp) * any data delivered to the tty layer after this will be * discarded (except for XON/XOFF) */ - tty->closing = 1; + tty_ldisc_closing(tty); spin_unlock_irqrestore(&hp->lock, flags); diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 345cebb..2ed8831 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -536,7 +536,6 @@ void ipwireless_tty_free(struct ipw_tty *tty) printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": deregistering %s device ttyIPWp%d\n", tty_type_name(ttyj->tty_type), j); - ttyj->closing = 1; if (ttyj->port.tty != NULL) { mutex_unlock(&ttyj->ipw_tty_mutex); tty_vhangup(ttyj->port.tty); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 2de0283..6342b37 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -114,6 +114,7 @@ struct n_tty_data { unsigned char echo_buf[N_TTY_BUF_SIZE]; int minimum_to_wake; + int closing; /* consumer-published */ size_t read_tail; @@ -1637,7 +1638,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_receive_buf_real_raw(tty, cp, fp, count); else if (ldata->raw || (L_EXTPROC(tty) && !preops)) n_tty_receive_buf_raw(tty, cp, fp, count); - else if (tty->closing && !L_EXTPROC(tty)) + else if (ldata->closing && !L_EXTPROC(tty)) n_tty_receive_buf_closing(tty, cp, fp, count); else { if (ldata->lnext) { @@ -1942,7 +1943,7 @@ static int n_tty_open(struct tty_struct *tty) ldata->num_overrun = 0; ldata->no_room = 0; ldata->lnext = 0; - tty->closing = 0; + ldata->closing = 0; /* indicate buffer work may resume */ clear_bit(TTY_LDISC_HALTED, &tty->flags); n_tty_set_termios(tty, NULL); @@ -2525,6 +2526,13 @@ static void n_tty_fasync(struct tty_struct *tty, int on) } } +static void n_tty_closing(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + ldata->closing = 1; +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", @@ -2541,6 +2549,7 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .write_wakeup = n_tty_write_wakeup, .fasync = n_tty_fasync, .receive_buf2 = n_tty_receive_buf2, + .closing = n_tty_closing, }; /** diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index c5179f8..a6b5ce0 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1043,7 +1043,6 @@ static void rp_close(struct tty_struct *tty, struct file *filp) } spin_lock_irq(&port->lock); info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE); - tty->closing = 0; spin_unlock_irq(&port->lock); mutex_unlock(&port->mutex); tty_port_tty_set(port, NULL); diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 0140ba4..9a6aaf0 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -1035,7 +1035,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ - tty->closing = 1; + tty_ldisc_closing(tty); if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, port->closing_wait); /* @@ -1052,7 +1052,6 @@ static void rs_close(struct tty_struct *tty, struct file * filp) rs_flush_buffer(tty); tty_ldisc_flush(tty); - tty->closing = 0; tty_port_tty_set(&info->tport, NULL); #warning "This is not and has never been valid so fix it" #if 0 diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index f13f2eb..0100410 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3620,7 +3620,7 @@ rs_close(struct tty_struct *tty, struct file * filp) * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ - tty->closing = 1; + tty_ldisc_closing(tty); if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->port.closing_wait); /* @@ -3646,7 +3646,6 @@ rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); rs_flush_buffer(tty); tty_ldisc_flush(tty); - tty->closing = 0; info->event = 0; info->port.tty = NULL; if (info->port.blocked_open) { diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index def5199..db27a40 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1441,7 +1441,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&port->mutex); tty_ldisc_flush(tty); - tty->closing = 0; } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index b776f2e..ab0f559 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -407,6 +407,26 @@ void tty_ldisc_flush(struct tty_struct *tty) EXPORT_SYMBOL_GPL(tty_ldisc_flush); /** + * tty_ldisc_closing - notify line discipline of final close + * @tty: tty + * + * Notify the line discipline the driver has begun final close of the tty. + * The N_TTY line discipline uses this notification to ignore new input + */ + +void tty_ldisc_closing(struct tty_struct *tty) +{ + struct tty_ldisc *ld = tty_ldisc_ref(tty); + + if (ld) { + if (ld->ops->closing) + ld->ops->closing(tty); + tty_ldisc_deref(ld); + } +} +EXPORT_SYMBOL_GPL(tty_ldisc_closing); + +/** * tty_set_termios_ldisc - set ldisc field * @tty: tty structure * @num: line discipline number diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index a76aec2..ecb6435 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -479,7 +479,7 @@ int tty_port_close_start(struct tty_port *port, set_bit(ASYNCB_CLOSING, &port->flags); spin_unlock_irqrestore(&port->lock, flags); - tty->closing = 1; + tty_ldisc_closing(tty); if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { /* Don't block on a stalled port, just pull the chain */ @@ -504,7 +504,6 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) unsigned long flags; tty_ldisc_flush(tty); - tty->closing = 0; spin_lock_irqsave(&port->lock, flags); diff --git a/include/linux/tty.h b/include/linux/tty.h index 4b6c62e..70f3a9c1 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -306,7 +306,6 @@ struct tty_struct { #define N_TTY_BUF_SIZE 4096 - int closing; unsigned char *write_buf; int write_cnt; /* If the tty has a pending do_SAK, queue it here - akpm */ @@ -498,6 +497,7 @@ extern const struct file_operations tty_ldiscs_proc_fops; extern void tty_wakeup(struct tty_struct *tty); extern void tty_ldisc_flush(struct tty_struct *tty); +extern void tty_ldisc_closing(struct tty_struct *tty); extern long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg); extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file, diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 00c9d68..0f3b19f 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -125,6 +125,14 @@ * received with a parity error, etc. <fp> may be NULL to indicate * all data received is TTY_NORMAL. * If assigned, prefer this function for automatic flow control. + * + * void (*closing)(struct tty_struct *); + * + * This function notifies the line discipline the driver is + * starting final close for this tty; tty and ldisc destruction + * is imminent. The N_TTY line discipline uses this notification + * to ignore new input other than IXON flow control changes. + * This notification is not received for ptys or vts. */ #include <linux/fs.h> @@ -212,6 +220,7 @@ struct tty_ldisc_ops { void (*fasync)(struct tty_struct *tty, int on); int (*receive_buf2)(struct tty_struct *, const unsigned char *cp, char *fp, int count); + void (*closing)(struct tty_struct *tty); struct module *owner; -- 2.6.3 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html