xref: /openbsd-src/sys/scsi/mpath.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
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