1 /* $OpenBSD: mpath.c,v 1.4 2009/09/14 00:03:28 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2009 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 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/buf.h> 22 #include <sys/kernel.h> 23 #include <sys/malloc.h> 24 #include <sys/device.h> 25 #include <sys/proc.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 37 #define MPATH_BUSWIDTH 256 38 39 int mpath_match(struct device *, void *, void *); 40 void mpath_attach(struct device *, struct device *, void *); 41 void mpath_shutdown(void *); 42 43 struct mpath_path { 44 struct scsi_link *path_link; 45 TAILQ_ENTRY(mpath_path) path_entry; 46 }; 47 TAILQ_HEAD(mpath_paths, mpath_path); 48 49 struct mpath_node { 50 struct devid node_id; 51 struct mpath_paths node_paths; 52 }; 53 54 struct mpath_softc { 55 struct device sc_dev; 56 struct scsi_link sc_link; 57 struct scsibus_softc *sc_scsibus; 58 }; 59 60 struct mpath_softc *mpath; 61 struct mpath_node *mpath_nodes[MPATH_BUSWIDTH]; 62 63 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 64 65 struct cfattach mpath_ca = { 66 sizeof(struct mpath_softc), 67 mpath_match, 68 mpath_attach 69 }; 70 71 struct cfdriver mpath_cd = { 72 NULL, 73 "mpath", 74 DV_DULL 75 }; 76 77 int mpath_cmd(struct scsi_xfer *); 78 void mpath_minphys(struct buf *, struct scsi_link *); 79 int mpath_probe(struct scsi_link *); 80 81 void mpath_done(struct scsi_xfer *); 82 83 struct scsi_adapter mpath_switch = { 84 mpath_cmd, 85 scsi_minphys, 86 mpath_probe, 87 NULL 88 }; 89 90 struct scsi_device mpath_dev = { 91 NULL, NULL, NULL, NULL 92 }; 93 94 void mpath_xs_stuffup(struct scsi_xfer *); 95 96 int 97 mpath_match(struct device *parent, void *match, void *aux) 98 { 99 return (1); 100 } 101 102 void 103 mpath_attach(struct device *parent, struct device *self, void *aux) 104 { 105 struct mpath_softc *sc = (struct mpath_softc *)self; 106 struct scsibus_attach_args saa; 107 108 mpath = sc; 109 110 printf("\n"); 111 112 sc->sc_link.device = &mpath_dev; 113 sc->sc_link.adapter = &mpath_switch; 114 sc->sc_link.adapter_softc = sc; 115 sc->sc_link.adapter_target = MPATH_BUSWIDTH; 116 sc->sc_link.adapter_buswidth = MPATH_BUSWIDTH; 117 sc->sc_link.openings = 1; 118 119 bzero(&saa, sizeof(saa)); 120 saa.saa_sc_link = &sc->sc_link; 121 122 sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev, 123 &saa, scsiprint); 124 } 125 126 void 127 mpath_xs_stuffup(struct scsi_xfer *xs) 128 { 129 int s; 130 131 xs->error = XS_DRIVER_STUFFUP; 132 xs->flags |= ITSDONE; 133 s = splbio(); 134 scsi_done(xs); 135 splx(s); 136 } 137 138 int 139 mpath_probe(struct scsi_link *link) 140 { 141 if (link->lun != 0 || mpath_nodes[link->target] == NULL) 142 return (ENXIO); 143 144 return (0); 145 } 146 147 int 148 mpath_cmd(struct scsi_xfer *xs) 149 { 150 struct scsi_link *link = xs->sc_link; 151 struct mpath_node *n = mpath_nodes[link->target]; 152 struct mpath_path *p = TAILQ_FIRST(&n->node_paths); 153 struct scsi_xfer *mxs; 154 155 if (n == NULL || p == NULL) { 156 mpath_xs_stuffup(xs); 157 return (COMPLETE); 158 } 159 160 mxs = scsi_xs_get(p->path_link, xs->flags); 161 if (mxs == NULL) { 162 mpath_xs_stuffup(xs); 163 return (COMPLETE); 164 } 165 166 memcpy(mxs->cmd, xs->cmd, xs->cmdlen); 167 mxs->cmdlen = xs->cmdlen; 168 mxs->data = xs->data; 169 mxs->datalen = xs->datalen; 170 mxs->retries = xs->retries; 171 mxs->timeout = xs->timeout; 172 mxs->req_sense_length = xs->req_sense_length; 173 174 mxs->cookie = xs; 175 mxs->done = mpath_done; 176 177 scsi_xs_exec(mxs); 178 179 return (COMPLETE); /* doesnt matter anymore */ 180 } 181 182 void 183 mpath_done(struct scsi_xfer *mxs) 184 { 185 struct scsi_xfer *xs = mxs->cookie; 186 int s; 187 188 xs->error = mxs->error; 189 xs->status = mxs->status; 190 xs->flags = mxs->flags; 191 xs->resid = mxs->resid; 192 193 memcpy(&xs->sense, &mxs->sense, sizeof(xs->sense)); 194 195 scsi_xs_put(mxs); 196 197 s = splbio(); 198 scsi_done(xs); 199 splx(s); 200 } 201 202 void 203 mpath_minphys(struct buf *bp, struct scsi_link *link) 204 { 205 struct mpath_node *n = mpath_nodes[link->target]; 206 struct mpath_path *p; 207 208 if (n == NULL) 209 return; 210 211 TAILQ_FOREACH(p, &n->node_paths, path_entry) 212 p->path_link->adapter->scsi_minphys(bp, p->path_link); 213 } 214 215 int 216 mpath_path_attach(struct scsi_link *link) 217 { 218 struct mpath_node *n; 219 struct mpath_path *p; 220 int probe = 0; 221 int target; 222 223 if (mpath != NULL && link->adapter_softc == mpath) 224 return (ENODEV); 225 226 /* XXX this is dumb. should check inq shizz */ 227 if (link->id.d_type == DEVID_NONE) 228 return (ENXIO); 229 230 for (target = 0; target < MPATH_BUSWIDTH; target++) { 231 if ((n = mpath_nodes[target]) == NULL) 232 continue; 233 234 if (DEVID_CMP(&n->node_id, &link->id)) 235 break; 236 237 n = NULL; 238 } 239 240 if (n == NULL) { 241 for (target = 0; target < MPATH_BUSWIDTH; target++) { 242 if (mpath_nodes[target] == NULL) 243 break; 244 } 245 if (target >= MPATH_BUSWIDTH) 246 return (ENXIO); 247 248 n = malloc(sizeof(*n), M_DEVBUF, M_WAITOK | M_ZERO); 249 TAILQ_INIT(&n->node_paths); 250 251 n->node_id.d_type = link->id.d_type; 252 n->node_id.d_len = link->id.d_len; 253 n->node_id.d_id = malloc(n->node_id.d_len, M_DEVBUF, M_DEVBUF); 254 memcpy(n->node_id.d_id, link->id.d_id, n->node_id.d_len); 255 256 mpath_nodes[target] = n; 257 probe = 1; 258 } 259 260 p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK); 261 262 p->path_link = link; 263 TAILQ_INSERT_TAIL(&n->node_paths, p, path_entry); 264 265 if (mpath != NULL && probe) 266 scsi_probe_target(mpath->sc_scsibus, target); 267 268 return (0); 269 } 270 271 int 272 mpath_path_detach(struct scsi_link *link, int flags) 273 { 274 struct mpath_node *n; 275 struct mpath_path *p; 276 int target; 277 278 for (target = 0; target < MPATH_BUSWIDTH; target++) { 279 if ((n = mpath_nodes[target]) == NULL) 280 continue; 281 282 if (DEVID_CMP(&n->node_id, &link->id)) 283 break; 284 285 n = NULL; 286 } 287 288 if (n == NULL) 289 panic("mpath: detaching a path from a nonexistant bus"); 290 291 TAILQ_FOREACH(p, &n->node_paths, path_entry) { 292 if (p->path_link == link) { 293 TAILQ_REMOVE(&n->node_paths, p, path_entry); 294 free(p, M_DEVBUF); 295 return (0); 296 } 297 } 298 299 panic("mpath: unable to locate path for detach"); 300 } 301