Blame SOURCES/0009-libmultipath-add-code-to-get-vendor-specific-vpd-dat.patch

b6d9ac
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
b6d9ac
From: Benjamin Marzinski <bmarzins@redhat.com>
b6d9ac
Date: Mon, 4 Nov 2019 15:38:25 -0600
b6d9ac
Subject: [PATCH] libmultipath: add code to get vendor specific vpd data
b6d9ac
b6d9ac
This adds the wildcard 'g' for multipath and path formatted printing,
b6d9ac
which returns extra data from a device's vendor specific vpd page.  The
b6d9ac
specific vendor vpd page to use, and the vendor/product id to decode it
b6d9ac
can be set in the hwentry with vpd_vendor_pg and vpd_vendor_id. It can
b6d9ac
be configured in the devices section of multipath.conf with the
b6d9ac
vpd_vendor parameter. Currently, the only devices that use this are HPE
b6d9ac
3PAR arrays, to return the Volume Name.
b6d9ac
b6d9ac
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
b6d9ac
---
b6d9ac
 libmultipath/config.c      |  4 ++++
b6d9ac
 libmultipath/config.h      |  2 ++
b6d9ac
 libmultipath/dict.c        | 34 ++++++++++++++++++++++++++++++++++
b6d9ac
 libmultipath/discovery.c   | 34 +++++++++++++++++++++++++++++++++-
b6d9ac
 libmultipath/hwtable.c     |  2 ++
b6d9ac
 libmultipath/print.c       | 27 +++++++++++++++++++++++++++
b6d9ac
 libmultipath/propsel.c     | 24 ++++++++++++++++++++++++
b6d9ac
 libmultipath/propsel.h     |  2 ++
b6d9ac
 libmultipath/structs.h     |  9 +++++++++
b6d9ac
 multipath/multipath.conf.5 |  8 ++++++++
b6d9ac
 10 files changed, 145 insertions(+), 1 deletion(-)
b6d9ac
b6d9ac
diff --git a/libmultipath/config.c b/libmultipath/config.c
b6d9ac
index 85626e96..72b8d37c 100644
b6d9ac
--- a/libmultipath/config.c
b6d9ac
+++ b/libmultipath/config.c
b6d9ac
@@ -369,6 +369,8 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
b6d9ac
 	merge_num(max_sectors_kb);
b6d9ac
 	merge_num(ghost_delay);
b6d9ac
 	merge_num(all_tg_pt);
b6d9ac
+	merge_num(vpd_vendor_pg);
b6d9ac
+	merge_num(vpd_vendor_id);
b6d9ac
 	merge_num(san_path_err_threshold);
b6d9ac
 	merge_num(san_path_err_forget_rate);
b6d9ac
 	merge_num(san_path_err_recovery_time);
b6d9ac
@@ -517,6 +519,8 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
b6d9ac
 	hwe->detect_prio = dhwe->detect_prio;
b6d9ac
 	hwe->detect_checker = dhwe->detect_checker;
b6d9ac
 	hwe->ghost_delay = dhwe->ghost_delay;
b6d9ac
+	hwe->vpd_vendor_pg = dhwe->vpd_vendor_pg;
b6d9ac
+	hwe->vpd_vendor_id = dhwe->vpd_vendor_id;
b6d9ac
 
b6d9ac
 	if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product)))
b6d9ac
 		goto out;
b6d9ac
diff --git a/libmultipath/config.h b/libmultipath/config.h
b6d9ac
index e69aa07c..589146de 100644
b6d9ac
--- a/libmultipath/config.h
b6d9ac
+++ b/libmultipath/config.h
b6d9ac
@@ -87,6 +87,8 @@ struct hwentry {
b6d9ac
 	int max_sectors_kb;
b6d9ac
 	int ghost_delay;
b6d9ac
 	int all_tg_pt;
b6d9ac
+	int vpd_vendor_pg;
b6d9ac
+	int vpd_vendor_id;
b6d9ac
 	char * bl_product;
b6d9ac
 };
