geom_vinum_volume.c revision 133318
1/*-
2 * Copyright (c) 2004 Lukas Ertl
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 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_volume.c 133318 2004-08-08 07:57:53Z phk $");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/conf.h>
33#include <sys/kernel.h>
34#include <sys/libkern.h>
35#include <sys/lock.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38#include <sys/mutex.h>
39#include <sys/systm.h>
40
41#include <geom/geom.h>
42#include <geom/vinum/geom_vinum_var.h>
43#include <geom/vinum/geom_vinum.h>
44
45static void
46gv_volume_orphan(struct g_consumer *cp)
47{
48	struct g_geom *gp;
49	struct gv_volume *v;
50	int error;
51
52	g_topology_assert();
53	gp = cp->geom;
54	g_trace(G_T_TOPOLOGY, "gv_volume_orphan(%s)", gp->name);
55	if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0)
56		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
57	error = cp->provider->error;
58	if (error == 0)
59		error = ENXIO;
60	g_detach(cp);
61	g_destroy_consumer(cp);
62	if (!LIST_EMPTY(&gp->consumer))
63		return;
64	v = gp->softc;
65	if (v != NULL)
66		v->geom = NULL;
67	gp->softc = NULL;
68	g_wither_geom(gp, error);
69}
70
71/* We end up here after the requests to our plexes are done. */
72static void
73gv_volume_done(struct bio *bp)
74{
75	struct g_consumer *cp;
76
77	/* The next plex in this volume. */
78	cp = LIST_NEXT(bp->bio_from, consumer);
79
80	switch (bp->bio_cmd) {
81	case BIO_READ:
82		/*
83		 * If no error occured on this request, or if we have no plex
84		 * left, finish here...
85		 */
86		if ((bp->bio_error == 0) || (cp == NULL)) {
87			g_std_done(bp);
88			return;
89		}
90
91		/* ... or try to read from the next plex. */
92		g_io_request(bp, cp);
93		return;
94
95	case BIO_WRITE:
96	case BIO_DELETE:
97		/* No more plexes left. */
98		if (cp == NULL) {
99			/*
100			 * Clear any errors if one of the previous writes
101			 * succeeded.
102			 */
103			if (bp->bio_caller1 == (int *)1)
104				bp->bio_error = 0;
105			g_std_done(bp);
106			return;
107		}
108
109		/* If this write request had no errors, remember that fact... */
110		if (bp->bio_error == 0)
111			bp->bio_caller1 = (int *)1;
112
113		/* ... and write to the next plex. */
114		g_io_request(bp, cp);
115		return;
116	}
117}
118
119static void
120gv_volume_start(struct bio *bp)
121{
122	struct g_geom *gp;
123	struct bio *bp2;
124	struct gv_volume *v;
125
126	gp = bp->bio_to->geom;
127	v = gp->softc;
128	if (v->state != GV_VOL_UP) {
129		g_io_deliver(bp, ENXIO);
130		return;
131	}
132	switch(bp->bio_cmd) {
133	case BIO_READ:
134	case BIO_WRITE:
135	case BIO_DELETE:
136		bp2 = g_clone_bio(bp);
137		if (bp2 == NULL) {
138			g_io_deliver(bp, ENOMEM);
139			return;
140		}
141		bp2->bio_done = gv_volume_done;
142		g_io_request(bp2, LIST_FIRST(&gp->consumer));
143		return;
144	default:
145		g_io_deliver(bp, EOPNOTSUPP);
146		return;
147	}
148}
149
150static int
151gv_volume_access(struct g_provider *pp, int dr, int dw, int de)
152{
153	struct g_geom *gp;
154	struct g_consumer *cp, *cp2;
155	int error;
156
157	gp = pp->geom;
158
159	error = ENXIO;
160	LIST_FOREACH(cp, &gp->consumer, consumer) {
161		error = g_access(cp, dr, dw, de);
162		if (error) {
163			LIST_FOREACH(cp2, &gp->consumer, consumer) {
164				if (cp == cp2)
165					break;
166				g_access(cp2, -dr, -dw, -de);
167			}
168			return (error);
169		}
170	}
171	return (error);
172}
173
174static struct g_geom *
175gv_volume_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
176{
177	struct g_geom *gp;
178	struct g_provider *pp2;
179	struct g_consumer *cp;
180	struct gv_softc *sc;
181	struct gv_volume *v;
182	struct gv_plex *p;
183	int first;
184
185	g_trace(G_T_TOPOLOGY, "gv_volume_taste(%s, %s)", mp->name, pp->name);
186	g_topology_assert();
187
188	/* First, find the VINUM class and its associated geom. */
189	gp = find_vinum_geom();
190	if (gp == NULL)
191		return (NULL);
192
193	sc = gp->softc;
194	KASSERT(sc != NULL, ("gv_volume_taste: NULL sc"));
195
196	gp = pp->geom;
197
198	/* We only want to attach to plexes. */
199	if (strcmp(gp->class->name, "VINUMPLEX"))
200		return (NULL);
201
202	first = 0;
203	p = gp->softc;
204	v = gv_find_vol(sc, p->volume);
205	if (v == NULL)
206		return (NULL);
207	if (v->geom == NULL) {
208		gp = g_new_geomf(mp, "%s", p->volume);
209		gp->start = gv_volume_start;
210		gp->orphan = gv_volume_orphan;
211		gp->access = gv_volume_access;
212		gp->softc = v;
213		first++;
214	} else
215		gp = v->geom;
216
217	cp = g_new_consumer(gp);
218	g_attach(cp, pp);
219	p->consumer = cp;
220
221	if (p->vol_sc != v) {
222		p->vol_sc = v;
223		v->plexcount++;
224		LIST_INSERT_HEAD(&v->plexes, p, in_volume);
225	}
226
227	/* We need to setup a new VINUMVOLUME geom. */
228	if (first) {
229		pp2 = g_new_providerf(gp, "gvinum/%s", v->name);
230		pp2->mediasize = pp->mediasize;
231		pp2->sectorsize = pp->sectorsize;
232		g_error_provider(pp2, 0);
233		v->size = pp2->mediasize;
234		v->geom = gp;
235		return (gp);
236	}
237
238	return (NULL);
239}
240
241static int
242gv_volume_destroy_geom(struct gctl_req *req, struct g_class *mp,
243    struct g_geom *gp)
244{
245	g_trace(G_T_TOPOLOGY, "gv_volume_destroy_geom: %s", gp->name);
246	g_topology_assert();
247
248	g_wither_geom(gp, ENXIO);
249	return (0);
250}
251
252#define	VINUMVOLUME_CLASS_NAME "VINUMVOLUME"
253
254static struct g_class g_vinum_volume_class = {
255	.name = VINUMVOLUME_CLASS_NAME,
256	.version = G_VERSION,
257	.taste = gv_volume_taste,
258	.destroy_geom = gv_volume_destroy_geom,
259};
260
261DECLARE_GEOM_CLASS(g_vinum_volume_class, g_vinum_volume);
262