geom_vinum_init.c revision 132654
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 THE 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 THE 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#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_init.c 132654 2004-07-26 07:30:21Z le $");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/kernel.h>
33#include <sys/kthread.h>
34#include <sys/libkern.h>
35#include <sys/malloc.h>
36#include <sys/queue.h>
37
38#include <geom/geom.h>
39#include <geom/vinum/geom_vinum_var.h>
40#include <geom/vinum/geom_vinum.h>
41#include <geom/vinum/geom_vinum_share.h>
42
43int	gv_init_plex(struct gv_plex *);
44int	gv_init_sd(struct gv_sd *);
45void	gv_init_td(void *);
46void	gv_start_plex(struct gv_plex *);
47void	gv_start_vol(struct gv_volume *);
48void	gv_sync(struct gv_volume *);
49void	gv_sync_td(void *);
50
51struct gv_sync_args {
52	struct gv_volume *v;
53	struct gv_plex *from;
54	struct gv_plex *to;
55	off_t syncsize;
56};
57
58void
59gv_start_obj(struct g_geom *gp, struct gctl_req *req)
60{
61	struct gv_softc *sc;
62	struct gv_volume *v;
63	struct gv_plex *p;
64	int *argc, *initsize;
65	char *argv, buf[20];
66	int i, type;
67
68	argc = gctl_get_paraml(req, "argc", sizeof(*argc));
69	initsize = gctl_get_paraml(req, "initsize", sizeof(*initsize));
70
71	if (argc == NULL || *argc == 0) {
72		gctl_error(req, "no arguments given");
73		return;
74	}
75
76	sc = gp->softc;
77
78	for (i = 0; i < *argc; i++) {
79		snprintf(buf, sizeof(buf), "argv%d", i);
80		argv = gctl_get_param(req, buf, NULL);
81		if (argv == NULL)
82			continue;
83		type = gv_object_type(sc, argv);
84		switch (type) {
85		case GV_TYPE_VOL:
86			v = gv_find_vol(sc, argv);
87			gv_start_vol(v);
88			break;
89
90		case GV_TYPE_PLEX:
91			p = gv_find_plex(sc, argv);
92			gv_start_plex(p);
93			break;
94
95		case GV_TYPE_SD:
96		case GV_TYPE_DRIVE:
97			/* XXX not yet */
98			gctl_error(req, "cannot start '%s'", argv);
99			return;
100		default:
101			gctl_error(req, "unknown object '%s'", argv);
102			return;
103		}
104	}
105}
106
107void
108gv_start_plex(struct gv_plex *p)
109{
110	struct gv_volume *v;
111
112	KASSERT(p != NULL, ("gv_start_plex: NULL p"));
113
114	if (p->state == GV_PLEX_UP)
115		return;
116
117	v = p->vol_sc;
118	if ((v != NULL) && (v->plexcount > 1))
119		gv_sync(v);
120	else if (p->org == GV_PLEX_RAID5)
121		gv_init_plex(p);
122
123	return;
124}
125
126void
127gv_start_vol(struct gv_volume *v)
128{
129	struct gv_plex *p;
130
131	KASSERT(v != NULL, ("gv_start_vol: NULL v"));
132
133	if (v->plexcount == 0)
134		return;
135
136	else if (v->plexcount == 1) {
137		p = LIST_FIRST(&v->plexes);
138		KASSERT(p != NULL, ("gv_start_vol: NULL p on %s", v->name));
139		if (p->org == GV_PLEX_RAID5) {
140			switch (p->state) {
141			case GV_PLEX_DOWN:
142				gv_init_plex(p);
143				break;
144			case GV_PLEX_DEGRADED:  /* XXX not yet */
145			default:
146				return;
147			}
148		}
149	} else
150		gv_sync(v);
151}
152
153void
154gv_sync(struct gv_volume *v)
155{
156	struct gv_softc *sc;
157	struct gv_plex *p, *up;
158	struct gv_sync_args *sync;
159
160	KASSERT(v != NULL, ("gv_sync: NULL v"));
161	sc = v->vinumconf;
162	KASSERT(sc != NULL, ("gv_sync: NULL sc on %s", v->name));
163
164	/* Find the plex that's up. */
165	up = NULL;
166	LIST_FOREACH(up, &v->plexes, in_volume) {
167		if (up->state == GV_PLEX_UP)
168			break;
169	}
170
171	/* Didn't find a good plex. */
172	if (up == NULL)
173		return;
174
175	LIST_FOREACH(p, &v->plexes, in_volume) {
176		if ((p == up) || (p->state == GV_PLEX_UP))
177			continue;
178		sync = g_malloc(sizeof(*sync), M_WAITOK | M_ZERO);
179		sync->v = v;
180		sync->from = up;
181		sync->to = p;
182		sync->syncsize = GV_DFLT_SYNCSIZE;
183		kthread_create(gv_sync_td, sync, NULL, 0, 0, "sync_p '%s'",
184		    p->name);
185	}
186}
187
188int
189gv_init_plex(struct gv_plex *p)
190{
191	struct gv_sd *s;
192	int err;
193
194	KASSERT(p != NULL, ("gv_init_plex: NULL p"));
195
196	LIST_FOREACH(s, &p->subdisks, in_plex) {
197		err = gv_init_sd(s);
198		if (err)
199			return (err);
200	}
201
202	return (0);
203}
204
205int
206gv_init_sd(struct gv_sd *s)
207{
208	KASSERT(s != NULL, ("gv_init_sd: NULL s"));
209
210	if (gv_set_sd_state(s, GV_SD_INITIALIZING, GV_SETSTATE_FORCE))
211		return (-1);
212
213	s->init_size = GV_DFLT_SYNCSIZE;
214	s->flags &= ~GV_SD_INITCANCEL;
215
216	/* Spawn the thread that does the work for us. */
217	kthread_create(gv_init_td, s, NULL, 0, 0, "init_sd %s", s->name);
218
219	return (0);
220}
221
222void
223gv_sync_td(void *arg)
224{
225	struct bio *bp;
226	struct gv_plex *p;
227	struct g_consumer *from, *to;
228	struct gv_sync_args *sync;
229	u_char *buf;
230	off_t i;
231	int error;
232
233	sync = arg;
234
235	from = sync->from->consumer;
236	to = sync->to->consumer;
237
238	p = sync->to;
239	p->synced = 0;
240	p->flags |= GV_PLEX_SYNCING;
241
242	error = 0;
243
244	g_topology_lock();
245	error = g_access(from, 1, 0, 0);
246	if (error) {
247		g_topology_unlock();
248		printf("gvinum: sync from '%s' failed to access consumer: %d\n",
249		    sync->from->name, error);
250		kthread_exit(error);
251	}
252	error = g_access(to, 0, 1, 0);
253	if (error) {
254		g_access(from, -1, 0, 0);
255		g_topology_unlock();
256		printf("gvinum: sync to '%s' failed to access consumer: %d\n",
257		    p->name, error);
258		kthread_exit(error);
259	}
260	g_topology_unlock();
261
262	for (i = 0; i < p->size; i+= sync->syncsize) {
263		/* Read some bits from the good plex. */
264		buf = g_read_data(from, i, sync->syncsize, &error);
265		if (buf == NULL) {
266			printf("gvinum: sync read from '%s' failed at offset "
267			    "%jd, errno: %d\n", sync->from->name, i, error);
268			break;
269		}
270
271		/*
272		 * Create a bio and schedule it down on the 'bad' plex.  We
273		 * cannot simply use g_write_data() because we have to let the
274		 * lower parts know that we are an initialization process and
275		 * not a 'normal' request.
276		 */
277		bp = g_new_bio();
278		if (bp == NULL) {
279			printf("gvinum: sync write to '%s' failed at offset "
280			    "%jd, out of memory\n", p->name, i);
281			g_free(buf);
282			break;
283		}
284		bp->bio_cmd = BIO_WRITE;
285		bp->bio_offset = i;
286		bp->bio_length = sync->syncsize;
287		bp->bio_data = buf;
288		bp->bio_done = NULL;
289
290		/*
291		 * This hack declare this bio as part of an initialization
292		 * process, so that the lower levels allow it to get through.
293		 */
294		bp->bio_caller1 = p;
295
296		/* Schedule it down ... */
297		g_io_request(bp, to);
298
299		/* ... and wait for the result. */
300		error = biowait(bp, "gwrite");
301		g_destroy_bio(bp);
302		g_free(buf);
303		if (error) {
304			printf("gvinum: sync write to '%s' failed at offset "
305			    "%jd, errno: %d\n", p->name, i, error);
306			break;
307		}
308
309		/* Note that we have synced a little bit more. */
310		p->synced += sync->syncsize;
311	}
312
313	g_topology_lock();
314	g_access(from, -1, 0, 0);
315	g_access(to, 0, -1, 0);
316	gv_save_config_all(p->vinumconf);
317	g_topology_unlock();
318
319	/* Successful initialization. */
320	if (!error) {
321		p->flags &= ~GV_PLEX_SYNCING;
322		printf("gvinum: plex '%s': sync finished\n", p->name);
323	}
324
325	g_free(sync);
326	kthread_exit(error);
327}
328
329void
330gv_init_td(void *arg)
331{
332	struct gv_sd *s;
333	struct gv_drive *d;
334	struct g_geom *gp;
335	struct g_consumer *cp;
336	int error;
337	off_t i, init_size, start, offset, length;
338	u_char *buf;
339
340	s = arg;
341	KASSERT(s != NULL, ("gv_init_td: NULL s"));
342	d = s->drive_sc;
343	KASSERT(d != NULL, ("gv_init_td: NULL d"));
344	gp = d->geom;
345	KASSERT(gp != NULL, ("gv_init_td: NULL gp"));
346
347	cp = LIST_FIRST(&gp->consumer);
348	KASSERT(cp != NULL, ("gv_init_td: NULL cp"));
349
350	s->init_error = 0;
351	init_size = s->init_size;
352	start = s->drive_offset + s->initialized;
353	offset = s->drive_offset;
354	length = s->size;
355
356	buf = g_malloc(s->init_size, M_WAITOK | M_ZERO);
357
358	g_topology_lock();
359	error = g_access(cp, 0, 1, 0);
360	if (error) {
361		s->init_error = error;
362		g_topology_unlock();
363		printf("geom_vinum: init '%s' failed to access consumer: %d\n",
364		    s->name, error);
365		kthread_exit(error);
366	}
367	g_topology_unlock();
368
369	for (i = start; i < offset + length; i += init_size) {
370		if (s->flags & GV_SD_INITCANCEL) {
371			printf("geom_vinum: subdisk '%s' init: cancelled at"
372			    " offset %jd (drive offset %jd)\n", s->name,
373			    (intmax_t)s->initialized, (intmax_t)i);
374			error = EAGAIN;
375			break;
376		}
377		error = g_write_data(cp, i, buf, init_size);
378		if (error) {
379			printf("geom_vinum: subdisk '%s' init: write failed"
380			    " at offset %jd (drive offset %jd)\n", s->name,
381			    (intmax_t)s->initialized, (intmax_t)i);
382			break;
383		}
384		s->initialized += init_size;
385	}
386
387	g_free(buf);
388
389	g_topology_lock();
390	g_access(cp, 0, -1, 0);
391	g_topology_unlock();
392	if (error) {
393		s->init_error = error;
394		g_topology_lock();
395		gv_set_sd_state(s, GV_SD_STALE,
396		    GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
397		g_topology_unlock();
398	} else {
399		g_topology_lock();
400		gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_CONFIG);
401		g_topology_unlock();
402		s->initialized = 0;
403		printf("geom_vinum: init '%s' finished\n", s->name);
404	}
405	kthread_exit(error);
406}
407