1 /* $OpenBSD: mpath_rdac.c,v 1.23 2015/03/14 03:38:52 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2010 David Gwynne <dlg@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* Redundant Disk Array Controller support for mpath(4) */ 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/kernel.h> 24 #include <sys/malloc.h> 25 #include <sys/device.h> 26 #include <sys/conf.h> 27 #include <sys/queue.h> 28 #include <sys/rwlock.h> 29 #include <sys/pool.h> 30 #include <sys/ioctl.h> 31 #include <sys/poll.h> 32 #include <sys/selinfo.h> 33 34 #include <scsi/scsi_all.h> 35 #include <scsi/scsiconf.h> 36 #include <scsi/mpathvar.h> 37 38 struct rdac_common_mode_page { 39 u_int8_t controller_serial[16]; 40 u_int8_t alt_controller_serial[16]; 41 u_int8_t mode[2]; 42 u_int8_t alt_mode[2]; 43 u_int8_t timeout; 44 u_int8_t options; 45 }; 46 47 /* 48 * RDAC VPD pages 49 */ 50 #define RDAC_VPD_HDWVER 0xc0 /* Hardware Version */ 51 #define RDAC_VPD_SERNUM 0xc1 /* Serial Numbers */ 52 #define RDAC_VPD_SFWVER 0xc2 53 #define RDAC_VPD_FEAPAR 0xc3 /* Feature Parameters */ 54 #define RDAC_VPD_SUBSYS 0xc4 55 #define RDAC_VPD_HSTINT 0xc5 56 #define RDAC_VPD_DGM 0xc6 57 #define RDAC_VPD_HSTINT2 0xc7 58 #define RDAC_VPD_EXTDEVID 0xc8 59 #define RDAC_VPD_VOLACCESSCTL 0xc9 60 61 struct rdac_vpd_hdwver { 62 struct scsi_vpd_hdr hdr; /* RDAC_VPD_HDWVER */ 63 u_int8_t pg_id[4]; 64 #define RDAC_VPD_ID_HDWVER 0x68777234 /* "hwr4" */ 65 u_int8_t num_channels; 66 u_int8_t flags; 67 u_int8_t proc_memory_size; 68 u_int8_t _reserved1[5]; 69 u_int8_t board_name[64]; 70 u_int8_t board_part_number[16]; 71 u_int8_t schematic_number[12]; 72 u_int8_t schematic_revision[4]; 73 u_int8_t serial_number[16]; 74 u_int8_t _reserved2[16]; 75 u_int8_t date_manufactured[8]; 76 u_int8_t board_revision[2]; 77 u_int8_t board_identifier[4]; 78 }; 79 80 struct rdac_vpd_subsys { 81 struct scsi_vpd_hdr hdr; /* RDAC_VPD_SUBSYS */ 82 u_int8_t pg_id[4]; 83 #define RDAC_VPD_ID_SUBSYS 0x73756273 /* "subs" */ 84 u_int8_t subsystem_id[16]; 85 u_int8_t subsystem_revision[4]; 86 u_int8_t controller_slot_id[2]; 87 u_int8_t _reserved[2]; 88 }; 89 90 struct rdac_vpd_extdevid { 91 struct scsi_vpd_hdr hdr; /* RDAC_VPD_EXTDEVID */ 92 u_int8_t pg_id[4]; 93 #define RDAC_VPD_ID_EXTDEVID 0x65646964 /* "edid" */ 94 u_int8_t _reserved[3]; 95 u_int8_t vol_id_len; 96 u_int8_t vol_id[16]; 97 u_int8_t vol_label_len; 98 u_int8_t vol_label[60]; 99 u_int8_t array_id_len; 100 u_int8_t array_id[16]; 101 u_int8_t array_label_len; 102 u_int8_t array_label[60]; 103 u_int8_t lun[8]; 104 }; 105 106 struct rdac_vpd_volaccessctl { 107 struct scsi_vpd_hdr hdr; /* RDAC_VPD_VOLACCESSCTL */ 108 u_int8_t pg_id[4]; 109 #define RDAC_VPD_ID_VOLACCESSCTL 0x76616331 /* "vac1" */ 110 u_int8_t avtcvp; 111 #define RDAC_VOLACCESSCTL_OWNER 0x01 112 #define RDAC_VOLACCESSCTL_AVT 0x70 113 u_int8_t _reserved1; 114 u_int8_t asym_access_state_cur; 115 u_int8_t vendor_specific_cur; 116 u_int8_t _reserved[36]; 117 }; 118 119 struct rdac_softc { 120 struct device sc_dev; 121 struct mpath_path sc_path; 122 struct scsi_xshandler sc_xsh; 123 struct rdac_vpd_volaccessctl *sc_pg; 124 }; 125 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 126 127 int rdac_match(struct device *, void *, void *); 128 void rdac_attach(struct device *, struct device *, void *); 129 int rdac_detach(struct device *, int); 130 int rdac_activate(struct device *, int); 131 132 struct cfattach rdac_ca = { 133 sizeof(struct rdac_softc), 134 rdac_match, 135 rdac_attach, 136 rdac_detach, 137 rdac_activate 138 }; 139 140 struct cfdriver rdac_cd = { 141 NULL, 142 "rdac", 143 DV_DULL 144 }; 145 146 void rdac_mpath_start(struct scsi_xfer *); 147 int rdac_mpath_checksense(struct scsi_xfer *); 148 void rdac_mpath_status(struct scsi_link *); 149 150 const struct mpath_ops rdac_mpath_ops = { 151 "rdac", 152 rdac_mpath_checksense, 153 rdac_mpath_status 154 }; 155 156 int rdac_extdevid(struct rdac_softc *); 157 int rdac_groupid(struct rdac_softc *); 158 159 void rdac_status(struct scsi_xfer *); 160 void rdac_status_done(struct scsi_xfer *); 161 162 struct rdac_device { 163 char *vendor; 164 char *product; 165 }; 166 167 struct rdac_device rdac_devices[] = { 168 /* " vendor " " device " */ 169 /* "01234567" "0123456789012345" */ 170 { "SUN ", "CSM200_" }, 171 { "DELL ", "MD3000 " }, 172 { "DELL ", "MD3000i " }, 173 { "DELL ", "MD32xx " }, 174 { "DELL ", "MD32xxi " } 175 }; 176 177 int 178 rdac_match(struct device *parent, void *match, void *aux) 179 { 180 struct scsi_attach_args *sa = aux; 181 struct scsi_inquiry_data *inq = sa->sa_inqbuf; 182 struct rdac_device *s; 183 int i; 184 185 if (mpath_path_probe(sa->sa_sc_link) != 0) 186 return (0); 187 188 for (i = 0; i < nitems(rdac_devices); i++) { 189 s = &rdac_devices[i]; 190 191 if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && 192 bcmp(s->product, inq->product, strlen(s->product)) == 0) 193 return (8); 194 } 195 196 return (0); 197 } 198 199 void 200 rdac_attach(struct device *parent, struct device *self, void *aux) 201 { 202 struct rdac_softc *sc = (struct rdac_softc *)self; 203 struct scsi_attach_args *sa = aux; 204 struct scsi_link *link = sa->sa_sc_link; 205 int id; 206 207 printf("\n"); 208 209 /* init link */ 210 link->device_softc = sc; 211 212 /* init path */ 213 scsi_xsh_set(&sc->sc_path.p_xsh, link, rdac_mpath_start); 214 sc->sc_path.p_link = link; 215 216 /* init status handler */ 217 scsi_xsh_set(&sc->sc_xsh, link, rdac_status); 218 sc->sc_pg = dma_alloc(sizeof(*sc->sc_pg), PR_WAITOK); 219 220 /* let's go */ 221 if (rdac_extdevid(sc) != 0) 222 return; 223 224 id = rdac_groupid(sc); 225 if (id == -1) { 226 /* error printed by rdac_groupid */ 227 return; 228 } 229 230 if (mpath_path_attach(&sc->sc_path, id, &rdac_mpath_ops) != 0) 231 printf("%s: unable to attach path\n", DEVNAME(sc)); 232 } 233 234 int 235 rdac_detach(struct device *self, int flags) 236 { 237 struct rdac_softc *sc = (struct rdac_softc *)self; 238 239 dma_free(sc->sc_pg, sizeof(*sc->sc_pg)); 240 241 return (0); 242 } 243 244 int 245 rdac_activate(struct device *self, int act) 246 { 247 struct rdac_softc *sc = (struct rdac_softc *)self; 248 int rv = 0; 249 250 switch (act) { 251 case DVACT_DEACTIVATE: 252 if (scsi_xsh_del(&sc->sc_xsh)) 253 mpath_path_status(&sc->sc_path, MPATH_S_UNKNOWN); 254 if (sc->sc_path.p_group != NULL) 255 mpath_path_detach(&sc->sc_path); 256 break; 257 } 258 return (rv); 259 } 260 261 void 262 rdac_mpath_start(struct scsi_xfer *xs) 263 { 264 struct rdac_softc *sc = xs->sc_link->device_softc; 265 266 mpath_start(&sc->sc_path, xs); 267 } 268 269 int 270 rdac_mpath_checksense(struct scsi_xfer *xs) 271 { 272 struct scsi_sense_data *sense = &xs->sense; 273 u_int8_t skey; 274 275 if ((sense->error_code & SSD_ERRCODE) != SSD_ERRCODE_CURRENT) 276 return (MPATH_SENSE_DECLINED); 277 278 skey = sense->flags & SSD_KEY; 279 280 /* i wish i knew what the magic numbers meant */ 281 282 /* invalid request due to current lu ownership */ 283 if (skey == SKEY_ILLEGAL_REQUEST && ASC_ASCQ(sense) == 0x9401) 284 return (MPATH_SENSE_FAILOVER); 285 286 if (skey == SKEY_UNIT_ATTENTION && ASC_ASCQ(sense) == 0x8b02) 287 return (MPATH_SENSE_FAILOVER); 288 289 return (MPATH_SENSE_DECLINED); 290 } 291 292 void 293 rdac_mpath_status(struct scsi_link *link) 294 { 295 struct rdac_softc *sc = link->device_softc; 296 297 scsi_xsh_add(&sc->sc_xsh); 298 } 299 300 void 301 rdac_status(struct scsi_xfer *xs) 302 { 303 struct scsi_link *link = xs->sc_link; 304 struct rdac_softc *sc = link->device_softc; 305 306 scsi_init_inquiry(xs, SI_EVPD, RDAC_VPD_VOLACCESSCTL, 307 sc->sc_pg, sizeof(*sc->sc_pg)); 308 309 xs->done = rdac_status_done; 310 311 scsi_xs_exec(xs); 312 } 313 314 void 315 rdac_status_done(struct scsi_xfer *xs) 316 { 317 struct scsi_link *link = xs->sc_link; 318 struct rdac_softc *sc = link->device_softc; 319 struct rdac_vpd_volaccessctl *pg = sc->sc_pg; 320 int status = MPATH_S_UNKNOWN; 321 322 if (xs->error == XS_NOERROR && 323 _4btol(pg->pg_id) == RDAC_VPD_ID_VOLACCESSCTL) { 324 status = (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_AVT) || 325 ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_OWNER)) ? 326 MPATH_S_ACTIVE : MPATH_S_PASSIVE; 327 } 328 329 scsi_xs_put(xs); 330 mpath_path_status(&sc->sc_path, status); 331 } 332 333 int 334 rdac_groupid(struct rdac_softc *sc) 335 { 336 struct rdac_vpd_subsys *pg; 337 int rv = -1; 338 339 pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO); 340 341 if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg), 342 RDAC_VPD_SUBSYS, scsi_autoconf) != 0) { 343 printf("%s: unable to fetch subsys vpd page\n", DEVNAME(sc)); 344 goto done; 345 } 346 347 if (_4btol(pg->pg_id) != RDAC_VPD_ID_SUBSYS) { 348 printf("%s: subsys page is invalid\n", DEVNAME(sc)); 349 goto done; 350 } 351 352 rv = _2btol(pg->controller_slot_id); 353 354 done: 355 dma_free(pg, sizeof(*pg)); 356 return (rv); 357 } 358 359 int 360 rdac_extdevid(struct rdac_softc *sc) 361 { 362 struct rdac_vpd_extdevid *pg; 363 char array[31]; 364 char vol[31]; 365 int i; 366 int rv = 1; 367 368 pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO); 369 370 if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg), 371 RDAC_VPD_EXTDEVID, scsi_autoconf) != 0) { 372 printf("%s: unable to fetch extdevid vpd page\n", DEVNAME(sc)); 373 goto done; 374 } 375 376 if (_4btol(pg->pg_id) != RDAC_VPD_ID_EXTDEVID) { 377 printf("%s: extdevid page is invalid\n", DEVNAME(sc)); 378 goto done; 379 } 380 381 memset(array, 0, sizeof(array)); 382 for (i = 0; i < sizeof(pg->array_label) / 2; i++) 383 array[i] = pg->array_label[i * 2 + 1]; 384 385 memset(vol, 0, sizeof(vol)); 386 for (i = 0; i < sizeof(pg->vol_label) / 2; i++) 387 vol[i] = pg->vol_label[i * 2 + 1]; 388 389 printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol); 390 391 rv = 0; 392 done: 393 dma_free(pg, sizeof(*pg)); 394 return (rv); 395 } 396