b6d9ac
 
b6d9ac
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
b6d9ac
index 2b046e1d..d6d8b79b 100644
b6d9ac
--- a/libmultipath/dict.c
b6d9ac
+++ b/libmultipath/dict.c
b6d9ac
@@ -1366,6 +1366,39 @@ def_uxsock_timeout_handler(struct config *conf, vector strvec)
b6d9ac
 	return 0;
b6d9ac
 }
b6d9ac
 
b6d9ac
+static int
b6d9ac
+hw_vpd_vendor_handler(struct config *conf, vector strvec)
b6d9ac
+{
b6d9ac
+	int rc = 0;
b6d9ac
+	char *buff;
b6d9ac
+
b6d9ac
+	struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
b6d9ac
+	if (!hwe)
b6d9ac
+		return 1;
b6d9ac
+
b6d9ac
+	buff = set_value(strvec);
b6d9ac
+	if (!buff)
b6d9ac
+		return 1;
b6d9ac
+	if (strcmp(buff, "hp3par") == 0) {
b6d9ac
+		hwe->vpd_vendor_pg = 0xc0;
b6d9ac
+		hwe->vpd_vendor_id = VPD_VP_HP3PAR;
b6d9ac
+	} else
b6d9ac
+		rc = 1;
b6d9ac
+	FREE(buff);
b6d9ac
+	return rc;
b6d9ac
+}
b6d9ac
+
b6d9ac
+static int
b6d9ac
+snprint_hw_vpd_vendor(struct config *conf, char * buff, int len,
b6d9ac
+		      const void * data)
b6d9ac
+{
b6d9ac
+	const struct hwentry * hwe = (const struct hwentry *)data;
b6d9ac
+
b6d9ac
+	if (hwe->vpd_vendor_pg == 0xc0 && hwe->vpd_vendor_id == VPD_VP_HP3PAR)
b6d9ac
+		return snprintf(buff, len, "hp3par");
b6d9ac
+	return 0;
b6d9ac
+}
b6d9ac
+
b6d9ac
 /*
b6d9ac
  * blacklist block handlers
b6d9ac
  */
b6d9ac
@@ -1806,6 +1839,7 @@ init_keywords(vector keywords)
b6d9ac
 	install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb);
b6d9ac
 	install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay);
b6d9ac
 	install_keyword("all_tg_pt", &hw_all_tg_pt_handler, &snprint_hw_all_tg_pt);
b6d9ac
+	install_keyword("vpd_vendor", &hw_vpd_vendor_handler, &snprint_hw_vpd_vendor);
b6d9ac
 	install_sublevel_end();
b6d9ac
 
b6d9ac
 	install_keyword_root("overrides", &overrides_handler);
b6d9ac
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
b6d9ac
index 1d79cbae..d2773c3a 100644
b6d9ac
--- a/libmultipath/discovery.c
b6d9ac
+++ b/libmultipath/discovery.c
b6d9ac
@@ -1103,6 +1103,30 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
b6d9ac
 	return len;
b6d9ac
 }
b6d9ac
 
b6d9ac
+static int
b6d9ac
+parse_vpd_c0_hp3par(const unsigned char *in, size_t in_len,
b6d9ac
+		    char *out, size_t out_len)
b6d9ac
+{
b6d9ac
+	size_t len;
b6d9ac
+
b6d9ac
+	memset(out, 0x0, out_len);
b6d9ac
+	if (in_len <= 4 || (in[4] > 3 && in_len < 44)) {
b6d9ac
+		condlog(3, "HP/3PAR vendor specific VPD page length too short: %lu", in_len);
b6d9ac
+		return -EINVAL;
b6d9ac
+	}
b6d9ac
+	if (in[4] <= 3) /* revision must be > 3 to have Vomlume Name */
b6d9ac
+		return -ENODATA;
b6d9ac
+	len = get_unaligned_be32(&in[40]);
b6d9ac
+	if (len > out_len || len + 44 > in_len) {
b6d9ac
+		condlog(3, "HP/3PAR vendor specific Volume name too long: %lu",
b6d9ac
+			len);
b6d9ac
+		return -EINVAL;
b6d9ac
+	}
b6d9ac
+	memcpy(out, &in[44], len);
b6d9ac
+	out[out_len - 1] = '\0';
b6d9ac
+	return len;
b6d9ac
+}
b6d9ac
+
b6d9ac
 static int
