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