1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 *  Copyright (c) 2004, 2007 Lukas Ertl
5 *  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/libkern.h>
35#include <sys/malloc.h>
36
37#include <geom/geom.h>
38#include <geom/geom_dbg.h>
39#include <geom/vinum/geom_vinum_var.h>
40#include <geom/vinum/geom_vinum.h>
41
42/* General 'remove' routine. */
43void
44gv_remove(struct g_geom *gp, struct gctl_req *req)
45{
46	struct gv_softc *sc;
47	struct gv_volume *v;
48	struct gv_plex *p;
49	struct gv_sd *s;
50	struct gv_drive *d;
51	int *argc, *flags;
52	char *argv, buf[20];
53	int i, type;
54
55	argc = gctl_get_paraml(req, "argc", sizeof(*argc));
56
57	if (argc == NULL || *argc == 0) {
58		gctl_error(req, "no arguments given");
59		return;
60	}
61
62	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
63	if (flags == NULL) {
64		gctl_error(req, "no flags given");
65		return;
66	}
67
68	sc = gp->softc;
69
70	/* XXX config locking */
71
72	for (i = 0; i < *argc; i++) {
73		snprintf(buf, sizeof(buf), "argv%d", i);
74		argv = gctl_get_param(req, buf, NULL);
75		if (argv == NULL)
76			continue;
77		type = gv_object_type(sc, argv);
78		switch (type) {
79		case GV_TYPE_VOL:
80			v = gv_find_vol(sc, argv);
81
82			/*
83			 * If this volume has plexes, we want a recursive
84			 * removal.
85			 */
86			if (!LIST_EMPTY(&v->plexes) && !(*flags & GV_FLAG_R)) {
87				gctl_error(req, "volume '%s' has attached "
88				    "plexes - need recursive removal", v->name);
89				return;
90			}
91
92			gv_post_event(sc, GV_EVENT_RM_VOLUME, v, NULL, 0, 0);
93			break;
94
95		case GV_TYPE_PLEX:
96			p = gv_find_plex(sc, argv);
97
98			/*
99			 * If this plex has subdisks, we want a recursive
100			 * removal.
101			 */
102			if (!LIST_EMPTY(&p->subdisks) &&
103			    !(*flags & GV_FLAG_R)) {
104				gctl_error(req, "plex '%s' has attached "
105				    "subdisks - need recursive removal",
106				    p->name);
107				return;
108			}
109
110			/* Don't allow removal of the only plex of a volume. */
111			if (p->vol_sc != NULL && p->vol_sc->plexcount == 1) {
112				gctl_error(req, "plex '%s' is still attached "
113				    "to volume '%s'", p->name, p->volume);
114				return;
115			}
116
117			gv_post_event(sc, GV_EVENT_RM_PLEX, p, NULL, 0, 0);
118			break;
119
120		case GV_TYPE_SD:
121			s = gv_find_sd(sc, argv);
122
123			/* Don't allow removal if attached to a plex. */
124			if (s->plex_sc != NULL) {
125				gctl_error(req, "subdisk '%s' is still attached"
126				    " to plex '%s'", s->name, s->plex_sc->name);
127				return;
128			}
129
130			gv_post_event(sc, GV_EVENT_RM_SD, s, NULL, 0, 0);
131			break;
132
133		case GV_TYPE_DRIVE:
134			d = gv_find_drive(sc, argv);
135			/* We don't allow to remove open drives. */
136			if (gv_consumer_is_open(d->consumer) &&
137			    !(*flags & GV_FLAG_F)) {
138				gctl_error(req, "drive '%s' is open", d->name);
139				return;
140			}
141
142			/* A drive with subdisks needs a recursive removal. */
143/*			if (!LIST_EMPTY(&d->subdisks) &&
144			    !(*flags & GV_FLAG_R)) {
145				gctl_error(req, "drive '%s' still has subdisks"
146				    " - need recursive removal", d->name);
147				return;
148			}*/
149
150			gv_post_event(sc, GV_EVENT_RM_DRIVE, d, NULL, *flags,
151			    0);
152			break;
153
154		default:
155			gctl_error(req, "unknown object '%s'", argv);
156			return;
157		}
158	}
159
160	gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
161}
162
163/* Resets configuration */
164int
165gv_resetconfig(struct gv_softc *sc)
166{
167	struct gv_drive *d, *d2;
168	struct gv_volume *v, *v2;
169	struct gv_plex *p, *p2;
170	struct gv_sd *s, *s2;
171
172	/* First make sure nothing is open. */
173        LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
174		if (gv_consumer_is_open(d->consumer)) {
175			return (GV_ERR_ISBUSY);
176		}
177	}
178
179	/* Make sure nothing is going on internally. */
180	LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
181		if (p->flags & (GV_PLEX_REBUILDING | GV_PLEX_GROWING))
182			return (GV_ERR_ISBUSY);
183	}
184
185	/* Then if not, we remove everything. */
186	LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2)
187		gv_rm_sd(sc, s);
188	LIST_FOREACH_SAFE(d, &sc->drives, drive, d2)
189		gv_rm_drive(sc, d, 0);
190	LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2)
191		gv_rm_plex(sc, p);
192	LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2)
193		gv_rm_vol(sc, v);
194
195	gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
196
197	return (0);
198}
199
200/* Remove a volume. */
201void
202gv_rm_vol(struct gv_softc *sc, struct gv_volume *v)
203{
204	struct g_provider *pp;
205	struct gv_plex *p, *p2;
206
207	KASSERT(v != NULL, ("gv_rm_vol: NULL v"));
208	pp = v->provider;
209	KASSERT(pp != NULL, ("gv_rm_vol: NULL pp"));
210
211	/* Check if any of our consumers is open. */
212	if (gv_provider_is_open(pp)) {
213		G_VINUM_DEBUG(0, "unable to remove %s: volume still in use",
214		    v->name);
215		return;
216	}
217
218	/* Remove the plexes our volume has. */
219	LIST_FOREACH_SAFE(p, &v->plexes, in_volume, p2)
220		gv_rm_plex(sc, p);
221
222	/* Clean up. */
223	LIST_REMOVE(v, volume);
224	g_free(v);
225
226	/* Get rid of the volume's provider. */
227	if (pp != NULL) {
228		g_topology_lock();
229		g_wither_provider(pp, ENXIO);
230		g_topology_unlock();
231	}
232}
233
234/* Remove a plex. */
235void
236gv_rm_plex(struct gv_softc *sc, struct gv_plex *p)
237{
238	struct gv_volume *v;
239	struct gv_sd *s, *s2;
240
241	KASSERT(p != NULL, ("gv_rm_plex: NULL p"));
242	v = p->vol_sc;
243
244	/* Check if any of our consumers is open. */
245	if (v != NULL && gv_provider_is_open(v->provider) && v->plexcount < 2) {
246		G_VINUM_DEBUG(0, "unable to remove %s: volume still in use",
247		    p->name);
248		return;
249	}
250
251	/* Remove the subdisks our plex has. */
252	LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2)
253		gv_rm_sd(sc, s);
254
255	v = p->vol_sc;
256	/* Clean up and let our geom fade away. */
257	LIST_REMOVE(p, plex);
258	if (p->vol_sc != NULL) {
259		p->vol_sc->plexcount--;
260		LIST_REMOVE(p, in_volume);
261		p->vol_sc = NULL;
262		/* Correctly update the volume size. */
263		gv_update_vol_size(v, gv_vol_size(v));
264	}
265
266	g_free(p);
267}
268
269/* Remove a subdisk. */
270void
271gv_rm_sd(struct gv_softc *sc, struct gv_sd *s)
272{
273	struct gv_plex *p;
274	struct gv_volume *v;
275
276	KASSERT(s != NULL, ("gv_rm_sd: NULL s"));
277
278	p = s->plex_sc;
279	v = NULL;
280
281	/* Clean up. */
282	if (p != NULL) {
283		LIST_REMOVE(s, in_plex);
284		s->plex_sc = NULL;
285		p->sdcount--;
286		/* Update the plexsize. */
287		p->size = gv_plex_size(p);
288		v = p->vol_sc;
289		if (v != NULL) {
290			/* Update the size of our plex' volume. */
291			gv_update_vol_size(v, gv_vol_size(v));
292		}
293	}
294	if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED))
295		LIST_REMOVE(s, from_drive);
296	LIST_REMOVE(s, sd);
297	gv_free_sd(s);
298	g_free(s);
299}
300
301/* Remove a drive. */
302void
303gv_rm_drive(struct gv_softc *sc, struct gv_drive *d, int flags)
304{
305	struct g_consumer *cp;
306	struct gv_freelist *fl, *fl2;
307	struct gv_plex *p;
308	struct gv_sd *s, *s2;
309	struct gv_volume *v;
310	struct gv_drive *d2;
311	int err;
312
313	KASSERT(d != NULL, ("gv_rm_drive: NULL d"));
314
315	cp = d->consumer;
316
317	if (cp != NULL) {
318		g_topology_lock();
319		err = g_access(cp, 0, 1, 0);
320		g_topology_unlock();
321
322		if (err) {
323			G_VINUM_DEBUG(0, "%s: unable to access '%s', "
324			    "errno: %d", __func__, cp->provider->name, err);
325			return;
326		}
327
328		/* Clear the Vinum Magic. */
329		d->hdr->magic = GV_NOMAGIC;
330		err = gv_write_header(cp, d->hdr);
331		if (err)
332			G_VINUM_DEBUG(0, "gv_rm_drive: error writing header to"
333			    " '%s', errno: %d", cp->provider->name, err);
334
335		g_topology_lock();
336		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
337		g_detach(cp);
338		g_destroy_consumer(cp);
339		g_topology_unlock();
340	}
341
342	/* Remove all associated subdisks, plexes, volumes. */
343	if (flags & GV_FLAG_R) {
344		if (!LIST_EMPTY(&d->subdisks)) {
345			LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
346				p = s->plex_sc;
347				if (p != NULL) {
348					v = p->vol_sc;
349					if (v != NULL)
350						gv_rm_vol(sc, v);
351				}
352			}
353		}
354	}
355
356	/* Clean up. */
357	LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
358		LIST_REMOVE(fl, freelist);
359		g_free(fl);
360	}
361
362	LIST_REMOVE(d, drive);
363	g_free(d->hdr);
364
365	/* Put ourself into referenced state if we have subdisks. */
366	if (d->sdcount > 0) {
367		d->consumer = NULL;
368		d->hdr = NULL;
369		d->flags |= GV_DRIVE_REFERENCED;
370		snprintf(d->device, sizeof(d->device), "???");
371		d->size = 0;
372		d->avail = 0;
373		d->freelist_entries = 0;
374		LIST_FOREACH(s, &d->subdisks, from_drive) {
375			s->flags |= GV_SD_TASTED;
376			gv_set_sd_state(s, GV_SD_DOWN, GV_SETSTATE_FORCE);
377		}
378		/* Shuffle around so we keep gv_is_newer happy. */
379		LIST_REMOVE(d, drive);
380		d2 = LIST_FIRST(&sc->drives);
381		if (d2 == NULL)
382			LIST_INSERT_HEAD(&sc->drives, d, drive);
383		else
384			LIST_INSERT_AFTER(d2, d, drive);
385		return;
386	}
387	g_free(d);
388
389	gv_save_config(sc);
390}
391