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