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 }