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