s390: nvme reipl
authorJason J. Herne <jjherne@linux.ibm.com>
Tue, 3 Mar 2020 19:11:19 +0000 (14:11 -0500)
committerVasily Gorbik <gor@linux.ibm.com>
Wed, 6 May 2020 13:19:35 +0000 (15:19 +0200)
Populate sysfs and structs with reipl entries for nvme ipl type.
This allows specifying a target nvme device when rebooting/reipling.

Signed-off-by: Jason J. Herne <jjherne@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/kernel/ipl.c

index 939d421017fd210ffb3b346b147c9f73e6a5ef9a..ccea9a245867a15f249840e1200e912d3bde139a 100644 (file)
@@ -136,6 +136,7 @@ static int reipl_capabilities = IPL_TYPE_UNKNOWN;
 
 static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
 static struct ipl_parameter_block *reipl_block_fcp;
+static struct ipl_parameter_block *reipl_block_nvme;
 static struct ipl_parameter_block *reipl_block_ccw;
 static struct ipl_parameter_block *reipl_block_nss;
 static struct ipl_parameter_block *reipl_block_actual;
@@ -785,6 +786,93 @@ static struct attribute_group reipl_fcp_attr_group = {
 static struct kobj_attribute sys_reipl_fcp_clear_attr =
        __ATTR(clear, 0644, reipl_fcp_clear_show, reipl_fcp_clear_store);
 
+/* NVME reipl device attributes */
+
+static ssize_t reipl_nvme_scpdata_read(struct file *filp, struct kobject *kobj,
+                                     struct bin_attribute *attr,
+                                     char *buf, loff_t off, size_t count)
+{
+       size_t size = reipl_block_nvme->nvme.scp_data_len;
+       void *scp_data = reipl_block_nvme->nvme.scp_data;
+
+       return memory_read_from_buffer(buf, count, &off, scp_data, size);
+}
+
+static ssize_t reipl_nvme_scpdata_write(struct file *filp, struct kobject *kobj,
+                                      struct bin_attribute *attr,
+                                      char *buf, loff_t off, size_t count)
+{
+       size_t scpdata_len = count;
+       size_t padding;
+
+       if (off)
+               return -EINVAL;
+
+       memcpy(reipl_block_nvme->nvme.scp_data, buf, count);
+       if (scpdata_len % 8) {
+               padding = 8 - (scpdata_len % 8);
+               memset(reipl_block_nvme->nvme.scp_data + scpdata_len,
+                      0, padding);
+               scpdata_len += padding;
+       }
+
+       reipl_block_nvme->hdr.len = IPL_BP_FCP_LEN + scpdata_len;
+       reipl_block_nvme->nvme.len = IPL_BP0_FCP_LEN + scpdata_len;
+       reipl_block_nvme->nvme.scp_data_len = scpdata_len;
+
+       return count;
+}
+
+static struct bin_attribute sys_reipl_nvme_scp_data_attr =
+       __BIN_ATTR(scp_data, (S_IRUGO | S_IWUSR), reipl_nvme_scpdata_read,
+                  reipl_nvme_scpdata_write, DIAG308_SCPDATA_SIZE);
+
+static struct bin_attribute *reipl_nvme_bin_attrs[] = {
+       &sys_reipl_nvme_scp_data_attr,
+       NULL,
+};
+
+DEFINE_IPL_ATTR_RW(reipl_nvme, fid, "0x%08llx\n", "%llx\n",
+                  reipl_block_nvme->nvme.fid);
+DEFINE_IPL_ATTR_RW(reipl_nvme, nsid, "0x%08llx\n", "%llx\n",
+                  reipl_block_nvme->nvme.nsid);
+DEFINE_IPL_ATTR_RW(reipl_nvme, bootprog, "%lld\n", "%lld\n",
+                  reipl_block_nvme->nvme.bootprog);
+DEFINE_IPL_ATTR_RW(reipl_nvme, br_lba, "%lld\n", "%lld\n",
+                  reipl_block_nvme->nvme.br_lba);
+
+/* nvme wrapper */
+static ssize_t reipl_nvme_loadparm_show(struct kobject *kobj,
+                                      struct kobj_attribute *attr, char *page)
+{
+       return reipl_generic_loadparm_show(reipl_block_nvme, page);
+}
+
+static ssize_t reipl_nvme_loadparm_store(struct kobject *kobj,
+                                       struct kobj_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return reipl_generic_loadparm_store(reipl_block_nvme, buf, len);
+}
+
+static struct kobj_attribute sys_reipl_nvme_loadparm_attr =
+       __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_nvme_loadparm_show,
+                                           reipl_nvme_loadparm_store);
+
+static struct attribute *reipl_nvme_attrs[] = {
+       &sys_reipl_nvme_fid_attr.attr,
+       &sys_reipl_nvme_nsid_attr.attr,
+       &sys_reipl_nvme_bootprog_attr.attr,
+       &sys_reipl_nvme_br_lba_attr.attr,
+       &sys_reipl_nvme_loadparm_attr.attr,
+       NULL,
+};
+
+static struct attribute_group reipl_nvme_attr_group = {
+       .attrs = reipl_nvme_attrs,
+       .bin_attrs = reipl_nvme_bin_attrs
+};
+
 /* CCW reipl device attributes */
 DEFINE_IPL_CCW_ATTR_RW(reipl_ccw, device, reipl_block_ccw->ccw);
 
