xref: /freebsd/sys/geom/vinum/geom_vinum_rm.c (revision a35d88931c87cfe6bd38f01d7bad22140b3b38f3)
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/libkern.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 
36 #include <geom/geom.h>
37 #include <geom/vinum/geom_vinum_var.h>
38 #include <geom/vinum/geom_vinum.h>
39 #include <geom/vinum/geom_vinum_share.h>
40 
41 static void	gv_cleanup_pp(void *, int);
42 static void	gv_free_sd(struct gv_sd *);
43 static int	gv_rm_drive(struct gv_softc *, struct gctl_req *,
44 		    struct gv_drive *, int);
45 static int	gv_rm_plex(struct gv_softc *, struct gctl_req *,
46 		    struct gv_plex *, int);
47 static int	gv_rm_sd(struct gv_softc *, struct gctl_req *, struct gv_sd *,
48 		    int);
49 static int	gv_rm_vol(struct gv_softc *, struct gctl_req *,
50 		    struct gv_volume *, int);
51 
52 /* General 'remove' routine. */
53 void
54 gv_remove(struct g_geom *gp, struct gctl_req *req)
55 {
56 	struct gv_softc *sc;
57 	struct gv_volume *v;
58 	struct gv_plex *p;
59 	struct gv_sd *s;
60 	struct gv_drive *d;
61 	int *argc, *flags;
62 	char *argv, buf[20];
63 	int i, type, err;
64 
65 	argc = gctl_get_paraml(req, "argc", sizeof(*argc));
66 	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
67 
68 	if (argc == NULL || *argc == 0) {
69 		gctl_error(req, "no arguments given");
70 		return;
71 	}
72 
73 	sc = gp->softc;
74 
75 	for (i = 0; i < *argc; i++) {
76 		snprintf(buf, sizeof(buf), "argv%d", i);
77 		argv = gctl_get_param(req, buf, NULL);
78 		if (argv == NULL)
79 			continue;
80 		type = gv_object_type(sc, argv);
81 		switch (type) {
82 		case GV_TYPE_VOL:
83 			v = gv_find_vol(sc, argv);
84 			if (v == NULL) {
85 				gctl_error(req, "unknown volume '%s'", argv);
86 				return;
87 			}
88 			err = gv_rm_vol(sc, req, v, *flags);
89 			if (err)
90 				return;
91 			break;
92 		case GV_TYPE_PLEX:
93 			p = gv_find_plex(sc, argv);
94 			if (p == NULL) {
95 				gctl_error(req, "unknown plex '%s'", argv);
96 				return;
97 			}
98 			err = gv_rm_plex(sc, req, p, *flags);
99 			if (err)
100 				return;
101 			break;
102 		case GV_TYPE_SD:
103 			s = gv_find_sd(sc, argv);
104 			if (s == NULL) {
105 				gctl_error(req, "unknown subdisk '%s'", argv);
106 				return;
107 			}
108 			err = gv_rm_sd(sc, req, s, *flags);
109 			if (err)
110 				return;
111 			break;
112 		case GV_TYPE_DRIVE:
113 			d = gv_find_drive(sc, argv);
114 			if (d == NULL) {
115 				gctl_error(req, "unknown drive '%s'", argv);
116 				return;
117 			}
118 			err = gv_rm_drive(sc, req, d, *flags);
119 			if (err)
120 				return;
121 			break;
122 		default:
123 			gctl_error(req, "unknown object '%s'", argv);
124 			return;
125 		}
126 	}
127 
128 	gv_save_config_all(sc);
129 }
130 
131 /* Remove a volume. */
132 static int
133 gv_rm_vol(struct gv_softc *sc, struct gctl_req *req, struct gv_volume *v, int flags)
134 {
135 	struct g_geom *gp;
136 	struct gv_plex *p, *p2;
137 	int err;
138 
139 	g_topology_assert();
140 	KASSERT(v != NULL, ("gv_rm_vol: NULL v"));
141 
142 	/* If this volume has plexes, we want a recursive removal. */
143 	if (!LIST_EMPTY(&v->plexes) && !(flags & GV_FLAG_R)) {
144 		gctl_error(req, "volume '%s' has attached plexes", v->name);
145 		return (-1);
146 	}
147 
148 	gp = v->geom;
149 
150 	/* Check if any of our consumers is open. */
151 	if (gp != NULL && gv_is_open(gp)) {
152 		gctl_error(req, "volume '%s' is busy", v->name);
153 		return (-1);
154 	}
155 
156 	/* Remove the plexes our volume has. */
157 	LIST_FOREACH_SAFE(p, &v->plexes, in_volume, p2) {
158 		v->plexcount--;
159 		LIST_REMOVE(p, in_volume);
160 		p->vol_sc = NULL;
161 
162 		err = gv_rm_plex(sc, req, p, flags);
163 		if (err)
164 			return (err);
165 	}
166 
167 	/* Clean up and let our geom fade away. */
168 	LIST_REMOVE(v, volume);
169 	gv_kill_vol_thread(v);
170 	g_free(v);
171 	if (gp != NULL) {
172 		gp->softc = NULL;
173 		g_wither_geom(gp, ENXIO);
174 	}
175 
176 	return (0);
177 }
178 
179 /* Remove a plex. */
180 static int
181 gv_rm_plex(struct gv_softc *sc, struct gctl_req *req, struct gv_plex *p, int flags)
182 {
183 	struct g_geom *gp;
184 	struct gv_sd *s, *s2;
185 	int err;
186 
187 	g_topology_assert();
188 
189 	KASSERT(p != NULL, ("gv_rm_plex: NULL p"));
190 
191 	/* If this plex has subdisks, we want a recursive removal. */
192 	if (!LIST_EMPTY(&p->subdisks) && !(flags & GV_FLAG_R)) {
193 		gctl_error(req, "plex '%s' has attached subdisks", p->name);
194 		return (-1);
195 	}
196 
197 	if (p->vol_sc != NULL && p->vol_sc->plexcount == 1) {
198 		gctl_error(req, "plex '%s' is still attached to volume '%s'",
199 		    p->name, p->volume);
200 		return (-1);
201 	}
202 
203 	gp = p->geom;
204 
205 	/* Check if any of our consumers is open. */
206 	if (gp != NULL && gv_is_open(gp)) {
207 		gctl_error(req, "plex '%s' is busy", p->name);
208 		return (-1);
209 	}
210 
211 	/* Remove the subdisks our plex has. */
212 	LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2) {
213 		p->sdcount--;
214 #if 0
215 		LIST_REMOVE(s, in_plex);
216 		s->plex_sc = NULL;
217 #endif
218 
219 		err = gv_rm_sd(sc, req, s, flags);
220 		if (err)
221 			return (err);
222 	}
223 
224 	/* Clean up and let our geom fade away. */
225 	LIST_REMOVE(p, plex);
226 	if (p->vol_sc != NULL) {
227 		p->vol_sc->plexcount--;
228 		LIST_REMOVE(p, in_volume);
229 		p->vol_sc = NULL;
230 	}
231 
232 	gv_kill_plex_thread(p);
233 	g_free(p);
234 
235 	if (gp != NULL) {
236 		gp->softc = NULL;
237 		g_wither_geom(gp, ENXIO);
238 	}
239 
240 	return (0);
241 }
242 
243 /* Remove a subdisk. */
244 static int
245 gv_rm_sd(struct gv_softc *sc, struct gctl_req *req, struct gv_sd *s, int flags)
246 {
247 	struct gv_drive *d;
248 	struct g_geom *gp;
249 	struct g_provider *pp;
250 
251 	KASSERT(s != NULL, ("gv_rm_sd: NULL s"));
252 	d = s->drive_sc;
253 	KASSERT(d != NULL, ("gv_rm_sd: NULL d"));
254 	gp = d->geom;
255 	KASSERT(gp != NULL, ("gv_rm_sd: NULL gp"));
256 
257 	pp = s->provider;
258 
259 	/* Clean up. */
260 	LIST_REMOVE(s, in_plex);
261 	LIST_REMOVE(s, from_drive);
262 	LIST_REMOVE(s, sd);
263 	gv_free_sd(s);
264 	g_free(s);
265 
266 	/* If the subdisk has a provider we need to clean up this one too. */
267 	if (pp != NULL) {
268 		g_orphan_provider(pp, ENXIO);
269 		if (LIST_EMPTY(&pp->consumers))
270 			g_destroy_provider(pp);
271 		else
272 			/* Schedule this left-over provider for destruction. */
273 			g_post_event(gv_cleanup_pp, pp, M_WAITOK, pp, NULL);
274 	}
275 
276 	return (0);
277 }
278 
279 /* Remove a drive. */
280 static int
281 gv_rm_drive(struct gv_softc *sc, struct gctl_req *req, struct gv_drive *d, int flags)
282 {
283 	struct g_geom *gp;
284 	struct g_consumer *cp;
285 	struct gv_freelist *fl, *fl2;
286 	struct gv_plex *p;
287 	struct gv_sd *s, *s2;
288 	struct gv_volume *v;
289 	int err;
290 
291 	KASSERT(d != NULL, ("gv_rm_drive: NULL d"));
292 	gp = d->geom;
293 	KASSERT(gp != NULL, ("gv_rm_drive: NULL gp"));
294 
295 	/* We don't allow to remove open drives. */
296 	if (gv_is_open(gp)) {
297 		gctl_error(req, "drive '%s' is open", d->name);
298 		return (-1);
299 	}
300 
301 	/* A drive with subdisks needs a recursive removal. */
302 	if (!LIST_EMPTY(&d->subdisks) && !(flags & GV_FLAG_R)) {
303 		gctl_error(req, "drive '%s' still has subdisks", d->name);
304 		return (-1);
305 	}
306 
307 	cp = LIST_FIRST(&gp->consumer);
308 	err = g_access(cp, 0, 1, 0);
309 	if (err) {
310 		printf("GEOM_VINUM: gv_rm_drive: couldn't access '%s', errno: "
311 		    "%d\n", cp->provider->name, err);
312 		return (err);
313 	}
314 
315 	/* Clear the Vinum Magic. */
316 	d->hdr->magic = GV_NOMAGIC;
317 	g_topology_unlock();
318 	err = g_write_data(cp, GV_HDR_OFFSET, d->hdr, GV_HDR_LEN);
319 	if (err) {
320 		printf("GEOM_VINUM: gv_rm_drive: couldn't write header to '%s'"
321 		    ", errno: %d\n", cp->provider->name, err);
322 		d->hdr->magic = GV_MAGIC;
323 	}
324 	g_topology_lock();
325 	g_access(cp, 0, -1, 0);
326 
327 	/* Remove all associated subdisks, plexes, volumes. */
328 	if (!LIST_EMPTY(&d->subdisks)) {
329 		LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
330 			p = s->plex_sc;
331 			if (p != NULL) {
332 				v = p->vol_sc;
333 				if (v != NULL)
334 					gv_rm_vol(sc, req, v, flags);
335 			}
336 		}
337 	}
338 
339 	/* Clean up. */
340 	LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
341 		LIST_REMOVE(fl, freelist);
342 		g_free(fl);
343 	}
344 	LIST_REMOVE(d, drive);
345 
346 	gv_kill_drive_thread(d);
347 	gp = d->geom;
348 	d->geom = NULL;
349 	g_free(d->hdr);
350 	g_free(d);
351 	gv_save_config_all(sc);
352 	g_wither_geom(gp, ENXIO);
353 
354 	return (err);
355 }
356 
357 /*
358  * This function is called from the event queue to clean up left-over subdisk
359  * providers.
360  */
361 static void
362 gv_cleanup_pp(void *arg, int flag)
363 {
364 	struct g_provider *pp;
365 
366 	g_topology_assert();
367 
368 	if (flag == EV_CANCEL)
369 		return;
370 
371 	pp = arg;
372 	if (pp == NULL) {
373 		printf("gv_cleanup_pp: provider has gone\n");
374 		return;
375 	}
376 
377 	if (!LIST_EMPTY(&pp->consumers)) {
378 		printf("gv_cleanup_pp: provider still not empty\n");
379 		return;
380 	}
381 
382 	g_destroy_provider(pp);
383 }
384 
385 static void
386 gv_free_sd(struct gv_sd *s)
387 {
388 	struct gv_drive *d;
389 	struct gv_freelist *fl, *fl2;
390 
391 	KASSERT(s != NULL, ("gv_free_sd: NULL s"));
392 	d = s->drive_sc;
393 	KASSERT(d != NULL, ("gv_free_sd: NULL d"));
394 
395 	/*
396 	 * First, find the free slot that's immediately before or after this
397 	 * subdisk.
398 	 */
399 	fl = NULL;
400 	LIST_FOREACH(fl, &d->freelist, freelist) {
401 		if (fl->offset == s->drive_offset + s->size)
402 			break;
403 		if (fl->offset + fl->size == s->drive_offset)
404 			break;
405 	}
406 
407 	/* If there is no free slot behind this subdisk, so create one. */
408 	if (fl == NULL) {
409 
410 		fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
411 		fl->size = s->size;
412 		fl->offset = s->drive_offset;
413 
414 		if (d->freelist_entries == 0) {
415 			LIST_INSERT_HEAD(&d->freelist, fl, freelist);
416 		} else {
417 			LIST_FOREACH(fl2, &d->freelist, freelist) {
418 				if (fl->offset < fl2->offset) {
419 					LIST_INSERT_BEFORE(fl2, fl, freelist);
420 					break;
421 				} else if (LIST_NEXT(fl2, freelist) == NULL) {
422 					LIST_INSERT_AFTER(fl2, fl, freelist);
423 					break;
424 				}
425 			}
426 		}
427 
428 		d->freelist_entries++;
429 
430 	/* Expand the free slot we just found. */
431 	} else {
432 		fl->size += s->size;
433 		if (fl->offset > s->drive_offset)
434 			fl->offset = s->drive_offset;
435 	}
436 
437 	d->avail += s->size;
438 }
439