1 /* $OpenBSD: mpath_emc.c,v 1.7 2011/07/03 15:47:18 matthew Exp $ */ 2 3 /* 4 * Copyright (c) 2011 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 /* EMC CLARiiON AX/CX 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 #define EMC_VPD_SP_INFO 0xc0 41 42 struct emc_vpd_sp_info { 43 struct scsi_vpd_hdr hdr; /* EMC_VPD_SP_INFO */ 44 45 u_int8_t lun_state; 46 #define EMC_SP_INFO_LUN_STATE_UNBOUND 0x00 47 #define EMC_SP_INFO_LUN_STATE_BOUND 0x01 48 #define EMC_SP_INFO_LUN_STATE_OWNED 0x02 49 u_int8_t default_sp; 50 u_int8_t _reserved1[1]; 51 u_int8_t port; 52 u_int8_t current_sp; 53 u_int8_t _reserved2[1]; 54 u_int8_t unique_id[16]; 55 u_int8_t _reserved3[1]; 56 u_int8_t type; 57 u_int8_t failover_mode; 58 u_int8_t _reserved4[21]; 59 u_int8_t serial[16]; 60 } __packed; 61 62 struct emc_softc { 63 struct device sc_dev; 64 struct mpath_path sc_path; 65 u_int sc_flags; 66 u_int8_t sc_sp; 67 u_int8_t sc_port; 68 u_int8_t sc_lun_state; 69 70 }; 71 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 72 73 int emc_match(struct device *, void *, void *); 74 void emc_attach(struct device *, struct device *, void *); 75 int emc_detach(struct device *, int); 76 int emc_activate(struct device *, int); 77 78 struct cfattach emc_ca = { 79 sizeof(struct emc_softc), 80 emc_match, 81 emc_attach, 82 emc_detach, 83 emc_activate 84 }; 85 86 struct cfdriver emc_cd = { 87 NULL, 88 "emc", 89 DV_DULL 90 }; 91 92 void emc_mpath_start(struct scsi_xfer *); 93 int emc_mpath_checksense(struct scsi_xfer *); 94 int emc_mpath_online(struct scsi_link *); 95 int emc_mpath_offline(struct scsi_link *); 96 97 struct mpath_ops emc_mpath_ops = { 98 "emc", 99 emc_mpath_checksense, 100 emc_mpath_online, 101 emc_mpath_offline, 102 }; 103 104 struct emc_device { 105 char *vendor; 106 char *product; 107 }; 108 109 int emc_inquiry(struct emc_softc *, char *, char *); 110 int emc_sp_info(struct emc_softc *); 111 112 struct emc_device emc_devices[] = { 113 /* " vendor " " device " */ 114 /* "01234567" "0123456789012345" */ 115 { "DGC ", "LUNZ" }, 116 { "DGC ", "RAID" }, 117 { "DGC ", "DISK" }, 118 { "DGC ", "VRAID" } 119 }; 120 121 int 122 emc_match(struct device *parent, void *match, void *aux) 123 { 124 struct scsi_attach_args *sa = aux; 125 struct scsi_inquiry_data *inq = sa->sa_inqbuf; 126 struct emc_device *s; 127 int i; 128 129 if (mpath_path_probe(sa->sa_sc_link) != 0) 130 return (0); 131 132 for (i = 0; i < nitems(emc_devices); i++) { 133 s = &emc_devices[i]; 134 135 if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && 136 bcmp(s->product, inq->product, strlen(s->product)) == 0) 137 return (3); 138 } 139 140 return (0); 141 } 142 143 void 144 emc_attach(struct device *parent, struct device *self, void *aux) 145 { 146 char model[256], serial[256]; 147 struct emc_softc *sc = (struct emc_softc *)self; 148 struct scsi_attach_args *sa = aux; 149 struct scsi_link *link = sa->sa_sc_link; 150 151 printf("\n"); 152 153 /* init link */ 154 link->device_softc = sc; 155 156 /* init path */ 157 scsi_xsh_set(&sc->sc_path.p_xsh, link, emc_mpath_start); 158 sc->sc_path.p_link = link; 159 sc->sc_path.p_ops = &emc_mpath_ops; 160 161 if (emc_sp_info(sc)) { 162 printf("%s: unable to get sp info\n", DEVNAME(sc)); 163 return; 164 } 165 166 if (emc_inquiry(sc, model, serial) != 0) { 167 printf("%s: unable to get inquiry data\n", DEVNAME(sc)); 168 return; 169 } 170 171 printf("%s: %s %s SP-%c port %d\n", DEVNAME(sc), model, serial, 172 sc->sc_sp + 'A', sc->sc_port); 173 174 if (sc->sc_lun_state == EMC_SP_INFO_LUN_STATE_OWNED) { 175 if (mpath_path_attach(&sc->sc_path) != 0) 176 printf("%s: unable to attach path\n", DEVNAME(sc)); 177 } 178 } 179 180 int 181 emc_detach(struct device *self, int flags) 182 { 183 return (0); 184 } 185 186 int 187 emc_activate(struct device *self, int act) 188 { 189 struct emc_softc *sc = (struct emc_softc *)self; 190 int rv = 0; 191 192 switch (act) { 193 case DVACT_SUSPEND: 194 case DVACT_RESUME: 195 break; 196 case DVACT_DEACTIVATE: 197 if (sc->sc_path.p_dev != NULL) 198 mpath_path_detach(&sc->sc_path); 199 break; 200 } 201 return (rv); 202 } 203 204 void 205 emc_mpath_start(struct scsi_xfer *xs) 206 { 207 struct emc_softc *sc = xs->sc_link->device_softc; 208 209 mpath_start(&sc->sc_path, xs); 210 } 211 212 int 213 emc_mpath_checksense(struct scsi_xfer *xs) 214 { 215 return (0); 216 } 217 218 int 219 emc_mpath_online(struct scsi_link *link) 220 { 221 return (0); 222 } 223 224 int 225 emc_mpath_offline(struct scsi_link *link) 226 { 227 return (0); 228 } 229 230 int 231 emc_inquiry(struct emc_softc *sc, char *model, char *serial) 232 { 233 u_int8_t *buffer; 234 struct scsi_xfer *xs; 235 size_t length; 236 int error; 237 u_int8_t slen, mlen; 238 239 length = MIN(sc->sc_path.p_link->inqdata.additional_length + 5, 255); 240 if (length < 160) { 241 printf("%s: FC (Legacy)\n"); 242 return (0); 243 } 244 245 buffer = dma_alloc(length, PR_WAITOK); 246 247 xs = scsi_xs_get(sc->sc_path.p_link, scsi_autoconf); 248 if (xs == NULL) { 249 error = EBUSY; 250 goto done; 251 } 252 253 scsi_init_inquiry(xs, 0, 0, buffer, length); 254 255 error = scsi_xs_sync(xs); 256 scsi_xs_put(xs); 257 258 if (error != 0) 259 goto done; 260 261 slen = buffer[160]; 262 if (slen == 0 || slen + 161 > length) { 263 error = EIO; 264 goto done; 265 } 266 267 mlen = buffer[99]; 268 if (mlen == 0 || slen + mlen + 161 > length) { 269 error = EIO; 270 goto done; 271 } 272 273 scsi_strvis(serial, buffer + 161, slen); 274 scsi_strvis(model, buffer + 161 + slen, mlen); 275 276 error = 0; 277 done: 278 dma_free(buffer, length); 279 return (error); 280 } 281 282 int 283 emc_sp_info(struct emc_softc *sc) 284 { 285 struct emc_vpd_sp_info *pg; 286 int error; 287 288 pg = dma_alloc(sizeof(*pg), PR_WAITOK); 289 290 error = scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg), 291 EMC_VPD_SP_INFO, scsi_autoconf); 292 if (error != 0) 293 goto done; 294 295 sc->sc_sp = pg->current_sp; 296 sc->sc_port = pg->port; 297 sc->sc_lun_state = pg->lun_state; 298 299 error = 0; 300 done: 301 dma_free(pg, sizeof(*pg)); 302 return (error); 303 } 304