geom_vinum.c revision 136065
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 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 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
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum.c 136065 2004-10-02 20:50:21Z le $");
30
31#include <sys/param.h>
32#include <sys/bio.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37#include <sys/mutex.h>
38#include <sys/systm.h>
39
40#include <geom/geom.h>
41#include <geom/vinum/geom_vinum_var.h>
42#include <geom/vinum/geom_vinum.h>
43#include <geom/vinum/geom_vinum_share.h>
44
45#if 0
46SYSCTL_DECL(_kern_geom);
47SYSCTL_NODE(_kern_geom, OID_AUTO, vinum, CTLFLAG_RW, 0, "GEOM_VINUM stuff");
48SYSCTL_UINT(_kern_geom_vinum, OID_AUTO, debug, CTLFLAG_RW, &gv_debug, 0,
49    "Debug level");
50#endif
51
52int	gv_create(struct g_geom *, struct gctl_req *);
53
54static void
55gv_orphan(struct g_consumer *cp)
56{
57	struct g_geom *gp;
58	struct gv_softc *sc;
59	int error;
60
61	g_topology_assert();
62
63	KASSERT(cp != NULL, ("gv_orphan: null cp"));
64	gp = cp->geom;
65	KASSERT(gp != NULL, ("gv_orphan: null gp"));
66	sc = gp->softc;
67
68	g_trace(G_T_TOPOLOGY, "gv_orphan(%s)", gp->name);
69
70	if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0)
71		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
72	error = cp->provider->error;
73	if (error == 0)
74		error = ENXIO;
75	g_detach(cp);
76	g_destroy_consumer(cp);
77	if (!LIST_EMPTY(&gp->consumer))
78		return;
79	g_free(sc);
80	g_wither_geom(gp, error);
81}
82
83static void
84gv_start(struct bio *bp)
85{
86	struct bio *bp2;
87	struct g_geom *gp;
88
89	gp = bp->bio_to->geom;
90	switch(bp->bio_cmd) {
91	case BIO_READ:
92	case BIO_WRITE:
93	case BIO_DELETE:
94		bp2 = g_clone_bio(bp);
95		if (bp2 == NULL)
96			g_io_deliver(bp, ENOMEM);
97		else {
98			bp2->bio_done = g_std_done;
99			g_io_request(bp2, LIST_FIRST(&gp->consumer));
100		}
101		return;
102	default:
103		g_io_deliver(bp, EOPNOTSUPP);
104		return;
105	}
106}
107
108static int
109gv_access(struct g_provider *pp, int dr, int dw, int de)
110{
111	struct g_geom *gp;
112	struct g_consumer *cp;
113	int error;
114
115	gp = pp->geom;
116	error = ENXIO;
117	cp = LIST_FIRST(&gp->consumer);
118	error = g_access(cp, dr, dw, de);
119	return (error);
120}
121
122static struct g_geom *
123gv_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
124{
125	struct g_geom *gp;
126	struct g_consumer *cp;
127	struct gv_softc *sc;
128	struct gv_hdr *vhdr;
129	int error, first;
130	char *buf;
131
132	vhdr = NULL;
133	buf = NULL;
134	first = 0;
135
136	g_trace(G_T_TOPOLOGY, "gv_taste(%s, %s)", mp->name, pp->name);
137	g_topology_assert();
138
139	if (pp->sectorsize == 0)
140		return (NULL);
141
142	/* Check if we already have a VINUM geom, or create a new one. */
143	if (LIST_EMPTY(&mp->geom)) {
144		gp = g_new_geomf(mp, "VINUM");
145		gp->spoiled = gv_orphan;
146		gp->orphan = gv_orphan;
147		gp->access = gv_access;
148		gp->start = gv_start;
149		gp->softc = g_malloc(sizeof(struct gv_softc),
150		    M_WAITOK | M_ZERO);
151		sc = gp->softc;
152		sc->geom = gp;
153		LIST_INIT(&sc->drives);
154		LIST_INIT(&sc->subdisks);
155		LIST_INIT(&sc->plexes);
156		LIST_INIT(&sc->volumes);
157		first++;
158	} else {
159		gp = LIST_FIRST(&mp->geom);
160		sc = gp->softc;
161	}
162
163
164	/* We need a temporary consumer to read the config from. */
165	cp = g_new_consumer(gp);
166	error = g_attach(cp, pp);
167	if (error) {
168		g_destroy_consumer(cp);
169		if (first) {
170			g_free(sc);
171			g_destroy_geom(gp);
172		}
173		return (NULL);
174	}
175	error = g_access(cp, 1, 0, 0);
176	if (error) {
177		g_detach(cp);
178		g_destroy_consumer(cp);
179		if (first) {
180			g_free(gp->softc);
181			g_destroy_geom(gp);
182		}
183		return (NULL);
184	}
185
186	g_topology_unlock();
187
188	/* Check if the provided slice is a valid vinum drive. */
189	vhdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, &error);
190	if (vhdr == NULL || error != 0) {
191		g_topology_lock();
192		g_access(cp, -1, 0, 0);
193		g_detach(cp);
194		g_destroy_consumer(cp);
195		if (first) {
196			g_free(sc);
197			g_destroy_geom(gp);
198		}
199		return (NULL);
200	}
201
202	/* This provider has no vinum magic on board. */
203	if (vhdr->magic != GV_MAGIC) {
204		/* Release the temporary consumer, we don't need it anymore. */
205		g_topology_lock();
206		g_access(cp, -1, 0, 0);
207		g_detach(cp);
208		g_destroy_consumer(cp);
209
210		g_free(vhdr);
211
212		/*
213		 * If there is no other VINUM geom yet just take this one; the
214		 * configuration is still empty, but it can be filled by other
215		 * valid vinum drives later.
216		 */
217		if (first)
218			return (gp);
219		else
220			return (NULL);
221
222	/*
223	 * We have found a valid vinum drive, now read the on-disk
224	 * configuration.
225	 */
226	} else {
227		g_free(vhdr);
228
229		buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN,
230		    &error);
231		if (buf == NULL || error != 0) {
232			g_topology_lock();
233			g_access(cp, -1, 0, 0);
234			g_detach(cp);
235			g_destroy_consumer(cp);
236			if (first) {
237				g_free(sc);
238				g_destroy_geom(gp);
239			}
240			return (NULL);
241		}
242
243		/* Release the temporary consumer, we don't need it anymore. */
244		g_topology_lock();
245		g_access(cp, -1, 0, 0);
246		g_detach(cp);
247		g_destroy_consumer(cp);
248
249		/* We are the first VINUM geom. */
250		if (first) {
251			gv_parse_config(sc, buf, 0);
252			g_free(buf);
253			return (gp);
254
255		/* Just merge the configs. */
256		} else {
257			gv_parse_config(sc, buf, 1);
258			g_free(buf);
259			return (NULL);
260		}
261	}
262}
263
264/* Handle userland requests for creating new objects. */
265int
266gv_create(struct g_geom *gp, struct gctl_req *req)
267{
268	struct gv_softc *sc;
269	struct gv_drive *d, *d2;
270	struct gv_plex *p, *p2;
271	struct gv_sd *s, *s2;
272	struct gv_volume *v, *v2;
273	struct g_consumer *cp;
274	struct g_provider *pp;
275	int error, i, *drives, *plexes, *subdisks, *volumes;
276	char buf[20], errstr[ERRBUFSIZ];
277
278	g_topology_assert();
279
280	sc = gp->softc;
281
282	/* Find out how many of each object have been passed in. */
283	volumes = gctl_get_paraml(req, "volumes", sizeof(*volumes));
284	plexes = gctl_get_paraml(req, "plexes", sizeof(*plexes));
285	subdisks = gctl_get_paraml(req, "subdisks", sizeof(*subdisks));
286	drives = gctl_get_paraml(req, "drives", sizeof(*drives));
287
288	/* First, handle drive definitions ... */
289	for (i = 0; i < *drives; i++) {
290		snprintf(buf, sizeof(buf), "drive%d", i);
291		d2 = gctl_get_paraml(req, buf, sizeof(*d2));
292
293		d = gv_find_drive(sc, d2->name);
294		if (d != NULL) {
295			gctl_error(req, "drive '%s' is already known",
296			    d->name);
297			continue;
298		}
299
300		d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO);
301		bcopy(d2, d, sizeof(*d));
302
303		/*
304		 * Make sure that the provider specified in the drive
305		 * specification is an active GEOM provider.
306		 */
307		pp = g_provider_by_name(d->device);
308		if (pp == NULL) {
309			gctl_error(req, "%s: drive not found", d->device);
310			g_free(d);
311			return (-1);
312		}
313		d->size = pp->mediasize - GV_DATA_START;
314		d->avail = d->size;
315
316		gv_config_new_drive(d);
317
318		LIST_INSERT_HEAD(&sc->drives, d, drive);
319	}
320
321	/* ... then volume definitions ... */
322	for (i = 0; i < *volumes; i++) {
323		error = 0;
324		snprintf(buf, sizeof(buf), "volume%d", i);
325		v2 = gctl_get_paraml(req, buf, sizeof(*v2));
326
327		v = gv_find_vol(sc, v2->name);
328		if (v != NULL) {
329			gctl_error(req, "volume '%s' is already known",
330			    v->name);
331			return (-1);
332		}
333
334		v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
335		bcopy(v2, v, sizeof(*v));
336
337		v->vinumconf = sc;
338		LIST_INIT(&v->plexes);
339		LIST_INSERT_HEAD(&sc->volumes, v, volume);
340	}
341
342	/* ... then plex definitions ... */
343	for (i = 0; i < *plexes; i++) {
344		error = 0;
345		snprintf(buf, sizeof(buf), "plex%d", i);
346		p2 = gctl_get_paraml(req, buf, sizeof(*p2));
347
348		p = gv_find_plex(sc, p2->name);
349		if (p != NULL) {
350			gctl_error(req, "plex '%s' is already known", p->name);
351			return (-1);
352		}
353
354		p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
355		bcopy(p2, p, sizeof(*p));
356
357		/* Find the volume this plex should be attached to. */
358		v = gv_find_vol(sc, p->volume);
359		if (v != NULL) {
360			if (v->plexcount)
361				p->flags |= GV_PLEX_ADDED;
362			p->vol_sc = v;
363			v->plexcount++;
364			LIST_INSERT_HEAD(&v->plexes, p, in_volume);
365		}
366
367		p->vinumconf = sc;
368		p->flags |= GV_PLEX_NEWBORN;
369		LIST_INIT(&p->subdisks);
370		LIST_INSERT_HEAD(&sc->plexes, p, plex);
371	}
372
373	/* ... and finally, subdisk definitions. */
374	for (i = 0; i < *subdisks; i++) {
375		error = 0;
376		snprintf(buf, sizeof(buf), "sd%d", i);
377		s2 = gctl_get_paraml(req, buf, sizeof(*s2));
378
379		s = gv_find_sd(sc, s2->name);
380		if (s != NULL) {
381			gctl_error(req, "subdisk '%s' is already known",
382			    s->name);
383			return (-1);
384		}
385
386		s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
387		bcopy(s2, s, sizeof(*s));
388
389		/* Find the drive where this subdisk should be put on. */
390		d = gv_find_drive(sc, s->drive);
391
392		/* drive not found - XXX */
393		if (d == NULL) {
394			printf("FOO: drive '%s' not found\n", s->drive);
395			g_free(s);
396			continue;
397		}
398
399		/* Find the plex where this subdisk belongs to. */
400		p = gv_find_plex(sc, s->plex);
401
402		/* plex not found - XXX */
403		if (p == NULL) {
404			printf("FOO: plex '%s' not found\n", s->plex);
405			g_free(s);
406			continue;
407		}
408
409		/*
410		 * First we give the subdisk to the drive, to handle autosized
411		 * values ...
412		 */
413		error = gv_sd_to_drive(sc, d, s, errstr, sizeof(errstr));
414		if (error) {
415			gctl_error(req, errstr);
416			g_free(s);
417			continue;
418		}
419
420		/*
421		 * Then, we give the subdisk to the plex; we check if the
422		 * given values are correct and maybe adjust them.
423		 */
424		error = gv_sd_to_plex(p, s, 1);
425		if (error) {
426			printf("FOO: couldn't give sd '%s' to plex '%s'\n",
427			    s->name, p->name);
428		}
429		s->flags |= GV_SD_NEWBORN;
430
431		s->vinumconf = sc;
432		LIST_INSERT_HEAD(&sc->subdisks, s, sd);
433	}
434
435	LIST_FOREACH(s, &sc->subdisks, sd)
436		gv_update_sd_state(s);
437	LIST_FOREACH(p, &sc->plexes, plex)
438		gv_update_plex_config(p);
439	LIST_FOREACH(v, &sc->volumes, volume)
440		gv_update_vol_state(v);
441
442	/*
443	 * Write out the configuration to each drive.  If the drive doesn't
444	 * have a valid geom_slice geom yet, attach it temporarily to our VINUM
445	 * geom.
446	 */
447	LIST_FOREACH(d, &sc->drives, drive) {
448		if (d->geom == NULL) {
449			/*
450			 * XXX if the provider disapears before we get a chance
451			 * to write the config out to the drive, should this
452			 * be handled any differently?
453			 */
454			pp = g_provider_by_name(d->device);
455			if (pp == NULL) {
456				printf("geom_vinum: %s: drive disapeared?\n",
457				    d->device);
458				continue;
459			}
460			cp = g_new_consumer(gp);
461			g_attach(cp, pp);
462			gv_save_config(cp, d, sc);
463			g_detach(cp);
464			g_destroy_consumer(cp);
465		} else
466			gv_save_config(NULL, d, sc);
467	}
468
469	return (0);
470}
471
472static void
473gv_config(struct gctl_req *req, struct g_class *mp, char const *verb)
474{
475	struct g_geom *gp;
476	struct gv_softc *sc;
477	struct sbuf *sb;
478	char *comment;
479
480	g_topology_assert();
481
482	gp = LIST_FIRST(&mp->geom);
483	sc = gp->softc;
484
485	if (!strcmp(verb, "list")) {
486		gv_list(gp, req);
487
488	/* Save our configuration back to disk. */
489	} else if (!strcmp(verb, "saveconfig")) {
490
491		gv_save_config_all(sc);
492
493	/* Return configuration in string form. */
494	} else if (!strcmp(verb, "getconfig")) {
495		comment = gctl_get_param(req, "comment", NULL);
496
497		sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
498		gv_format_config(sc, sb, 0, comment);
499		sbuf_finish(sb);
500		gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1);
501		sbuf_delete(sb);
502
503	} else if (!strcmp(verb, "create")) {
504		gv_create(gp, req);
505
506	} else if (!strcmp(verb, "remove")) {
507		gv_remove(gp, req);
508
509	} else if (!strcmp(verb, "start")) {
510		gv_start_obj(gp, req);
511
512	} else
513		gctl_error(req, "Unknown verb parameter");
514}
515
516#if 0
517static int
518gv_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
519{
520	struct g_geom *gp2;
521	struct g_consumer *cp;
522	struct gv_softc *sc;
523	struct gv_drive *d, *d2;
524	struct gv_plex *p, *p2;
525	struct gv_sd *s, *s2;
526	struct gv_volume *v, *v2;
527	struct gv_freelist *fl, *fl2;
528
529	g_trace(G_T_TOPOLOGY, "gv_destroy_geom: %s", gp->name);
530	g_topology_assert();
531
532	KASSERT(gp != NULL, ("gv_destroy_geom: null gp"));
533	KASSERT(gp->softc != NULL, ("gv_destroy_geom: null sc"));
534
535	sc = gp->softc;
536
537	/*
538	 * Check if any of our drives is still open; if so, refuse destruction.
539	 */
540	LIST_FOREACH(d, &sc->drives, drive) {
541		gp2 = d->geom;
542		cp = LIST_FIRST(&gp2->consumer);
543		if (cp != NULL)
544			g_access(cp, -1, -1, -1);
545		if (gv_is_open(gp2))
546			return (EBUSY);
547	}
548
549	/* Clean up and deallocate what we allocated. */
550	LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
551		LIST_REMOVE(d, drive);
552		g_free(d->hdr);
553		d->hdr = NULL;
554		LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
555			d->freelist_entries--;
556			LIST_REMOVE(fl, freelist);
557			g_free(fl);
558			fl = NULL;
559		}
560		d->geom->softc = NULL;
561		g_free(d);
562	}
563
564	LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) {
565		LIST_REMOVE(s, sd);
566		s->drive_sc = NULL;
567		s->plex_sc = NULL;
568		s->provider = NULL;
569		s->consumer = NULL;
570		g_free(s);
571	}
572
573	LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
574		LIST_REMOVE(p, plex);
575		gv_kill_thread(p);
576		p->vol_sc = NULL;
577		p->geom->softc = NULL;
578		p->provider = NULL;
579		p->consumer = NULL;
580		if (p->org == GV_PLEX_RAID5) {
581			mtx_destroy(&p->worklist_mtx);
582		}
583		g_free(p);
584	}
585
586	LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) {
587		LIST_REMOVE(v, volume);
588		v->geom->softc = NULL;
589		g_free(v);
590	}
591
592	gp->softc = NULL;
593	g_free(sc);
594	g_wither_geom(gp, ENXIO);
595	return (0);
596}
597#endif
598
599#define	VINUM_CLASS_NAME "VINUM"
600
601static struct g_class g_vinum_class	= {
602	.name = VINUM_CLASS_NAME,
603	.version = G_VERSION,
604	.taste = gv_taste,
605	/*.destroy_geom = gv_destroy_geom,*/
606	.ctlreq = gv_config,
607};
608
609DECLARE_GEOM_CLASS(g_vinum_class, g_vinum);
610