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
_init()100 _init()
101 {
102 	return (mod_install(&modlinkage));
103 }
104 
105 int
_fini()106 _fini()
107 {
108 	return (mod_remove(&modlinkage));
109 }
110 
111 int
_info(struct modinfo * modinfop)112 _info(struct modinfo *modinfop)
113 {
114 	return (mod_info(&modlinkage, modinfop));
115 }
116 
117 static void
hds_sym_init()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
hds_sym_device_probe(struct scsi_device * sd,struct scsi_inquiry * stdinq,void ** ctprivp)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
hds_sym_device_unprobe(struct scsi_device * sd,void * ctpriv)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
hds_get_inquiry_vpd_page(struct scsi_device * sd,unsigned char page,unsigned char * buf,int size)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
hds_sym_get_opinfo(struct scsi_device * sd,struct scsi_path_opinfo * opinfo,void * ctpriv)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