geom_vinum_init.c revision 145619
1251881Speter/*-
2251881Speter * Copyright (c) 2004 Lukas Ertl
3251881Speter * All rights reserved.
4251881Speter *
5251881Speter * Redistribution and use in source and binary forms, with or without
6251881Speter * modification, are permitted provided that the following conditions
7251881Speter * are met:
8251881Speter * 1. Redistributions of source code must retain the above copyright
9251881Speter *    notice, this list of conditions and the following disclaimer.
10251881Speter * 2. Redistributions in binary form must reproduce the above copyright
11251881Speter *    notice, this list of conditions and the following disclaimer in the
12251881Speter *    documentation and/or other materials provided with the distribution.
13251881Speter *
14251881Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17251881Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24251881Speter * SUCH DAMAGE.
25251881Speter */
26251881Speter
27251881Speter#include <sys/cdefs.h>
28251881Speter__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_init.c 145619 2005-04-28 13:09:00Z le $");
29251881Speter
30251881Speter#include <sys/param.h>
31251881Speter#include <sys/bio.h>
32251881Speter#include <sys/kernel.h>
33251881Speter#include <sys/kthread.h>
34251881Speter#include <sys/libkern.h>
35251881Speter#include <sys/malloc.h>
36251881Speter#include <sys/queue.h>
37251881Speter
38251881Speter#include <geom/geom.h>
39251881Speter#include <geom/vinum/geom_vinum_var.h>
40251881Speter#include <geom/vinum/geom_vinum.h>
41251881Speter#include <geom/vinum/geom_vinum_share.h>
42251881Speter
43251881Speterint	gv_init_plex(struct gv_plex *);
44251881Speterint	gv_init_sd(struct gv_sd *);
45251881Spetervoid	gv_init_td(void *);
46251881Spetervoid	gv_rebuild_plex(struct gv_plex *);
47251881Spetervoid	gv_rebuild_td(void *);
48251881Spetervoid	gv_start_plex(struct gv_plex *);
49251881Spetervoid	gv_start_vol(struct gv_volume *);
50251881Spetervoid	gv_sync(struct gv_volume *);
51299742Sdimvoid	gv_sync_td(void *);
52251881Speter
53251881Speterstruct gv_sync_args {
54251881Speter	struct gv_volume *v;
55251881Speter	struct gv_plex *from;
56251881Speter	struct gv_plex *to;
57251881Speter	off_t syncsize;
58299742Sdim};
59262253Speter
60251881Spetervoid
61299742Sdimgv_parityop(struct g_geom *gp, struct gctl_req *req)
62299742Sdim{
63299742Sdim	struct gv_softc *sc;
64299742Sdim	struct gv_plex *p;
65251881Speter	struct bio *bp;
66251881Speter	struct g_consumer *cp;
67251881Speter	int error, *flags, type, *rebuild, rv;
68251881Speter	char *plex;
69251881Speter
70251881Speter	rv = -1;
71251881Speter
72299742Sdim	plex = gctl_get_param(req, "plex", NULL);
73251881Speter	if (plex == NULL) {
74251881Speter		gctl_error(req, "no plex given");
75251881Speter		goto out;
76251881Speter	}
77251881Speter
78251881Speter	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
79251881Speter	if (flags == NULL) {
80251881Speter		gctl_error(req, "no flags given");
81251881Speter		goto out;
82251881Speter	}
83251881Speter
84251881Speter	rebuild = gctl_get_paraml(req, "rebuild", sizeof(*rebuild));
85251881Speter	if (rebuild == NULL) {
86251881Speter		gctl_error(req, "no rebuild op given");
87251881Speter		goto out;
88251881Speter	}
89251881Speter
90251881Speter	sc = gp->softc;
91251881Speter	type = gv_object_type(sc, plex);
92251881Speter	switch (type) {
93251881Speter	case GV_TYPE_PLEX:
94251881Speter		break;
95251881Speter	case GV_TYPE_VOL:
96251881Speter	case GV_TYPE_SD:
97251881Speter	case GV_TYPE_DRIVE:
98251881Speter	default:
99251881Speter		gctl_error(req, "'%s' is not a plex", plex);
100251881Speter		goto out;
101251881Speter	}
102251881Speter
103251881Speter	p = gv_find_plex(sc, plex);
104251881Speter	if (p->state != GV_PLEX_UP) {
105251881Speter		gctl_error(req, "plex %s is not completely accessible",
106251881Speter		    p->name);
107251881Speter		goto out;
108251881Speter	}
109251881Speter	if (p->org != GV_PLEX_RAID5) {
110251881Speter		gctl_error(req, "plex %s is not a RAID5 plex", p->name);
111251881Speter		goto out;
112251881Speter	}
113299742Sdim
114251881Speter	cp = p->consumer;
115299742Sdim	error = g_access(cp, 1, 1, 0);
116299742Sdim	if (error) {
117299742Sdim		gctl_error(req, "cannot access consumer");
118299742Sdim		goto out;
119299742Sdim	}
120299742Sdim	g_topology_unlock();
121299742Sdim
122299742Sdim	/* Reset the check pointer when using -f. */
123299742Sdim	if (*flags & GV_FLAG_F)
124299742Sdim		p->synced = 0;
125299742Sdim
126299742Sdim	bp = g_new_bio();
127299742Sdim	if (bp == NULL) {
128299742Sdim		gctl_error(req, "cannot create BIO - out of memory");
129299742Sdim		g_topology_lock();
130299742Sdim		error = g_access(cp, -1, -1, 0);
131299742Sdim		goto out;
132299742Sdim	}
133299742Sdim	bp->bio_cmd = BIO_WRITE;
134299742Sdim	bp->bio_done = NULL;
135299742Sdim	bp->bio_data = g_malloc(p->stripesize, M_WAITOK | M_ZERO);
136299742Sdim	bp->bio_cflags |= GV_BIO_CHECK;
137299742Sdim	if (*rebuild)
138299742Sdim		bp->bio_cflags |= GV_BIO_PARITY;
139299742Sdim	bp->bio_offset = p->synced;
140299742Sdim	bp->bio_length = p->stripesize;
141299742Sdim
142299742Sdim	/* Schedule it down ... */
143299742Sdim	g_io_request(bp, cp);
144299742Sdim
145299742Sdim	/* ... and wait for the result. */
146299742Sdim	error = biowait(bp, "gwrite");
147299742Sdim	g_free(bp->bio_data);
148299742Sdim	g_destroy_bio(bp);
149299742Sdim
150299742Sdim	if (error) {
151299742Sdim		/* Incorrect parity. */
152299742Sdim		if (error == EAGAIN)
153299742Sdim			rv = 1;
154299742Sdim
155299742Sdim		/* Some other error happened. */
156299742Sdim		else
157299742Sdim			gctl_error(req, "Parity check failed at offset 0x%jx, "
158251881Speter			    "errno %d", (intmax_t)p->synced, error);
159251881Speter
160251881Speter	/* Correct parity. */
161251881Speter	} else
162251881Speter		rv = 0;
163251881Speter
164251881Speter	gctl_set_param(req, "offset", &p->synced, sizeof(p->synced));
165251881Speter
166251881Speter	/* Advance the checkpointer if there was no error. */
167251881Speter	if (rv == 0)
168251881Speter		p->synced += p->stripesize;
169251881Speter
170251881Speter	/* End of plex; reset the check pointer and signal it to the caller. */
171251881Speter	if (p->synced >= p->size) {
172251881Speter		p->synced = 0;
173251881Speter		rv = -2;
174251881Speter	}
175251881Speter
176251881Speter	g_topology_lock();
177251881Speter	error = g_access(cp, -1, -1, 0);
178251881Speter
179251881Speterout:
180251881Speter	gctl_set_param(req, "rv", &rv, sizeof(rv));
181251881Speter}
182251881Speter
183251881Spetervoid
184251881Spetergv_start_obj(struct g_geom *gp, struct gctl_req *req)
185251881Speter{
186251881Speter	struct gv_softc *sc;
187251881Speter	struct gv_volume *v;
188251881Speter	struct gv_plex *p;
189251881Speter	int *argc, *initsize;
190251881Speter	char *argv, buf[20];
191251881Speter	int i, type;
192251881Speter
193251881Speter	argc = gctl_get_paraml(req, "argc", sizeof(*argc));
194251881Speter	initsize = gctl_get_paraml(req, "initsize", sizeof(*initsize));
195251881Speter
196251881Speter	if (argc == NULL || *argc == 0) {
197251881Speter		gctl_error(req, "no arguments given");
198251881Speter		return;
199251881Speter	}
200251881Speter
201251881Speter	sc = gp->softc;
202251881Speter
203251881Speter	for (i = 0; i < *argc; i++) {
204251881Speter		snprintf(buf, sizeof(buf), "argv%d", i);
205251881Speter		argv = gctl_get_param(req, buf, NULL);
206251881Speter		if (argv == NULL)
207251881Speter			continue;
208251881Speter		type = gv_object_type(sc, argv);
209251881Speter		switch (type) {
210299742Sdim		case GV_TYPE_VOL:
211299742Sdim			v = gv_find_vol(sc, argv);
212299742Sdim			gv_start_vol(v);
213251881Speter			break;
214251881Speter
215251881Speter		case GV_TYPE_PLEX:
216251881Speter			p = gv_find_plex(sc, argv);
217251881Speter			gv_start_plex(p);
218251881Speter			break;
219251881Speter
220251881Speter		case GV_TYPE_SD:
221251881Speter		case GV_TYPE_DRIVE:
222251881Speter			/* XXX not yet */
223251881Speter			gctl_error(req, "cannot start '%s'", argv);
224251881Speter			return;
225251881Speter		default:
226251881Speter			gctl_error(req, "unknown object '%s'", argv);
227251881Speter			return;
228251881Speter		}
229251881Speter	}
230251881Speter}
231251881Speter
232251881Spetervoid
233251881Spetergv_start_plex(struct gv_plex *p)
234251881Speter{
235251881Speter	struct gv_volume *v;
236251881Speter
237251881Speter	KASSERT(p != NULL, ("gv_start_plex: NULL p"));
238251881Speter
239251881Speter	if (p->state == GV_PLEX_UP)
240251881Speter		return;
241251881Speter
242251881Speter	v = p->vol_sc;
243251881Speter	if ((v != NULL) && (v->plexcount > 1))
244251881Speter		gv_sync(v);
245251881Speter	else if (p->org == GV_PLEX_RAID5) {
246251881Speter		if (p->state == GV_PLEX_DEGRADED)
247251881Speter			gv_rebuild_plex(p);
248251881Speter		else
249251881Speter			gv_init_plex(p);
250251881Speter	}
251251881Speter
252251881Speter	return;
253251881Speter}
254251881Speter
255251881Spetervoid
256251881Spetergv_start_vol(struct gv_volume *v)
257251881Speter{
258251881Speter	struct gv_plex *p;
259251881Speter	struct gv_sd *s;
260251881Speter
261251881Speter	KASSERT(v != NULL, ("gv_start_vol: NULL v"));
262251881Speter
263251881Speter	if (v->plexcount == 0)
264251881Speter		return;
265251881Speter
266251881Speter	else if (v->plexcount == 1) {
267251881Speter		p = LIST_FIRST(&v->plexes);
268251881Speter		KASSERT(p != NULL, ("gv_start_vol: NULL p on %s", v->name));
269251881Speter		if (p->org == GV_PLEX_RAID5) {
270251881Speter			switch (p->state) {
271251881Speter			case GV_PLEX_DOWN:
272262253Speter				gv_init_plex(p);
273251881Speter				break;
274299742Sdim			case GV_PLEX_DEGRADED:
275299742Sdim				gv_rebuild_plex(p);
276299742Sdim				break;
277251881Speter			default:
278251881Speter				return;
279251881Speter			}
280251881Speter		} else {
281251881Speter			LIST_FOREACH(s, &p->subdisks, in_plex) {
282299742Sdim				gv_set_sd_state(s, GV_SD_UP,
283251881Speter				    GV_SETSTATE_CONFIG);
284299742Sdim			}
285251881Speter		}
286251881Speter	} else
287251881Speter		gv_sync(v);
288251881Speter}
289251881Speter
290299742Sdimvoid
291251881Spetergv_sync(struct gv_volume *v)
292251881Speter{
293251881Speter	struct gv_softc *sc;
294251881Speter	struct gv_plex *p, *up;
295251881Speter	struct gv_sync_args *sync;
296251881Speter
297251881Speter	KASSERT(v != NULL, ("gv_sync: NULL v"));
298299742Sdim	sc = v->vinumconf;
299251881Speter	KASSERT(sc != NULL, ("gv_sync: NULL sc on %s", v->name));
300251881Speter
301251881Speter	/* Find the plex that's up. */
302251881Speter	up = NULL;
303251881Speter	LIST_FOREACH(up, &v->plexes, in_volume) {
304251881Speter		if (up->state == GV_PLEX_UP)
305251881Speter			break;
306251881Speter	}
307299742Sdim
308299742Sdim	/* Didn't find a good plex. */
309299742Sdim	if (up == NULL)
310299742Sdim		return;
311299742Sdim
312299742Sdim	LIST_FOREACH(p, &v->plexes, in_volume) {
313299742Sdim		if ((p == up) || (p->state == GV_PLEX_UP))
314299742Sdim			continue;
315251881Speter		sync = g_malloc(sizeof(*sync), M_WAITOK | M_ZERO);
316251881Speter		sync->v = v;
317251881Speter		sync->from = up;
318251881Speter		sync->to = p;
319251881Speter		sync->syncsize = GV_DFLT_SYNCSIZE;
320299742Sdim		kthread_create(gv_sync_td, sync, NULL, 0, 0, "gv_sync '%s'",
321299742Sdim		    p->name);
322299742Sdim	}
323299742Sdim}
324299742Sdim
325299742Sdimvoid
326299742Sdimgv_rebuild_plex(struct gv_plex *p)
327299742Sdim{
328299742Sdim	struct gv_sync_args *sync;
329299742Sdim
330299742Sdim	if ((p->flags & GV_PLEX_SYNCING) || gv_is_open(p->geom))
331299742Sdim		return;
332299742Sdim
333299742Sdim	sync = g_malloc(sizeof(*sync), M_WAITOK | M_ZERO);
334299742Sdim	sync->to = p;
335299742Sdim	sync->syncsize = GV_DFLT_SYNCSIZE;
336299742Sdim
337299742Sdim	kthread_create(gv_rebuild_td, sync, NULL, 0, 0, "gv_rebuild %s",
338299742Sdim	    p->name);
339299742Sdim}
340299742Sdim
341299742Sdimint
342299742Sdimgv_init_plex(struct gv_plex *p)
343299742Sdim{
344299742Sdim	struct gv_sd *s;
345299742Sdim	int err;
346251881Speter
347299742Sdim	KASSERT(p != NULL, ("gv_init_plex: NULL p"));
348251881Speter
349251881Speter	LIST_FOREACH(s, &p->subdisks, in_plex) {
350251881Speter		err = gv_init_sd(s);
351251881Speter		if (err)
352251881Speter			return (err);
353299742Sdim	}
354251881Speter
355299742Sdim	return (0);
356251881Speter}
357251881Speter
358251881Speterint
359251881Spetergv_init_sd(struct gv_sd *s)
360251881Speter{
361251881Speter	KASSERT(s != NULL, ("gv_init_sd: NULL s"));
362251881Speter
363251881Speter	if (gv_set_sd_state(s, GV_SD_INITIALIZING, GV_SETSTATE_FORCE))
364251881Speter		return (-1);
365251881Speter
366251881Speter	s->init_size = GV_DFLT_SYNCSIZE;
367251881Speter	s->flags &= ~GV_SD_INITCANCEL;
368251881Speter
369251881Speter	/* Spawn the thread that does the work for us. */
370251881Speter	kthread_create(gv_init_td, s, NULL, 0, 0, "gv_init %s", s->name);
371251881Speter
372251881Speter	return (0);
373251881Speter}
374251881Speter
375251881Speter/* This thread is responsible for rebuilding a degraded RAID5 plex. */
376251881Spetervoid
377251881Spetergv_rebuild_td(void *arg)
378251881Speter{
379251881Speter	struct bio *bp;
380251881Speter	struct gv_plex *p;
381251881Speter	struct g_consumer *cp;
382251881Speter	struct gv_sync_args *sync;
383251881Speter	u_char *buf;
384251881Speter	off_t i;
385251881Speter	int error;
386251881Speter
387251881Speter	buf = NULL;
388251881Speter	bp = NULL;
389251881Speter
390251881Speter	sync = arg;
391251881Speter	p = sync->to;
392251881Speter	p->synced = 0;
393251881Speter	p->flags |= GV_PLEX_SYNCING;
394251881Speter	cp = p->consumer;
395251881Speter
396251881Speter	g_topology_lock();
397251881Speter	error = g_access(cp, 1, 1, 0);
398251881Speter	if (error) {
399251881Speter		g_topology_unlock();
400251881Speter		printf("GEOM_VINUM: rebuild of %s failed to access consumer: "
401299742Sdim		    "%d\n", p->name, error);
402299742Sdim		kthread_exit(error);
403299742Sdim	}
404251881Speter	g_topology_unlock();
405251881Speter
406299742Sdim	buf = g_malloc(sync->syncsize, M_WAITOK);
407251881Speter
408251881Speter	printf("GEOM_VINUM: rebuild of %s started\n", p->name);
409251881Speter	i = 0;
410299742Sdim	for (i = 0; i < p->size; i += (p->stripesize * (p->sdcount - 1))) {
411299742Sdim/*
412299742Sdim		if (i + sync->syncsize > p->size)
413251881Speter			sync->syncsize = p->size - i;
414251881Speter*/
415299742Sdim		bp = g_new_bio();
416251881Speter		if (bp == NULL) {
417251881Speter			printf("GEOM_VINUM: rebuild of %s failed creating bio: "
418251881Speter			    "out of memory\n", p->name);
419251881Speter			break;
420251881Speter		}
421251881Speter		bp->bio_cmd = BIO_WRITE;
422251881Speter		bp->bio_done = NULL;
423251881Speter		bp->bio_data = buf;
424251881Speter		bp->bio_cflags |= GV_BIO_REBUILD;
425251881Speter		bp->bio_offset = i;
426251881Speter		bp->bio_length = p->stripesize;
427251881Speter
428251881Speter		/* Schedule it down ... */
429251881Speter		g_io_request(bp, cp);
430251881Speter
431251881Speter		/* ... and wait for the result. */
432251881Speter		error = biowait(bp, "gwrite");
433251881Speter		if (error) {
434251881Speter			printf("GEOM_VINUM: rebuild of %s failed at offset %jd "
435251881Speter			    "errno: %d\n", p->name, i, error);
436251881Speter			break;
437251881Speter		}
438251881Speter		g_destroy_bio(bp);
439251881Speter		bp = NULL;
440251881Speter	}
441251881Speter
442251881Speter	if (bp != NULL)
443251881Speter		g_destroy_bio(bp);
444251881Speter	if (buf != NULL)
445251881Speter		g_free(buf);
446251881Speter
447251881Speter	g_topology_lock();
448251881Speter	g_access(cp, -1, -1, 0);
449251881Speter	gv_save_config_all(p->vinumconf);
450251881Speter	g_topology_unlock();
451251881Speter
452251881Speter	p->flags &= ~GV_PLEX_SYNCING;
453251881Speter	p->synced = 0;
454251881Speter
455251881Speter	/* Successful initialization. */
456251881Speter	if (!error)
457251881Speter		printf("GEOM_VINUM: rebuild of %s finished\n", p->name);
458251881Speter
459251881Speter	g_free(sync);
460251881Speter	kthread_exit(error);
461251881Speter}
462251881Speter
463251881Spetervoid
464251881Spetergv_sync_td(void *arg)
465251881Speter{
466251881Speter	struct bio *bp;
467251881Speter	struct gv_plex *p;
468251881Speter	struct g_consumer *from, *to;
469251881Speter	struct gv_sync_args *sync;
470251881Speter	u_char *buf;
471251881Speter	off_t i;
472251881Speter	int error;
473251881Speter
474251881Speter	sync = arg;
475299742Sdim
476299742Sdim	from = sync->from->consumer;
477299742Sdim	to = sync->to->consumer;
478299742Sdim
479299742Sdim	p = sync->to;
480299742Sdim
481299742Sdim	if (p->flags & GV_PLEX_SYNCING) {
482299742Sdim		printf("GEOM_VINUM: plex '%s' is already syncing.\n", p->name);
483299742Sdim		g_free(sync);
484299742Sdim		kthread_exit(0);
485299742Sdim	}
486299742Sdim
487299742Sdim	p->synced = 0;
488299742Sdim	p->flags |= GV_PLEX_SYNCING;
489251881Speter
490299742Sdim	error = 0;
491299742Sdim
492299742Sdim	g_topology_lock();
493251881Speter	error = g_access(from, 1, 0, 0);
494299742Sdim	if (error) {
495299742Sdim		g_topology_unlock();
496299742Sdim		printf("GEOM_VINUM: sync from '%s' failed to access "
497299742Sdim		    "consumer: %d\n", sync->from->name, error);
498299742Sdim		g_free(sync);
499251881Speter		kthread_exit(error);
500299742Sdim	}
501299742Sdim	error = g_access(to, 0, 1, 0);
502299742Sdim	if (error) {
503299742Sdim		g_access(from, -1, 0, 0);
504299742Sdim		g_topology_unlock();
505299742Sdim		printf("GEOM_VINUM: sync to '%s' failed to access "
506251881Speter		    "consumer: %d\n", p->name, error);
507299742Sdim		g_free(sync);
508299742Sdim		kthread_exit(error);
509299742Sdim	}
510299742Sdim	g_topology_unlock();
511299742Sdim
512251881Speter	printf("GEOM_VINUM: plex sync %s -> %s started\n", sync->from->name,
513299742Sdim	    sync->to->name);
514299742Sdim	for (i = 0; i < p->size; i+= sync->syncsize) {
515299742Sdim		/* Read some bits from the good plex. */
516299742Sdim		buf = g_read_data(from, i, sync->syncsize, &error);
517299742Sdim		if (buf == NULL) {
518299742Sdim			printf("GEOM_VINUM: sync read from '%s' failed at "
519299742Sdim			    "offset %jd; errno: %d\n", sync->from->name, i,
520299742Sdim			    error);
521299742Sdim			break;
522299742Sdim		}
523299742Sdim
524299742Sdim		/*
525299742Sdim		 * Create a bio and schedule it down on the 'bad' plex.  We
526251881Speter		 * cannot simply use g_write_data() because we have to let the
527251881Speter		 * lower parts know that we are an initialization process and
528299742Sdim		 * not a 'normal' request.
529299742Sdim		 */
530299742Sdim		bp = g_new_bio();
531251881Speter		if (bp == NULL) {
532299742Sdim			printf("GEOM_VINUM: sync write to '%s' failed at "
533251881Speter			    "offset %jd; out of memory\n", p->name, i);
534299742Sdim			g_free(buf);
535251881Speter			break;
536251881Speter		}
537299742Sdim		bp->bio_cmd = BIO_WRITE;
538299742Sdim		bp->bio_offset = i;
539299742Sdim		bp->bio_length = sync->syncsize;
540299742Sdim		bp->bio_data = buf;
541299742Sdim		bp->bio_done = NULL;
542299742Sdim
543299742Sdim		/*
544299742Sdim		 * This hack declare this bio as part of an initialization
545299742Sdim		 * process, so that the lower levels allow it to get through.
546299742Sdim		 */
547251881Speter		bp->bio_cflags |= GV_BIO_SYNCREQ;
548299742Sdim
549299742Sdim		/* Schedule it down ... */
550299742Sdim		g_io_request(bp, to);
551299742Sdim
552299742Sdim		/* ... and wait for the result. */
553299742Sdim		error = biowait(bp, "gwrite");
554299742Sdim		g_destroy_bio(bp);
555299742Sdim		g_free(buf);
556299742Sdim		if (error) {
557299742Sdim			printf("GEOM_VINUM: sync write to '%s' failed at "
558299742Sdim			    "offset %jd; errno: %d\n", p->name, i, error);
559299742Sdim			break;
560299742Sdim		}
561299742Sdim
562299742Sdim		/* Note that we have synced a little bit more. */
563299742Sdim		p->synced += sync->syncsize;
564299742Sdim	}
565299742Sdim
566299742Sdim	g_topology_lock();
567251881Speter	g_access(from, -1, 0, 0);
568299742Sdim	g_access(to, 0, -1, 0);
569299742Sdim	gv_save_config_all(p->vinumconf);
570299742Sdim	g_topology_unlock();
571299742Sdim
572299742Sdim	/* Successful initialization. */
573299742Sdim	if (!error)
574299742Sdim		printf("GEOM_VINUM: plex sync %s -> %s finished\n",
575299742Sdim		    sync->from->name, sync->to->name);
576299742Sdim
577299742Sdim	p->flags &= ~GV_PLEX_SYNCING;
578299742Sdim	p->synced = 0;
579299742Sdim
580299742Sdim	g_free(sync);
581299742Sdim	kthread_exit(error);
582299742Sdim}
583299742Sdim
584299742Sdimvoid
585299742Sdimgv_init_td(void *arg)
586299742Sdim{
587299742Sdim	struct gv_sd *s;
588299742Sdim	struct gv_drive *d;
589251881Speter	struct g_geom *gp;
590251881Speter	struct g_consumer *cp;
591299742Sdim	int error;
592299742Sdim	off_t i, init_size, start, offset, length;
593299742Sdim	u_char *buf;
594251881Speter
595299742Sdim	s = arg;
596251881Speter	KASSERT(s != NULL, ("gv_init_td: NULL s"));
597299742Sdim	d = s->drive_sc;
598299742Sdim	KASSERT(d != NULL, ("gv_init_td: NULL d"));
599299742Sdim	gp = d->geom;
600299742Sdim	KASSERT(gp != NULL, ("gv_init_td: NULL gp"));
601299742Sdim
602299742Sdim	cp = LIST_FIRST(&gp->consumer);
603299742Sdim	KASSERT(cp != NULL, ("gv_init_td: NULL cp"));
604299742Sdim
605299742Sdim	s->init_error = 0;
606299742Sdim	init_size = s->init_size;
607299742Sdim	start = s->drive_offset + s->initialized;
608299742Sdim	offset = s->drive_offset;
609299742Sdim	length = s->size;
610299742Sdim
611299742Sdim	buf = g_malloc(s->init_size, M_WAITOK | M_ZERO);
612299742Sdim
613299742Sdim	g_topology_lock();
614299742Sdim	error = g_access(cp, 0, 1, 0);
615251881Speter	if (error) {
616251881Speter		s->init_error = error;
617299742Sdim		g_topology_unlock();
618251881Speter		printf("geom_vinum: init '%s' failed to access consumer: %d\n",
619251881Speter		    s->name, error);
620251881Speter		kthread_exit(error);
621251881Speter	}
622251881Speter	g_topology_unlock();
623251881Speter
624251881Speter	for (i = start; i < offset + length; i += init_size) {
625251881Speter		if (s->flags & GV_SD_INITCANCEL) {
626251881Speter			printf("geom_vinum: subdisk '%s' init: cancelled at"
627251881Speter			    " offset %jd (drive offset %jd)\n", s->name,
628262253Speter			    (intmax_t)s->initialized, (intmax_t)i);
629251881Speter			error = EAGAIN;
630262253Speter			break;
631251881Speter		}
632251881Speter		error = g_write_data(cp, i, buf, init_size);
633251881Speter		if (error) {
634251881Speter			printf("geom_vinum: subdisk '%s' init: write failed"
635251881Speter			    " at offset %jd (drive offset %jd)\n", s->name,
636251881Speter			    (intmax_t)s->initialized, (intmax_t)i);
637251881Speter			break;
638251881Speter		}
639251881Speter		s->initialized += init_size;
640251881Speter	}
641251881Speter
642251881Speter	g_free(buf);
643251881Speter
644251881Speter	g_topology_lock();
645251881Speter	g_access(cp, 0, -1, 0);
646251881Speter	g_topology_unlock();
647251881Speter	if (error) {
648251881Speter		s->init_error = error;
649251881Speter		g_topology_lock();
650251881Speter		gv_set_sd_state(s, GV_SD_STALE,
651251881Speter		    GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
652251881Speter		g_topology_unlock();
653251881Speter	} else {
654251881Speter		g_topology_lock();
655262253Speter		gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_CONFIG);
656251881Speter		g_topology_unlock();
657251881Speter		s->initialized = 0;
658251881Speter		printf("geom_vinum: init '%s' finished\n", s->name);
659299742Sdim	}
660299742Sdim	kthread_exit(error);
661299742Sdim}
662299742Sdim