b6d9ac
 get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
b6d9ac
 {
b6d9ac
@@ -1170,7 +1194,9 @@ get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen)
b6d9ac
 			len = (buff_len <= maxlen)? buff_len : maxlen;
b6d9ac
 			memcpy (str, buff, len);
b6d9ac
 		}
b6d9ac
-	} else
b6d9ac
+	} else if (pg == 0xc0 && vend_id == VPD_VP_HP3PAR)
b6d9ac
+		len = parse_vpd_c0_hp3par(buff, buff_len, str, maxlen);
b6d9ac
+	else
b6d9ac
 		len = -ENOSYS;
b6d9ac
 
b6d9ac
 	return len;
b6d9ac
@@ -1544,6 +1570,12 @@ scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask)
b6d9ac
 	if (!(mask & DI_SERIAL))
b6d9ac
 		return;
b6d9ac
 
b6d9ac
+	select_vpd_vendor_pg(conf, pp);
b6d9ac
+	select_vpd_vendor_id(conf, pp);
b6d9ac
+
b6d9ac
+	if (pp->vpd_vendor_pg != 0 && get_vpd_sgio(pp->fd, pp->vpd_vendor_pg, pp->vpd_vendor_id, pp->vpd_data, sizeof(pp->vpd_data)) < 0)
b6d9ac
+		condlog(3, "%s: failed to get extra vpd data", pp->dev);
b6d9ac
+
b6d9ac
 	parent = pp->udev;
b6d9ac
 	while (parent) {
b6d9ac
 		const char *subsys = udev_device_get_subsystem(parent);
b6d9ac
diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
b6d9ac
index 16627ec5..1f27450c 100644
b6d9ac
--- a/libmultipath/hwtable.c
b6d9ac
+++ b/libmultipath/hwtable.c
b6d9ac
@@ -117,6 +117,8 @@ static struct hwentry default_hw[] = {
b6d9ac
 		.no_path_retry = 18,
b6d9ac
 		.fast_io_fail  = 10,
b6d9ac
 		.dev_loss      = MAX_DEV_LOSS_TMO,
b6d9ac
+		.vpd_vendor_pg = 0xc0,
b6d9ac
+		.vpd_vendor_id = VPD_VP_HP3PAR,
b6d9ac
 	},
b6d9ac
 	{
b6d9ac
 		/* RA8000 / ESA12000 */
b6d9ac
diff --git a/libmultipath/print.c b/libmultipath/print.c
b6d9ac
index 907469ad..0aafe3cb 100644
b6d9ac
--- a/libmultipath/print.c
b6d9ac
+++ b/libmultipath/print.c
b6d9ac
@@ -358,6 +358,23 @@ snprint_action (char * buff, size_t len, const struct multipath * mpp)
b6d9ac
 	}
b6d9ac
 }
b6d9ac
 
b6d9ac
+static int
b6d9ac
+snprint_multipath_vpd_data(char * buff, size_t len,
b6d9ac
+			   const struct multipath * mpp)
b6d9ac
+{
b6d9ac
+	struct pathgroup * pgp;
b6d9ac
+	struct path * pp;
b6d9ac
+	int i, j;
b6d9ac
+
b6d9ac
+	vector_foreach_slot(mpp->pg, pgp, i) {
b6d9ac
+		vector_foreach_slot(pgp->paths, pp, j) {
b6d9ac
+			if (strlen(pp->vpd_data))
b6d9ac
+				return snprintf(buff, len, "%s", pp->vpd_data);
b6d9ac
+		}
b6d9ac
+	}
b6d9ac
+	return 0;
b6d9ac
+}
b6d9ac
+
b6d9ac
 /*
b6d9ac
  * path info printing functions
b6d9ac
  */
