tty/hvc_iucv: Disconnect IUCV connection when lowering DTR
authorHendrik Brueckner <brueckner@linux.vnet.ibm.com>
Tue, 2 Jul 2013 15:07:15 +0000 (17:07 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Jul 2013 22:21:13 +0000 (15:21 -0700)
Implement the dtr_rts() hvc console callback to improve control when to
disconnect the IUCV connection.  Previously, the IUCV connection was
disconnected during the notifier_del() callback, i.e., when the last file
descriptor to the hvc terminal device was closed.

Recent changes in login programs caused undesired disconnects during the
login phase.  To prevent these kind of disconnects, implement the dtr_rts
callback to implicitly handle the HUPCL termios control via the hvc_console
driver.

Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/hvc/hvc_iucv.c

index 9d47f50c2755a37835ec04662a194db631dba08a..fd17a9b804b8ab8608097661b824e27c5bb1cd02 100644 (file)
@@ -655,6 +655,49 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
        spin_unlock_bh(&priv->lock);
 }
 
+/**
+ * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS
+ * @hp:                Pointer the HVC device (struct hvc_struct)
+ * @raise:     Non-zero to raise or zero to lower DTR/RTS lines
+ *
+ * This routine notifies the HVC back-end to raise or lower DTR/RTS
+ * lines.  Raising DTR/RTS is ignored.  Lowering DTR/RTS indicates to
+ * drop the IUCV connection (similar to hang up the modem).
+ */
+static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise)
+{
+       struct hvc_iucv_private *priv;
+       struct iucv_path        *path;
+
+       /* Raising the DTR/RTS is ignored as IUCV connections can be
+        * established at any times.
+        */
+       if (raise)
+               return;
+
+       priv = hvc_iucv_get_private(hp->vtermno);
+       if (!priv)
+               return;
+
+       /* Lowering the DTR/RTS lines disconnects an established IUCV
+        * connection.
+        */
+       flush_sndbuf_sync(priv);
+
+       spin_lock_bh(&priv->lock);
+       path = priv->path;              /* save reference to IUCV path */
+       priv->path = NULL;
+       priv->iucv_state = IUCV_DISCONN;
+       spin_unlock_bh(&priv->lock);
+
+       /* Sever IUCV path outside of priv->lock due to lock ordering of:
+        * priv->lock <--> iucv_table_lock */
+       if (path) {
+               iucv_path_sever(path, NULL);
+               iucv_path_free(path);
+       }
+}
+
 /**
  * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
  * @hp:                Pointer to the HVC device (struct hvc_struct)
@@ -662,15 +705,15 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
  *             the index of an struct hvc_iucv_private instance.
  *
  * This routine notifies the HVC back-end that the last tty device fd has been
- * closed.  The function calls hvc_iucv_cleanup() to clean up the struct
- * hvc_iucv_private instance.
+ * closed.  The function cleans up tty resources.  The clean-up of the IUCV
+ * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios
+ * control setting.
  *
  * Locking:    struct hvc_iucv_private->lock
  */
 static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
 {
        struct hvc_iucv_private *priv;
-       struct iucv_path        *path;
 
        priv = hvc_iucv_get_private(id);
        if (!priv)
@@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
        flush_sndbuf_sync(priv);
 
        spin_lock_bh(&priv->lock);
-       path = priv->path;              /* save reference to IUCV path */
-       priv->path = NULL;
-       hvc_iucv_cleanup(priv);
+       destroy_tty_buffer_list(&priv->tty_outqueue);
+       destroy_tty_buffer_list(&priv->tty_inqueue);
+       priv->tty_state = TTY_CLOSED;
+       priv->sndbuf_len = 0;
        spin_unlock_bh(&priv->lock);
-
-       /* sever IUCV path outside of priv->lock due to lock ordering of:
-        * priv->lock <--> iucv_table_lock */
-       if (path) {
-               iucv_path_sever(path, NULL);
-               iucv_path_free(path);
-       }
 }
 
 /**
@@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = {
        .notifier_add = hvc_iucv_notifier_add,
        .notifier_del = hvc_iucv_notifier_del,
        .notifier_hangup = hvc_iucv_notifier_hangup,
+       .dtr_rts = hvc_iucv_dtr_rts,
 };
 
 /* Suspend / resume device operations */