1 /* $OpenBSD: mpath.c,v 1.10 2009/11/12 06:20:27 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 struct scsi_adapter mpath_switch = { 82 mpath_cmd, 83 scsi_minphys, 84 mpath_probe, 85 NULL 86 }; 87 88 struct scsi_device mpath_dev = { 89 NULL, NULL, NULL, NULL 90 }; 91 92 void mpath_xs_stuffup(struct scsi_xfer *); 93 94 int 95 mpath_match(struct device *parent, void *match, void *aux) 96 { 97 return (1); 98 } 99 100 void 101 mpath_attach(struct device *parent, struct device *self, void *aux) 102 { 103 struct mpath_softc *sc = (struct mpath_softc *)self; 104 struct scsibus_attach_args saa; 105 106 mpath = sc; 107 108 printf("\n"); 109 110 sc->sc_link.device = &mpath_dev; 111 sc->sc_link.adapter = &mpath_switch; 112 sc->sc_link.adapter_softc = sc; 113 sc->sc_link.adapter_target = MPATH_BUSWIDTH; 114 sc->sc_link.adapter_buswidth = MPATH_BUSWIDTH; 115 sc->sc_link.openings = 1; 116 117 bzero(&saa, sizeof(saa)); 118 saa.saa_sc_link = &sc->sc_link; 119 120 sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev, 121 &saa, scsiprint); 122 } 123 124 void 125 mpath_xs_stuffup(struct scsi_xfer *xs) 126 { 127 int s; 128 129 xs->error = XS_DRIVER_STUFFUP; 130 xs->flags |= ITSDONE; 131 s = splbio(); 132 scsi_done(xs); 133 splx(s); 134 } 135 136 int 137 mpath_probe(struct scsi_link *link) 138 { 139 if (link->lun != 0 || mpath_nodes[link->target] == NULL) 140 return (ENXIO); 141 142 return (0); 143 } 144 145 int 146 mpath_cmd(struct scsi_xfer *xs) 147 { 148 struct scsi_link *link = xs->sc_link; 149 struct mpath_node *n = mpath_nodes[link->target]; 150 struct mpath_path *p = TAILQ_FIRST(&n->node_paths); 151 int rv; 152 int s; 153 154 if (n == NULL || p == NULL) { 155 mpath_xs_stuffup(xs); 156 return (COMPLETE); 157 } 158 159 rv = scsi_scsi_cmd(p->path_link, xs->cmd, xs->cmdlen, 160 xs->data, xs->datalen, 161 2, xs->timeout, NULL, SCSI_POLL | 162 (xs->flags & (SCSI_DATA_IN|SCSI_DATA_OUT))); 163 164 165 xs->flags |= ITSDONE; 166 if (rv == 0) { 167 xs->error = XS_NOERROR; 168 xs->status = SCSI_OK; 169 xs->resid = 0; 170 } else { 171 printf("%s: t%dl%d rv %d cmd %x\n", DEVNAME(mpath), 172 link->target, link->lun, rv, xs->cmd->opcode); 173 xs->error = XS_DRIVER_STUFFUP; 174 } 175 176 s = splbio(); 177 scsi_done(xs); 178 splx(s); 179 180 return (COMPLETE); 181 } 182 183 void 184 mpath_minphys(struct buf *bp, struct scsi_link *link) 185 { 186 struct mpath_node *n = mpath_nodes[link->target]; 187 struct mpath_path *p; 188 189 if (n == NULL) 190 return; 191 192 TAILQ_FOREACH(p, &n->node_paths, path_entry) 193 p->path_link->adapter->scsi_minphys(bp, p->path_link); 194 } 195 196 int 197 mpath_path_attach(struct scsi_link *link) 198 { 199 struct mpath_node *n; 200 struct mpath_path *p; 201 int probe = 0; 202 int target; 203 204 if (mpath != NULL && link->adapter_softc == mpath) 205 return (ENODEV); 206 207 /* XXX this is dumb. should check inq shizz */ 208 if (link->id.d_type == DEVID_NONE) 209 return (ENXIO); 210 211 for (target = 0; target < MPATH_BUSWIDTH; target++) { 212 if ((n = mpath_nodes[target]) == NULL) 213 continue; 214 215 if (DEVID_CMP(&n->node_id, &link->id)) 216 break; 217 218 n = NULL; 219 } 220 221 if (n == NULL) { 222 for (target = 0; target < MPATH_BUSWIDTH; target++) { 223 if (mpath_nodes[target] == NULL) 224 break; 225 } 226 if (target >= MPATH_BUSWIDTH) 227 return (ENXIO); 228 229 n = malloc(sizeof(*n), M_DEVBUF, M_WAITOK | M_ZERO); 230 TAILQ_INIT(&n->node_paths); 231 232 n->node_id.d_type = link->id.d_type; 233 n->node_id.d_len = link->id.d_len; 234 n->node_id.d_id = malloc(n->node_id.d_len, M_DEVBUF, M_DEVBUF); 235 memcpy(n->node_id.d_id, link->id.d_id, n->node_id.d_len); 236 237 mpath_nodes[target] = n; 238 probe = 1; 239 } 240 241 p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK); 242 243 p->path_link = link; 244 TAILQ_INSERT_TAIL(&n->node_paths, p, path_entry); 245 246 if (mpath != NULL && probe) 247 scsi_probe_target(mpath->sc_scsibus, target); 248 249 return (0); 250 } 251 252 int 253 mpath_path_detach(struct scsi_link *link, int flags) 254 { 255 struct mpath_node *n; 256 struct mpath_path *p; 257 int target; 258 259 for (target = 0; target < MPATH_BUSWIDTH; target++) { 260 if ((n = mpath_nodes[target]) == NULL) 261 continue; 262 263 if (DEVID_CMP(&n->node_id, &link->id)) 264 break; 265 266 n = NULL; 267 } 268 269 if (n == NULL) 270 panic("mpath: detaching a path from a nonexistant bus"); 271 272 TAILQ_FOREACH(p, &n->node_paths, path_entry) { 273 if (p->path_link == link) { 274 TAILQ_REMOVE(&n->node_paths, p, path_entry); 275 free(p, M_DEVBUF); 276 return (0); 277 } 278 } 279 280 panic("mpath: unable to locate path for detach"); 281 } 282