b6d9ac
@@ -688,6 +705,14 @@ snprint_path_marginal(char * buff, size_t len, const struct path * pp)
b6d9ac
 	return snprintf(buff, len, "normal");
b6d9ac
 }
b6d9ac
 
b6d9ac
+static int
b6d9ac
+snprint_path_vpd_data(char * buff, size_t len, const struct path * pp)
b6d9ac
+{
b6d9ac
+	if (strlen(pp->vpd_data) > 0)
b6d9ac
+		return snprintf(buff, len, "%s", pp->vpd_data);
b6d9ac
+	return 0;
b6d9ac
+}
b6d9ac
+
b6d9ac
 struct multipath_data mpd[] = {
b6d9ac
 	{'n', "name",          0, snprint_name},
b6d9ac
 	{'w', "uuid",          0, snprint_multipath_uuid},
b6d9ac
@@ -712,6 +737,7 @@ struct multipath_data mpd[] = {
b6d9ac
 	{'p', "prod",          0, snprint_multipath_prod},
b6d9ac
 	{'e', "rev",           0, snprint_multipath_rev},
b6d9ac
 	{'G', "foreign",       0, snprint_multipath_foreign},
b6d9ac
+	{'g', "vpd page data", 0, snprint_multipath_vpd_data},
b6d9ac
 	{0, NULL, 0 , NULL}
b6d9ac
 };
b6d9ac
 
b6d9ac
@@ -737,6 +763,7 @@ struct path_data pd[] = {
b6d9ac
 	{'r', "target WWPN",   0, snprint_tgt_wwpn},
b6d9ac
 	{'a', "host adapter",  0, snprint_host_adapter},
b6d9ac
 	{'G', "foreign",       0, snprint_path_foreign},
b6d9ac
+	{'g', "vpd page data", 0, snprint_path_vpd_data},
b6d9ac
 	{'0', "failures",      0, snprint_path_failures},
b6d9ac
 	{'P', "protocol",      0, snprint_path_protocol},
b6d9ac
 	{0, NULL, 0 , NULL}
b6d9ac
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
b6d9ac
index b5b5b89f..3c99f2d4 100644
b6d9ac
--- a/libmultipath/propsel.c
b6d9ac
+++ b/libmultipath/propsel.c
b6d9ac
@@ -1203,3 +1203,27 @@ out:
b6d9ac
 		origin);
b6d9ac
 	return 0;
b6d9ac
 }
b6d9ac
+
b6d9ac
+int select_vpd_vendor_pg (struct config *conf, struct path *pp)
b6d9ac
+{
b6d9ac
+	const char *origin;
b6d9ac
+
b6d9ac
+	pp_set_hwe(vpd_vendor_pg);
b6d9ac
+	pp_set_default(vpd_vendor_pg, 0);
b6d9ac
+out:
b6d9ac
+	condlog(3, "%s: vpd_vendor_pg = 0x%x %s", pp->dev, pp->vpd_vendor_pg,
b6d9ac
+		origin);
b6d9ac
+	return 0;
b6d9ac
+}
b6d9ac
+
b6d9ac
+int select_vpd_vendor_id (struct config *conf, struct path *pp)
b6d9ac
+{
b6d9ac
+	const char *origin;
b6d9ac
+
b6d9ac
+	pp_set_hwe(vpd_vendor_id);
b6d9ac
+	pp_set_default(vpd_vendor_id, 0);
b6d9ac
+out:
b6d9ac
+	condlog(3, "%s: vpd_vendor_id = 0x%x %s", pp->dev, pp->vpd_vendor_id,
b6d9ac
+		origin);
b6d9ac
+	return 0;
b6d9ac
+}
b6d9ac
diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h
b6d9ac
index ddfd6262..3f6d319a 100644
b6d9ac
--- a/libmultipath/propsel.h
b6d9ac
+++ b/libmultipath/propsel.h
b6d9ac
@@ -37,3 +37,5 @@ void reconcile_features_with_options(const char *id, char **features,
b6d9ac
 				     int* no_path_retry,
b6d9ac
 				     int *retain_hwhandler);