@@ -949,6 +1037,9 @@ static int reipl_set_type(enum ipl_type type)
        case IPL_TYPE_FCP:
                reipl_block_actual = reipl_block_fcp;
                break;
+       case IPL_TYPE_NVME:
+               reipl_block_actual = reipl_block_nvme;
+               break;
        case IPL_TYPE_NSS:
                reipl_block_actual = reipl_block_nss;
                break;
@@ -975,6 +1066,8 @@ static ssize_t reipl_type_store(struct kobject *kobj,
                rc = reipl_set_type(IPL_TYPE_CCW);
        else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
                rc = reipl_set_type(IPL_TYPE_FCP);
+       else if (strncmp(buf, IPL_NVME_STR, strlen(IPL_NVME_STR)) == 0)
+               rc = reipl_set_type(IPL_TYPE_NVME);
        else if (strncmp(buf, IPL_NSS_STR, strlen(IPL_NSS_STR)) == 0)
                rc = reipl_set_type(IPL_TYPE_NSS);
        return (rc != 0) ? rc : len;
@@ -985,6 +1078,7 @@ static struct kobj_attribute reipl_type_attr =
 
 static struct kset *reipl_kset;
 static struct kset *reipl_fcp_kset;
+static struct kset *reipl_nvme_kset;
 
 static void __reipl_run(void *unused)
 {
@@ -1003,11 +1097,14 @@ static void __reipl_run(void *unused)
                else
                        diag308(DIAG308_LOAD_NORMAL, NULL);
                break;
+       case IPL_TYPE_NVME:
+               diag308(DIAG308_SET, reipl_block_nvme);
+               diag308(DIAG308_LOAD_CLEAR, NULL);
+               break;
        case IPL_TYPE_NSS:
                diag308(DIAG308_SET, reipl_block_nss);
                diag308(DIAG308_LOAD_CLEAR, NULL);
                break;
-       case IPL_TYPE_NVME:
        case IPL_TYPE_UNKNOWN:
                diag308(DIAG308_LOAD_CLEAR, NULL);
                break;
@@ -1152,6 +1249,49 @@ out1:
        return rc;
 }
 
+static int __init reipl_nvme_init(void)
+{
+       int rc;
+
+       reipl_block_nvme = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!reipl_block_nvme)
+               return -ENOMEM;
+
+       /* sysfs: create kset for mixing attr group and bin attrs */
+       reipl_nvme_kset = kset_create_and_add(IPL_NVME_STR, NULL,
+                                            &reipl_kset->kobj);
+       if (!reipl_nvme_kset) {
+               free_page((unsigned long) reipl_block_nvme);
+               return -ENOMEM;
+       }
+
+       rc = sysfs_create_group(&reipl_nvme_kset->kobj, &reipl_nvme_attr_group);
+       if (rc) {
+               kset_unregister(reipl_nvme_kset);
+               free_page((unsigned long) reipl_block_nvme);
+               return rc;
+       }
+
+       if (ipl_info.type == IPL_TYPE_NVME) {
+               memcpy(reipl_block_nvme, &ipl_block, sizeof(ipl_block));
+               /*
+                * Fix loadparm: There are systems where the (SCSI) LOADPARM
+                * is invalid in the IPL parameter block, so take it
+                * always from sclp_ipl_info.
+                */
+               memcpy(reipl_block_nvme->nvme.loadparm, sclp_ipl_info.loadparm,
+                      LOADPARM_LEN);
+       } else {
+               reipl_block_nvme->hdr.len = IPL_BP_NVME_LEN;
+               reipl_block_nvme->hdr.version = IPL_PARM_BLOCK_VERSION;
+               reipl_block_nvme->nvme.len = IPL_BP0_NVME_LEN;
+               reipl_block_nvme->nvme.pbt = IPL_PBT_NVME;
+               reipl_block_nvme->nvme.opt = IPL_PB0_NVME_OPT_IPL;
+       }
+       reipl_capabilities |= IPL_TYPE_NVME;
+       return 0;
+}
+
 static int __init reipl_type_init(void)
 {
        enum ipl_type reipl_type = ipl_info.type;
@@ -1167,6 +1307,9 @@ static int __init reipl_type_init(void)
        if (reipl_block->pb0_hdr.pbt == IPL_PBT_FCP) {
                memcpy(reipl_block_fcp, reipl_block, size);
                reipl_type = IPL_TYPE_FCP;
+       } else if (reipl_block->pb0_hdr.pbt == IPL_PBT_NVME) {
+               memcpy(reipl_block_nvme, reipl_block, size);
+               reipl_type = IPL_TYPE_NVME;
        } else if (reipl_block->pb0_hdr.pbt == IPL_PBT_CCW) {
                memcpy(reipl_block_ccw, reipl_block, size);
                reipl_type = IPL_TYPE_CCW;
@@ -1191,6 +1334,9 @@ static int __init reipl_init(void)
        if (rc)
                return rc;
        rc = reipl_fcp_init();
+       if (rc)
+               return rc;
+       rc = reipl_nvme_init();
        if (rc)
                return rc;
        rc = reipl_nss_init();