xref: /openbsd-src/sys/scsi/mpath_rdac.c (revision 1525749fb89da5e6992ed4d61159727e5f0e49b5)
1*1525749fSvisa /*	$OpenBSD: mpath_rdac.c,v 1.27 2022/07/02 08:50:42 visa Exp $ */
27e24ec3eSdlg 
37e24ec3eSdlg /*
47e24ec3eSdlg  * Copyright (c) 2010 David Gwynne <dlg@openbsd.org>
57e24ec3eSdlg  *
67e24ec3eSdlg  * Permission to use, copy, modify, and distribute this software for any
77e24ec3eSdlg  * purpose with or without fee is hereby granted, provided that the above
87e24ec3eSdlg  * copyright notice and this permission notice appear in all copies.
97e24ec3eSdlg  *
107e24ec3eSdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
117e24ec3eSdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127e24ec3eSdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137e24ec3eSdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147e24ec3eSdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157e24ec3eSdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167e24ec3eSdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177e24ec3eSdlg  */
187e24ec3eSdlg 
197e24ec3eSdlg /* Redundant Disk Array Controller support for mpath(4) */
207e24ec3eSdlg 
217e24ec3eSdlg #include <sys/param.h>
227e24ec3eSdlg #include <sys/systm.h>
237e24ec3eSdlg #include <sys/kernel.h>
247e24ec3eSdlg #include <sys/malloc.h>
257e24ec3eSdlg #include <sys/device.h>
267e24ec3eSdlg #include <sys/conf.h>
277e24ec3eSdlg #include <sys/queue.h>
287e24ec3eSdlg #include <sys/rwlock.h>
297e24ec3eSdlg #include <sys/pool.h>
307e24ec3eSdlg #include <sys/ioctl.h>
317e24ec3eSdlg 
327e24ec3eSdlg #include <scsi/scsi_all.h>
337e24ec3eSdlg #include <scsi/scsiconf.h>
347e24ec3eSdlg #include <scsi/mpathvar.h>
357e24ec3eSdlg 
367e24ec3eSdlg struct rdac_common_mode_page {
377e24ec3eSdlg 	u_int8_t	controller_serial[16];
387e24ec3eSdlg 	u_int8_t	alt_controller_serial[16];
397e24ec3eSdlg 	u_int8_t	mode[2];
407e24ec3eSdlg 	u_int8_t	alt_mode[2];
417e24ec3eSdlg 	u_int8_t	timeout;
427e24ec3eSdlg 	u_int8_t	options;
437e24ec3eSdlg };
447e24ec3eSdlg 
457e24ec3eSdlg /*
467e24ec3eSdlg  * RDAC VPD pages
477e24ec3eSdlg  */
487e24ec3eSdlg #define RDAC_VPD_HDWVER		0xc0	/* Hardware Version */
497e24ec3eSdlg #define RDAC_VPD_SERNUM		0xc1	/* Serial Numbers */
507e24ec3eSdlg #define RDAC_VPD_SFWVER		0xc2
517e24ec3eSdlg #define RDAC_VPD_FEAPAR		0xc3	/* Feature Parameters */
527e24ec3eSdlg #define RDAC_VPD_SUBSYS		0xc4
537e24ec3eSdlg #define RDAC_VPD_HSTINT		0xc5
547e24ec3eSdlg #define RDAC_VPD_DGM		0xc6
557e24ec3eSdlg #define RDAC_VPD_HSTINT2	0xc7
567e24ec3eSdlg #define RDAC_VPD_EXTDEVID	0xc8
577e24ec3eSdlg #define RDAC_VPD_VOLACCESSCTL	0xc9
587e24ec3eSdlg 
597e24ec3eSdlg struct rdac_vpd_hdwver {
607e24ec3eSdlg 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_HDWVER */
617e24ec3eSdlg 	u_int8_t		pg_id[4];
627e24ec3eSdlg #define RDAC_VPD_ID_HDWVER		0x68777234 /* "hwr4" */
637e24ec3eSdlg 	u_int8_t		num_channels;
647e24ec3eSdlg 	u_int8_t		flags;
657e24ec3eSdlg 	u_int8_t		proc_memory_size;
667e24ec3eSdlg 	u_int8_t		_reserved1[5];
677e24ec3eSdlg 	u_int8_t		board_name[64];
687e24ec3eSdlg 	u_int8_t		board_part_number[16];
697e24ec3eSdlg 	u_int8_t		schematic_number[12];
707e24ec3eSdlg 	u_int8_t		schematic_revision[4];
717e24ec3eSdlg 	u_int8_t		serial_number[16];
727e24ec3eSdlg 	u_int8_t		_reserved2[16];
737e24ec3eSdlg 	u_int8_t		date_manufactured[8];
747e24ec3eSdlg 	u_int8_t		board_revision[2];
757e24ec3eSdlg 	u_int8_t		board_identifier[4];
767e24ec3eSdlg };
777e24ec3eSdlg 
787e24ec3eSdlg struct rdac_vpd_subsys {
797e24ec3eSdlg 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_SUBSYS */
807e24ec3eSdlg 	u_int8_t		pg_id[4];
817e24ec3eSdlg #define RDAC_VPD_ID_SUBSYS		0x73756273 /* "subs" */
827e24ec3eSdlg 	u_int8_t		subsystem_id[16];
837e24ec3eSdlg 	u_int8_t		subsystem_revision[4];
847e24ec3eSdlg 	u_int8_t		controller_slot_id[2];
857e24ec3eSdlg 	u_int8_t		_reserved[2];
867e24ec3eSdlg };
877e24ec3eSdlg 
887e24ec3eSdlg struct rdac_vpd_extdevid {
897e24ec3eSdlg 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_EXTDEVID */
907e24ec3eSdlg 	u_int8_t		pg_id[4];
917e24ec3eSdlg #define RDAC_VPD_ID_EXTDEVID		0x65646964 /* "edid" */
927e24ec3eSdlg 	u_int8_t		_reserved[3];
937e24ec3eSdlg 	u_int8_t		vol_id_len;
947e24ec3eSdlg 	u_int8_t		vol_id[16];
957e24ec3eSdlg 	u_int8_t		vol_label_len;
967e24ec3eSdlg 	u_int8_t		vol_label[60];
977e24ec3eSdlg 	u_int8_t		array_id_len;
987e24ec3eSdlg 	u_int8_t		array_id[16];
997e24ec3eSdlg 	u_int8_t		array_label_len;
1007e24ec3eSdlg 	u_int8_t		array_label[60];
1017e24ec3eSdlg 	u_int8_t		lun[8];
1027e24ec3eSdlg };
1037e24ec3eSdlg 
1047e24ec3eSdlg struct rdac_vpd_volaccessctl {
1057e24ec3eSdlg 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_VOLACCESSCTL */
1067e24ec3eSdlg 	u_int8_t		pg_id[4];
1077e24ec3eSdlg #define RDAC_VPD_ID_VOLACCESSCTL	0x76616331 /* "vac1" */
1087e24ec3eSdlg 	u_int8_t		avtcvp;
1097e24ec3eSdlg #define RDAC_VOLACCESSCTL_OWNER		0x01
1107e24ec3eSdlg #define RDAC_VOLACCESSCTL_AVT		0x70
111e08cdcacSdlg 	u_int8_t		_reserved1;
112e08cdcacSdlg 	u_int8_t		asym_access_state_cur;
113e08cdcacSdlg 	u_int8_t		vendor_specific_cur;
114e08cdcacSdlg 	u_int8_t		_reserved[36];
1157e24ec3eSdlg };
1167e24ec3eSdlg 
1177e24ec3eSdlg struct rdac_softc {
1187e24ec3eSdlg 	struct device		sc_dev;
1197e24ec3eSdlg 	struct mpath_path	sc_path;
120e08cdcacSdlg 	struct scsi_xshandler	sc_xsh;
121e08cdcacSdlg 	struct rdac_vpd_volaccessctl *sc_pg;
1227e24ec3eSdlg };
1237e24ec3eSdlg #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
1247e24ec3eSdlg 
1257e24ec3eSdlg int		rdac_match(struct device *, void *, void *);
1267e24ec3eSdlg void		rdac_attach(struct device *, struct device *, void *);
1277e24ec3eSdlg int		rdac_detach(struct device *, int);
1287e24ec3eSdlg int		rdac_activate(struct device *, int);
1297e24ec3eSdlg 
1309eaf72d1Smpi const struct cfattach rdac_ca = {
1317e24ec3eSdlg 	sizeof(struct rdac_softc),
1327e24ec3eSdlg 	rdac_match,
1337e24ec3eSdlg 	rdac_attach,
1347e24ec3eSdlg 	rdac_detach,
1357e24ec3eSdlg 	rdac_activate
1367e24ec3eSdlg };
1377e24ec3eSdlg 
1387e24ec3eSdlg struct cfdriver rdac_cd = {
1397e24ec3eSdlg 	NULL,
1407e24ec3eSdlg 	"rdac",
1417e24ec3eSdlg 	DV_DULL
1427e24ec3eSdlg };
1437e24ec3eSdlg 
1447e24ec3eSdlg void		rdac_mpath_start(struct scsi_xfer *);
1457e24ec3eSdlg int		rdac_mpath_checksense(struct scsi_xfer *);
1469ce0d10eSdlg void		rdac_mpath_status(struct scsi_link *);
1477e24ec3eSdlg 
148c32a5b15Sdlg const struct mpath_ops rdac_mpath_ops = {
1497e24ec3eSdlg 	"rdac",
1507e24ec3eSdlg 	rdac_mpath_checksense,
1518f5a0873Sdlg 	rdac_mpath_status
1527e24ec3eSdlg };
1537e24ec3eSdlg 
1543c83f1daSdlg int		rdac_extdevid(struct rdac_softc *);
155ecfe95d9Sdlg int		rdac_groupid(struct rdac_softc *);
156e08cdcacSdlg 
157e08cdcacSdlg void		rdac_status(struct scsi_xfer *);
158e08cdcacSdlg void		rdac_status_done(struct scsi_xfer *);
1597e24ec3eSdlg 
1607e24ec3eSdlg struct rdac_device {
1617e24ec3eSdlg 	char *vendor;
1627e24ec3eSdlg 	char *product;
1637e24ec3eSdlg };
1647e24ec3eSdlg 
1657e24ec3eSdlg struct rdac_device rdac_devices[] = {
1667e24ec3eSdlg /*	  " vendor "  "     device     " */
1677e24ec3eSdlg /*	  "01234567"  "0123456789012345" */
1687e24ec3eSdlg 	{ "SUN     ", "CSM200_" },
1697c9637abSdlg 	{ "DELL    ", "MD3000          " },
1707c9637abSdlg 	{ "DELL    ", "MD3000i         " },
1717c9637abSdlg 	{ "DELL    ", "MD32xx          " },
1727c9637abSdlg 	{ "DELL    ", "MD32xxi         " }
1737e24ec3eSdlg };
1747e24ec3eSdlg 
1757e24ec3eSdlg int
rdac_match(struct device * parent,void * match,void * aux)1767e24ec3eSdlg rdac_match(struct device *parent, void *match, void *aux)
1777e24ec3eSdlg {
1787e24ec3eSdlg 	struct scsi_attach_args *sa = aux;
1790fbd355cSkrw 	struct scsi_inquiry_data *inq = &sa->sa_sc_link->inqdata;
1807e24ec3eSdlg 	struct rdac_device *s;
1817e24ec3eSdlg 	int i;
1827e24ec3eSdlg 
1837e24ec3eSdlg 	if (mpath_path_probe(sa->sa_sc_link) != 0)
1847e24ec3eSdlg 		return (0);
1857e24ec3eSdlg 
1867e24ec3eSdlg 	for (i = 0; i < nitems(rdac_devices); i++) {
1877e24ec3eSdlg 		s = &rdac_devices[i];
1887e24ec3eSdlg 
1897e24ec3eSdlg 		if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 &&
1907e24ec3eSdlg 		    bcmp(s->product, inq->product, strlen(s->product)) == 0)
191b2d08956Sdlg 			return (8);
1927e24ec3eSdlg 	}
1937e24ec3eSdlg 
1947e24ec3eSdlg 	return (0);
1957e24ec3eSdlg }
1967e24ec3eSdlg 
1977e24ec3eSdlg void
rdac_attach(struct device * parent,struct device * self,void * aux)1987e24ec3eSdlg rdac_attach(struct device *parent, struct device *self, void *aux)
1997e24ec3eSdlg {
2007e24ec3eSdlg 	struct rdac_softc *sc = (struct rdac_softc *)self;
2017e24ec3eSdlg 	struct scsi_attach_args *sa = aux;
2027e24ec3eSdlg 	struct scsi_link *link = sa->sa_sc_link;
203bba3b8d8Sdlg 	int id;
2047e24ec3eSdlg 
2057e24ec3eSdlg 	printf("\n");
2067e24ec3eSdlg 
2077e24ec3eSdlg 	/* init link */
2087e24ec3eSdlg 	link->device_softc = sc;
2097e24ec3eSdlg 
2107e24ec3eSdlg 	/* init path */
2117e24ec3eSdlg 	scsi_xsh_set(&sc->sc_path.p_xsh, link, rdac_mpath_start);
2127e24ec3eSdlg 	sc->sc_path.p_link = link;
2137e24ec3eSdlg 
214e08cdcacSdlg 	/* init status handler */
215e08cdcacSdlg 	scsi_xsh_set(&sc->sc_xsh, link, rdac_status);
216e08cdcacSdlg 	sc->sc_pg = dma_alloc(sizeof(*sc->sc_pg), PR_WAITOK);
2177e24ec3eSdlg 
218e08cdcacSdlg 	/* let's go */
219e08cdcacSdlg 	if (rdac_extdevid(sc) != 0)
2207e24ec3eSdlg 		return;
2217e24ec3eSdlg 
222bba3b8d8Sdlg 	id = rdac_groupid(sc);
223bba3b8d8Sdlg 	if (id == -1) {
224bba3b8d8Sdlg 		/* error printed by rdac_groupid */
225bba3b8d8Sdlg 		return;
226bba3b8d8Sdlg 	}
227bba3b8d8Sdlg 
228bba3b8d8Sdlg 	if (mpath_path_attach(&sc->sc_path, id, &rdac_mpath_ops) != 0)
2297e24ec3eSdlg 		printf("%s: unable to attach path\n", DEVNAME(sc));
2307e24ec3eSdlg }
2317e24ec3eSdlg 
2327e24ec3eSdlg int
rdac_detach(struct device * self,int flags)2337e24ec3eSdlg rdac_detach(struct device *self, int flags)
2347e24ec3eSdlg {
235e08cdcacSdlg 	struct rdac_softc *sc = (struct rdac_softc *)self;
236e08cdcacSdlg 
237e08cdcacSdlg 	dma_free(sc->sc_pg, sizeof(*sc->sc_pg));
238e08cdcacSdlg 
2397e24ec3eSdlg 	return (0);
2407e24ec3eSdlg }
2417e24ec3eSdlg 
2427e24ec3eSdlg int
rdac_activate(struct device * self,int act)2437e24ec3eSdlg rdac_activate(struct device *self, int act)
2447e24ec3eSdlg {
2457e24ec3eSdlg 	struct rdac_softc *sc = (struct rdac_softc *)self;
2467e24ec3eSdlg 
2477e24ec3eSdlg 	switch (act) {
2487e24ec3eSdlg 	case DVACT_DEACTIVATE:
249e08cdcacSdlg 		if (scsi_xsh_del(&sc->sc_xsh))
250e08cdcacSdlg 			mpath_path_status(&sc->sc_path, MPATH_S_UNKNOWN);
2515a08a2c9Sdlg 		if (sc->sc_path.p_group != NULL)
2527e24ec3eSdlg 			mpath_path_detach(&sc->sc_path);
2537e24ec3eSdlg 		break;
2547e24ec3eSdlg 	}
255f7a04f0fSkrw 	return (0);
2567e24ec3eSdlg }
2577e24ec3eSdlg 
2587e24ec3eSdlg void
rdac_mpath_start(struct scsi_xfer * xs)2597e24ec3eSdlg rdac_mpath_start(struct scsi_xfer *xs)
2607e24ec3eSdlg {
2617e24ec3eSdlg 	struct rdac_softc *sc = xs->sc_link->device_softc;
2627e24ec3eSdlg 
2637e24ec3eSdlg 	mpath_start(&sc->sc_path, xs);
2647e24ec3eSdlg }
2657e24ec3eSdlg 
2667e24ec3eSdlg int
rdac_mpath_checksense(struct scsi_xfer * xs)2677e24ec3eSdlg rdac_mpath_checksense(struct scsi_xfer *xs)
2687e24ec3eSdlg {
269401e70b2Sdlg 	struct scsi_sense_data *sense = &xs->sense;
270401e70b2Sdlg 	u_int8_t skey;
271401e70b2Sdlg 
272401e70b2Sdlg 	if ((sense->error_code & SSD_ERRCODE) != SSD_ERRCODE_CURRENT)
273401e70b2Sdlg 		return (MPATH_SENSE_DECLINED);
274401e70b2Sdlg 
275401e70b2Sdlg 	skey = sense->flags & SSD_KEY;
276401e70b2Sdlg 
277401e70b2Sdlg 	/* i wish i knew what the magic numbers meant */
278401e70b2Sdlg 
27950e5b7a7Sdlg 	/* invalid request due to current lu ownership */
280401e70b2Sdlg 	if (skey == SKEY_ILLEGAL_REQUEST && ASC_ASCQ(sense) == 0x9401)
281401e70b2Sdlg 		return (MPATH_SENSE_FAILOVER);
282401e70b2Sdlg 
283401e70b2Sdlg 	if (skey == SKEY_UNIT_ATTENTION && ASC_ASCQ(sense) == 0x8b02)
284401e70b2Sdlg 		return (MPATH_SENSE_FAILOVER);
285401e70b2Sdlg 
286cdf96446Sdlg 	return (MPATH_SENSE_DECLINED);
2877e24ec3eSdlg }
2887e24ec3eSdlg 
2899ce0d10eSdlg void
rdac_mpath_status(struct scsi_link * link)2909ce0d10eSdlg rdac_mpath_status(struct scsi_link *link)
2917e24ec3eSdlg {
2929ce0d10eSdlg 	struct rdac_softc *sc = link->device_softc;
2937e24ec3eSdlg 
294e08cdcacSdlg 	scsi_xsh_add(&sc->sc_xsh);
295e08cdcacSdlg }
296e08cdcacSdlg 
297e08cdcacSdlg void
rdac_status(struct scsi_xfer * xs)298e08cdcacSdlg rdac_status(struct scsi_xfer *xs)
299e08cdcacSdlg {
300e08cdcacSdlg 	struct scsi_link *link = xs->sc_link;
301e08cdcacSdlg 	struct rdac_softc *sc = link->device_softc;
302e08cdcacSdlg 
303e08cdcacSdlg 	scsi_init_inquiry(xs, SI_EVPD, RDAC_VPD_VOLACCESSCTL,
304e08cdcacSdlg 	    sc->sc_pg, sizeof(*sc->sc_pg));
305e08cdcacSdlg 
306e08cdcacSdlg 	xs->done = rdac_status_done;
307e08cdcacSdlg 
308e08cdcacSdlg 	scsi_xs_exec(xs);
309e08cdcacSdlg }
310e08cdcacSdlg 
311e08cdcacSdlg void
rdac_status_done(struct scsi_xfer * xs)312e08cdcacSdlg rdac_status_done(struct scsi_xfer *xs)
313e08cdcacSdlg {
314e08cdcacSdlg 	struct scsi_link *link = xs->sc_link;
315e08cdcacSdlg 	struct rdac_softc *sc = link->device_softc;
316e08cdcacSdlg 	struct rdac_vpd_volaccessctl *pg = sc->sc_pg;
317e08cdcacSdlg 	int status = MPATH_S_UNKNOWN;
318e08cdcacSdlg 
319e08cdcacSdlg 	if (xs->error == XS_NOERROR &&
320e08cdcacSdlg 	    _4btol(pg->pg_id) == RDAC_VPD_ID_VOLACCESSCTL) {
321e08cdcacSdlg 		status = (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_AVT) ||
322e08cdcacSdlg 		    ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_OWNER)) ?
323e08cdcacSdlg 		    MPATH_S_ACTIVE : MPATH_S_PASSIVE;
324e08cdcacSdlg 	}
325e08cdcacSdlg 
326e08cdcacSdlg 	scsi_xs_put(xs);
327e08cdcacSdlg 	mpath_path_status(&sc->sc_path, status);
3287e24ec3eSdlg }
3297e24ec3eSdlg 
3307e24ec3eSdlg int
rdac_groupid(struct rdac_softc * sc)331ecfe95d9Sdlg rdac_groupid(struct rdac_softc *sc)
332ecfe95d9Sdlg {
333ecfe95d9Sdlg 	struct rdac_vpd_subsys *pg;
334ecfe95d9Sdlg 	int rv = -1;
335ecfe95d9Sdlg 
336ecfe95d9Sdlg 	pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
337ecfe95d9Sdlg 
338ecfe95d9Sdlg 	if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
339ecfe95d9Sdlg 	    RDAC_VPD_SUBSYS, scsi_autoconf) != 0) {
340ecfe95d9Sdlg 		printf("%s: unable to fetch subsys vpd page\n", DEVNAME(sc));
341ecfe95d9Sdlg 		goto done;
342ecfe95d9Sdlg 	}
343ecfe95d9Sdlg 
344ecfe95d9Sdlg 	if (_4btol(pg->pg_id) != RDAC_VPD_ID_SUBSYS) {
345ecfe95d9Sdlg 		printf("%s: subsys page is invalid\n", DEVNAME(sc));
346ecfe95d9Sdlg 		goto done;
347ecfe95d9Sdlg 	}
348ecfe95d9Sdlg 
349ecfe95d9Sdlg 	rv = _2btol(pg->controller_slot_id);
350ecfe95d9Sdlg 
351ecfe95d9Sdlg done:
352ecfe95d9Sdlg 	dma_free(pg, sizeof(*pg));
353ecfe95d9Sdlg 	return (rv);
354ecfe95d9Sdlg }
355ecfe95d9Sdlg 
356ecfe95d9Sdlg int
rdac_extdevid(struct rdac_softc * sc)3573c83f1daSdlg rdac_extdevid(struct rdac_softc *sc)
3587e24ec3eSdlg {
3595dc09c93Sdlg 	struct rdac_vpd_extdevid *pg;
3607e24ec3eSdlg 	char array[31];
3617e24ec3eSdlg 	char vol[31];
3627e24ec3eSdlg 	int i;
3635dc09c93Sdlg 	int rv = 1;
3647e24ec3eSdlg 
3655dc09c93Sdlg 	pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
3665dc09c93Sdlg 
3673c83f1daSdlg 	if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
3683c83f1daSdlg 	    RDAC_VPD_EXTDEVID, scsi_autoconf) != 0) {
3693c83f1daSdlg 		printf("%s: unable to fetch extdevid vpd page\n", DEVNAME(sc));
3705dc09c93Sdlg 		goto done;
3717e24ec3eSdlg 	}
3727e24ec3eSdlg 
3735dc09c93Sdlg 	if (_4btol(pg->pg_id) != RDAC_VPD_ID_EXTDEVID) {
37467ad556dSdlg 		printf("%s: extdevid page is invalid\n", DEVNAME(sc));
3755dc09c93Sdlg 		goto done;
3767e24ec3eSdlg 	}
3777e24ec3eSdlg 
3787e24ec3eSdlg 	memset(array, 0, sizeof(array));
3795dc09c93Sdlg 	for (i = 0; i < sizeof(pg->array_label) / 2; i++)
3805dc09c93Sdlg 		array[i] = pg->array_label[i * 2 + 1];
3817e24ec3eSdlg 
3827e24ec3eSdlg 	memset(vol, 0, sizeof(vol));
3835dc09c93Sdlg 	for (i = 0; i < sizeof(pg->vol_label) / 2; i++)
3845dc09c93Sdlg 		vol[i] = pg->vol_label[i * 2 + 1];
3857e24ec3eSdlg 
3867e24ec3eSdlg 	printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol);
3877e24ec3eSdlg 
3885dc09c93Sdlg 	rv = 0;
3895dc09c93Sdlg done:
3905dc09c93Sdlg 	dma_free(pg, sizeof(*pg));
3915dc09c93Sdlg 	return (rv);
3927e24ec3eSdlg }
393