1190513Slulf/*-
2190513Slulf * Copyright (c) 2007 Lukas Ertl
3190513Slulf * Copyright (c) 2007, 2009 Ulf Lilleengen
4190513Slulf * All rights reserved.
5190513Slulf *
6190513Slulf * Redistribution and use in source and binary forms, with or without
7190513Slulf * modification, are permitted provided that the following conditions
8190513Slulf * are met:
9190513Slulf * 1. Redistributions of source code must retain the above copyright
10190513Slulf *    notice, this list of conditions and the following disclaimer.
11190513Slulf * 2. Redistributions in binary form must reproduce the above copyright
12190513Slulf *    notice, this list of conditions and the following disclaimer in the
13190513Slulf *    documentation and/or other materials provided with the distribution.
14190513Slulf *
15190513Slulf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16190513Slulf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17190513Slulf * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18190513Slulf * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19190513Slulf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20190513Slulf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21190513Slulf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22190513Slulf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23190513Slulf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24190513Slulf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25190513Slulf * SUCH DAMAGE.
26190513Slulf */
27190513Slulf
28190513Slulf#include <sys/cdefs.h>
29190513Slulf__FBSDID("$FreeBSD$");
30190513Slulf
31190513Slulf#include <sys/param.h>
32190513Slulf#include <sys/bio.h>
33190513Slulf#include <sys/conf.h>
34193066Sjamie#include <sys/jail.h>
35190513Slulf#include <sys/kernel.h>
36190513Slulf#include <sys/malloc.h>
37190513Slulf#include <sys/systm.h>
38190513Slulf
39190513Slulf#include <geom/geom.h>
40190513Slulf#include <geom/vinum/geom_vinum_var.h>
41190513Slulf#include <geom/vinum/geom_vinum.h>
42190513Slulf
43190513Slulf#define DEFAULT_STRIPESIZE	262144
44190513Slulf
45190513Slulf/*
46190513Slulf * Create a new drive object, either by user request, during taste of the drive
47190513Slulf * itself, or because it was referenced by a subdisk during taste.
48190513Slulf */
49190513Slulfint
50190513Slulfgv_create_drive(struct gv_softc *sc, struct gv_drive *d)
51190513Slulf{
52190513Slulf	struct g_geom *gp;
53190513Slulf	struct g_provider *pp;
54190513Slulf	struct g_consumer *cp, *cp2;
55190513Slulf	struct gv_drive *d2;
56190513Slulf	struct gv_hdr *hdr;
57190513Slulf	struct gv_freelist *fl;
58190513Slulf
59190513Slulf	KASSERT(d != NULL, ("gv_create_drive: NULL d"));
60190513Slulf
61190513Slulf	gp = sc->geom;
62190513Slulf
63190513Slulf	pp = NULL;
64190513Slulf	cp = cp2 = NULL;
65190513Slulf
66190513Slulf	/* The drive already has a consumer if it was tasted before. */
67190513Slulf	if (d->consumer != NULL) {
68190513Slulf		cp = d->consumer;
69190513Slulf		cp->private = d;
70190513Slulf		pp = cp->provider;
71190513Slulf	} else if (!(d->flags & GV_DRIVE_REFERENCED)) {
72190513Slulf		if (gv_find_drive(sc, d->name) != NULL) {
73190513Slulf			G_VINUM_DEBUG(0, "drive '%s' already exists", d->name);
74190513Slulf			g_free(d);
75190513Slulf			return (GV_ERR_CREATE);
76190513Slulf		}
77190513Slulf
78190513Slulf		if (gv_find_drive_device(sc, d->device) != NULL) {
79190513Slulf			G_VINUM_DEBUG(0, "provider '%s' already in use by "
80190513Slulf			    "gvinum", d->device);
81190513Slulf			return (GV_ERR_CREATE);
82190513Slulf		}
83190513Slulf
84190513Slulf		pp = g_provider_by_name(d->device);
85190513Slulf		if (pp == NULL) {
86190513Slulf			G_VINUM_DEBUG(0, "create '%s': device '%s' disappeared",
87190513Slulf			    d->name, d->device);
88190513Slulf			g_free(d);
89190513Slulf			return (GV_ERR_CREATE);
90190513Slulf		}
91190513Slulf
92190513Slulf		g_topology_lock();
93190513Slulf		cp = g_new_consumer(gp);
94190513Slulf		if (g_attach(cp, pp) != 0) {
95190513Slulf			g_destroy_consumer(cp);
96190513Slulf			g_topology_unlock();
97197767Slulf			G_VINUM_DEBUG(0, "create drive '%s': unable to attach",
98190513Slulf			    d->name);
99190513Slulf			g_free(d);
100190513Slulf			return (GV_ERR_CREATE);
101190513Slulf		}
102190513Slulf		g_topology_unlock();
103190513Slulf
104190513Slulf		d->consumer = cp;
105190513Slulf		cp->private = d;
106190513Slulf	}
107190513Slulf
108190513Slulf	/*
109190513Slulf	 * If this was just a "referenced" drive, we're almost finished, but
110190513Slulf	 * insert this drive not on the head of the drives list, as
111190513Slulf	 * gv_drive_is_newer() expects a "real" drive from LIST_FIRST().
112190513Slulf	 */
113190513Slulf	if (d->flags & GV_DRIVE_REFERENCED) {
114190513Slulf		snprintf(d->device, sizeof(d->device), "???");
115190513Slulf		d2 = LIST_FIRST(&sc->drives);
116190513Slulf		if (d2 == NULL)
117190513Slulf			LIST_INSERT_HEAD(&sc->drives, d, drive);
118190513Slulf		else
119190513Slulf			LIST_INSERT_AFTER(d2, d, drive);
120190513Slulf		return (0);
121190513Slulf	}
122190513Slulf
123190513Slulf	/*
124190513Slulf	 * Update access counts of the new drive to those of an already
125190513Slulf	 * existing drive.
126190513Slulf	 */
127190513Slulf	LIST_FOREACH(d2, &sc->drives, drive) {
128190513Slulf		if ((d == d2) || (d2->consumer == NULL))
129190513Slulf			continue;
130190513Slulf
131190513Slulf		cp2 = d2->consumer;
132190513Slulf		g_topology_lock();
133190513Slulf		if ((cp2->acr || cp2->acw || cp2->ace) &&
134190513Slulf		    (g_access(cp, cp2->acr, cp2->acw, cp2->ace) != 0)) {
135190513Slulf			g_detach(cp);
136190513Slulf			g_destroy_consumer(cp);
137190513Slulf			g_topology_unlock();
138197767Slulf			G_VINUM_DEBUG(0, "create drive '%s': unable to update "
139190513Slulf			    "access counts", d->name);
140190513Slulf			if (d->hdr != NULL)
141190513Slulf				g_free(d->hdr);
142190513Slulf			g_free(d);
143190513Slulf			return (GV_ERR_CREATE);
144190513Slulf		}
145190513Slulf		g_topology_unlock();
146190513Slulf		break;
147190513Slulf	}
148190513Slulf
149190513Slulf	d->size = pp->mediasize - GV_DATA_START;
150190513Slulf	d->avail = d->size;
151190513Slulf	d->vinumconf = sc;
152190513Slulf	LIST_INIT(&d->subdisks);
153190513Slulf	LIST_INIT(&d->freelist);
154190513Slulf
155190513Slulf	/* The header might have been set during taste. */
156190513Slulf	if (d->hdr == NULL) {
157190513Slulf		hdr = g_malloc(sizeof(*hdr), M_WAITOK | M_ZERO);
158190513Slulf		hdr->magic = GV_MAGIC;
159190513Slulf		hdr->config_length = GV_CFG_LEN;
160193066Sjamie		getcredhostname(NULL, hdr->label.sysname, GV_HOSTNAME_LEN);
161190513Slulf		strlcpy(hdr->label.name, d->name, sizeof(hdr->label.name));
162190513Slulf		microtime(&hdr->label.date_of_birth);
163190513Slulf		d->hdr = hdr;
164190513Slulf	}
165190513Slulf
166190513Slulf	/* We also need a freelist entry. */
167190513Slulf	fl = g_malloc(sizeof(struct gv_freelist), M_WAITOK | M_ZERO);
168190513Slulf	fl->offset = GV_DATA_START;
169190513Slulf	fl->size = d->avail;
170190513Slulf	LIST_INSERT_HEAD(&d->freelist, fl, freelist);
171190513Slulf	d->freelist_entries = 1;
172190513Slulf
173190513Slulf	if (gv_find_drive(sc, d->name) == NULL)
174190513Slulf		LIST_INSERT_HEAD(&sc->drives, d, drive);
175190513Slulf
176190513Slulf	gv_set_drive_state(d, GV_DRIVE_UP, 0);
177190513Slulf	return (0);
178190513Slulf}
179190513Slulf
180190513Slulfint
181190513Slulfgv_create_volume(struct gv_softc *sc, struct gv_volume *v)
182190513Slulf{
183190513Slulf	KASSERT(v != NULL, ("gv_create_volume: NULL v"));
184190513Slulf
185190513Slulf	v->vinumconf = sc;
186190513Slulf	v->flags |= GV_VOL_NEWBORN;
187190513Slulf	LIST_INIT(&v->plexes);
188190513Slulf	LIST_INSERT_HEAD(&sc->volumes, v, volume);
189190513Slulf	v->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
190190513Slulf	bioq_init(v->wqueue);
191190513Slulf	return (0);
192190513Slulf}
193190513Slulf
194190513Slulfint
195190513Slulfgv_create_plex(struct gv_softc *sc, struct gv_plex *p)
196190513Slulf{
197190513Slulf	struct gv_volume *v;
198190513Slulf
199190513Slulf	KASSERT(p != NULL, ("gv_create_plex: NULL p"));
200190513Slulf
201190513Slulf	/* Find the volume this plex should be attached to. */
202190513Slulf	v = gv_find_vol(sc, p->volume);
203190513Slulf	if (v == NULL) {
204190513Slulf		G_VINUM_DEBUG(0, "create plex '%s': volume '%s' not found",
205190513Slulf		    p->name, p->volume);
206190513Slulf		g_free(p);
207190513Slulf		return (GV_ERR_CREATE);
208190513Slulf	}
209190513Slulf	if (!(v->flags & GV_VOL_NEWBORN))
210190513Slulf		p->flags |= GV_PLEX_ADDED;
211190513Slulf	p->vol_sc = v;
212190513Slulf	v->plexcount++;
213190513Slulf	p->vinumconf = sc;
214190513Slulf	p->synced = 0;
215190513Slulf	p->flags |= GV_PLEX_NEWBORN;
216190513Slulf	LIST_INSERT_HEAD(&v->plexes, p, in_volume);
217190513Slulf	LIST_INIT(&p->subdisks);
218190513Slulf	TAILQ_INIT(&p->packets);
219190513Slulf	LIST_INSERT_HEAD(&sc->plexes, p, plex);
220190513Slulf	p->bqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
221190513Slulf	bioq_init(p->bqueue);
222190513Slulf	p->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
223190513Slulf	bioq_init(p->wqueue);
224190513Slulf	p->rqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
225190513Slulf	bioq_init(p->rqueue);
226190513Slulf	return (0);
227190513Slulf}
228190513Slulf
229190513Slulfint
230190513Slulfgv_create_sd(struct gv_softc *sc, struct gv_sd *s)
231190513Slulf{
232190513Slulf	struct gv_plex *p;
233190513Slulf	struct gv_drive *d;
234190513Slulf
235190513Slulf	KASSERT(s != NULL, ("gv_create_sd: NULL s"));
236190513Slulf
237190513Slulf	/* Find the drive where this subdisk should be put on. */
238190513Slulf	d = gv_find_drive(sc, s->drive);
239190513Slulf	if (d == NULL) {
240190513Slulf		/*
241190513Slulf		 * It's possible that the subdisk references a drive that
242190513Slulf		 * doesn't exist yet (during the taste process), so create a
243190513Slulf		 * practically empty "referenced" drive.
244190513Slulf		 */
245190513Slulf		if (s->flags & GV_SD_TASTED) {
246190513Slulf			d = g_malloc(sizeof(struct gv_drive),
247190513Slulf			    M_WAITOK | M_ZERO);
248190513Slulf			d->flags |= GV_DRIVE_REFERENCED;
249190513Slulf			strlcpy(d->name, s->drive, sizeof(d->name));
250190513Slulf			gv_create_drive(sc, d);
251190513Slulf		} else {
252190513Slulf			G_VINUM_DEBUG(0, "create sd '%s': drive '%s' not found",
253190513Slulf			    s->name, s->drive);
254190513Slulf			g_free(s);
255190513Slulf			return (GV_ERR_CREATE);
256190513Slulf		}
257190513Slulf	}
258190513Slulf
259190513Slulf	/* Find the plex where this subdisk belongs to. */
260190513Slulf	p = gv_find_plex(sc, s->plex);
261190513Slulf	if (p == NULL) {
262190513Slulf		G_VINUM_DEBUG(0, "create sd '%s': plex '%s' not found",
263190513Slulf		    s->name, s->plex);
264190513Slulf		g_free(s);
265190513Slulf		return (GV_ERR_CREATE);
266190513Slulf	}
267190513Slulf
268190513Slulf	/*
269190513Slulf	 * First we give the subdisk to the drive, to handle autosized
270190513Slulf	 * values ...
271190513Slulf	 */
272190513Slulf	if (gv_sd_to_drive(s, d) != 0) {
273190513Slulf		g_free(s);
274190513Slulf		return (GV_ERR_CREATE);
275190513Slulf	}
276190513Slulf
277190513Slulf	/*
278190513Slulf	 * Then, we give the subdisk to the plex; we check if the
279190513Slulf	 * given values are correct and maybe adjust them.
280190513Slulf	 */
281190513Slulf	if (gv_sd_to_plex(s, p) != 0) {
282190513Slulf		G_VINUM_DEBUG(0, "unable to give sd '%s' to plex '%s'",
283190513Slulf		    s->name, p->name);
284190513Slulf		if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED))
285190513Slulf			LIST_REMOVE(s, from_drive);
286190513Slulf		gv_free_sd(s);
287190513Slulf		g_free(s);
288190513Slulf		/*
289190513Slulf		 * If this subdisk can't be created, we won't create
290190513Slulf		 * the attached plex either, if it is also a new one.
291190513Slulf		 */
292190513Slulf		if (!(p->flags & GV_PLEX_NEWBORN))
293190513Slulf			return (GV_ERR_CREATE);
294190513Slulf		gv_rm_plex(sc, p);
295190513Slulf		return (GV_ERR_CREATE);
296190513Slulf	}
297190513Slulf	s->flags |= GV_SD_NEWBORN;
298190513Slulf
299190513Slulf	s->vinumconf = sc;
300190513Slulf	LIST_INSERT_HEAD(&sc->subdisks, s, sd);
301190513Slulf
302190513Slulf	return (0);
303190513Slulf}
304190513Slulf
305190513Slulf/*
306190513Slulf * Create a concatenated volume from specified drives or drivegroups.
307190513Slulf */
308190513Slulfvoid
309190513Slulfgv_concat(struct g_geom *gp, struct gctl_req *req)
310190513Slulf{
311190513Slulf	struct gv_drive *d;
312190513Slulf	struct gv_sd *s;
313190513Slulf	struct gv_volume *v;
314190513Slulf	struct gv_plex *p;
315190513Slulf	struct gv_softc *sc;
316190513Slulf	char *drive, buf[30], *vol;
317202974Strasz	int *drives, dcount;
318190513Slulf
319190513Slulf	sc = gp->softc;
320190513Slulf	dcount = 0;
321190513Slulf	vol = gctl_get_param(req, "name", NULL);
322190513Slulf	if (vol == NULL) {
323197767Slulf		gctl_error(req, "volume name not given");
324190513Slulf		return;
325190513Slulf	}
326190513Slulf
327190513Slulf	drives = gctl_get_paraml(req, "drives", sizeof(*drives));
328190513Slulf
329190513Slulf	if (drives == NULL) {
330190513Slulf		gctl_error(req, "drive names not given");
331190513Slulf		return;
332190513Slulf	}
333190513Slulf
334190513Slulf	/* First we create the volume. */
335190513Slulf	v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
336190513Slulf	strlcpy(v->name, vol, sizeof(v->name));
337190513Slulf	v->state = GV_VOL_UP;
338190513Slulf	gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
339190513Slulf
340190513Slulf	/* Then we create the plex. */
341190513Slulf	p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
342190513Slulf	snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
343190513Slulf	strlcpy(p->volume, v->name, sizeof(p->volume));
344190513Slulf	p->org = GV_PLEX_CONCAT;
345190513Slulf	p->stripesize = 0;
346190513Slulf	gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
347190513Slulf
348190513Slulf	/* Drives are first (right now) priority */
349190513Slulf	for (dcount = 0; dcount < *drives; dcount++) {
350190513Slulf		snprintf(buf, sizeof(buf), "drive%d", dcount);
351190513Slulf		drive = gctl_get_param(req, buf, NULL);
352190513Slulf		d = gv_find_drive(sc, drive);
353190513Slulf		if (d == NULL) {
354190513Slulf			gctl_error(req, "No such drive '%s'", drive);
355190513Slulf			continue;
356190513Slulf		}
357190513Slulf		s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
358190513Slulf		snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
359190513Slulf		strlcpy(s->plex, p->name, sizeof(s->plex));
360190513Slulf		strlcpy(s->drive, drive, sizeof(s->drive));
361190513Slulf		s->plex_offset = -1;
362190513Slulf		s->drive_offset = -1;
363190513Slulf		s->size = -1;
364190513Slulf		gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
365190513Slulf	}
366190513Slulf	gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
367190513Slulf	gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
368190513Slulf}
369190513Slulf
370190513Slulf/*
371190513Slulf * Create a mirrored volume from specified drives or drivegroups.
372190513Slulf */
373190513Slulfvoid
374190513Slulfgv_mirror(struct g_geom *gp, struct gctl_req *req)
375190513Slulf{
376190513Slulf	struct gv_drive *d;
377190513Slulf	struct gv_sd *s;
378190513Slulf	struct gv_volume *v;
379190513Slulf	struct gv_plex *p;
380190513Slulf	struct gv_softc *sc;
381190513Slulf	char *drive, buf[30], *vol;
382190513Slulf	int *drives, *flags, dcount, pcount, scount;
383190513Slulf
384190513Slulf	sc = gp->softc;
385190513Slulf	dcount = 0;
386190513Slulf	scount = 0;
387190513Slulf	pcount = 0;
388190513Slulf	vol = gctl_get_param(req, "name", NULL);
389190513Slulf	if (vol == NULL) {
390197767Slulf		gctl_error(req, "volume name not given");
391190513Slulf		return;
392190513Slulf	}
393190513Slulf
394190513Slulf	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
395190513Slulf	drives = gctl_get_paraml(req, "drives", sizeof(*drives));
396190513Slulf
397190513Slulf	if (drives == NULL) {
398197767Slulf		gctl_error(req, "drive names not given");
399190513Slulf		return;
400190513Slulf	}
401190513Slulf
402190513Slulf	/* We must have an even number of drives. */
403190513Slulf	if (*drives % 2 != 0) {
404190513Slulf		gctl_error(req, "mirror organization must have an even number "
405190513Slulf		    "of drives");
406190513Slulf		return;
407190513Slulf	}
408190513Slulf	if (*flags & GV_FLAG_S && *drives < 4) {
409190513Slulf		gctl_error(req, "must have at least 4 drives for striped plex");
410190513Slulf		return;
411190513Slulf	}
412190513Slulf
413190513Slulf	/* First we create the volume. */
414190513Slulf	v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
415190513Slulf	strlcpy(v->name, vol, sizeof(v->name));
416190513Slulf	v->state = GV_VOL_UP;
417190513Slulf	gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
418190513Slulf
419190513Slulf	/* Then we create the plexes. */
420190513Slulf	for (pcount = 0; pcount < 2; pcount++) {
421190513Slulf		p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
422190513Slulf		snprintf(p->name, sizeof(p->name), "%s.p%d", v->name,
423190513Slulf		    pcount);
424190513Slulf		strlcpy(p->volume, v->name, sizeof(p->volume));
425190513Slulf		if (*flags & GV_FLAG_S) {
426190513Slulf			p->org = GV_PLEX_STRIPED;
427190513Slulf			p->stripesize = DEFAULT_STRIPESIZE;
428190513Slulf		} else {
429190513Slulf			p->org = GV_PLEX_CONCAT;
430190513Slulf			p->stripesize = -1;
431190513Slulf		}
432190513Slulf		gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
433190513Slulf
434190513Slulf		/*
435190513Slulf		 * We just gives each even drive to plex one, and each odd to
436190513Slulf		 * plex two.
437190513Slulf		 */
438190513Slulf		scount = 0;
439190513Slulf		for (dcount = pcount; dcount < *drives; dcount += 2) {
440190513Slulf			snprintf(buf, sizeof(buf), "drive%d", dcount);
441190513Slulf			drive = gctl_get_param(req, buf, NULL);
442190513Slulf			d = gv_find_drive(sc, drive);
443190513Slulf			if (d == NULL) {
444190513Slulf				gctl_error(req, "No such drive '%s', aborting",
445190513Slulf				    drive);
446190513Slulf				scount++;
447190513Slulf				break;
448190513Slulf			}
449190513Slulf			s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
450190513Slulf			snprintf(s->name, sizeof(s->name), "%s.s%d", p->name,
451190513Slulf			    scount);
452190513Slulf			strlcpy(s->plex, p->name, sizeof(s->plex));
453190513Slulf			strlcpy(s->drive, drive, sizeof(s->drive));
454190513Slulf			s->plex_offset = -1;
455190513Slulf			s->drive_offset = -1;
456190513Slulf			s->size = -1;
457190513Slulf			gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
458190513Slulf			scount++;
459190513Slulf		}
460190513Slulf	}
461190513Slulf	gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
462190513Slulf	gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
463190513Slulf}
464190513Slulf
465190513Slulfvoid
466190513Slulfgv_raid5(struct g_geom *gp, struct gctl_req *req)
467190513Slulf{
468190513Slulf	struct gv_softc *sc;
469190513Slulf	struct gv_drive *d;
470190513Slulf	struct gv_volume *v;
471190513Slulf	struct gv_plex *p;
472190513Slulf	struct gv_sd *s;
473190513Slulf	int *drives, *flags, dcount;
474190513Slulf	char *vol, *drive, buf[30];
475190513Slulf	off_t *stripesize;
476190513Slulf
477190513Slulf	sc = gp->softc;
478190513Slulf
479190513Slulf	vol = gctl_get_param(req, "name", NULL);
480190513Slulf	if (vol == NULL) {
481197767Slulf		gctl_error(req, "volume name not given");
482190513Slulf		return;
483190513Slulf	}
484190513Slulf	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
485190513Slulf	drives = gctl_get_paraml(req, "drives", sizeof(*drives));
486190513Slulf	stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize));
487190513Slulf
488190513Slulf	if (stripesize == NULL) {
489190513Slulf		gctl_error(req, "no stripesize given");
490190513Slulf		return;
491190513Slulf	}
492190513Slulf
493190513Slulf	if (drives == NULL) {
494197767Slulf		gctl_error(req, "drive names not given");
495190513Slulf		return;
496190513Slulf	}
497190513Slulf
498190513Slulf	/* We must have at least three drives. */
499190513Slulf	if (*drives < 3) {
500190513Slulf		gctl_error(req, "must have at least three drives for this "
501190513Slulf		    "plex organisation");
502190513Slulf		return;
503190513Slulf	}
504190513Slulf	/* First we create the volume. */
505190513Slulf	v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
506190513Slulf	strlcpy(v->name, vol, sizeof(v->name));
507190513Slulf	v->state = GV_VOL_UP;
508190513Slulf	gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
509190513Slulf
510190513Slulf	/* Then we create the plex. */
511190513Slulf	p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
512190513Slulf	snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
513190513Slulf	strlcpy(p->volume, v->name, sizeof(p->volume));
514190513Slulf	p->org = GV_PLEX_RAID5;
515190513Slulf	p->stripesize = *stripesize;
516190513Slulf	gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
517190513Slulf
518190513Slulf	/* Create subdisks on drives. */
519190513Slulf	for (dcount = 0; dcount < *drives; dcount++) {
520190513Slulf		snprintf(buf, sizeof(buf), "drive%d", dcount);
521190513Slulf		drive = gctl_get_param(req, buf, NULL);
522190513Slulf		d = gv_find_drive(sc, drive);
523190513Slulf		if (d == NULL) {
524190513Slulf			gctl_error(req, "No such drive '%s'", drive);
525190513Slulf			continue;
526190513Slulf		}
527190513Slulf		s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
528190513Slulf		snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
529190513Slulf		strlcpy(s->plex, p->name, sizeof(s->plex));
530190513Slulf		strlcpy(s->drive, drive, sizeof(s->drive));
531190513Slulf		s->plex_offset = -1;
532190513Slulf		s->drive_offset = -1;
533190513Slulf		s->size = -1;
534190513Slulf		gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
535190513Slulf	}
536190513Slulf	gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
537190513Slulf	gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
538190513Slulf}
539190513Slulf
540190513Slulf/*
541190513Slulf * Create a striped volume from specified drives or drivegroups.
542190513Slulf */
543190513Slulfvoid
544190513Slulfgv_stripe(struct g_geom *gp, struct gctl_req *req)
545190513Slulf{
546190513Slulf	struct gv_drive *d;
547190513Slulf	struct gv_sd *s;
548190513Slulf	struct gv_volume *v;
549190513Slulf	struct gv_plex *p;
550190513Slulf	struct gv_softc *sc;
551190513Slulf	char *drive, buf[30], *vol;
552190513Slulf	int *drives, *flags, dcount, pcount;
553190513Slulf
554190513Slulf	sc = gp->softc;
555190513Slulf	dcount = 0;
556190513Slulf	pcount = 0;
557190513Slulf	vol = gctl_get_param(req, "name", NULL);
558190513Slulf	if (vol == NULL) {
559197767Slulf		gctl_error(req, "volume name not given");
560190513Slulf		return;
561190513Slulf	}
562190513Slulf	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
563190513Slulf	drives = gctl_get_paraml(req, "drives", sizeof(*drives));
564190513Slulf
565190513Slulf	if (drives == NULL) {
566197767Slulf		gctl_error(req, "drive names not given");
567190513Slulf		return;
568190513Slulf	}
569190513Slulf
570190513Slulf	/* We must have at least two drives. */
571190513Slulf	if (*drives < 2) {
572190513Slulf		gctl_error(req, "must have at least 2 drives");
573190513Slulf		return;
574190513Slulf	}
575190513Slulf
576190513Slulf	/* First we create the volume. */
577190513Slulf	v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
578190513Slulf	strlcpy(v->name, vol, sizeof(v->name));
579190513Slulf	v->state = GV_VOL_UP;
580190513Slulf	gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
581190513Slulf
582190513Slulf	/* Then we create the plex. */
583190513Slulf	p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
584190513Slulf	snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
585190513Slulf	strlcpy(p->volume, v->name, sizeof(p->volume));
586190513Slulf	p->org = GV_PLEX_STRIPED;
587190513Slulf	p->stripesize = 262144;
588190513Slulf	gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
589190513Slulf
590190513Slulf	/* Create subdisks on drives. */
591190513Slulf	for (dcount = 0; dcount < *drives; dcount++) {
592190513Slulf		snprintf(buf, sizeof(buf), "drive%d", dcount);
593190513Slulf		drive = gctl_get_param(req, buf, NULL);
594190513Slulf		d = gv_find_drive(sc, drive);
595190513Slulf		if (d == NULL) {
596190513Slulf			gctl_error(req, "No such drive '%s'", drive);
597190513Slulf			continue;
598190513Slulf		}
599190513Slulf		s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
600190513Slulf		snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
601190513Slulf		strlcpy(s->plex, p->name, sizeof(s->plex));
602190513Slulf		strlcpy(s->drive, drive, sizeof(s->drive));
603190513Slulf		s->plex_offset = -1;
604190513Slulf		s->drive_offset = -1;
605190513Slulf		s->size = -1;
606190513Slulf		gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
607190513Slulf	}
608190513Slulf	gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
609190513Slulf	gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
610190513Slulf}
611