17bc7346cScm /*
27bc7346cScm  * CDDL HEADER START
37bc7346cScm  *
47bc7346cScm  * The contents of this file are subject to the terms of the
57bc7346cScm  * Common Development and Distribution License (the "License").
67bc7346cScm  * You may not use this file except in compliance with the License.
77bc7346cScm  *
87bc7346cScm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97bc7346cScm  * or http://www.opensolaris.org/os/licensing.
107bc7346cScm  * See the License for the specific language governing permissions
117bc7346cScm  * and limitations under the License.
127bc7346cScm  *
137bc7346cScm  * When distributing Covered Code, include this CDDL HEADER in each
147bc7346cScm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157bc7346cScm  * If applicable, add the following below this CDDL HEADER, with the
167bc7346cScm  * fields enclosed by brackets "[]" replaced with your own identifying
177bc7346cScm  * information: Portions Copyright [yyyy] [name of copyright owner]
187bc7346cScm  *
197bc7346cScm  * CDDL HEADER END
207bc7346cScm  */
217bc7346cScm 
22123a6614Scm /* Portions Copyright 2008 Hitachi Ltd. */
237bc7346cScm 
247bc7346cScm /*
25*55e592a2SRandall Ralphs  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
267bc7346cScm  * Use is subject to license terms.
277bc7346cScm  */
287bc7346cScm 
297bc7346cScm /*
307bc7346cScm  * Implementation of "scsi_vhci_f_sym_hds" asymmetric-active-active
317bc7346cScm  * failover_ops. The device has a preferred(owner)/non-preferred
327bc7346cScm  * with no action needed to use the non-preferred path. This is really
337bc7346cScm  * more inline with symmetric device so am using that prefix.
347bc7346cScm  *
357bc7346cScm  * This file imports the standard "scsi_vhci_f_sym", but with HDS specific
367bc7346cScm  * knowledge related to preferred/non-preferred path.
377bc7346cScm  */
387bc7346cScm 
397bc7346cScm #include <sys/conf.h>
407bc7346cScm #include <sys/file.h>
417bc7346cScm #include <sys/ddi.h>
427bc7346cScm #include <sys/sunddi.h>
437bc7346cScm #include <sys/scsi/scsi.h>
447bc7346cScm #include <sys/scsi/adapters/scsi_vhci.h>
457bc7346cScm 
467bc7346cScm /* Supported device table entries.  */
477bc7346cScm char *hds_sym_dev_table[] = {
487bc7346cScm /*	"                  111111" */
497bc7346cScm /*	"012345670123456789012345" */
507bc7346cScm /*	"|-VID--||-----PID------|" */
517bc7346cScm 
527bc7346cScm 	"HITACHI DF",
537bc7346cScm 	NULL
547bc7346cScm };
557bc7346cScm 
567bc7346cScm static int	hds_sym_device_probe(struct scsi_device *,
577bc7346cScm 			struct scsi_inquiry *, void **);
587bc7346cScm static void	hds_sym_device_unprobe(struct scsi_device *, void *);
597bc7346cScm static void	hds_sym_init();
607bc7346cScm static int	hds_sym_get_opinfo(struct scsi_device *sd,
617bc7346cScm 			struct scsi_path_opinfo *opinfo, void *ctpriv);
627bc7346cScm 
637bc7346cScm #ifdef	lint
647bc7346cScm #define	scsi_vhci_failover_ops	scsi_vhci_failover_ops_f_sym_hds
657bc7346cScm #endif	/* lint */
667bc7346cScm /*
677bc7346cScm  * Use the following for the Asymmetric-Active-Active fops.
687bc7346cScm  * A different fops may get used for the Symmetric-Active-Active.
697bc7346cScm  */
707bc7346cScm struct scsi_failover_ops scsi_vhci_failover_ops = {
717bc7346cScm 	SFO_REV,
727bc7346cScm 	SFO_NAME_SYM "_hds",
737bc7346cScm 	hds_sym_dev_table,
747bc7346cScm 	hds_sym_init,
757bc7346cScm 	hds_sym_device_probe,
767bc7346cScm 	hds_sym_device_unprobe,
777bc7346cScm 	NULL,
787bc7346cScm 	NULL,
797bc7346cScm 	hds_sym_get_opinfo,
807bc7346cScm 	/* The rest of the implementation comes from SFO_NAME_SYM import  */
817bc7346cScm };
827bc7346cScm 
837bc7346cScm static struct modlmisc modlmisc = {
8439b361b2SRichard Bean 	&mod_miscops, "f_sym_hds"
857bc7346cScm };
867bc7346cScm 
877bc7346cScm static struct modlinkage modlinkage = {
887bc7346cScm 	MODREV_1, (void *)&modlmisc, NULL
897bc7346cScm };
907bc7346cScm 
917bc7346cScm #define	HDS_MAX_INQ_BUF_SIZE		0xff
927bc7346cScm #define	HDS_INQ_PAGE_E0			0xe0
937bc7346cScm #define	HDS_SAA_TYPE			"DF00"
947bc7346cScm #define	ASYM_ACTIVE_ACTIVE		0
957bc7346cScm #define	SYM_ACTIVE_ACTIVE		1
967bc7346cScm 
977bc7346cScm extern struct scsi_failover_ops	*vhci_failover_ops_by_name(char *);
987bc7346cScm 
997bc7346cScm int
_init()1007bc7346cScm _init()
1017bc7346cScm {
1027bc7346cScm 	return (mod_install(&modlinkage));
1037bc7346cScm }
1047bc7346cScm 
1057bc7346cScm int
_fini()1067bc7346cScm _fini()
1077bc7346cScm {
1087bc7346cScm 	return (mod_remove(&modlinkage));
1097bc7346cScm }
1107bc7346cScm 
1117bc7346cScm int
_info(struct modinfo * modinfop)1127bc7346cScm _info(struct modinfo *modinfop)
1137bc7346cScm {
1147bc7346cScm 	return (mod_info(&modlinkage, modinfop));
1157bc7346cScm }
1167bc7346cScm 
1177bc7346cScm static void
hds_sym_init()1187bc7346cScm hds_sym_init()
1197bc7346cScm {
1207bc7346cScm 	struct scsi_failover_ops	*sfo, *ssfo, clone;
1217bc7346cScm 
1227bc7346cScm 	/* clone SFO_NAME_SYM implementation for most things */
1237bc7346cScm 	ssfo = vhci_failover_ops_by_name(SFO_NAME_SYM);
1247bc7346cScm 	if (ssfo == NULL) {
1257bc7346cScm 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!hds_sym_init: "
1267bc7346cScm 		    "can't import " SFO_NAME_SYM "\n"));
1277bc7346cScm 		return;
1287bc7346cScm 	}
1297bc7346cScm 	sfo				= &scsi_vhci_failover_ops;
1307bc7346cScm 	clone				= *ssfo;
1317bc7346cScm 	clone.sfo_rev			= sfo->sfo_rev;
1327bc7346cScm 	clone.sfo_name			= sfo->sfo_name;
1337bc7346cScm 	clone.sfo_devices		= sfo->sfo_devices;
1347bc7346cScm 	clone.sfo_init			= sfo->sfo_init;
1357bc7346cScm 	clone.sfo_device_probe		= sfo->sfo_device_probe;
1367bc7346cScm 	clone.sfo_device_unprobe	= sfo->sfo_device_unprobe;
1377bc7346cScm 	clone.sfo_path_get_opinfo	= sfo->sfo_path_get_opinfo;
1387bc7346cScm 	*sfo				= clone;
1397bc7346cScm }
1407bc7346cScm 
1417bc7346cScm /* ARGSUSED */
1427bc7346cScm static int
hds_sym_device_probe(struct scsi_device * sd,struct scsi_inquiry * stdinq,void ** ctprivp)1437bc7346cScm hds_sym_device_probe(struct scsi_device *sd, struct scsi_inquiry *stdinq,
144*55e592a2SRandall Ralphs     void **ctprivp)
1457bc7346cScm {
146*55e592a2SRandall Ralphs 	char		**dt;
147*55e592a2SRandall Ralphs 	char		*dftype;
1487bc7346cScm 	unsigned char	len;
1497bc7346cScm 	unsigned char	*inq_data = (unsigned char *)stdinq;
150*55e592a2SRandall Ralphs 	unsigned char	pv;
151*55e592a2SRandall Ralphs 	int		ret;
1527bc7346cScm 
1537bc7346cScm 	VHCI_DEBUG(6, (CE_NOTE, NULL, "hds_sym_device_probe: vidpid %s\n",
1547bc7346cScm 	    stdinq->inq_vid));
1557bc7346cScm 	for (dt = hds_sym_dev_table; *dt; dt++) {
1567bc7346cScm 		if (strncmp(stdinq->inq_vid, *dt, strlen(*dt)))
1577bc7346cScm 			continue;
1587bc7346cScm 		len = inq_data[4];
1597bc7346cScm 		if (len < 128) {
1607bc7346cScm 			vhci_log(CE_NOTE, NULL,
1617bc7346cScm 			    "hds_sym_device_probe: vidpid %s len error: %d\n",
1627bc7346cScm 			    stdinq->inq_vid, len);
1637bc7346cScm 			return (SFO_DEVICE_PROBE_PHCI);
1647bc7346cScm 		}
165*55e592a2SRandall Ralphs 
1667bc7346cScm 		dftype = (char *)&inq_data[128];
1677bc7346cScm 		if (*dftype == 0) {
1687bc7346cScm 			VHCI_DEBUG(4, (CE_NOTE, NULL,
1697bc7346cScm 			    "hds_sym_device_probe: vidpid %s"
1707bc7346cScm 			    " ASYM_ACTIVE_ACTIVE\n", stdinq->inq_vid));
171*55e592a2SRandall Ralphs 			pv = ASYM_ACTIVE_ACTIVE;
172*55e592a2SRandall Ralphs 			ret = SFO_DEVICE_PROBE_VHCI;
173*55e592a2SRandall Ralphs 		} else if (strncmp(dftype, HDS_SAA_TYPE,
174*55e592a2SRandall Ralphs 		    strlen(HDS_SAA_TYPE)) == 0) {
1757bc7346cScm 			VHCI_DEBUG(4, (CE_NOTE, NULL,
1767bc7346cScm 			    "hds_sym_device_probe: vidpid %s"
1777bc7346cScm 			    " SYM_ACTIVE_ACTIVE\n", stdinq->inq_vid));
178*55e592a2SRandall Ralphs 			pv = SYM_ACTIVE_ACTIVE;
179*55e592a2SRandall Ralphs 			ret = SFO_DEVICE_PROBE_VHCI;
180*55e592a2SRandall Ralphs 		} else
181*55e592a2SRandall Ralphs 			ret = SFO_DEVICE_PROBE_PHCI;
182*55e592a2SRandall Ralphs 
183*55e592a2SRandall Ralphs 		if (ret == SFO_DEVICE_PROBE_VHCI) {
184*55e592a2SRandall Ralphs 			/* ctprivp is NULL for vhci_is_dev_supported() probe */
185*55e592a2SRandall Ralphs 			if (ctprivp) {
186*55e592a2SRandall Ralphs 				/*
187*55e592a2SRandall Ralphs 				 * Allocate failover module's 'client' private
188*55e592a2SRandall Ralphs 				 * data on the first successfull path probe.
189*55e592a2SRandall Ralphs 				 * NOTE: 'client' private means per lun guid,
190*55e592a2SRandall Ralphs 				 * not per-path.
191*55e592a2SRandall Ralphs 				 */
192*55e592a2SRandall Ralphs 				if (*ctprivp == NULL)
193*55e592a2SRandall Ralphs 					*ctprivp = kmem_alloc(sizeof (pv),
194*55e592a2SRandall Ralphs 					    KM_SLEEP);
195*55e592a2SRandall Ralphs 
196*55e592a2SRandall Ralphs 				/* update private data */
197*55e592a2SRandall Ralphs 				*((unsigned char *)*ctprivp) = pv;
198*55e592a2SRandall Ralphs 			}
199*55e592a2SRandall Ralphs 		} else {
200*55e592a2SRandall Ralphs 			VHCI_DEBUG(4, (CE_NOTE, NULL,
201*55e592a2SRandall Ralphs 			    "hds_sym_device_probe: vidpid %s"
202*55e592a2SRandall Ralphs 			    " - unknown dftype: %d\n",
203*55e592a2SRandall Ralphs 			    stdinq->inq_vid, *dftype));
2047bc7346cScm 		}
2057bc7346cScm 		return (SFO_DEVICE_PROBE_PHCI);
2067bc7346cScm 
2077bc7346cScm 	}
2087bc7346cScm 	return (SFO_DEVICE_PROBE_PHCI);
2097bc7346cScm }
2107bc7346cScm 
2117bc7346cScm /* ARGSUSED */
2127bc7346cScm static void
hds_sym_device_unprobe(struct scsi_device * sd,void * ctpriv)2137bc7346cScm hds_sym_device_unprobe(struct scsi_device *sd, void *ctpriv)
2147bc7346cScm {
2157bc7346cScm 	if (ctpriv != NULL) {
2167bc7346cScm 		kmem_free(ctpriv, sizeof (unsigned char));
2177bc7346cScm 	}
2187bc7346cScm }
2197bc7346cScm 
2207bc7346cScm 
2217bc7346cScm /*
2227bc7346cScm  * Local routine to get inquiry VPD page from the device.
2237bc7346cScm  *
2247bc7346cScm  * return 1 for failure
2257bc7346cScm  * return 0 for success
2267bc7346cScm  */
2277bc7346cScm static int
hds_get_inquiry_vpd_page(struct scsi_device * sd,unsigned char page,unsigned char * buf,int size)2287bc7346cScm hds_get_inquiry_vpd_page(struct scsi_device *sd, unsigned char page,
2297bc7346cScm     unsigned char *buf, int size)
2307bc7346cScm {
2317bc7346cScm 	int		retval = 0;
2327bc7346cScm 	struct buf	*bp;
2337bc7346cScm 	struct scsi_pkt	*pkt;
2347bc7346cScm 	struct scsi_address	*ap;
2357bc7346cScm 
2367bc7346cScm 	if ((buf == NULL) || (size == 0)) {
2377bc7346cScm 		return (1);
2387bc7346cScm 	}
2397bc7346cScm 	bp = getrbuf(KM_NOSLEEP);
2407bc7346cScm 	if (bp == NULL) {
2417bc7346cScm 		return (1);
2427bc7346cScm 	}
2437bc7346cScm 	bp->b_un.b_addr = (char *)buf;
2447bc7346cScm 	bp->b_flags = B_READ;
2457bc7346cScm 	bp->b_bcount = size;
2467bc7346cScm 	bp->b_resid = 0;
2477bc7346cScm 
2487bc7346cScm 	ap = &sd->sd_address;
2497bc7346cScm 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
2507bc7346cScm 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
2517bc7346cScm 	if (pkt == NULL) {
2527bc7346cScm 		VHCI_DEBUG(4, (CE_WARN, NULL,
2537bc7346cScm 		    "hds_get_inquiry_vpd_page:"
2547bc7346cScm 		    "Failed to initialize packet"));
2557bc7346cScm 		freerbuf(bp);
2567bc7346cScm 		return (1);
2577bc7346cScm 	}
2587bc7346cScm 
2597bc7346cScm 	/*
2607bc7346cScm 	 * Send the inquiry command for page xx to the target.
2617bc7346cScm 	 * Data is returned in the buf pointed to by buf.
2627bc7346cScm 	 */
2637bc7346cScm 
2647bc7346cScm 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
2657bc7346cScm 	pkt->pkt_cdbp[1] = 0x1;
2667bc7346cScm 	pkt->pkt_cdbp[2] = page;
2677bc7346cScm 	pkt->pkt_cdbp[4] = (unsigned char)size;
2687bc7346cScm 	pkt->pkt_time = 90;
2697bc7346cScm 	retval = vhci_do_scsi_cmd(pkt);
2707bc7346cScm 	scsi_destroy_pkt(pkt);
2717bc7346cScm 	freerbuf(bp);
2727bc7346cScm 	return (!retval);
2737bc7346cScm 
2747bc7346cScm }
2757bc7346cScm 
2767bc7346cScm /* ARGSUSED */
2777bc7346cScm static int
hds_sym_get_opinfo(struct scsi_device * sd,struct scsi_path_opinfo * opinfo,void * ctpriv)2787bc7346cScm hds_sym_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo,
2797bc7346cScm     void *ctpriv)
2807bc7346cScm {
2817bc7346cScm 	unsigned char	inq_vpd_buf[HDS_MAX_INQ_BUF_SIZE];
2827bc7346cScm 
2837bc7346cScm 	opinfo->opinfo_rev = OPINFO_REV;
2847bc7346cScm 	(void) strcpy(opinfo->opinfo_path_attr, "primary");
2857bc7346cScm 	opinfo->opinfo_path_state  = SCSI_PATH_ACTIVE;
2867bc7346cScm 	opinfo->opinfo_pswtch_best = 0;		/* N/A */
2877bc7346cScm 	opinfo->opinfo_pswtch_worst = 0;	/* N/A */
2887bc7346cScm 	opinfo->opinfo_xlf_capable = 0;
2897bc7346cScm 	opinfo->opinfo_mode = SCSI_NO_FAILOVER;
2907bc7346cScm 	ASSERT(ctpriv != NULL);
2917bc7346cScm 	if (*((unsigned char *)ctpriv) == SYM_ACTIVE_ACTIVE) {
2927bc7346cScm 		VHCI_DEBUG(4, (CE_NOTE, NULL,
2937bc7346cScm 		    "hds_get_opinfo: sd(%p): sym_active_active "
2947bc7346cScm 		    "preferred bit set ", (void*)sd));
2957bc7346cScm 		opinfo->opinfo_preferred = PCLASS_PREFERRED;
2967bc7346cScm 		return (0);
2977bc7346cScm 	}
2987bc7346cScm 	/* check if this is the preferred path */
2997bc7346cScm 	if (hds_get_inquiry_vpd_page(sd, HDS_INQ_PAGE_E0, inq_vpd_buf,
3007bc7346cScm 	    sizeof (inq_vpd_buf)) != 0) {
3017bc7346cScm 		VHCI_DEBUG(4, (CE_WARN, NULL,
3027bc7346cScm 		    "hds_get_opinfo: sd(%p):Unable to "
3037bc7346cScm 		    "get inquiry Page %x", (void*)sd, HDS_INQ_PAGE_E0));
3047bc7346cScm 		return (1);
3057bc7346cScm 	}
3067bc7346cScm 	if (inq_vpd_buf[4] & 0x80) {
3077bc7346cScm 		if (inq_vpd_buf[4] & 0x40) {
3087bc7346cScm 			VHCI_DEBUG(4, (CE_NOTE, NULL,
3097bc7346cScm 			    "hds_get_opinfo: sd(%p): preferred bit set ",
3107bc7346cScm 			    (void*)sd));
3117bc7346cScm 			opinfo->opinfo_preferred = PCLASS_PREFERRED;
3127bc7346cScm 		} else {
3137bc7346cScm 			VHCI_DEBUG(4, (CE_NOTE, NULL,
3147bc7346cScm 			    "hds_get_opinfo: sd(%p): non-preferred bit set ",
3157bc7346cScm 			    (void*)sd));
3167bc7346cScm 			opinfo->opinfo_preferred = PCLASS_NONPREFERRED;
3177bc7346cScm 		}
3187bc7346cScm 	} else {
3197bc7346cScm 		vhci_log(CE_NOTE, NULL,
3207bc7346cScm 		    "hds_get_opinfo: sd(%p): "
3217bc7346cScm 		    "get inquiry Page %x has invalid P/SVid bit set",
3227bc7346cScm 		    (void*)sd, HDS_INQ_PAGE_E0);
3237bc7346cScm 		return (1);
3247bc7346cScm 	}
3257bc7346cScm 
3267bc7346cScm 	return (0);
3277bc7346cScm }
328