Commit 23a457b8 authored by Jason J. Herne's avatar Jason J. Herne Committed by Vasily Gorbik
Browse files

s390: nvme reipl



Populate sysfs and structs with reipl entries for nvme ipl type.
This allows specifying a target nvme device when rebooting/reipling.

Signed-off-by: default avatarJason J. Herne <jjherne@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 3737e8ee
Loading
Loading
Loading
Loading
+147 −1
Original line number Diff line number Diff line
@@ -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();