These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / s390 / cio / chsc.c
index e3bf885..a831d18 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/chsc.h>
 #include <asm/crw.h>
 #include <asm/isc.h>
+#include <asm/ebcdic.h>
 
 #include "css.h"
 #include "cio.h"
@@ -272,36 +273,6 @@ static void s390_process_res_acc(struct chp_link *link)
        css_schedule_reprobe();
 }
 
-static int
-__get_chpid_from_lir(void *data)
-{
-       struct lir {
-               u8  iq;
-               u8  ic;
-               u16 sci;
-               /* incident-node descriptor */
-               u32 indesc[28];
-               /* attached-node descriptor */
-               u32 andesc[28];
-               /* incident-specific information */
-               u32 isinfo[28];
-       } __attribute__ ((packed)) *lir;
-
-       lir = data;
-       if (!(lir->iq&0x80))
-               /* NULL link incident record */
-               return -EINVAL;
-       if (!(lir->indesc[0]&0xc0000000))
-               /* node descriptor not valid */
-               return -EINVAL;
-       if (!(lir->indesc[0]&0x10000000))
-               /* don't handle device-type nodes - FIXME */
-               return -EINVAL;
-       /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
-
-       return (u16) (lir->indesc[0]&0x000000ff);
-}
-
 struct chsc_sei_nt0_area {
        u8  flags;
        u8  vf;                         /* validity flags */
@@ -341,22 +312,132 @@ struct chsc_sei {
        } u;
 } __packed;
 
+/*
+ * Node Descriptor as defined in SA22-7204, "Common I/O-Device Commands"
+ */
+
+#define ND_VALIDITY_VALID      0
+#define ND_VALIDITY_OUTDATED   1
+#define ND_VALIDITY_INVALID    2
+
+struct node_descriptor {
+       /* Flags. */
+       union {
+               struct {
+                       u32 validity:3;
+                       u32 reserved:5;
+               } __packed;
+               u8 byte0;
+       } __packed;
+
+       /* Node parameters. */
+       u32 params:24;
+
+       /* Node ID. */
+       char type[6];
+       char model[3];
+       char manufacturer[3];
+       char plant[2];
+       char seq[12];
+       u16 tag;
+} __packed;
+
+/*
+ * Link Incident Record as defined in SA22-7202, "ESCON I/O Interface"
+ */
+
+#define LIR_IQ_CLASS_INFO              0
+#define LIR_IQ_CLASS_DEGRADED          1
+#define LIR_IQ_CLASS_NOT_OPERATIONAL   2
+
+struct lir {
+       struct {
+               u32 null:1;
+               u32 reserved:3;
+               u32 class:2;
+               u32 reserved2:2;
+       } __packed iq;
+       u32 ic:8;
+       u32 reserved:16;
+       struct node_descriptor incident_node;
+       struct node_descriptor attached_node;
+       u8 reserved2[32];
+} __packed;
+
+#define PARAMS_LEN     10      /* PARAMS=xx,xxxxxx */
+#define NODEID_LEN     35      /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
+
+/* Copy EBCIDC text, convert to ASCII and optionally add delimiter. */
+static char *store_ebcdic(char *dest, const char *src, unsigned long len,
+                         char delim)
+{
+       memcpy(dest, src, len);
+       EBCASC(dest, len);
+
+       if (delim)
+               dest[len++] = delim;
+
+       return dest + len;
+}
+
+/* Format node ID and parameters for output in LIR log message. */
+static void format_node_data(char *params, char *id, struct node_descriptor *nd)
+{
+       memset(params, 0, PARAMS_LEN);
+       memset(id, 0, NODEID_LEN);
+
+       if (nd->validity != ND_VALIDITY_VALID) {
+               strncpy(params, "n/a", PARAMS_LEN - 1);
+               strncpy(id, "n/a", NODEID_LEN - 1);
+               return;
+       }
+
+       /* PARAMS=xx,xxxxxx */
+       snprintf(params, PARAMS_LEN, "%02x,%06x", nd->byte0, nd->params);
+       /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
+       id = store_ebcdic(id, nd->type, sizeof(nd->type), '/');
+       id = store_ebcdic(id, nd->model, sizeof(nd->model), ',');
+       id = store_ebcdic(id, nd->manufacturer, sizeof(nd->manufacturer), '.');
+       id = store_ebcdic(id, nd->plant, sizeof(nd->plant), 0);
+       id = store_ebcdic(id, nd->seq, sizeof(nd->seq), ',');
+       sprintf(id, "%04X", nd->tag);
+}
+
 static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area)
 {
-       struct chp_id chpid;
-       int id;
+       struct lir *lir = (struct lir *) &sei_area->ccdf;
+       char iuparams[PARAMS_LEN], iunodeid[NODEID_LEN], auparams[PARAMS_LEN],
+            aunodeid[NODEID_LEN];
 
-       CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
-                     sei_area->rs, sei_area->rsid);
-       if (sei_area->rs != 4)
+       CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x, iq=%02x)\n",
+                     sei_area->rs, sei_area->rsid, sei_area->ccdf[0]);
+
+       /* Ignore NULL Link Incident Records. */
+       if (lir->iq.null)
                return;
