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