geom_vinum_init.c revision 135426
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 135426 2004-09-18 13:44:43Z 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	printf("GEOM_VINUM: plex sync %s -> %s started\n", sync->from->name,
263	    sync->to->name);
264	for (i = 0; i < p->size; i+= sync->syncsize) {
265		/* Read some bits from the good plex. */
266		buf = g_read_data(from, i, sync->syncsize, &error);
267		if (buf == NULL) {
268			printf("gvinum: sync read from '%s' failed at offset "
269			    "%jd, errno: %d\n", sync->from->name, i, error);
270			break;
271		}
272
273		/*
274		 * Create a bio and schedule it down on the 'bad' plex.  We
275		 * cannot simply use g_write_data() because we have to let the
276		 * lower parts know that we are an initialization process and
277		 * not a 'normal' request.
278		 */
279		bp = g_new_bio();
280		if (bp == NULL) {
281			printf("gvinum: sync write to '%s' failed at offset "
282			    "%jd, out of memory\n", p->name, i);
283			g_free(buf);
284			break;
285		}
286		bp->bio_cmd = BIO_WRITE;
287		bp->bio_offset = i;
288		bp->bio_length = sync->syncsize;
289		bp->bio_data = buf;
290		bp->bio_done = NULL;
291
292		/*
293		 * This hack declare this bio as part of an initialization
294		 * process, so that the lower levels allow it to get through.
295		 */
296		bp->bio_cflags |= GV_BIO_SYNCREQ;
297
298		/* Schedule it down ... */
299		g_io_request(bp, to);
300
301		/* ... and wait for the result. */
302		error = biowait(bp, "gwrite");
303		g_destroy_bio(bp);
304		g_free(buf);
305		if (error) {
306			printf("gvinum: sync write to '%s' failed at offset "
307			    "%jd, errno: %d\n", p->name, i, error);
308			break;
309		}
310
311		/* Note that we have synced a little bit more. */
312		p->synced += sync->syncsize;
313	}
314
315	g_topology_lock();
316	g_access(from, -1, 0, 0);
317	g_access(to, 0, -1, 0);
318	gv_save_config_all(p->vinumconf);
319	g_topology_unlock();
320
321	/* Successful initialization. */
322	if (!error) {
323		p->flags &= ~GV_PLEX_SYNCING;
324		printf("GEOM_VINUM: plex sync %s -> %s finished\n",
325		    sync->from->name, sync->to->name);
326	}
327
328	g_free(sync);
329	kthread_exit(error);
330}
331
332void
333gv_init_td(void *arg)
334{
335	struct gv_sd *s;
336	struct gv_drive *d;
337	struct g_geom *gp;
338	struct g_consumer *cp;
339	int error;
340	off_t i, init_size, start, offset, length;
341	u_char *buf;
342
343	s = arg;
344	KASSERT(s != NULL, ("gv_init_td: NULL s"));
345	d = s->drive_sc;
346	KASSERT(d != NULL, ("gv_init_td: NULL d"));
347	gp = d->geom;
348	KASSERT(gp != NULL, ("gv_init_td: NULL gp"));
349
350	cp = LIST_FIRST(&gp->consumer);
351	KASSERT(cp != NULL, ("gv_init_td: NULL cp"));
352
353	s->init_error = 0;
354	init_size = s->init_size;
355	start = s->drive_offset + s->initialized;
356	offset = s->drive_offset;
357	length = s->size;
358
359	buf = g_malloc(s->init_size, M_WAITOK | M_ZERO);
360
361	g_topology_lock();
362	error = g_access(cp, 0, 1, 0);
363	if (error) {
364		s->init_error = error;
365		g_topology_unlock();
366		printf("geom_vinum: init '%s' failed to access consumer: %d\n",
367		    s->name, error);
368		kthread_exit(error);
369	}
370	g_topology_unlock();
371
372	for (i = start; i < offset + length; i += init_size) {
373		if (s->flags & GV_SD_INITCANCEL) {
374			printf("geom_vinum: subdisk '%s' init: cancelled at"
375			    " offset %jd (drive offset %jd)\n", s->name,
376			    (intmax_t)s->initialized, (intmax_t)i);
377			error = EAGAIN;
378			break;
379		}
380		error = g_write_data(cp, i, buf, init_size);
381		if (error) {
382			printf("geom_vinum: subdisk '%s' init: write failed"
383			    " at offset %jd (drive offset %jd)\n", s->name,
384			    (intmax_t)s->initialized, (intmax_t)i);
385			break;
386		}
387		s->initialized += init_size;
388	}
389
390	g_free(buf);
391
392	g_topology_lock();
393	g_access(cp, 0, -1, 0);
394	g_topology_unlock();
395	if (error) {
396		s->init_error = error;
397		g_topology_lock();
398		gv_set_sd_state(s, GV_SD_STALE,
399		    GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
400		g_topology_unlock();
401	} else {
402		g_topology_lock();
403		gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_CONFIG);
404		g_topology_unlock();
405		s->initialized = 0;
406		printf("geom_vinum: init '%s' finished\n", s->name);
407	}
408	kthread_exit(error);
409}
410