1 /* $OpenBSD: mpath_emc.c,v 1.19 2014/04/19 05:05:43 jmatthew 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 struct scsi_xshandler sc_xsh; 66 struct emc_vpd_sp_info *sc_pg; 67 }; 68 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 69 70 int emc_match(struct device *, void *, void *); 71 void emc_attach(struct device *, struct device *, void *); 72 int emc_detach(struct device *, int); 73 int emc_activate(struct device *, int); 74 75 struct cfattach emc_ca = { 76 sizeof(struct emc_softc), 77 emc_match, 78 emc_attach, 79 emc_detach, 80 emc_activate 81 }; 82 83 struct cfdriver emc_cd = { 84 NULL, 85 "emc", 86 DV_DULL 87 }; 88 89 void emc_mpath_start(struct scsi_xfer *); 90 int emc_mpath_checksense(struct scsi_xfer *); 91 void emc_mpath_status(struct scsi_link *); 92 93 const struct mpath_ops emc_mpath_ops = { 94 "emc", 95 emc_mpath_checksense, 96 emc_mpath_status 97 }; 98 99 struct emc_device { 100 char *vendor; 101 char *product; 102 }; 103 104 void emc_status(struct scsi_xfer *); 105 void emc_status_done(struct scsi_xfer *); 106 107 int emc_sp_info(struct emc_softc *, int *); 108 109 struct emc_device emc_devices[] = { 110 /* " vendor " " device " */ 111 /* "01234567" "0123456789012345" */ 112 { "DGC ", "RAID" }, 113 { "DGC ", "DISK" }, 114 { "DGC ", "VRAID" } 115 }; 116 117 int 118 emc_match(struct device *parent, void *match, void *aux) 119 { 120 struct scsi_attach_args *sa = aux; 121 struct scsi_inquiry_data *inq = sa->sa_inqbuf; 122 struct emc_device *s; 123 int i; 124 125 if (mpath_path_probe(sa->sa_sc_link) != 0) 126 return (0); 127 128 for (i = 0; i < nitems(emc_devices); i++) { 129 s = &emc_devices[i]; 130 131 if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && 132 bcmp(s->product, inq->product, strlen(s->product)) == 0) 133 return (8); 134 } 135 136 return (0); 137 } 138 139 void 140 emc_attach(struct device *parent, struct device *self, void *aux) 141 { 142 struct emc_softc *sc = (struct emc_softc *)self; 143 struct scsi_attach_args *sa = aux; 144 struct scsi_link *link = sa->sa_sc_link; 145 int sp; 146 147 printf("\n"); 148 149 /* init link */ 150 link->device_softc = sc; 151 152 /* init path */ 153 scsi_xsh_set(&sc->sc_path.p_xsh, link, emc_mpath_start); 154 sc->sc_path.p_link = link; 155 156 /* init status handler */ 157 scsi_xsh_set(&sc->sc_xsh, link, emc_status); 158 sc->sc_pg = dma_alloc(sizeof(*sc->sc_pg), PR_WAITOK); 159 160 /* let's go */ 161 162 if (emc_sp_info(sc, &sp)) { 163 printf("%s: unable to get sp info\n", DEVNAME(sc)); 164 return; 165 } 166 167 if (mpath_path_attach(&sc->sc_path, sp, &emc_mpath_ops) != 0) 168 printf("%s: unable to attach path\n", DEVNAME(sc)); 169 } 170 171 int 172 emc_detach(struct device *self, int flags) 173 { 174 struct emc_softc *sc = (struct emc_softc *)self; 175 176 dma_free(sc->sc_pg, sizeof(*sc->sc_pg)); 177 178 return (0); 179 } 180 181 int 182 emc_activate(struct device *self, int act) 183 { 184 struct emc_softc *sc = (struct emc_softc *)self; 185 int rv = 0; 186 187 switch (act) { 188 case DVACT_DEACTIVATE: 189 if (sc->sc_path.p_group != NULL) 190 mpath_path_detach(&sc->sc_path); 191 break; 192 } 193 return (rv); 194 } 195 196 void 197 emc_mpath_start(struct scsi_xfer *xs) 198 { 199 struct emc_softc *sc = xs->sc_link->device_softc; 200 201 mpath_start(&sc->sc_path, xs); 202 } 203 204 int 205 emc_mpath_checksense(struct scsi_xfer *xs) 206 { 207 struct scsi_sense_data *sense = &xs->sense; 208 209 if ((sense->error_code & SSD_ERRCODE) == SSD_ERRCODE_CURRENT && 210 (sense->flags & SSD_KEY) == SKEY_NOT_READY && 211 ASC_ASCQ(sense) == 0x0403) { 212 /* Logical Unit Not Ready, Manual Intervention Required */ 213 return (MPATH_SENSE_FAILOVER); 214 } 215 216 return (MPATH_SENSE_DECLINED); 217 } 218 219 void 220 emc_mpath_status(struct scsi_link *link) 221 { 222 struct emc_softc *sc = link->device_softc; 223 224 scsi_xsh_add(&sc->sc_xsh); 225 } 226 227 void 228 emc_status(struct scsi_xfer *xs) 229 { 230 struct scsi_link *link = xs->sc_link; 231 struct emc_softc *sc = link->device_softc; 232 233 scsi_init_inquiry(xs, SI_EVPD, EMC_VPD_SP_INFO, 234 sc->sc_pg, sizeof(*sc->sc_pg)); 235 236 xs->done = emc_status_done; 237 238 scsi_xs_exec(xs); 239 } 240 241 void 242 emc_status_done(struct scsi_xfer *xs) 243 { 244 struct scsi_link *link = xs->sc_link; 245 struct emc_softc *sc = link->device_softc; 246 struct emc_vpd_sp_info *pg = sc->sc_pg; 247 int status = MPATH_S_UNKNOWN; 248 249 if (xs->error == XS_NOERROR) { 250 status = (pg->lun_state == EMC_SP_INFO_LUN_STATE_OWNED) ? 251 MPATH_S_ACTIVE : MPATH_S_PASSIVE; 252 } 253 254 scsi_xs_put(xs); 255 256 mpath_path_status(&sc->sc_path, status); 257 } 258 259 int 260 emc_sp_info(struct emc_softc *sc, int *sp) 261 { 262 struct emc_vpd_sp_info *pg = sc->sc_pg; 263 int error; 264 265 error = scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg), 266 EMC_VPD_SP_INFO, scsi_autoconf); 267 if (error != 0) 268 return (error); 269 270 *sp = pg->current_sp; 271 272 printf("%s: SP-%c port %d\n", DEVNAME(sc), pg->current_sp + 'A', 273 pg->port); 274 275 return (0); 276 } 277