diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 7b627368caac68b7362f795ab97b4f7493ce0adf..a53496828b76949a3e0b50c82fa4372d8c6f21dd 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -60,6 +60,7 @@ enum tty_state_t { struct hvc_iucv_private { struct hvc_struct *hvc; /* HVC struct reference */ u8 srv_name[8]; /* IUCV service name (ebcdic) */ + unsigned char is_console; /* Linux console usage flag */ enum iucv_state_t iucv_state; /* IUCV connection status */ enum tty_state_t tty_state; /* TTY status */ struct iucv_path *path; /* IUCV path pointer */ @@ -93,6 +94,7 @@ static unsigned long hvc_iucv_devices = 1; /* Array of allocated hvc iucv tty lines... */ static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; +#define IUCV_HVC_CON_IDX (0) /* Kmem cache and mempool for iucv_tty_buffer elements */ static struct kmem_cache *hvc_iucv_buffer_cache; @@ -139,7 +141,7 @@ static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) bufp = mempool_alloc(hvc_iucv_mempool, flags); if (!bufp) return NULL; - memset(bufp, 0, sizeof(struct iucv_tty_buffer)); + memset(bufp, 0, sizeof(*bufp)); if (size > 0) { bufp->msg.length = MSG_SIZE(size); @@ -463,7 +465,7 @@ static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) * @id: Additional data (originally passed to hvc_alloc): the index of an struct * hvc_iucv_private instance. * - * The function sets the tty state to TTY_OPEN for the struct hvc_iucv_private + * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private * instance that is derived from @id. Always returns 0. * * Locking: struct hvc_iucv_private->lock, spin_lock_bh @@ -707,6 +709,9 @@ static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) /* If the tty has not yet been opened, clean up the hvc_iucv_private * structure to allow re-connects. + * This is also done for our console device because console hangups + * are handled specially and no notifier is called by HVC. + * The tty session is active (TTY_OPEN) and ready for re-connects... * * If it has been opened, let get_chars() return -EPIPE to signal the * HVC layer to hang up the tty. @@ -716,7 +721,11 @@ static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) if (priv->tty_state == TTY_CLOSED) hvc_iucv_cleanup(priv); else - hvc_kick(); + if (priv->is_console) { + hvc_iucv_cleanup(priv); + priv->tty_state = TTY_OPENED; + } else + hvc_kick(); spin_unlock(&priv->lock); /* finally sever path (outside of priv->lock due to lock ordering) */ @@ -747,7 +756,6 @@ static void hvc_iucv_msg_pending(struct iucv_path *path, return; } - spin_lock(&priv->lock); /* reject messages if tty has not yet been opened */ @@ -814,13 +822,14 @@ static struct hv_ops hvc_iucv_ops = { /** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance - * @id: hvc_iucv_table index + * @id: hvc_iucv_table index + * @is_console: Flag if the instance is used as Linux console * * This function allocates a new hvc_iucv_private structure and stores * the instance in hvc_iucv_table at index @id. * Returns 0 on success; otherwise non-zero. */ -static int __init hvc_iucv_alloc(int id) +static int __init hvc_iucv_alloc(int id, unsigned int is_console) { struct hvc_iucv_private *priv; char name[9]; @@ -842,6 +851,9 @@ static int __init hvc_iucv_alloc(int id) return -ENOMEM; } + /* set console flag */ + priv->is_console = is_console; + /* finally allocate hvc */ priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); @@ -869,7 +881,8 @@ static int __init hvc_iucv_alloc(int id) */ static int __init hvc_iucv_init(void) { - int rc, i; + int rc; + unsigned int i; if (!MACHINE_IS_VM) { pr_info("The z/VM IUCV HVC device driver cannot " @@ -901,14 +914,16 @@ static int __init hvc_iucv_init(void) /* register the first terminal device as console * (must be done before allocating hvc terminal devices) */ - rc = hvc_instantiate(HVC_IUCV_MAGIC, 0, &hvc_iucv_ops); - if (rc) - pr_warning("Registering HVC terminal device as " - "Linux console failed\n"); + rc = hvc_instantiate(HVC_IUCV_MAGIC, IUCV_HVC_CON_IDX, &hvc_iucv_ops); + if (rc) { + pr_err("Registering HVC terminal device as " + "Linux console failed\n"); + goto out_error_memory; + } /* allocate hvc_iucv_private structs */ for (i = 0; i < hvc_iucv_devices; i++) { - rc = hvc_iucv_alloc(i); + rc = hvc_iucv_alloc(i, (i == IUCV_HVC_CON_IDX) ? 1 : 0); if (rc) { pr_err("Creating a new HVC terminal device " "failed with error code=%d\n", rc); @@ -935,6 +950,7 @@ static int __init hvc_iucv_init(void) hvc_remove(hvc_iucv_table[i]->hvc); kfree(hvc_iucv_table[i]); } +out_error_memory: mempool_destroy(hvc_iucv_mempool); kmem_cache_destroy(hvc_iucv_buffer_cache); return rc;