diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 878f6d667b17..35480dd57a30 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -559,6 +559,9 @@ void __tty_hangup(struct tty_struct *tty) tty_lock(); + /* some functions below drop BTM, so we need this bit */ + set_bit(TTY_HUPPING, &tty->flags); + /* inuse_filps is protected by the single tty lock, this really needs to change if we want to flush the workqueue with the lock held */ @@ -578,6 +581,10 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&tty_files_lock); + /* + * it drops BTM and thus races with reopen + * we protect the race by TTY_HUPPING + */ tty_ldisc_hangup(tty); read_lock(&tasklist_lock); @@ -615,7 +622,6 @@ void __tty_hangup(struct tty_struct *tty) tty->session = NULL; tty->pgrp = NULL; tty->ctrl_status = 0; - set_bit(TTY_HUPPED, &tty->flags); spin_unlock_irqrestore(&tty->ctrl_lock, flags); /* Account for the p->signal references we killed */ @@ -641,6 +647,7 @@ void __tty_hangup(struct tty_struct *tty) * can't yet guarantee all that. */ set_bit(TTY_HUPPED, &tty->flags); + clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); tty_unlock(); @@ -1311,6 +1318,7 @@ static int tty_reopen(struct tty_struct *tty) struct tty_driver *driver = tty->driver; if (test_bit(TTY_CLOSING, &tty->flags) || + test_bit(TTY_HUPPING, &tty->flags) || test_bit(TTY_LDISC_CHANGING, &tty->flags)) return -EIO; diff --git a/include/linux/tty.h b/include/linux/tty.h index c7ea9bc8897c..021bfd2f0860 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -367,6 +367,7 @@ struct tty_file_private { #define TTY_HUPPED 18 /* Post driver->hangup() */ #define TTY_FLUSHING 19 /* Flushing to ldisc in progress */ #define TTY_FLUSHPENDING 20 /* Queued buffer flush pending */ +#define TTY_HUPPING 21 /* ->hangup() in progress */ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))