-       id = __get_chpid_from_lir(sei_area->ccdf);
-       if (id < 0)
-               CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
-       else {
-               chp_id_init(&chpid);
-               chpid.id = id;
-               chsc_chp_offline(chpid);
+
+       /* Inform user that a link requires maintenance actions because it has
+        * become degraded or not operational. Note that this log message is
+        * the primary intention behind a Link Incident Record. */
+
+       format_node_data(iuparams, iunodeid, &lir->incident_node);
+       format_node_data(auparams, aunodeid, &lir->attached_node);
+
+       switch (lir->iq.class) {
+       case LIR_IQ_CLASS_DEGRADED:
+               pr_warn("Link degraded: RS=%02x RSID=%04x IC=%02x "
+                       "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
+                       sei_area->rs, sei_area->rsid, lir->ic, iuparams,
+                       iunodeid, auparams, aunodeid);
+               break;
+       case LIR_IQ_CLASS_NOT_OPERATIONAL:
+               pr_err("Link stopped: RS=%02x RSID=%04x IC=%02x "
+                      "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
+                      sei_area->rs, sei_area->rsid, lir->ic, iuparams,
+                      iunodeid, auparams, aunodeid);
+               break;
+       default:
+               break;
        }
 }
 
@@ -999,28 +1080,10 @@ void __init chsc_init_cleanup(void)
        free_page((unsigned long)sei_page);
 }
 
-int chsc_enable_facility(int operation_code)
+int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code)
 {
-       unsigned long flags;
        int ret;
-       struct {
-               struct chsc_header request;
-               u8 reserved1:4;
-               u8 format:4;
-               u8 reserved2;
-               u16 operation_code;
-               u32 reserved3;
-               u32 reserved4;
-               u32 operation_data_area[252];
-               struct chsc_header response;
-               u32 reserved5:4;
-               u32 format2:4;
-               u32 reserved6:24;
-       } __attribute__ ((packed)) *sda_area;
 
-       spin_lock_irqsave(&chsc_page_lock, flags);
-       memset(chsc_page, 0, PAGE_SIZE);
-       sda_area = chsc_page;
        sda_area->request.length = 0x0400;
        sda_area->request.code = 0x0031;
        sda_area->operation_code = operation_code;
@@ -1038,10 +1101,25 @@ int chsc_enable_facility(int operation_code)
        default:
                ret = chsc_error_from_response(sda_area->response.code);
        }
+out:
+       return ret;
+}
+
+int chsc_enable_facility(int operation_code)
+{
+       struct chsc_sda_area *sda_area;
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&chsc_page_lock, flags);
+       memset(chsc_page, 0, PAGE_SIZE);
+       sda_area = chsc_page;
+
+       ret = __chsc_enable_facility(sda_area, operation_code);
        if (ret != 0)
                CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
                              operation_code, sda_area->response.code);
-out:
+
        spin_unlock_irqrestore(&chsc_page_lock, flags);
        return ret;
 }