Home | History | Annotate | Download | only in fops
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /* Portions Copyright 2008 Hitachi Ltd. */
     23 
     24 /*
     25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     26  * Use is subject to license terms.
     27  */
     28 
     29 /*
     30  * Implementation of "scsi_vhci_f_sym_hds" asymmetric-active-active
     31  * failover_ops. The device has a preferred(owner)/non-preferred
     32  * with no action needed to use the non-preferred path. This is really
     33  * more inline with symmetric device so am using that prefix.
     34  *
     35  * This file imports the standard "scsi_vhci_f_sym", but with HDS specific
     36  * knowledge related to preferred/non-preferred path.
     37  */
     38 
     39 #include <sys/conf.h>
     40 #include <sys/file.h>
     41 #include <sys/ddi.h>
     42 #include <sys/sunddi.h>
     43 #include <sys/scsi/scsi.h>
     44 #include <sys/scsi/adapters/scsi_vhci.h>
     45 
     46 /* Supported device table entries.  */
     47 char *hds_sym_dev_table[] = {
     48 /*	"                  111111" */
     49 /*	"012345670123456789012345" */
     50 /*	"|-VID--||-----PID------|" */
     51 
     52 	"HITACHI DF",
     53 	NULL
     54 };
     55 
     56 static int	hds_sym_device_probe(struct scsi_device *,
     57 			struct scsi_inquiry *, void **);
     58 static void	hds_sym_device_unprobe(struct scsi_device *, void *);
     59 static void	hds_sym_init();
     60 static int	hds_sym_get_opinfo(struct scsi_device *sd,
     61 			struct scsi_path_opinfo *opinfo, void *ctpriv);
     62 
     63 #ifdef	lint
     64 #define	scsi_vhci_failover_ops	scsi_vhci_failover_ops_f_sym_hds
     65 #endif	/* lint */
     66 /*
     67  * Use the following for the Asymmetric-Active-Active fops.
     68  * A different fops may get used for the Symmetric-Active-Active.
     69  */
     70 struct scsi_failover_ops scsi_vhci_failover_ops = {
     71 	SFO_REV,
     72 	SFO_NAME_SYM "_hds",
     73 	hds_sym_dev_table,
     74 	hds_sym_init,
     75 	hds_sym_device_probe,
     76 	hds_sym_device_unprobe,
     77 	NULL,
     78 	NULL,
     79 	hds_sym_get_opinfo,
     80 	/* The rest of the implementation comes from SFO_NAME_SYM import  */
     81 };
     82 
     83 static struct modlmisc modlmisc = {
     84 	&mod_miscops, "f_sym_hds"
     85 };
     86 
     87 static struct modlinkage modlinkage = {
     88 	MODREV_1, (void *)&modlmisc, NULL
     89 };
     90 
     91 #define	HDS_MAX_INQ_BUF_SIZE		0xff
     92 #define	HDS_INQ_PAGE_E0			0xe0
     93 #define	HDS_SAA_TYPE			"DF00"
     94 #define	ASYM_ACTIVE_ACTIVE		0
     95 #define	SYM_ACTIVE_ACTIVE		1
     96 
     97 extern struct scsi_failover_ops	*vhci_failover_ops_by_name(char *);
     98 
     99 int
    100 _init()
    101 {
    102 	return (mod_install(&modlinkage));
    103 }
    104 
    105 int
    106 _fini()
    107 {
    108 	return (mod_remove(&modlinkage));
    109 }
    110 
    111 int
    112 _info(struct modinfo *modinfop)
    113 {
    114 	return (mod_info(&modlinkage, modinfop));
    115 }
    116 
    117 static void
    118 hds_sym_init()
    119 {
    120 	struct scsi_failover_ops	*sfo, *ssfo, clone;
    121 
    122 	/* clone SFO_NAME_SYM implementation for most things */
    123 	ssfo = vhci_failover_ops_by_name(SFO_NAME_SYM);
    124 	if (ssfo == NULL) {
    125 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!hds_sym_init: "
    126 		    "can't import " SFO_NAME_SYM "\n"));
    127 		return;
    128 	}
    129 	sfo				= &scsi_vhci_failover_ops;
    130 	clone				= *ssfo;
    131 	clone.sfo_rev			= sfo->sfo_rev;
    132 	clone.sfo_name			= sfo->sfo_name;
    133 	clone.sfo_devices		= sfo->sfo_devices;
    134 	clone.sfo_init			= sfo->sfo_init;
    135 	clone.sfo_device_probe		= sfo->sfo_device_probe;
    136 	clone.sfo_device_unprobe	= sfo->sfo_device_unprobe;
    137 	clone.sfo_path_get_opinfo	= sfo->sfo_path_get_opinfo;
    138 	*sfo				= clone;
    139 }
    140 
    141 /* ARGSUSED */
    142 static int
    143 hds_sym_device_probe(struct scsi_device *sd, struct scsi_inquiry *stdinq,
    144     void **ctprivp)
    145 {
    146 	char		**dt;
    147 	char		*dftype;
    148 	unsigned char	len;
    149 	unsigned char	*inq_data = (unsigned char *)stdinq;
    150 	unsigned char	pv;
    151 	int		ret;
    152 
    153 	VHCI_DEBUG(6, (CE_NOTE, NULL, "hds_sym_device_probe: vidpid %s\n",
    154 	    stdinq->inq_vid));
    155 	for (dt = hds_sym_dev_table; *dt; dt++) {
    156 		if (strncmp(stdinq->inq_vid, *dt, strlen(*dt)))
    157 			continue;
    158 		len = inq_data[4];
    159 		if (len < 128) {
    160 			vhci_log(CE_NOTE, NULL,
    161 			    "hds_sym_device_probe: vidpid %s len error: %d\n",
    162 			    stdinq->inq_vid, len);
    163 			return (SFO_DEVICE_PROBE_PHCI);
    164 		}
    165 
    166 		dftype = (char *)&inq_data[128];
    167 		if (*dftype == 0) {
    168 			VHCI_DEBUG(4, (CE_NOTE, NULL,
    169 			    "hds_sym_device_probe: vidpid %s"
    170 			    " ASYM_ACTIVE_ACTIVE\n", stdinq->inq_vid));
    171 			pv = ASYM_ACTIVE_ACTIVE;
    172 			ret = SFO_DEVICE_PROBE_VHCI;
    173 		} else if (strncmp(dftype, HDS_SAA_TYPE,
    174 		    strlen(HDS_SAA_TYPE)) == 0) {
    175 			VHCI_DEBUG(4, (CE_NOTE, NULL,
    176 			    "hds_sym_device_probe: vidpid %s"
    177 			    " SYM_ACTIVE_ACTIVE\n", stdinq->inq_vid));
    178 			pv = SYM_ACTIVE_ACTIVE;
    179 			ret = SFO_DEVICE_PROBE_VHCI;
    180 		} else
    181 			ret = SFO_DEVICE_PROBE_PHCI;
    182 
    183 		if (ret == SFO_DEVICE_PROBE_VHCI) {
    184 			/* ctprivp is NULL for vhci_is_dev_supported() probe */
    185 			if (ctprivp) {
    186 				/*
    187 				 * Allocate failover module's 'client' private
    188 				 * data on the first successfull path probe.
    189 				 * NOTE: 'client' private means per lun guid,
    190 				 * not per-path.
    191 				 */
    192 				if (*ctprivp == NULL)
    193 					*ctprivp = kmem_alloc(sizeof (pv),
    194 					    KM_SLEEP);
    195 
    196 				/* update private data */
    197 				*((unsigned char *)*ctprivp) = pv;
    198 			}
    199 		} else {
    200 			VHCI_DEBUG(4, (CE_NOTE, NULL,
    201 			    "hds_sym_device_probe: vidpid %s"
    202 			    " - unknown dftype: %d\n",
    203 			    stdinq->inq_vid, *dftype));
    204 		}
    205 		return (SFO_DEVICE_PROBE_PHCI);
    206 
    207 	}
    208 	return (SFO_DEVICE_PROBE_PHCI);
    209 }
    210 
    211 /* ARGSUSED */
    212 static void
    213 hds_sym_device_unprobe(struct scsi_device *sd, void *ctpriv)
    214 {
    215 	if (ctpriv != NULL) {
    216 		kmem_free(ctpriv, sizeof (unsigned char));
    217 	}
    218 }
    219 
    220 
    221 /*
    222  * Local routine to get inquiry VPD page from the device.
    223  *
    224  * return 1 for failure
    225  * return 0 for success
    226  */
    227 static int
    228 hds_get_inquiry_vpd_page(struct scsi_device *sd, unsigned char page,
    229     unsigned char *buf, int size)
    230 {
    231 	int		retval = 0;
    232 	struct buf	*bp;
    233 	struct scsi_pkt	*pkt;
    234 	struct scsi_address	*ap;
    235 
    236 	if ((buf == NULL) || (size == 0)) {
    237 		return (1);
    238 	}
    239 	bp = getrbuf(KM_NOSLEEP);
    240 	if (bp == NULL) {
    241 		return (1);
    242 	}
    243 	bp->b_un.b_addr = (char *)buf;
    244 	bp->b_flags = B_READ;
    245 	bp->b_bcount = size;
    246 	bp->b_resid = 0;
    247 
    248 	ap = &sd->sd_address;
    249 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
    250 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
    251 	if (pkt == NULL) {
    252 		VHCI_DEBUG(4, (CE_WARN, NULL,
    253 		    "hds_get_inquiry_vpd_page:"
    254 		    "Failed to initialize packet"));
    255 		freerbuf(bp);
    256 		return (1);
    257 	}
    258 
    259 	/*
    260 	 * Send the inquiry command for page xx to the target.
    261 	 * Data is returned in the buf pointed to by buf.
    262 	 */
    263 
    264 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
    265 	pkt->pkt_cdbp[1] = 0x1;
    266 	pkt->pkt_cdbp[2] = page;
    267 	pkt->pkt_cdbp[4] = (unsigned char)size;
    268 	pkt->pkt_time = 90;
    269 	retval = vhci_do_scsi_cmd(pkt);
    270 	scsi_destroy_pkt(pkt);
    271 	freerbuf(bp);
    272 	return (!retval);
    273 
    274 }
    275 
    276 /* ARGSUSED */
    277 static int
    278 hds_sym_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo,
    279     void *ctpriv)
    280 {
    281 	unsigned char	inq_vpd_buf[HDS_MAX_INQ_BUF_SIZE];
    282 
    283 	opinfo->opinfo_rev = OPINFO_REV;
    284 	(void) strcpy(opinfo->opinfo_path_attr, "primary");
    285 	opinfo->opinfo_path_state  = SCSI_PATH_ACTIVE;
    286 	opinfo->opinfo_pswtch_best = 0;		/* N/A */
    287 	opinfo->opinfo_pswtch_worst = 0;	/* N/A */
    288 	opinfo->opinfo_xlf_capable = 0;
    289 	opinfo->opinfo_mode = SCSI_NO_FAILOVER;
    290 	ASSERT(ctpriv != NULL);
    291 	if (*((unsigned char *)ctpriv) == SYM_ACTIVE_ACTIVE) {
    292 		VHCI_DEBUG(4, (CE_NOTE, NULL,
    293 		    "hds_get_opinfo: sd(%p): sym_active_active "
    294 		    "preferred bit set ", (void*)sd));
    295 		opinfo->opinfo_preferred = PCLASS_PREFERRED;
    296 		return (0);
    297 	}
    298 	/* check if this is the preferred path */
    299 	if (hds_get_inquiry_vpd_page(sd, HDS_INQ_PAGE_E0, inq_vpd_buf,
    300 	    sizeof (inq_vpd_buf)) != 0) {
    301 		VHCI_DEBUG(4, (CE_WARN, NULL,
    302 		    "hds_get_opinfo: sd(%p):Unable to "
    303 		    "get inquiry Page %x", (void*)sd, HDS_INQ_PAGE_E0));
    304 		return (1);
    305 	}
    306 	if (inq_vpd_buf[4] & 0x80) {
    307 		if (inq_vpd_buf[4] & 0x40) {
    308 			VHCI_DEBUG(4, (CE_NOTE, NULL,
    309 			    "hds_get_opinfo: sd(%p): preferred bit set ",
    310 			    (void*)sd));
    311 			opinfo->opinfo_preferred = PCLASS_PREFERRED;
    312 		} else {
    313 			VHCI_DEBUG(4, (CE_NOTE, NULL,
    314 			    "hds_get_opinfo: sd(%p): non-preferred bit set ",
    315 			    (void*)sd));
    316 			opinfo->opinfo_preferred = PCLASS_NONPREFERRED;
    317 		}
    318 	} else {
    319 		vhci_log(CE_NOTE, NULL,
    320 		    "hds_get_opinfo: sd(%p): "
    321 		    "get inquiry Page %x has invalid P/SVid bit set",
    322 		    (void*)sd, HDS_INQ_PAGE_E0);
    323 		return (1);
    324 	}
    325 
    326 	return (0);
    327 }
    328