|
@@ -530,14 +530,17 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout)
|
|
|
/**
|
|
|
* tty_ldisc_halt - shut down the line discipline
|
|
|
* @tty: tty device
|
|
|
+ * @o_tty: paired pty device (can be NULL)
|
|
|
* @pending: returns true if work was scheduled when cancelled
|
|
|
* (can be set to NULL)
|
|
|
+ * @o_pending: returns true if work was scheduled when cancelled
|
|
|
+ * (can be set to NULL)
|
|
|
* @timeout: # of jiffies to wait for ldisc refs to be released
|
|
|
*
|
|
|
- * Shut down the line discipline and work queue for this tty device.
|
|
|
- * The TTY_LDISC flag being cleared ensures no further references can
|
|
|
- * be obtained while the delayed work queue halt ensures that no more
|
|
|
- * data is fed to the ldisc.
|
|
|
+ * Shut down the line discipline and work queue for this tty device and
|
|
|
+ * its paired pty (if exists). Clearing the TTY_LDISC flag ensures
|
|
|
+ * no further references can be obtained while the work queue halt
|
|
|
+ * ensures that no more data is fed to the ldisc.
|
|
|
*
|
|
|
* Furthermore, guarantee that existing ldisc references have been
|
|
|
* released, which in turn, guarantees that no future buffer work
|
|
@@ -548,19 +551,32 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout)
|
|
|
* flushed.
|
|
|
*/
|
|
|
|
|
|
-static int tty_ldisc_halt(struct tty_struct *tty, int *pending, long timeout)
|
|
|
+static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty,
|
|
|
+ int *pending, int *o_pending, long timeout)
|
|
|
{
|
|
|
- int scheduled, retval;
|
|
|
+ int scheduled, o_scheduled, retval;
|
|
|
|
|
|
clear_bit(TTY_LDISC, &tty->flags);
|
|
|
+ if (o_tty)
|
|
|
+ clear_bit(TTY_LDISC, &o_tty->flags);
|
|
|
+
|
|
|
retval = tty_ldisc_wait_idle(tty, timeout);
|
|
|
+ if (!retval && o_tty)
|
|
|
+ retval = tty_ldisc_wait_idle(o_tty, timeout);
|
|
|
if (retval)
|
|
|
return retval;
|
|
|
|
|
|
scheduled = cancel_work_sync(&tty->port->buf.work);
|
|
|
set_bit(TTY_LDISC_HALTED, &tty->flags);
|
|
|
+ if (o_tty) {
|
|
|
+ o_scheduled = cancel_work_sync(&o_tty->port->buf.work);
|
|
|
+ set_bit(TTY_LDISC_HALTED, &o_tty->flags);
|
|
|
+ }
|
|
|
+
|
|
|
if (pending)
|
|
|
*pending = scheduled;
|
|
|
+ if (o_tty && o_pending)
|
|
|
+ *o_pending = o_scheduled;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -702,9 +718,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
|
|
|
* parallel to the change and re-referencing the tty.
|
|
|
*/
|
|
|
|
|
|
- retval = tty_ldisc_halt(tty, &work, 5 * HZ);
|
|
|
- if (!retval && o_tty)
|
|
|
- retval = tty_ldisc_halt(o_tty, &o_work, 5 * HZ);
|
|
|
+ retval = tty_ldisc_halt(tty, o_tty, &work, &o_work, 5 * HZ);
|
|
|
|
|
|
/*
|
|
|
* Wait for ->hangup_work and ->buf.work handlers to terminate.
|
|
@@ -965,12 +979,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
|
|
|
* race with the set_ldisc code path.
|
|
|
*/
|
|
|
|
|
|
- tty_ldisc_halt(tty, NULL, MAX_SCHEDULE_TIMEOUT);
|
|
|
+ tty_ldisc_halt(tty, o_tty, NULL, NULL, MAX_SCHEDULE_TIMEOUT);
|
|
|
tty_ldisc_flush_works(tty);
|
|
|
- if (o_tty) {
|
|
|
- tty_ldisc_halt(o_tty, NULL, MAX_SCHEDULE_TIMEOUT);
|
|
|
+ if (o_tty)
|
|
|
tty_ldisc_flush_works(o_tty);
|
|
|
- }
|
|
|
|
|
|
tty_lock_pair(tty, o_tty);
|
|
|
/* This will need doing differently if we need to lock */
|