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