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