b6d9ac
 int select_all_tg_pt (struct config *conf, struct multipath * mp);
b6d9ac
+int select_vpd_vendor_pg (struct config *conf, struct path *pp);
b6d9ac
+int select_vpd_vendor_id (struct config *conf, struct path *pp);
b6d9ac
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
b6d9ac
index 1c32a799..1ad5f64a 100644
b6d9ac
--- a/libmultipath/structs.h
b6d9ac
+++ b/libmultipath/structs.h
b6d9ac
@@ -21,6 +21,7 @@
b6d9ac
 #define HOST_NAME_LEN		16
b6d9ac
 #define SLOT_NAME_SIZE		40
b6d9ac
 #define PRKEY_SIZE		19
b6d9ac
+#define VPD_DATA_SIZE		128
b6d9ac
 
b6d9ac
 #define SCSI_VENDOR_SIZE	9
b6d9ac
 #define SCSI_PRODUCT_SIZE	17
b6d9ac
@@ -243,6 +244,11 @@ struct hd_geometry {
b6d9ac
 };
b6d9ac
 #endif
b6d9ac
 
b6d9ac
+/*
b6d9ac
+ * from sg_vpd_vendor.c
b6d9ac
+ */
b6d9ac
+#define VPD_VP_HP3PAR 4
b6d9ac
+
b6d9ac
 struct path {
b6d9ac
 	char dev[FILE_NAME_SIZE];
b6d9ac
 	char dev_t[BLK_DEV_SIZE];
b6d9ac
@@ -255,6 +261,7 @@ struct path {
b6d9ac
 	char rev[PATH_REV_SIZE];
b6d9ac
 	char serial[SERIAL_SIZE];
b6d9ac
 	char tgt_node_name[NODE_NAME_SIZE];
b6d9ac
+	char vpd_data[VPD_DATA_SIZE];
b6d9ac
 	unsigned long long size;
b6d9ac
 	unsigned int checkint;
b6d9ac
 	unsigned int tick;
b6d9ac
@@ -287,6 +294,8 @@ struct path {
b6d9ac
 	int io_err_pathfail_starttime;
b6d9ac
 	int find_multipaths_timeout;
b6d9ac
 	int marginal;
b6d9ac
+	int vpd_vendor_pg;
b6d9ac
+	int vpd_vendor_id;
b6d9ac
 	/* configlet pointers */
b6d9ac
 	vector hwe;
b6d9ac
 	struct gen_path generic_path;
b6d9ac
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
b6d9ac
index e866da23..dc103fd8 100644
b6d9ac
--- a/multipath/multipath.conf.5
b6d9ac
+++ b/multipath/multipath.conf.5
b6d9ac
@@ -1472,6 +1472,14 @@ the \fIproduct\fR attribute set to the value of \fIproduct_blacklist\fR.
b6d9ac
 The user_friendly_names prefix to use for this
b6d9ac
 device type, instead of the default "mpath".
b6d9ac
 .TP
b6d9ac
+.B vpd_vendor
b6d9ac
+The vendor specific vpd page information, using the vpd page abbreviation.
b6d9ac
+The vpd page abbreviation can be found by running \fIsg_vpd -e\fR. multipathd
b6d9ac
+will use this information to gather device specific information that can be
b6d9ac
+displayed with the \fI%g\fR wilcard for the \fImultipathd show maps format\fR
b6d9ac
+and \fImultipathd show paths format\fR commands. Currently only the
b6d9ac
+\fBhp3par\fR vpd page is supported.
b6d9ac
+.TP
b6d9ac
 .B hardware_handler
b6d9ac
 The hardware handler to use for this device type.
b6d9ac
 The following hardware handler are implemented:
b6d9ac
-- 
b6d9ac
2.17.2
b6d9ac