Commit 74b3b4cd authored by Hendrik Brueckner's avatar Hendrik Brueckner Committed by Greg Kroah-Hartman

tty/hvc_iucv: Disconnect IUCV connection when lowering DTR

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: default avatarHendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 33e745a1
...@@ -655,6 +655,49 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) ...@@ -655,6 +655,49 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
spin_unlock_bh(&priv->lock); 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. * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
* @hp: Pointer to the HVC device (struct hvc_struct) * @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) ...@@ -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. * the index of an struct hvc_iucv_private instance.
* *
* This routine notifies the HVC back-end that the last tty device fd has been * 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 * closed. The function cleans up tty resources. The clean-up of the IUCV
* hvc_iucv_private instance. * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios
* control setting.
* *
* Locking: struct hvc_iucv_private->lock * Locking: struct hvc_iucv_private->lock
*/ */
static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
{ {
struct hvc_iucv_private *priv; struct hvc_iucv_private *priv;
struct iucv_path *path;
priv = hvc_iucv_get_private(id); priv = hvc_iucv_get_private(id);
if (!priv) if (!priv)
...@@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) ...@@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
flush_sndbuf_sync(priv); flush_sndbuf_sync(priv);
spin_lock_bh(&priv->lock); spin_lock_bh(&priv->lock);
path = priv->path; /* save reference to IUCV path */ destroy_tty_buffer_list(&priv->tty_outqueue);
priv->path = NULL; destroy_tty_buffer_list(&priv->tty_inqueue);
hvc_iucv_cleanup(priv); priv->tty_state = TTY_CLOSED;
priv->sndbuf_len = 0;
spin_unlock_bh(&priv->lock); 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 = { ...@@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = {
.notifier_add = hvc_iucv_notifier_add, .notifier_add = hvc_iucv_notifier_add,
.notifier_del = hvc_iucv_notifier_del, .notifier_del = hvc_iucv_notifier_del,
.notifier_hangup = hvc_iucv_notifier_hangup, .notifier_hangup = hvc_iucv_notifier_hangup,
.dtr_rts = hvc_iucv_dtr_rts,
}; };
/* Suspend / resume device operations */ /* Suspend / resume device operations */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment