]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/geom/vinum/geom_vinum_init.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / geom / vinum / geom_vinum_init.c
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 static int      gv_init_plex(struct gv_plex *);
44 void    gv_init_td(void *);
45 static int      gv_rebuild_plex(struct gv_plex *);
46 void    gv_rebuild_td(void *);
47 static int      gv_start_plex(struct gv_plex *);
48 static int      gv_start_vol(struct gv_volume *);
49 static int      gv_sync(struct gv_volume *);
50 void    gv_sync_td(void *);
51
52 struct gv_sync_args {
53         struct gv_volume *v;
54         struct gv_plex *from;
55         struct gv_plex *to;
56         off_t syncsize;
57 };
58
59 void
60 gv_parityop(struct g_geom *gp, struct gctl_req *req)
61 {
62         struct gv_softc *sc;
63         struct gv_plex *p;
64         struct bio *bp;
65         struct g_consumer *cp;
66         int error, *flags, type, *rebuild, rv;
67         char *plex;
68
69         rv = -1;
70
71         plex = gctl_get_param(req, "plex", NULL);
72         if (plex == NULL) {
73                 gctl_error(req, "no plex given");
74                 goto out;
75         }
76
77         flags = gctl_get_paraml(req, "flags", sizeof(*flags));
78         if (flags == NULL) {
79                 gctl_error(req, "no flags given");
80                 goto out;
81         }
82
83         rebuild = gctl_get_paraml(req, "rebuild", sizeof(*rebuild));
84         if (rebuild == NULL) {
85                 gctl_error(req, "no rebuild op given");
86                 goto out;
87         }
88
89         sc = gp->softc;
90         type = gv_object_type(sc, plex);
91         switch (type) {
92         case GV_TYPE_PLEX:
93                 break;
94         case GV_TYPE_VOL:
95         case GV_TYPE_SD:
96         case GV_TYPE_DRIVE:
97         default:
98                 gctl_error(req, "'%s' is not a plex", plex);
99                 goto out;
100         }
101
102         p = gv_find_plex(sc, plex);
103         if (p->state != GV_PLEX_UP) {
104                 gctl_error(req, "plex %s is not completely accessible",
105                     p->name);
106                 goto out;
107         }
108         if (p->org != GV_PLEX_RAID5) {
109                 gctl_error(req, "plex %s is not a RAID5 plex", p->name);
110                 goto out;
111         }
112
113         cp = p->consumer;
114         error = g_access(cp, 1, 1, 0);
115         if (error) {
116                 gctl_error(req, "cannot access consumer");
117                 goto out;
118         }
119         g_topology_unlock();
120
121         /* Reset the check pointer when using -f. */
122         if (*flags & GV_FLAG_F)
123                 p->synced = 0;
124
125         bp = g_new_bio();
126         if (bp == NULL) {
127                 gctl_error(req, "cannot create BIO - out of memory");
128                 g_topology_lock();
129                 error = g_access(cp, -1, -1, 0);
130                 goto out;
131         }
132         bp->bio_cmd = BIO_WRITE;
133         bp->bio_done = NULL;
134         bp->bio_data = g_malloc(p->stripesize, M_WAITOK | M_ZERO);
135         bp->bio_cflags |= GV_BIO_CHECK;
136         if (*rebuild)
137                 bp->bio_cflags |= GV_BIO_PARITY;
138         bp->bio_offset = p->synced;
139         bp->bio_length = p->stripesize;
140
141         /* Schedule it down ... */
142         g_io_request(bp, cp);
143
144         /* ... and wait for the result. */
145         error = biowait(bp, "gwrite");
146         g_free(bp->bio_data);
147         g_destroy_bio(bp);
148
149         if (error) {
150                 /* Incorrect parity. */
151                 if (error == EAGAIN)
152                         rv = 1;
153
154                 /* Some other error happened. */
155                 else
156                         gctl_error(req, "Parity check failed at offset 0x%jx, "
157                             "errno %d", (intmax_t)p->synced, error);
158
159         /* Correct parity. */
160         } else
161                 rv = 0;
162
163         gctl_set_param(req, "offset", &p->synced, sizeof(p->synced));
164
165         /* Advance the checkpointer if there was no error. */
166         if (rv == 0)
167                 p->synced += p->stripesize;
168
169         /* End of plex; reset the check pointer and signal it to the caller. */
170         if (p->synced >= p->size) {
171                 p->synced = 0;
172                 rv = -2;
173         }
174
175         g_topology_lock();
176         error = g_access(cp, -1, -1, 0);
177
178 out:
179         gctl_set_param(req, "rv", &rv, sizeof(rv));
180 }
181
182 void
183 gv_start_obj(struct g_geom *gp, struct gctl_req *req)
184 {
185         struct gv_softc *sc;
186         struct gv_volume *v;
187         struct gv_plex *p;
188         int *argc, *initsize;
189         char *argv, buf[20];
190         int err, i, type;
191
192         argc = gctl_get_paraml(req, "argc", sizeof(*argc));
193         initsize = gctl_get_paraml(req, "initsize", sizeof(*initsize));
194
195         if (argc == NULL || *argc == 0) {
196                 gctl_error(req, "no arguments given");
197                 return;
198         }
199
200         sc = gp->softc;
201
202         for (i = 0; i < *argc; i++) {
203                 snprintf(buf, sizeof(buf), "argv%d", i);
204                 argv = gctl_get_param(req, buf, NULL);
205                 if (argv == NULL)
206                         continue;
207                 type = gv_object_type(sc, argv);
208                 switch (type) {
209                 case GV_TYPE_VOL:
210                         v = gv_find_vol(sc, argv);
211                         err = gv_start_vol(v);
212                         if (err) {
213                                 if (err == EINPROGRESS) {
214                                         gctl_error(req, "cannot start volume "
215                                             "'%s': already in progress", argv);
216                                 } else {
217                                         gctl_error(req, "cannot start volume "
218                                             "'%s'; errno: %d", argv, err);
219                                 }
220                                 return;
221                         }
222                         break;
223
224                 case GV_TYPE_PLEX:
225                         p = gv_find_plex(sc, argv);
226                         err = gv_start_plex(p);
227                         if (err) {
228                                 if (err == EINPROGRESS) {
229                                         gctl_error(req, "cannot start plex "
230                                             "'%s': already in progress", argv);
231                                 } else {
232                                         gctl_error(req, "cannot start plex "
233                                             "'%s'; errno: %d", argv, err);
234                                 }
235                                 return;
236                         }
237                         break;
238
239                 case GV_TYPE_SD:
240                 case GV_TYPE_DRIVE:
241                         /* XXX not yet */
242                         gctl_error(req, "cannot start '%s' - not yet supported",
243                             argv);
244                         return;
245                 default:
246                         gctl_error(req, "unknown object '%s'", argv);
247                         return;
248                 }
249         }
250 }
251
252 static int
253 gv_start_plex(struct gv_plex *p)
254 {
255         struct gv_volume *v;
256         int error;
257
258         KASSERT(p != NULL, ("gv_start_plex: NULL p"));
259
260         if (p->state == GV_PLEX_UP)
261                 return (0);
262
263         error = 0;
264         v = p->vol_sc;
265         if ((v != NULL) && (v->plexcount > 1))
266                 error = gv_sync(v);
267         else if (p->org == GV_PLEX_RAID5) {
268                 if (p->state == GV_PLEX_DEGRADED)
269                         error = gv_rebuild_plex(p);
270                 else
271                         error = gv_init_plex(p);
272         }
273
274         return (error);
275 }
276
277 static int
278 gv_start_vol(struct gv_volume *v)
279 {
280         struct gv_plex *p;
281         struct gv_sd *s;
282         int error;
283
284         KASSERT(v != NULL, ("gv_start_vol: NULL v"));
285
286         error = 0;
287
288         if (v->plexcount == 0)
289                 return (ENXIO);
290
291         else if (v->plexcount == 1) {
292                 p = LIST_FIRST(&v->plexes);
293                 KASSERT(p != NULL, ("gv_start_vol: NULL p on %s", v->name));
294                 if (p->org == GV_PLEX_RAID5) {
295                         switch (p->state) {
296                         case GV_PLEX_DOWN:
297                                 error = gv_init_plex(p);
298                                 break;
299                         case GV_PLEX_DEGRADED:
300                                 error = gv_rebuild_plex(p);
301                                 break;
302                         default:
303                                 return (0);
304                         }
305                 } else {
306                         LIST_FOREACH(s, &p->subdisks, in_plex) {
307                                 gv_set_sd_state(s, GV_SD_UP,
308                                     GV_SETSTATE_CONFIG);
309                         }
310                 }
311         } else
312                 error = gv_sync(v);
313
314         return (error);
315 }
316
317 static int
318 gv_sync(struct gv_volume *v)
319 {
320         struct gv_softc *sc;
321         struct gv_plex *p, *up;
322         struct gv_sync_args *sync;
323
324         KASSERT(v != NULL, ("gv_sync: NULL v"));
325         sc = v->vinumconf;
326         KASSERT(sc != NULL, ("gv_sync: NULL sc on %s", v->name));
327
328         /* Find the plex that's up. */
329         up = NULL;
330         LIST_FOREACH(up, &v->plexes, in_volume) {
331                 if (up->state == GV_PLEX_UP)
332                         break;
333         }
334
335         /* Didn't find a good plex. */
336         if (up == NULL)
337                 return (ENXIO);
338
339         LIST_FOREACH(p, &v->plexes, in_volume) {
340                 if ((p == up) || (p->state == GV_PLEX_UP))
341                         continue;
342                 if (p->flags & GV_PLEX_SYNCING) {
343                         return (EINPROGRESS);
344                 }
345                 p->flags |= GV_PLEX_SYNCING;
346                 sync = g_malloc(sizeof(*sync), M_WAITOK | M_ZERO);
347                 sync->v = v;
348                 sync->from = up;
349                 sync->to = p;
350                 sync->syncsize = GV_DFLT_SYNCSIZE;
351                 kthread_create(gv_sync_td, sync, NULL, 0, 0, "gv_sync '%s'",
352                     p->name);
353         }
354
355         return (0);
356 }
357
358 static int
359 gv_rebuild_plex(struct gv_plex *p)
360 {
361         struct gv_sync_args *sync;
362
363         if (gv_is_open(p->geom))
364                 return (EBUSY);
365
366         if (p->flags & GV_PLEX_SYNCING)
367                 return (EINPROGRESS);
368         p->flags |= GV_PLEX_SYNCING;
369
370         sync = g_malloc(sizeof(*sync), M_WAITOK | M_ZERO);
371         sync->to = p;
372         sync->syncsize = GV_DFLT_SYNCSIZE;
373
374         kthread_create(gv_rebuild_td, sync, NULL, 0, 0, "gv_rebuild %s",
375             p->name);
376
377         return (0);
378 }
379
380 static int
381 gv_init_plex(struct gv_plex *p)
382 {
383         struct gv_sd *s;
384
385         KASSERT(p != NULL, ("gv_init_plex: NULL p"));
386
387         LIST_FOREACH(s, &p->subdisks, in_plex) {
388                 if (s->state == GV_SD_INITIALIZING)
389                         return (EINPROGRESS);
390                 gv_set_sd_state(s, GV_SD_INITIALIZING, GV_SETSTATE_FORCE);
391                 s->init_size = GV_DFLT_SYNCSIZE;
392                 kthread_create(gv_init_td, s, NULL, 0, 0, "gv_init %s",
393                     s->name);
394         }
395
396         return (0);
397 }
398
399 /* This thread is responsible for rebuilding a degraded RAID5 plex. */
400 void
401 gv_rebuild_td(void *arg)
402 {
403         struct bio *bp;
404         struct gv_plex *p;
405         struct g_consumer *cp;
406         struct gv_sync_args *sync;
407         u_char *buf;
408         off_t i;
409         int error;
410
411         buf = NULL;
412         bp = NULL;
413
414         sync = arg;
415         p = sync->to;
416         p->synced = 0;
417         cp = p->consumer;
418
419         g_topology_lock();
420         error = g_access(cp, 1, 1, 0);
421         if (error) {
422                 g_topology_unlock();
423                 printf("GEOM_VINUM: rebuild of %s failed to access consumer: "
424                     "%d\n", p->name, error);
425                 kthread_exit(error);
426         }
427         g_topology_unlock();
428
429         buf = g_malloc(sync->syncsize, M_WAITOK);
430
431         printf("GEOM_VINUM: rebuild of %s started\n", p->name);
432         i = 0;
433         for (i = 0; i < p->size; i += (p->stripesize * (p->sdcount - 1))) {
434 /*
435                 if (i + sync->syncsize > p->size)
436                         sync->syncsize = p->size - i;
437 */
438                 bp = g_new_bio();
439                 if (bp == NULL) {
440                         printf("GEOM_VINUM: rebuild of %s failed creating bio: "
441                             "out of memory\n", p->name);
442                         break;
443                 }
444                 bp->bio_cmd = BIO_WRITE;
445                 bp->bio_done = NULL;
446                 bp->bio_data = buf;
447                 bp->bio_cflags |= GV_BIO_REBUILD;
448                 bp->bio_offset = i;
449                 bp->bio_length = p->stripesize;
450
451                 /* Schedule it down ... */
452                 g_io_request(bp, cp);
453
454                 /* ... and wait for the result. */
455                 error = biowait(bp, "gwrite");
456                 if (error) {
457                         printf("GEOM_VINUM: rebuild of %s failed at offset %jd "
458                             "errno: %d\n", p->name, i, error);
459                         break;
460                 }
461                 g_destroy_bio(bp);
462                 bp = NULL;
463         }
464
465         if (bp != NULL)
466                 g_destroy_bio(bp);
467         if (buf != NULL)
468                 g_free(buf);
469
470         g_topology_lock();
471         g_access(cp, -1, -1, 0);
472         gv_save_config_all(p->vinumconf);
473         g_topology_unlock();
474
475         p->flags &= ~GV_PLEX_SYNCING;
476         p->synced = 0;
477
478         /* Successful initialization. */
479         if (!error)
480                 printf("GEOM_VINUM: rebuild of %s finished\n", p->name);
481
482         g_free(sync);
483         kthread_exit(error);
484 }
485
486 void
487 gv_sync_td(void *arg)
488 {
489         struct bio *bp;
490         struct gv_plex *p;
491         struct g_consumer *from, *to;
492         struct gv_sync_args *sync;
493         u_char *buf;
494         off_t i;
495         int error;
496
497         sync = arg;
498
499         from = sync->from->consumer;
500         to = sync->to->consumer;
501
502         p = sync->to;
503         p->synced = 0;
504
505         error = 0;
506
507         g_topology_lock();
508         error = g_access(from, 1, 0, 0);
509         if (error) {
510                 g_topology_unlock();
511                 printf("GEOM_VINUM: sync from '%s' failed to access "
512                     "consumer: %d\n", sync->from->name, error);
513                 g_free(sync);
514                 kthread_exit(error);
515         }
516         error = g_access(to, 0, 1, 0);
517         if (error) {
518                 g_access(from, -1, 0, 0);
519                 g_topology_unlock();
520                 printf("GEOM_VINUM: sync to '%s' failed to access "
521                     "consumer: %d\n", p->name, error);
522                 g_free(sync);
523                 kthread_exit(error);
524         }
525         g_topology_unlock();
526
527         printf("GEOM_VINUM: plex sync %s -> %s started\n", sync->from->name,
528             sync->to->name);
529         for (i = 0; i < p->size; i+= sync->syncsize) {
530                 /* Read some bits from the good plex. */
531                 buf = g_read_data(from, i, sync->syncsize, &error);
532                 if (buf == NULL) {
533                         printf("GEOM_VINUM: sync read from '%s' failed at "
534                             "offset %jd; errno: %d\n", sync->from->name, i,
535                             error);
536                         break;
537                 }
538
539                 /*
540                  * Create a bio and schedule it down on the 'bad' plex.  We
541                  * cannot simply use g_write_data() because we have to let the
542                  * lower parts know that we are an initialization process and
543                  * not a 'normal' request.
544                  */
545                 bp = g_new_bio();
546                 if (bp == NULL) {
547                         printf("GEOM_VINUM: sync write to '%s' failed at "
548                             "offset %jd; out of memory\n", p->name, i);
549                         g_free(buf);
550                         break;
551                 }
552                 bp->bio_cmd = BIO_WRITE;
553                 bp->bio_offset = i;
554                 bp->bio_length = sync->syncsize;
555                 bp->bio_data = buf;
556                 bp->bio_done = NULL;
557
558                 /*
559                  * This hack declare this bio as part of an initialization
560                  * process, so that the lower levels allow it to get through.
561                  */
562                 bp->bio_cflags |= GV_BIO_SYNCREQ;
563
564                 /* Schedule it down ... */
565                 g_io_request(bp, to);
566
567                 /* ... and wait for the result. */
568                 error = biowait(bp, "gwrite");
569                 g_destroy_bio(bp);
570                 g_free(buf);
571                 if (error) {
572                         printf("GEOM_VINUM: sync write to '%s' failed at "
573                             "offset %jd; errno: %d\n", p->name, i, error);
574                         break;
575                 }
576
577                 /* Note that we have synced a little bit more. */
578                 p->synced += sync->syncsize;
579         }
580
581         g_topology_lock();
582         g_access(from, -1, 0, 0);
583         g_access(to, 0, -1, 0);
584         gv_save_config_all(p->vinumconf);
585         g_topology_unlock();
586
587         /* Successful initialization. */
588         if (!error)
589                 printf("GEOM_VINUM: plex sync %s -> %s finished\n",
590                     sync->from->name, sync->to->name);
591
592         p->flags &= ~GV_PLEX_SYNCING;
593         p->synced = 0;
594
595         g_free(sync);
596         kthread_exit(error);
597 }
598
599 void
600 gv_init_td(void *arg)
601 {
602         struct gv_sd *s;
603         struct gv_drive *d;
604         struct g_geom *gp;
605         struct g_consumer *cp;
606         int error;
607         off_t i, init_size, start, offset, length;
608         u_char *buf;
609
610         s = arg;
611         KASSERT(s != NULL, ("gv_init_td: NULL s"));
612         d = s->drive_sc;
613         KASSERT(d != NULL, ("gv_init_td: NULL d"));
614         gp = d->geom;
615         KASSERT(gp != NULL, ("gv_init_td: NULL gp"));
616
617         cp = LIST_FIRST(&gp->consumer);
618         KASSERT(cp != NULL, ("gv_init_td: NULL cp"));
619
620         s->init_error = 0;
621         init_size = s->init_size;
622         start = s->drive_offset + s->initialized;
623         offset = s->drive_offset;
624         length = s->size;
625
626         buf = g_malloc(s->init_size, M_WAITOK | M_ZERO);
627
628         g_topology_lock();
629         error = g_access(cp, 0, 1, 0);
630         if (error) {
631                 s->init_error = error;
632                 g_topology_unlock();
633                 printf("GEOM_VINUM: subdisk '%s' init: failed to access "
634                     "consumer; error: %d\n", s->name, error);
635                 kthread_exit(error);
636         }
637         g_topology_unlock();
638
639         for (i = start; i < offset + length; i += init_size) {
640                 error = g_write_data(cp, i, buf, init_size);
641                 if (error) {
642                         printf("GEOM_VINUM: subdisk '%s' init: write failed"
643                             " at offset %jd (drive offset %jd); error %d\n",
644                             s->name, (intmax_t)s->initialized, (intmax_t)i,
645                             error);
646                         break;
647                 }
648                 s->initialized += init_size;
649         }
650
651         g_free(buf);
652
653         g_topology_lock();
654         g_access(cp, 0, -1, 0);
655         g_topology_unlock();
656         if (error) {
657                 s->init_error = error;
658                 g_topology_lock();
659                 gv_set_sd_state(s, GV_SD_STALE,
660                     GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
661                 g_topology_unlock();
662         } else {
663                 g_topology_lock();
664                 gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_CONFIG);
665                 g_topology_unlock();
666                 s->initialized = 0;
667                 printf("GEOM_VINUM: subdisk '%s' init: finished successfully\n",
668                     s->name);
669         }
670         kthread_exit(error);
671 }