1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2004, 2007 Lukas Ertl
5 * Copyright (c) 2007, 2009 Ulf Lilleengen
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/libkern.h>
33#include <sys/malloc.h>
34
35#include <geom/geom.h>
36#include <geom/geom_dbg.h>
37#include <geom/vinum/geom_vinum_var.h>
38#include <geom/vinum/geom_vinum.h>
39
40static int		 gv_sync(struct gv_volume *);
41static int		 gv_rebuild_plex(struct gv_plex *);
42static int		 gv_init_plex(struct gv_plex *);
43static int		 gv_grow_plex(struct gv_plex *);
44static int		 gv_sync_plex(struct gv_plex *, struct gv_plex *);
45static struct gv_plex	*gv_find_good_plex(struct gv_volume *);
46
47void
48gv_start_obj(struct g_geom *gp, struct gctl_req *req)
49{
50	struct gv_softc *sc;
51	struct gv_volume *v;
52	struct gv_plex *p;
53	int *argc, *initsize;
54	char *argv, buf[20];
55	int i, type;
56
57	argc = gctl_get_paraml(req, "argc", sizeof(*argc));
58	initsize = gctl_get_paraml(req, "initsize", sizeof(*initsize));
59
60	if (argc == NULL || *argc == 0) {
61		gctl_error(req, "no arguments given");
62		return;
63	}
64
65	sc = gp->softc;
66
67	for (i = 0; i < *argc; i++) {
68		snprintf(buf, sizeof(buf), "argv%d", i);
69		argv = gctl_get_param(req, buf, NULL);
70		if (argv == NULL)
71			continue;
72		type = gv_object_type(sc, argv);
73		switch (type) {
74		case GV_TYPE_VOL:
75			v = gv_find_vol(sc, argv);
76			if (v != NULL)
77				gv_post_event(sc, GV_EVENT_START_VOLUME, v,
78				    NULL, *initsize, 0);
79			break;
80
81		case GV_TYPE_PLEX:
82			p = gv_find_plex(sc, argv);
83			if (p != NULL)
84				gv_post_event(sc, GV_EVENT_START_PLEX, p, NULL,
85				    *initsize, 0);
86			break;
87
88		case GV_TYPE_SD:
89		case GV_TYPE_DRIVE:
90			/* XXX Not implemented, but what is the use? */
91			gctl_error(req, "unable to start '%s' - not yet supported",
92			    argv);
93			return;
94		default:
95			gctl_error(req, "unknown object '%s'", argv);
96			return;
97		}
98	}
99}
100
101int
102gv_start_plex(struct gv_plex *p)
103{
104	struct gv_volume *v;
105	struct gv_plex *up;
106	struct gv_sd *s;
107	int error;
108
109	KASSERT(p != NULL, ("gv_start_plex: NULL p"));
110
111	error = 0;
112	v = p->vol_sc;
113
114	/* RAID5 plexes can either be init, rebuilt or grown. */
115	if (p->org == GV_PLEX_RAID5) {
116		if (p->state > GV_PLEX_DEGRADED) {
117			LIST_FOREACH(s, &p->subdisks, in_plex) {
118				if (s->flags & GV_SD_GROW) {
119					error = gv_grow_plex(p);
120					return (error);
121				}
122			}
123		} else if (p->state == GV_PLEX_DEGRADED) {
124			error = gv_rebuild_plex(p);
125		} else
126			error = gv_init_plex(p);
127	} else {
128		/* We want to sync from the other plex if we're down. */
129		if (p->state == GV_PLEX_DOWN && v->plexcount > 1) {
130			up = gv_find_good_plex(v);
131			if (up == NULL) {
132				G_VINUM_DEBUG(1, "unable to find a good plex");
133				return (ENXIO);
134			}
135			g_topology_lock();
136			error = gv_access(v->provider, 1, 1, 0);
137			if (error) {
138				g_topology_unlock();
139				G_VINUM_DEBUG(0, "sync from '%s' failed to "
140				    "access volume: %d", up->name, error);
141				return (error);
142			}
143			g_topology_unlock();
144			error = gv_sync_plex(p, up);
145			if (error)
146				return (error);
147		/*
148		 * In case we have a stripe that is up, check whether it can be
149		 * grown.
150		 */
151		} else if (p->org == GV_PLEX_STRIPED &&
152		    p->state != GV_PLEX_DOWN) {
153			LIST_FOREACH(s, &p->subdisks, in_plex) {
154				if (s->flags & GV_SD_GROW) {
155					error = gv_grow_plex(p);
156					break;
157				}
158			}
159		}
160	}
161	return (error);
162}
163
164int
165gv_start_vol(struct gv_volume *v)
166{
167	struct gv_plex *p;
168	int error;
169
170	KASSERT(v != NULL, ("gv_start_vol: NULL v"));
171
172	error = 0;
173
174	if (v->plexcount == 0)
175		return (ENXIO);
176
177	else if (v->plexcount == 1) {
178		p = LIST_FIRST(&v->plexes);
179		KASSERT(p != NULL, ("gv_start_vol: NULL p on %s", v->name));
180		error = gv_start_plex(p);
181	} else
182		error = gv_sync(v);
183
184	return (error);
185}
186
187/* Sync a plex p from the plex up.  */
188static int
189gv_sync_plex(struct gv_plex *p, struct gv_plex *up)
190{
191	int error;
192
193	KASSERT(p != NULL, ("%s: NULL p", __func__));
194	KASSERT(up != NULL, ("%s: NULL up", __func__));
195	if ((p == up) || (p->state == GV_PLEX_UP))
196		return (0);
197	if (p->flags & GV_PLEX_SYNCING ||
198	    p->flags & GV_PLEX_REBUILDING ||
199	    p->flags & GV_PLEX_GROWING) {
200		return (EINPROGRESS);
201	}
202	p->synced = 0;
203	p->flags |= GV_PLEX_SYNCING;
204	G_VINUM_DEBUG(1, "starting sync of plex %s", p->name);
205	error = gv_sync_request(up, p, p->synced,
206	    MIN(GV_DFLT_SYNCSIZE, up->size - p->synced),
207	    BIO_READ, NULL);
208	if (error) {
209		G_VINUM_DEBUG(0, "error syncing plex %s", p->name);
210		return (error);
211	}
212	return (0);
213}
214
215/* Return a good plex from volume v. */
216static struct gv_plex *
217gv_find_good_plex(struct gv_volume *v)
218{
219	struct gv_plex *up;
220
221	/* Find the plex that's up. */
222	up = NULL;
223	LIST_FOREACH(up, &v->plexes, in_volume) {
224		if (up->state == GV_PLEX_UP)
225			break;
226	}
227	/* Didn't find a good plex. */
228	return (up);
229}
230
231static int
232gv_sync(struct gv_volume *v)
233{
234	struct gv_softc *sc __diagused;
235	struct gv_plex *p, *up;
236	int error;
237
238	KASSERT(v != NULL, ("gv_sync: NULL v"));
239	sc = v->vinumconf;
240	KASSERT(sc != NULL, ("gv_sync: NULL sc on %s", v->name));
241
242	up = gv_find_good_plex(v);
243	if (up == NULL)
244		return (ENXIO);
245	g_topology_lock();
246	error = gv_access(v->provider, 1, 1, 0);
247	if (error) {
248		g_topology_unlock();
249		G_VINUM_DEBUG(0, "sync from '%s' failed to access volume: %d",
250		    up->name, error);
251		return (error);
252	}
253	g_topology_unlock();
254
255	/* Go through the good plex, and issue BIO's to all other plexes. */
256	LIST_FOREACH(p, &v->plexes, in_volume) {
257		error = gv_sync_plex(p, up);
258		if (error)
259			break;
260	}
261	return (0);
262}
263
264static int
265gv_rebuild_plex(struct gv_plex *p)
266{
267	struct gv_drive *d;
268	struct gv_sd *s;
269	int error;
270
271	if (p->flags & GV_PLEX_SYNCING ||
272	    p->flags & GV_PLEX_REBUILDING ||
273	    p->flags & GV_PLEX_GROWING)
274		return (EINPROGRESS);
275	/*
276	 * Make sure that all subdisks have consumers. We won't allow a rebuild
277	 * unless every subdisk have one.
278	 */
279	LIST_FOREACH(s, &p->subdisks, in_plex) {
280		d = s->drive_sc;
281		if (d == NULL || (d->flags & GV_DRIVE_REFERENCED)) {
282			G_VINUM_DEBUG(0, "unable to rebuild %s, subdisk(s) have"
283			    " no drives", p->name);
284			return (ENXIO);
285		}
286	}
287	p->flags |= GV_PLEX_REBUILDING;
288	p->synced = 0;
289
290	g_topology_assert_not();
291	g_topology_lock();
292	error = gv_access(p->vol_sc->provider, 1, 1, 0);
293	if (error) {
294		G_VINUM_DEBUG(0, "unable to access provider");
295		return (0);
296	}
297	g_topology_unlock();
298
299	gv_parity_request(p, GV_BIO_REBUILD, 0);
300	return (0);
301}
302
303static int
304gv_grow_plex(struct gv_plex *p)
305{
306	struct gv_volume *v;
307	struct gv_sd *s;
308	off_t origsize, origlength;
309	int error, sdcount;
310
311	KASSERT(p != NULL, ("gv_grow_plex: NULL p"));
312	v = p->vol_sc;
313	KASSERT(v != NULL, ("gv_grow_plex: NULL v"));
314
315	if (p->flags & GV_PLEX_GROWING ||
316	    p->flags & GV_PLEX_SYNCING ||
317	    p->flags & GV_PLEX_REBUILDING)
318		return (EINPROGRESS);
319	g_topology_lock();
320	error = gv_access(v->provider, 1, 1, 0);
321	g_topology_unlock();
322	if (error) {
323		G_VINUM_DEBUG(0, "unable to access provider");
324		return (error);
325	}
326
327	/* XXX: This routine with finding origsize is used two other places as
328	 * well, so we should create a function for it. */
329	sdcount = p->sdcount;
330	LIST_FOREACH(s, &p->subdisks, in_plex) {
331		if (s->flags & GV_SD_GROW)
332			sdcount--;
333	}
334	s = LIST_FIRST(&p->subdisks);
335	if (s == NULL) {
336		G_VINUM_DEBUG(0, "error growing plex without subdisks");
337		return (GV_ERR_NOTFOUND);
338	}
339	p->flags |= GV_PLEX_GROWING;
340	origsize = (sdcount - 1) * s->size;
341	origlength = (sdcount - 1) * p->stripesize;
342	p->synced = 0;
343	G_VINUM_DEBUG(1, "starting growing of plex %s", p->name);
344	gv_grow_request(p, 0, MIN(origlength, origsize), BIO_READ, NULL);
345
346	return (0);
347}
348
349static int
350gv_init_plex(struct gv_plex *p)
351{
352	struct gv_drive *d;
353	struct gv_sd *s;
354	int error;
355	off_t start;
356	caddr_t data;
357
358	KASSERT(p != NULL, ("gv_init_plex: NULL p"));
359
360	LIST_FOREACH(s, &p->subdisks, in_plex) {
361		if (s->state == GV_SD_INITIALIZING)
362			return (EINPROGRESS);
363		gv_set_sd_state(s, GV_SD_INITIALIZING, GV_SETSTATE_FORCE);
364		s->init_size = GV_DFLT_SYNCSIZE;
365		start = s->drive_offset + s->initialized;
366		d = s->drive_sc;
367		if (d == NULL) {
368			G_VINUM_DEBUG(0, "subdisk %s has no drive yet", s->name);
369			break;
370		}
371		/*
372		 * Take the lock here since we need to avoid a race in
373		 * gv_init_request if the BIO is completed before the lock is
374		 * released.
375		 */
376		g_topology_lock();
377		error = g_access(d->consumer, 0, 1, 0);
378		g_topology_unlock();
379		if (error) {
380			G_VINUM_DEBUG(0, "error accessing consumer when "
381			    "initializing %s", s->name);
382			break;
383		}
384		data = g_malloc(s->init_size, M_WAITOK | M_ZERO);
385		gv_init_request(s, start, data, s->init_size);
386	}
387	return (0);
388}
389