xref: /freebsd/sys/geom/vinum/geom_vinum_init.c (revision 6fd05b64b5b65dd4ba9b86482a0634a5f0b96c29)
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$");
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 
43 int	gv_init_plex(struct gv_plex *);
44 int	gv_init_sd(struct gv_sd *);
45 void	gv_init_td(void *);
46 void	gv_start_plex(struct gv_plex *);
47 void	gv_start_vol(struct gv_volume *);
48 void	gv_sync(struct gv_volume *);
49 void	gv_sync_td(void *);
50 
51 struct gv_sync_args {
52 	struct gv_volume *v;
53 	struct gv_plex *from;
54 	struct gv_plex *to;
55 	off_t syncsize;
56 };
57 
58 void
59 gv_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 
107 void
108 gv_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 
126 void
127 gv_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 
153 void
154 gv_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 
188 int
189 gv_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 
205 int
206 gv_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 
222 void
223 gv_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 	g_topology_unlock();
317 
318 	/* Successful initialization. */
319 	if (!error) {
320 		p->flags &= ~GV_PLEX_SYNCING;
321 		printf("gvinum: plex '%s': sync finished\n", p->name);
322 	}
323 
324 	g_free(sync);
325 	kthread_exit(error);
326 }
327 
328 void
329 gv_init_td(void *arg)
330 {
331 	struct gv_sd *s;
332 	struct gv_drive *d;
333 	struct g_geom *gp;
334 	struct g_consumer *cp;
335 	int error;
336 	off_t i, init_size, start, offset, length;
337 	u_char *buf;
338 
339 	s = arg;
340 	KASSERT(s != NULL, ("gv_init_td: NULL s"));
341 	d = s->drive_sc;
342 	KASSERT(d != NULL, ("gv_init_td: NULL d"));
343 	gp = d->geom;
344 	KASSERT(gp != NULL, ("gv_init_td: NULL gp"));
345 
346 	cp = LIST_FIRST(&gp->consumer);
347 	KASSERT(cp != NULL, ("gv_init_td: NULL cp"));
348 
349 	s->init_error = 0;
350 	init_size = s->init_size;
351 	start = s->drive_offset + s->initialized;
352 	offset = s->drive_offset;
353 	length = s->size;
354 
355 	buf = g_malloc(s->init_size, M_WAITOK | M_ZERO);
356 
357 	g_topology_lock();
358 	error = g_access(cp, 0, 1, 0);
359 	if (error) {
360 		s->init_error = error;
361 		g_topology_unlock();
362 		printf("geom_vinum: init '%s' failed to access consumer: %d\n",
363 		    s->name, error);
364 		kthread_exit(error);
365 	}
366 	g_topology_unlock();
367 
368 	for (i = start; i < offset + length; i += init_size) {
369 		if (s->flags & GV_SD_INITCANCEL) {
370 			printf("geom_vinum: subdisk '%s' init: cancelled at"
371 			    " offset %jd (drive offset %jd)\n", s->name,
372 			    (intmax_t)s->initialized, (intmax_t)i);
373 			error = EAGAIN;
374 			break;
375 		}
376 		error = g_write_data(cp, i, buf, init_size);
377 		if (error) {
378 			printf("geom_vinum: subdisk '%s' init: write failed"
379 			    " at offset %jd (drive offset %jd)\n", s->name,
380 			    (intmax_t)s->initialized, (intmax_t)i);
381 			break;
382 		}
383 		s->initialized += init_size;
384 	}
385 
386 	g_free(buf);
387 
388 	g_topology_lock();
389 	g_access(cp, 0, -1, 0);
390 	g_topology_unlock();
391 	if (error) {
392 		s->init_error = error;
393 		g_topology_lock();
394 		gv_set_sd_state(s, GV_SD_STALE,
395 		    GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
396 		g_topology_unlock();
397 	} else {
398 		g_topology_lock();
399 		gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_CONFIG);
400 		g_topology_unlock();
401 		s->initialized = 0;
402 		printf("geom_vinum: init '%s' finished\n", s->name);
403 	}
404 	kthread_exit(error);
405 }
406