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