xref: /freebsd/sys/geom/vinum/geom_vinum.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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$");
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
46 SYSCTL_DECL(_kern_geom);
47 SYSCTL_NODE(_kern_geom, OID_AUTO, vinum, CTLFLAG_RW, 0, "GEOM_VINUM stuff");
48 SYSCTL_UINT(_kern_geom_vinum, OID_AUTO, debug, CTLFLAG_RW, &gv_debug, 0,
49     "Debug level");
50 #endif
51 
52 int	gv_create(struct g_geom *, struct gctl_req *);
53 
54 static void
55 gv_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 
83 static void
84 gv_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 
108 static int
109 gv_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 
122 static void
123 gv_init(struct g_class *mp)
124 {
125 	struct g_geom *gp;
126 	struct gv_softc *sc;
127 
128 	g_trace(G_T_TOPOLOGY, "gv_init(%p)", mp);
129 
130 	gp = g_new_geomf(mp, "VINUM");
131 	gp->spoiled = gv_orphan;
132 	gp->orphan = gv_orphan;
133 	gp->access = gv_access;
134 	gp->start = gv_start;
135 	gp->softc = g_malloc(sizeof(struct gv_softc), M_WAITOK | M_ZERO);
136 	sc = gp->softc;
137 	sc->geom = gp;
138 	LIST_INIT(&sc->drives);
139 	LIST_INIT(&sc->subdisks);
140 	LIST_INIT(&sc->plexes);
141 	LIST_INIT(&sc->volumes);
142 }
143 
144 /* Handle userland requests for creating new objects. */
145 int
146 gv_create(struct g_geom *gp, struct gctl_req *req)
147 {
148 	struct gv_softc *sc;
149 	struct gv_drive *d, *d2;
150 	struct gv_plex *p, *p2;
151 	struct gv_sd *s, *s2;
152 	struct gv_volume *v, *v2;
153 	struct g_consumer *cp;
154 	struct g_provider *pp;
155 	int error, i, *drives, *plexes, *subdisks, *volumes;
156 	char buf[20], errstr[ERRBUFSIZ];
157 
158 	g_topology_assert();
159 
160 	sc = gp->softc;
161 
162 	/* Find out how many of each object have been passed in. */
163 	volumes = gctl_get_paraml(req, "volumes", sizeof(*volumes));
164 	plexes = gctl_get_paraml(req, "plexes", sizeof(*plexes));
165 	subdisks = gctl_get_paraml(req, "subdisks", sizeof(*subdisks));
166 	drives = gctl_get_paraml(req, "drives", sizeof(*drives));
167 
168 	/* First, handle drive definitions ... */
169 	for (i = 0; i < *drives; i++) {
170 		snprintf(buf, sizeof(buf), "drive%d", i);
171 		d2 = gctl_get_paraml(req, buf, sizeof(*d2));
172 
173 		d = gv_find_drive(sc, d2->name);
174 		if (d != NULL) {
175 			gctl_error(req, "drive '%s' is already known",
176 			    d->name);
177 			continue;
178 		}
179 
180 		d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO);
181 		bcopy(d2, d, sizeof(*d));
182 
183 		/*
184 		 * Make sure that the provider specified in the drive
185 		 * specification is an active GEOM provider.
186 		 */
187 		pp = g_provider_by_name(d->device);
188 		if (pp == NULL) {
189 			gctl_error(req, "%s: drive not found", d->device);
190 			g_free(d);
191 			return (-1);
192 		}
193 		d->size = pp->mediasize - GV_DATA_START;
194 		d->avail = d->size;
195 
196 		gv_config_new_drive(d);
197 
198 		d->flags |= GV_DRIVE_NEWBORN;
199 		LIST_INSERT_HEAD(&sc->drives, d, drive);
200 	}
201 
202 	/* ... then volume definitions ... */
203 	for (i = 0; i < *volumes; i++) {
204 		error = 0;
205 		snprintf(buf, sizeof(buf), "volume%d", i);
206 		v2 = gctl_get_paraml(req, buf, sizeof(*v2));
207 
208 		v = gv_find_vol(sc, v2->name);
209 		if (v != NULL) {
210 			gctl_error(req, "volume '%s' is already known",
211 			    v->name);
212 			return (-1);
213 		}
214 
215 		v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
216 		bcopy(v2, v, sizeof(*v));
217 
218 		v->vinumconf = sc;
219 		LIST_INIT(&v->plexes);
220 		LIST_INSERT_HEAD(&sc->volumes, v, volume);
221 	}
222 
223 	/* ... then plex definitions ... */
224 	for (i = 0; i < *plexes; i++) {
225 		error = 0;
226 		snprintf(buf, sizeof(buf), "plex%d", i);
227 		p2 = gctl_get_paraml(req, buf, sizeof(*p2));
228 
229 		p = gv_find_plex(sc, p2->name);
230 		if (p != NULL) {
231 			gctl_error(req, "plex '%s' is already known", p->name);
232 			return (-1);
233 		}
234 
235 		p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
236 		bcopy(p2, p, sizeof(*p));
237 
238 		/* Find the volume this plex should be attached to. */
239 		v = gv_find_vol(sc, p->volume);
240 		if (v == NULL) {
241 			gctl_error(req, "volume '%s' not found", p->volume);
242 			g_free(p);
243 			continue;
244 		}
245 		if (v->plexcount)
246 			p->flags |= GV_PLEX_ADDED;
247 		p->vol_sc = v;
248 		v->plexcount++;
249 		LIST_INSERT_HEAD(&v->plexes, p, in_volume);
250 
251 		p->vinumconf = sc;
252 		p->flags |= GV_PLEX_NEWBORN;
253 		LIST_INIT(&p->subdisks);
254 		LIST_INSERT_HEAD(&sc->plexes, p, plex);
255 	}
256 
257 	/* ... and finally, subdisk definitions. */
258 	for (i = 0; i < *subdisks; i++) {
259 		error = 0;
260 		snprintf(buf, sizeof(buf), "sd%d", i);
261 		s2 = gctl_get_paraml(req, buf, sizeof(*s2));
262 
263 		s = gv_find_sd(sc, s2->name);
264 		if (s != NULL) {
265 			gctl_error(req, "subdisk '%s' is already known",
266 			    s->name);
267 			return (-1);
268 		}
269 
270 		s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
271 		bcopy(s2, s, sizeof(*s));
272 
273 		/* Find the drive where this subdisk should be put on. */
274 		d = gv_find_drive(sc, s->drive);
275 
276 		/* drive not found - XXX */
277 		if (d == NULL) {
278 			gctl_error(req, "drive '%s' not found", s->drive);
279 			g_free(s);
280 			continue;
281 		}
282 
283 		/* Find the plex where this subdisk belongs to. */
284 		p = gv_find_plex(sc, s->plex);
285 
286 		/* plex not found - XXX */
287 		if (p == NULL) {
288 			gctl_error(req, "plex '%s' not found\n", s->plex);
289 			g_free(s);
290 			continue;
291 		}
292 
293 		/*
294 		 * First we give the subdisk to the drive, to handle autosized
295 		 * values ...
296 		 */
297 		error = gv_sd_to_drive(sc, d, s, errstr, sizeof(errstr));
298 		if (error) {
299 			gctl_error(req, errstr);
300 			g_free(s);
301 			continue;
302 		}
303 
304 		/*
305 		 * Then, we give the subdisk to the plex; we check if the
306 		 * given values are correct and maybe adjust them.
307 		 */
308 		error = gv_sd_to_plex(p, s, 1);
309 		if (error) {
310 			gctl_error(req, "GEOM_VINUM: couldn't give sd '%s' "
311 			    "to plex '%s'\n", s->name, p->name);
312 			if (s->drive_sc)
313 				LIST_REMOVE(s, from_drive);
314 			gv_free_sd(s);
315 			g_free(s);
316 			/*
317 			 * If this subdisk can't be created, we won't create
318 			 * the attached plex either, if it is also a new one.
319 			 */
320 			if (!(p->flags & GV_PLEX_NEWBORN))
321 				continue;
322 			LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2) {
323 				if (s->drive_sc)
324 					LIST_REMOVE(s, from_drive);
325 				p->sdcount--;
326 				LIST_REMOVE(s, in_plex);
327 				LIST_REMOVE(s, sd);
328 				gv_free_sd(s);
329 				g_free(s);
330 			}
331 			if (p->vol_sc != NULL) {
332 				LIST_REMOVE(p, in_volume);
333 				p->vol_sc->plexcount--;
334 			}
335 			LIST_REMOVE(p, plex);
336 			g_free(p);
337 			continue;
338 		}
339 		s->flags |= GV_SD_NEWBORN;
340 
341 		s->vinumconf = sc;
342 		LIST_INSERT_HEAD(&sc->subdisks, s, sd);
343 	}
344 
345 	LIST_FOREACH(s, &sc->subdisks, sd)
346 		gv_update_sd_state(s);
347 	LIST_FOREACH(p, &sc->plexes, plex)
348 		gv_update_plex_config(p);
349 	LIST_FOREACH(v, &sc->volumes, volume)
350 		gv_update_vol_state(v);
351 
352 	/*
353 	 * Write out the configuration to each drive.  If the drive doesn't
354 	 * have a valid geom_slice geom yet, attach it temporarily to our VINUM
355 	 * geom.
356 	 */
357 	LIST_FOREACH(d, &sc->drives, drive) {
358 		if (d->geom == NULL) {
359 			/*
360 			 * XXX if the provider disapears before we get a chance
361 			 * to write the config out to the drive, should this
362 			 * be handled any differently?
363 			 */
364 			pp = g_provider_by_name(d->device);
365 			if (pp == NULL) {
366 				printf("geom_vinum: %s: drive disapeared?\n",
367 				    d->device);
368 				continue;
369 			}
370 			cp = g_new_consumer(gp);
371 			g_attach(cp, pp);
372 			gv_save_config(cp, d, sc);
373 			g_detach(cp);
374 			g_destroy_consumer(cp);
375 		} else
376 			gv_save_config(NULL, d, sc);
377 		d->flags &= ~GV_DRIVE_NEWBORN;
378 	}
379 
380 	return (0);
381 }
382 
383 static void
384 gv_config(struct gctl_req *req, struct g_class *mp, char const *verb)
385 {
386 	struct g_geom *gp;
387 	struct gv_softc *sc;
388 	struct sbuf *sb;
389 	char *comment;
390 
391 	g_topology_assert();
392 
393 	gp = LIST_FIRST(&mp->geom);
394 	sc = gp->softc;
395 
396 	if (!strcmp(verb, "list")) {
397 		gv_list(gp, req);
398 
399 	/* Save our configuration back to disk. */
400 	} else if (!strcmp(verb, "saveconfig")) {
401 
402 		gv_save_config_all(sc);
403 
404 	/* Return configuration in string form. */
405 	} else if (!strcmp(verb, "getconfig")) {
406 		comment = gctl_get_param(req, "comment", NULL);
407 
408 		sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
409 		gv_format_config(sc, sb, 0, comment);
410 		sbuf_finish(sb);
411 		gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1);
412 		sbuf_delete(sb);
413 
414 	} else if (!strcmp(verb, "create")) {
415 		gv_create(gp, req);
416 
417 	} else if (!strcmp(verb, "move")) {
418 		gv_move(gp, req);
419 
420 	} else if (!strcmp(verb, "parityop")) {
421 		gv_parityop(gp, req);
422 
423 	} else if (!strcmp(verb, "remove")) {
424 		gv_remove(gp, req);
425 
426 	} else if (!strcmp(verb, "rename")) {
427 		gv_rename(gp, req);
428 
429 	} else if (!strcmp(verb, "resetconfig")) {
430 		gv_resetconfig(gp, req);
431 
432 	} else if (!strcmp(verb, "start")) {
433 		gv_start_obj(gp, req);
434 
435 	} else if (!strcmp(verb, "setstate")) {
436 		gv_setstate(gp, req);
437 
438 	} else
439 		gctl_error(req, "Unknown verb parameter");
440 }
441 
442 #if 0
443 static int
444 gv_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
445 {
446 	struct g_geom *gp2;
447 	struct g_consumer *cp;
448 	struct gv_softc *sc;
449 	struct gv_drive *d, *d2;
450 	struct gv_plex *p, *p2;
451 	struct gv_sd *s, *s2;
452 	struct gv_volume *v, *v2;
453 	struct gv_freelist *fl, *fl2;
454 
455 	g_trace(G_T_TOPOLOGY, "gv_destroy_geom: %s", gp->name);
456 	g_topology_assert();
457 
458 	KASSERT(gp != NULL, ("gv_destroy_geom: null gp"));
459 	KASSERT(gp->softc != NULL, ("gv_destroy_geom: null sc"));
460 
461 	sc = gp->softc;
462 
463 	/*
464 	 * Check if any of our drives is still open; if so, refuse destruction.
465 	 */
466 	LIST_FOREACH(d, &sc->drives, drive) {
467 		gp2 = d->geom;
468 		cp = LIST_FIRST(&gp2->consumer);
469 		if (cp != NULL)
470 			g_access(cp, -1, -1, -1);
471 		if (gv_is_open(gp2))
472 			return (EBUSY);
473 	}
474 
475 	/* Clean up and deallocate what we allocated. */
476 	LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
477 		LIST_REMOVE(d, drive);
478 		g_free(d->hdr);
479 		d->hdr = NULL;
480 		LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
481 			d->freelist_entries--;
482 			LIST_REMOVE(fl, freelist);
483 			g_free(fl);
484 			fl = NULL;
485 		}
486 		d->geom->softc = NULL;
487 		g_free(d);
488 	}
489 
490 	LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) {
491 		LIST_REMOVE(s, sd);
492 		s->drive_sc = NULL;
493 		s->plex_sc = NULL;
494 		s->provider = NULL;
495 		s->consumer = NULL;
496 		g_free(s);
497 	}
498 
499 	LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
500 		LIST_REMOVE(p, plex);
501 		gv_kill_thread(p);
502 		p->vol_sc = NULL;
503 		p->geom->softc = NULL;
504 		p->provider = NULL;
505 		p->consumer = NULL;
506 		if (p->org == GV_PLEX_RAID5) {
507 			mtx_destroy(&p->worklist_mtx);
508 		}
509 		g_free(p);
510 	}
511 
512 	LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) {
513 		LIST_REMOVE(v, volume);
514 		v->geom->softc = NULL;
515 		g_free(v);
516 	}
517 
518 	gp->softc = NULL;
519 	g_free(sc);
520 	g_wither_geom(gp, ENXIO);
521 	return (0);
522 }
523 #endif
524 
525 #define	VINUM_CLASS_NAME "VINUM"
526 
527 static struct g_class g_vinum_class	= {
528 	.name = VINUM_CLASS_NAME,
529 	.version = G_VERSION,
530 	.init = gv_init,
531 	/*.destroy_geom = gv_destroy_geom,*/
532 	.ctlreq = gv_config,
533 };
534 
535 DECLARE_GEOM_CLASS(g_vinum_class, g_vinum);
536