geom_fox.c revision 152967
1/*-
2 * Copyright (c) 2003 Poul-Henning Kamp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The names of the authors may not be used to endorse or promote
14 *    products derived from this software without specific prior written
15 *    permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/sys/geom/geom_fox.c 152967 2005-11-30 19:24:51Z sobomax $
30 */
31
32/* This is a GEOM module for handling path selection for multi-path
33 * storage devices.  It is named "fox" because it, like they, prefer
34 * to have multiple exits to choose from.
35 *
36 */
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/conf.h>
42#include <sys/bio.h>
43#include <sys/malloc.h>
44#include <sys/lock.h>
45#include <sys/mutex.h>
46#include <sys/libkern.h>
47#include <sys/endian.h>
48#include <sys/md5.h>
49#include <sys/errno.h>
50#include <geom/geom.h>
51
52#define FOX_CLASS_NAME "FOX"
53#define FOX_MAGIC	"GEOM::FOX"
54
55struct g_fox_softc {
56	off_t			mediasize;
57	u_int			sectorsize;
58	TAILQ_HEAD(, bio)	queue;
59	struct mtx		lock;
60	u_char 			magic[16];
61	struct g_consumer 	*path;
62	struct g_consumer 	*opath;
63	int			waiting;
64	int			cr, cw, ce;
65};
66
67/*
68 * This function is called whenever we need to select a new path.
69 */
70static void
71g_fox_select_path(void *arg, int flag)
72{
73	struct g_geom *gp;
74	struct g_fox_softc *sc;
75	struct g_consumer *cp1;
76	struct bio *bp;
77	int error;
78
79	g_topology_assert();
80	if (flag == EV_CANCEL)
81		return;
82	gp = arg;
83	sc = gp->softc;
84
85	if (sc->opath != NULL) {
86		/*
87		 * First, close the old path entirely.
88		 */
89		printf("Closing old path (%s) on fox (%s)\n",
90			sc->opath->provider->name, gp->name);
91
92		cp1 = LIST_NEXT(sc->opath, consumer);
93
94		g_access(sc->opath, -sc->cr, -sc->cw, -(sc->ce + 1));
95
96		/*
97		 * The attempt to reopen it with a exclusive count
98		 */
99		error = g_access(sc->opath, 0, 0, 1);
100		if (error) {
101			/*
102			 * Ok, ditch this consumer, we can't use it.
103			 */
104			printf("Drop old path (%s) on fox (%s)\n",
105				sc->opath->provider->name, gp->name);
106			g_detach(sc->opath);
107			g_destroy_consumer(sc->opath);
108			if (LIST_EMPTY(&gp->consumer)) {
109				/* No consumers left */
110				g_wither_geom(gp, ENXIO);
111				for (;;) {
112					bp = TAILQ_FIRST(&sc->queue);
113					if (bp == NULL)
114						break;
115					TAILQ_REMOVE(&sc->queue, bp, bio_queue);
116					bp->bio_error = ENXIO;
117					g_std_done(bp);
118				}
119				return;
120			}
121		} else {
122			printf("Got e-bit on old path (%s) on fox (%s)\n",
123				sc->opath->provider->name, gp->name);
124		}
125		sc->opath = NULL;
126	} else {
127		cp1 = LIST_FIRST(&gp->consumer);
128	}
129	if (cp1 == NULL)
130		cp1 = LIST_FIRST(&gp->consumer);
131	printf("Open new path (%s) on fox (%s)\n",
132		cp1->provider->name, gp->name);
133	error = g_access(cp1, sc->cr, sc->cw, sc->ce);
134	if (error) {
135		/*
136		 * If we failed, we take another trip through here
137		 */
138		printf("Open new path (%s) on fox (%s) failed, reselect.\n",
139			cp1->provider->name, gp->name);
140		sc->opath = cp1;
141		g_post_event(g_fox_select_path, gp, M_WAITOK, gp, NULL);
142	} else {
143		printf("Open new path (%s) on fox (%s) succeeded\n",
144			cp1->provider->name, gp->name);
145		mtx_lock(&sc->lock);
146		sc->path = cp1;
147		sc->waiting = 0;
148		for (;;) {
149			bp = TAILQ_FIRST(&sc->queue);
150			if (bp == NULL)
151				break;
152			TAILQ_REMOVE(&sc->queue, bp, bio_queue);
153			g_io_request(bp, sc->path);
154		}
155		mtx_unlock(&sc->lock);
156	}
157}
158
159static void
160g_fox_orphan(struct g_consumer *cp)
161{
162	struct g_geom *gp;
163	struct g_fox_softc *sc;
164	int error, mark;
165
166	g_topology_assert();
167	gp = cp->geom;
168	sc = gp->softc;
169	printf("Removing path (%s) from fox (%s)\n",
170	    cp->provider->name, gp->name);
171	mtx_lock(&sc->lock);
172	if (cp == sc->path) {
173		sc->opath = NULL;
174		sc->path = NULL;
175		sc->waiting = 1;
176		mark = 1;
177	} else {
178		mark = 0;
179	}
180	mtx_unlock(&sc->lock);
181
182	g_access(cp, -cp->acr, -cp->acw, -cp->ace);
183	error = cp->provider->error;
184	g_detach(cp);
185	g_destroy_consumer(cp);
186	if (!LIST_EMPTY(&gp->consumer)) {
187		if (mark)
188			g_post_event(g_fox_select_path, gp, M_WAITOK, gp, NULL);
189		return;
190	}
191
192	mtx_destroy(&sc->lock);
193	g_free(gp->softc);
194	gp->softc = NULL;
195	g_wither_geom(gp, ENXIO);
196}
197
198static void
199g_fox_done(struct bio *bp)
200{
201	struct g_geom *gp;
202	struct g_fox_softc *sc;
203	int error;
204
205	if (bp->bio_error == 0) {
206		g_std_done(bp);
207		return;
208	}
209	gp = bp->bio_from->geom;
210	sc = gp->softc;
211	if (bp->bio_from != sc->path) {
212		g_io_request(bp, sc->path);
213		return;
214	}
215	mtx_lock(&sc->lock);
216	sc->opath = sc->path;
217	sc->path = NULL;
218	error = g_post_event(g_fox_select_path, gp, M_NOWAIT, gp, NULL);
219	if (error) {
220		bp->bio_error = ENOMEM;
221		g_std_done(bp);
222	} else {
223		sc->waiting = 1;
224		TAILQ_INSERT_TAIL(&sc->queue, bp, bio_queue);
225	}
226	mtx_unlock(&sc->lock);
227}
228
229static void
230g_fox_start(struct bio *bp)
231{
232	struct g_geom *gp;
233	struct bio *bp2;
234	struct g_fox_softc *sc;
235	int error;
236
237	gp = bp->bio_to->geom;
238	sc = gp->softc;
239	if (sc == NULL) {
240		g_io_deliver(bp, ENXIO);
241		return;
242	}
243	switch(bp->bio_cmd) {
244	case BIO_READ:
245	case BIO_WRITE:
246	case BIO_DELETE:
247		bp2 = g_clone_bio(bp);
248		if (bp2 == NULL) {
249			g_io_deliver(bp, ENOMEM);
250			break;
251		}
252		bp2->bio_offset += sc->sectorsize;
253		bp2->bio_done = g_fox_done;
254		mtx_lock(&sc->lock);
255		if (sc->path == NULL || !TAILQ_EMPTY(&sc->queue)) {
256			if (sc->waiting == 0) {
257				error = g_post_event(g_fox_select_path, gp,
258				    M_NOWAIT, gp, NULL);
259				if (error) {
260					g_destroy_bio(bp2);
261					bp2 = NULL;
262					g_io_deliver(bp, error);
263				} else {
264					sc->waiting = 1;
265				}
266			}
267			if (bp2 != NULL)
268				TAILQ_INSERT_TAIL(&sc->queue, bp2,
269				    bio_queue);
270		} else {
271			g_io_request(bp2, sc->path);
272		}
273		mtx_unlock(&sc->lock);
274		break;
275	default:
276		g_io_deliver(bp, EOPNOTSUPP);
277		break;
278	}
279	return;
280}
281
282static int
283g_fox_access(struct g_provider *pp, int dr, int dw, int de)
284{
285	struct g_geom *gp;
286	struct g_fox_softc *sc;
287	struct g_consumer *cp1;
288	int error;
289
290	g_topology_assert();
291	gp = pp->geom;
292	sc = gp->softc;
293	if (sc == NULL) {
294		if (dr <= 0 && dw <= 0 && de <= 0)
295			return (0);
296		else
297			return (ENXIO);
298	}
299
300	if (sc->cr == 0 && sc->cw == 0 && sc->ce == 0) {
301		/*
302		 * First open, open all consumers with an exclusive bit
303		 */
304		error = 0;
305		LIST_FOREACH(cp1, &gp->consumer, consumer) {
306			error = g_access(cp1, 0, 0, 1);
307			if (error) {
308				printf("FOX: access(%s,0,0,1) = %d\n",
309				    cp1->provider->name, error);
310				break;
311			}
312		}
313		if (error) {
314			LIST_FOREACH(cp1, &gp->consumer, consumer) {
315				if (cp1->ace)
316					g_access(cp1, 0, 0, -1);
317			}
318			return (error);
319		}
320	}
321	if (sc->path == NULL)
322		g_fox_select_path(gp, 0);
323	if (sc->path == NULL)
324		error = ENXIO;
325	else
326		error = g_access(sc->path, dr, dw, de);
327	if (error == 0) {
328		sc->cr += dr;
329		sc->cw += dw;
330		sc->ce += de;
331		if (sc->cr == 0 && sc->cw == 0 && sc->ce == 0) {
332			/*
333			 * Last close, remove e-bit on all consumers
334			 */
335			LIST_FOREACH(cp1, &gp->consumer, consumer)
336				g_access(cp1, 0, 0, -1);
337		}
338	}
339	return (error);
340}
341
342static struct g_geom *
343g_fox_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
344{
345	struct g_geom *gp, *gp2;
346	struct g_provider *pp2;
347	struct g_consumer *cp, *cp2;
348	struct g_fox_softc *sc, *sc2;
349	int error;
350	u_int sectorsize;
351	u_char *buf;
352
353	g_trace(G_T_TOPOLOGY, "fox_taste(%s, %s)", mp->name, pp->name);
354	g_topology_assert();
355	if (!strcmp(pp->geom->class->name, mp->name))
356		return (NULL);
357	gp = g_new_geomf(mp, "%s.fox", pp->name);
358	gp->softc = g_malloc(sizeof(struct g_fox_softc), M_WAITOK | M_ZERO);
359	sc = gp->softc;
360
361	cp = g_new_consumer(gp);
362	g_attach(cp, pp);
363	error = g_access(cp, 1, 0, 0);
364	if (error) {
365		g_free(sc);
366		g_detach(cp);
367		g_destroy_consumer(cp);
368		g_destroy_geom(gp);
369		return(NULL);
370	}
371	do {
372		sectorsize = cp->provider->sectorsize;
373		g_topology_unlock();
374		buf = g_read_data(cp, 0, sectorsize, &error);
375		g_topology_lock();
376		if (buf == NULL)
377			break;
378		if (memcmp(buf, FOX_MAGIC, strlen(FOX_MAGIC)))
379			break;
380
381		/*
382		 * First we need to see if this a new path for an existing fox.
383		 */
384		LIST_FOREACH(gp2, &mp->geom, geom) {
385			sc2 = gp2->softc;
386			if (sc2 == NULL)
387				continue;
388			if (memcmp(buf + 16, sc2->magic, sizeof sc2->magic))
389				continue;
390			break;
391		}
392		if (gp2 != NULL) {
393			/*
394			 * It was.  Create a new consumer for that fox,
395			 * attach it, and if the fox is open, open this
396			 * path with an exclusive count of one.
397			 */
398			printf("Adding path (%s) to fox (%s)\n",
399			    pp->name, gp2->name);
400			cp2 = g_new_consumer(gp2);
401			g_attach(cp2, pp);
402			pp2 = LIST_FIRST(&gp2->provider);
403			if (pp2->acr > 0 || pp2->acw > 0 || pp2->ace > 0) {
404				error = g_access(cp2, 0, 0, 1);
405				if (error) {
406					/*
407					 * This is bad, or more likely,
408					 * the user is doing something stupid
409					 */
410					printf(
411	"WARNING: New path (%s) to fox(%s) not added: %s\n%s",
412					    cp2->provider->name, gp2->name,
413	"Could not get exclusive bit.",
414	"WARNING: This indicates a risk of data inconsistency."
415					);
416					g_detach(cp2);
417					g_destroy_consumer(cp2);
418				}
419			}
420			break;
421		}
422		printf("Creating new fox (%s)\n", pp->name);
423		sc->path = cp;
424		memcpy(sc->magic, buf + 16, sizeof sc->magic);
425		pp2 = g_new_providerf(gp, "%s", gp->name);
426		pp2->mediasize = sc->mediasize = pp->mediasize - pp->sectorsize;
427		pp2->sectorsize = sc->sectorsize = pp->sectorsize;
428printf("fox %s lock %p\n", gp->name, &sc->lock);
429
430		mtx_init(&sc->lock, "fox queue", NULL, MTX_DEF);
431		TAILQ_INIT(&sc->queue);
432		g_error_provider(pp2, 0);
433	} while (0);
434	if (buf != NULL)
435		g_free(buf);
436	g_access(cp, -1, 0, 0);
437
438	if (!LIST_EMPTY(&gp->provider))
439		return (gp);
440
441	g_free(gp->softc);
442	g_detach(cp);
443	g_destroy_consumer(cp);
444	g_destroy_geom(gp);
445	return (NULL);
446}
447
448static int
449g_fox_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
450{
451	struct g_fox_softc *sc;
452
453	g_topology_assert();
454	sc = gp->softc;
455	mtx_destroy(&sc->lock);
456	g_free(gp->softc);
457	gp->softc = NULL;
458	g_wither_geom(gp, ENXIO);
459	return (0);
460}
461
462static struct g_class g_fox_class	= {
463	.name = FOX_CLASS_NAME,
464	.version = G_VERSION,
465	.taste = g_fox_taste,
466	.destroy_geom = g_fox_destroy_geom,
467	.start = g_fox_start,
468	.spoiled = g_fox_orphan,
469	.orphan = g_fox_orphan,
470	.access= g_fox_access,
471};
472
473DECLARE_GEOM_CLASS(g_fox_class, g_fox);
474