1 /* $OpenBSD: mpath_rdac.c,v 1.7 2011/07/11 01:02:48 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 path_priority; 116 u_int8_t _reserved[38]; 117 }; 118 119 struct rdac_softc { 120 struct device sc_dev; 121 struct mpath_path sc_path; 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 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 int rdac_mpath_online(struct scsi_link *); 147 int rdac_mpath_offline(struct scsi_link *); 148 149 const struct mpath_ops rdac_mpath_ops = { 150 "rdac", 151 rdac_mpath_checksense, 152 rdac_mpath_online, 153 rdac_mpath_offline, 154 MPATH_ROUNDROBIN 155 }; 156 157 int rdac_c8(struct rdac_softc *); 158 int rdac_c9(struct rdac_softc *); 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_inqbuf; 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 (3); 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 204 printf("\n"); 205 206 /* init link */ 207 link->device_softc = sc; 208 209 /* init path */ 210 scsi_xsh_set(&sc->sc_path.p_xsh, link, rdac_mpath_start); 211 sc->sc_path.p_link = link; 212 213 if (rdac_c8(sc) != 0) 214 return; 215 216 if (rdac_c9(sc) != 0) 217 return; 218 219 if (mpath_path_attach(&sc->sc_path, &rdac_mpath_ops) != 0) 220 printf("%s: unable to attach path\n", DEVNAME(sc)); 221 } 222 223 int 224 rdac_detach(struct device *self, int flags) 225 { 226 return (0); 227 } 228 229 int 230 rdac_activate(struct device *self, int act) 231 { 232 struct rdac_softc *sc = (struct rdac_softc *)self; 233 int rv = 0; 234 235 switch (act) { 236 case DVACT_SUSPEND: 237 case DVACT_RESUME: 238 break; 239 case DVACT_DEACTIVATE: 240 if (sc->sc_path.p_dev != NULL) 241 mpath_path_detach(&sc->sc_path); 242 break; 243 } 244 return (rv); 245 } 246 247 void 248 rdac_mpath_start(struct scsi_xfer *xs) 249 { 250 struct rdac_softc *sc = xs->sc_link->device_softc; 251 252 mpath_start(&sc->sc_path, xs); 253 } 254 255 int 256 rdac_mpath_checksense(struct scsi_xfer *xs) 257 { 258 return (0); 259 } 260 261 int 262 rdac_mpath_online(struct scsi_link *link) 263 { 264 return (0); 265 } 266 267 int 268 rdac_mpath_offline(struct scsi_link *link) 269 { 270 return (0); 271 } 272 273 int 274 rdac_c8(struct rdac_softc *sc) 275 { 276 struct rdac_vpd_extdevid *pg; 277 char array[31]; 278 char vol[31]; 279 int i; 280 int rv = 1; 281 282 pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO); 283 284 if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg), 0xc8, 285 scsi_autoconf) != 0) { 286 printf("%s: unable to fetch vpd page c8\n", DEVNAME(sc)); 287 goto done; 288 } 289 290 if (_4btol(pg->pg_id) != RDAC_VPD_ID_EXTDEVID) { 291 printf("%s: extended hardware id page is invalid\n", 292 DEVNAME(sc)); 293 goto done; 294 } 295 296 memset(array, 0, sizeof(array)); 297 for (i = 0; i < sizeof(pg->array_label) / 2; i++) 298 array[i] = pg->array_label[i * 2 + 1]; 299 300 memset(vol, 0, sizeof(vol)); 301 for (i = 0; i < sizeof(pg->vol_label) / 2; i++) 302 vol[i] = pg->vol_label[i * 2 + 1]; 303 304 printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol); 305 306 rv = 0; 307 done: 308 dma_free(pg, sizeof(*pg)); 309 return (rv); 310 } 311 312 int 313 rdac_c9(struct rdac_softc *sc) 314 { 315 struct rdac_vpd_volaccessctl *pg; 316 int rv = 1; 317 318 pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO); 319 320 if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg), 321 RDAC_VPD_VOLACCESSCTL, scsi_autoconf) != 0) { 322 printf("%s: unable to fetch vpd page c9\n", DEVNAME(sc)); 323 goto done; 324 } 325 326 if (_4btol(pg->pg_id) != RDAC_VPD_ID_VOLACCESSCTL) { 327 printf("%s: volume access control page id is invalid\n", 328 DEVNAME(sc)); 329 goto done; 330 } 331 332 if (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_AVT)) { 333 printf("%s: avt\n", DEVNAME(sc)); 334 rv = 0; 335 } else if (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_OWNER)) { 336 printf("%s: owner\n", DEVNAME(sc)); 337 rv = 0; 338 } else 339 printf("%s: unowned\n", DEVNAME(sc)); 340 341 done: 342 dma_free(pg, sizeof(*pg)); 343 return (rv); 344 } 345 346