]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - sys/geom/concat/g_concat.c
MFC: r227309 (partial)
[FreeBSD/stable/9.git] / sys / geom / concat / g_concat.c
1 /*-
2  * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
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 AUTHORS 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 AUTHORS 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/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/bio.h>
37 #include <sys/sbuf.h>
38 #include <sys/sysctl.h>
39 #include <sys/malloc.h>
40 #include <geom/geom.h>
41 #include <geom/concat/g_concat.h>
42
43 FEATURE(geom_concat, "GEOM concatenation support");
44
45 static MALLOC_DEFINE(M_CONCAT, "concat_data", "GEOM_CONCAT Data");
46
47 SYSCTL_DECL(_kern_geom);
48 static SYSCTL_NODE(_kern_geom, OID_AUTO, concat, CTLFLAG_RW, 0,
49     "GEOM_CONCAT stuff");
50 static u_int g_concat_debug = 0;
51 TUNABLE_INT("kern.geom.concat.debug", &g_concat_debug);
52 SYSCTL_UINT(_kern_geom_concat, OID_AUTO, debug, CTLFLAG_RW, &g_concat_debug, 0,
53     "Debug level");
54
55 static int g_concat_destroy(struct g_concat_softc *sc, boolean_t force);
56 static int g_concat_destroy_geom(struct gctl_req *req, struct g_class *mp,
57     struct g_geom *gp);
58
59 static g_taste_t g_concat_taste;
60 static g_ctl_req_t g_concat_config;
61 static g_dumpconf_t g_concat_dumpconf;
62
63 struct g_class g_concat_class = {
64         .name = G_CONCAT_CLASS_NAME,
65         .version = G_VERSION,
66         .ctlreq = g_concat_config,
67         .taste = g_concat_taste,
68         .destroy_geom = g_concat_destroy_geom
69 };
70
71
72 /*
73  * Greatest Common Divisor.
74  */
75 static u_int
76 gcd(u_int a, u_int b)
77 {
78         u_int c;
79
80         while (b != 0) {
81                 c = a;
82                 a = b;
83                 b = (c % b);
84         }
85         return (a);
86 }
87
88 /*
89  * Least Common Multiple.
90  */
91 static u_int
92 lcm(u_int a, u_int b)
93 {
94
95         return ((a * b) / gcd(a, b));
96 }
97
98 /*
99  * Return the number of valid disks.
100  */
101 static u_int
102 g_concat_nvalid(struct g_concat_softc *sc)
103 {
104         u_int i, no;
105
106         no = 0;
107         for (i = 0; i < sc->sc_ndisks; i++) {
108                 if (sc->sc_disks[i].d_consumer != NULL)
109                         no++;
110         }
111
112         return (no);
113 }
114
115 static void
116 g_concat_remove_disk(struct g_concat_disk *disk)
117 {
118         struct g_consumer *cp;
119         struct g_concat_softc *sc;
120
121         KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__));
122         sc = disk->d_softc;
123         cp = disk->d_consumer;
124
125         G_CONCAT_DEBUG(0, "Disk %s removed from %s.", cp->provider->name,
126             sc->sc_name);
127
128         disk->d_consumer = NULL;
129         if (sc->sc_provider != NULL) {
130                 sc->sc_provider->flags |= G_PF_WITHER;
131                 g_orphan_provider(sc->sc_provider, ENXIO);
132                 sc->sc_provider = NULL;
133                 G_CONCAT_DEBUG(0, "Device %s removed.", sc->sc_name);
134         }
135
136         if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
137                 g_access(cp, -cp->acr, -cp->acw, -cp->ace);
138         g_detach(cp);
139         g_destroy_consumer(cp);
140 }
141
142 static void
143 g_concat_orphan(struct g_consumer *cp)
144 {
145         struct g_concat_softc *sc;
146         struct g_concat_disk *disk;
147         struct g_geom *gp;
148
149         g_topology_assert();
150         gp = cp->geom;
151         sc = gp->softc;
152         if (sc == NULL)
153                 return;
154
155         disk = cp->private;
156         if (disk == NULL)       /* Possible? */
157                 return;
158         g_concat_remove_disk(disk);
159
160         /* If there are no valid disks anymore, remove device. */
161         if (g_concat_nvalid(sc) == 0)
162                 g_concat_destroy(sc, 1);
163 }
164
165 static int
166 g_concat_access(struct g_provider *pp, int dr, int dw, int de)
167 {
168         struct g_consumer *cp1, *cp2;
169         struct g_concat_softc *sc;
170         struct g_geom *gp;
171         int error;
172
173         gp = pp->geom;
174         sc = gp->softc;
175
176         if (sc == NULL) {
177                 /*
178                  * It looks like geom is being withered.
179                  * In that case we allow only negative requests.
180                  */
181                 KASSERT(dr <= 0 && dw <= 0 && de <= 0,
182                     ("Positive access request (device=%s).", pp->name));
183                 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 &&
184                     (pp->ace + de) == 0) {
185                         G_CONCAT_DEBUG(0, "Device %s definitely destroyed.",
186                             gp->name);
187                 }
188                 return (0);
189         }
190
191         /* On first open, grab an extra "exclusive" bit */
192         if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
193                 de++;
194         /* ... and let go of it on last close */
195         if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0)
196                 de--;
197
198         error = ENXIO;
199         LIST_FOREACH(cp1, &gp->consumer, consumer) {
200                 error = g_access(cp1, dr, dw, de);
201                 if (error == 0)
202                         continue;
203                 /*
204                  * If we fail here, backout all previous changes.
205                  */
206                 LIST_FOREACH(cp2, &gp->consumer, consumer) {
207                         if (cp1 == cp2)
208                                 return (error);
209                         g_access(cp2, -dr, -dw, -de);
210                 }
211                 /* NOTREACHED */
212         }
213
214         return (error);
215 }
216
217 static void
218 g_concat_kernel_dump(struct bio *bp)
219 {
220         struct g_concat_softc *sc;
221         struct g_concat_disk *disk;
222         struct bio *cbp;
223         struct g_kerneldump *gkd;
224         u_int i;
225
226         sc = bp->bio_to->geom->softc;
227         gkd = (struct g_kerneldump *)bp->bio_data;
228         for (i = 0; i < sc->sc_ndisks; i++) {
229                 if (sc->sc_disks[i].d_start <= gkd->offset &&
230                     sc->sc_disks[i].d_end > gkd->offset)
231                         break;
232         }
233         if (i == sc->sc_ndisks)
234                 g_io_deliver(bp, EOPNOTSUPP);
235         disk = &sc->sc_disks[i];
236         gkd->offset -= disk->d_start;
237         if (gkd->length > disk->d_end - disk->d_start - gkd->offset)
238                 gkd->length = disk->d_end - disk->d_start - gkd->offset;
239         cbp = g_clone_bio(bp);
240         if (cbp == NULL) {
241                 g_io_deliver(bp, ENOMEM);
242                 return;
243         }
244         cbp->bio_done = g_std_done;
245         g_io_request(cbp, disk->d_consumer);
246         G_CONCAT_DEBUG(1, "Kernel dump will go to %s.",
247             disk->d_consumer->provider->name);
248 }
249
250 static void
251 g_concat_flush(struct g_concat_softc *sc, struct bio *bp)
252 {
253         struct bio_queue_head queue;
254         struct g_consumer *cp;
255         struct bio *cbp;
256         u_int no;
257
258         bioq_init(&queue);
259         for (no = 0; no < sc->sc_ndisks; no++) {
260                 cbp = g_clone_bio(bp);
261                 if (cbp == NULL) {
262                         for (cbp = bioq_first(&queue); cbp != NULL;
263                             cbp = bioq_first(&queue)) {
264                                 bioq_remove(&queue, cbp);
265                                 g_destroy_bio(cbp);
266                         }
267                         if (bp->bio_error == 0)
268                                 bp->bio_error = ENOMEM;
269                         g_io_deliver(bp, bp->bio_error);
270                         return;
271                 }
272                 bioq_insert_tail(&queue, cbp);
273                 cbp->bio_done = g_std_done;
274                 cbp->bio_caller1 = sc->sc_disks[no].d_consumer;
275                 cbp->bio_to = sc->sc_disks[no].d_consumer->provider;
276         }
277         for (cbp = bioq_first(&queue); cbp != NULL; cbp = bioq_first(&queue)) {
278                 bioq_remove(&queue, cbp);
279                 G_CONCAT_LOGREQ(cbp, "Sending request.");
280                 cp = cbp->bio_caller1;
281                 cbp->bio_caller1 = NULL;
282                 g_io_request(cbp, cp);
283         }
284 }
285
286 static void
287 g_concat_start(struct bio *bp)
288 {
289         struct bio_queue_head queue;
290         struct g_concat_softc *sc;
291         struct g_concat_disk *disk;
292         struct g_provider *pp;
293         off_t offset, end, length, off, len;
294         struct bio *cbp;
295         char *addr;
296         u_int no;
297
298         pp = bp->bio_to;
299         sc = pp->geom->softc;
300         /*
301          * If sc == NULL, provider's error should be set and g_concat_start()
302          * should not be called at all.
303          */
304         KASSERT(sc != NULL,
305             ("Provider's error should be set (error=%d)(device=%s).",
306             bp->bio_to->error, bp->bio_to->name));
307
308         G_CONCAT_LOGREQ(bp, "Request received.");
309
310         switch (bp->bio_cmd) {
311         case BIO_READ:
312         case BIO_WRITE:
313         case BIO_DELETE:
314                 break;
315         case BIO_FLUSH:
316                 g_concat_flush(sc, bp);
317                 return;
318         case BIO_GETATTR:
319                 if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) {
320                         g_concat_kernel_dump(bp);
321                         return;
322                 }
323                 /* To which provider it should be delivered? */
324                 /* FALLTHROUGH */
325         default:
326                 g_io_deliver(bp, EOPNOTSUPP);
327                 return;
328         }
329
330         offset = bp->bio_offset;
331         length = bp->bio_length;
332         addr = bp->bio_data;
333         end = offset + length;
334
335         bioq_init(&queue);
336         for (no = 0; no < sc->sc_ndisks; no++) {
337                 disk = &sc->sc_disks[no];
338                 if (disk->d_end <= offset)
339                         continue;
340                 if (disk->d_start >= end)
341                         break;
342
343                 off = offset - disk->d_start;
344                 len = MIN(length, disk->d_end - offset);
345                 length -= len;
346                 offset += len;
347
348                 cbp = g_clone_bio(bp);
349                 if (cbp == NULL) {
350                         for (cbp = bioq_first(&queue); cbp != NULL;
351                             cbp = bioq_first(&queue)) {
352                                 bioq_remove(&queue, cbp);
353                                 g_destroy_bio(cbp);
354                         }
355                         if (bp->bio_error == 0)
356                                 bp->bio_error = ENOMEM;
357                         g_io_deliver(bp, bp->bio_error);
358                         return;
359                 }
360                 bioq_insert_tail(&queue, cbp);
361                 /*
362                  * Fill in the component buf structure.
363                  */
364                 cbp->bio_done = g_std_done;
365                 cbp->bio_offset = off;
366                 cbp->bio_data = addr;
367                 addr += len;
368                 cbp->bio_length = len;
369                 cbp->bio_to = disk->d_consumer->provider;
370                 cbp->bio_caller1 = disk;
371
372                 if (length == 0)
373                         break;
374         }
375         KASSERT(length == 0,
376             ("Length is still greater than 0 (class=%s, name=%s).",
377             bp->bio_to->geom->class->name, bp->bio_to->geom->name));
378         for (cbp = bioq_first(&queue); cbp != NULL; cbp = bioq_first(&queue)) {
379                 bioq_remove(&queue, cbp);
380                 G_CONCAT_LOGREQ(cbp, "Sending request.");
381                 disk = cbp->bio_caller1;
382                 cbp->bio_caller1 = NULL;
383                 g_io_request(cbp, disk->d_consumer);
384         }
385 }
386
387 static void
388 g_concat_check_and_run(struct g_concat_softc *sc)
389 {
390         struct g_concat_disk *disk;
391         struct g_provider *pp;
392         u_int no, sectorsize = 0;
393         off_t start;
394
395         if (g_concat_nvalid(sc) != sc->sc_ndisks)
396                 return;
397
398         pp = g_new_providerf(sc->sc_geom, "concat/%s", sc->sc_name);
399         start = 0;
400         for (no = 0; no < sc->sc_ndisks; no++) {
401                 disk = &sc->sc_disks[no];
402                 disk->d_start = start;
403                 disk->d_end = disk->d_start +
404                     disk->d_consumer->provider->mediasize;
405                 if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC)
406                         disk->d_end -= disk->d_consumer->provider->sectorsize;
407                 start = disk->d_end;
408                 if (no == 0)
409                         sectorsize = disk->d_consumer->provider->sectorsize;
410                 else {
411                         sectorsize = lcm(sectorsize,
412                             disk->d_consumer->provider->sectorsize);
413                 }
414         }
415         pp->sectorsize = sectorsize;
416         /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */
417         pp->mediasize = start;
418         pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize;
419         pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset;
420         sc->sc_provider = pp;
421         g_error_provider(pp, 0);
422
423         G_CONCAT_DEBUG(0, "Device %s activated.", sc->sc_name);
424 }
425
426 static int
427 g_concat_read_metadata(struct g_consumer *cp, struct g_concat_metadata *md)
428 {
429         struct g_provider *pp;
430         u_char *buf;
431         int error;
432
433         g_topology_assert();
434
435         error = g_access(cp, 1, 0, 0);
436         if (error != 0)
437                 return (error);
438         pp = cp->provider;
439         g_topology_unlock();
440         buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
441             &error);
442         g_topology_lock();
443         g_access(cp, -1, 0, 0);
444         if (buf == NULL)
445                 return (error);
446
447         /* Decode metadata. */
448         concat_metadata_decode(buf, md);
449         g_free(buf);
450
451         return (0);
452 }
453
454 /*
455  * Add disk to given device.
456  */
457 static int
458 g_concat_add_disk(struct g_concat_softc *sc, struct g_provider *pp, u_int no)
459 {
460         struct g_concat_disk *disk;
461         struct g_consumer *cp, *fcp;
462         struct g_geom *gp;
463         int error;
464
465         /* Metadata corrupted? */
466         if (no >= sc->sc_ndisks)
467                 return (EINVAL);
468
469         disk = &sc->sc_disks[no];
470         /* Check if disk is not already attached. */
471         if (disk->d_consumer != NULL)
472                 return (EEXIST);
473
474         gp = sc->sc_geom;
475         fcp = LIST_FIRST(&gp->consumer);
476
477         cp = g_new_consumer(gp);
478         error = g_attach(cp, pp);
479         if (error != 0) {
480                 g_destroy_consumer(cp);
481                 return (error);
482         }
483
484         if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) {
485                 error = g_access(cp, fcp->acr, fcp->acw, fcp->ace);
486                 if (error != 0) {
487                         g_detach(cp);
488                         g_destroy_consumer(cp);
489                         return (error);
490                 }
491         }
492         if (sc->sc_type == G_CONCAT_TYPE_AUTOMATIC) {
493                 struct g_concat_metadata md;
494
495                 /* Re-read metadata. */
496                 error = g_concat_read_metadata(cp, &md);
497                 if (error != 0)
498                         goto fail;
499
500                 if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0 ||
501                     strcmp(md.md_name, sc->sc_name) != 0 ||
502                     md.md_id != sc->sc_id) {
503                         G_CONCAT_DEBUG(0, "Metadata on %s changed.", pp->name);
504                         goto fail;
505                 }
506         }
507
508         cp->private = disk;
509         disk->d_consumer = cp;
510         disk->d_softc = sc;
511         disk->d_start = 0;      /* not yet */
512         disk->d_end = 0;        /* not yet */
513
514         G_CONCAT_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name);
515
516         g_concat_check_and_run(sc);
517
518         return (0);
519 fail:
520         if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0))
521                 g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace);
522         g_detach(cp);
523         g_destroy_consumer(cp);
524         return (error);
525 }
526
527 static struct g_geom *
528 g_concat_create(struct g_class *mp, const struct g_concat_metadata *md,
529     u_int type)
530 {
531         struct g_concat_softc *sc;
532         struct g_geom *gp;
533         u_int no;
534
535         G_CONCAT_DEBUG(1, "Creating device %s (id=%u).", md->md_name,
536             md->md_id);
537
538         /* One disks is minimum. */
539         if (md->md_all < 1)
540                 return (NULL);
541
542         /* Check for duplicate unit */
543         LIST_FOREACH(gp, &mp->geom, geom) {
544                 sc = gp->softc;
545                 if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) {
546                         G_CONCAT_DEBUG(0, "Device %s already configured.",
547                             gp->name);
548                         return (NULL);
549                 }
550         }
551         gp = g_new_geomf(mp, "%s", md->md_name);
552         sc = malloc(sizeof(*sc), M_CONCAT, M_WAITOK | M_ZERO);
553         gp->start = g_concat_start;
554         gp->spoiled = g_concat_orphan;
555         gp->orphan = g_concat_orphan;
556         gp->access = g_concat_access;
557         gp->dumpconf = g_concat_dumpconf;
558
559         sc->sc_id = md->md_id;
560         sc->sc_ndisks = md->md_all;
561         sc->sc_disks = malloc(sizeof(struct g_concat_disk) * sc->sc_ndisks,
562             M_CONCAT, M_WAITOK | M_ZERO);
563         for (no = 0; no < sc->sc_ndisks; no++)
564                 sc->sc_disks[no].d_consumer = NULL;
565         sc->sc_type = type;
566
567         gp->softc = sc;
568         sc->sc_geom = gp;
569         sc->sc_provider = NULL;
570
571         G_CONCAT_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id);
572
573         return (gp);
574 }
575
576 static int
577 g_concat_destroy(struct g_concat_softc *sc, boolean_t force)
578 {
579         struct g_provider *pp;
580         struct g_geom *gp;
581         u_int no;
582
583         g_topology_assert();
584
585         if (sc == NULL)
586                 return (ENXIO);
587
588         pp = sc->sc_provider;
589         if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
590                 if (force) {
591                         G_CONCAT_DEBUG(0, "Device %s is still open, so it "
592                             "can't be definitely removed.", pp->name);
593                 } else {
594                         G_CONCAT_DEBUG(1,
595                             "Device %s is still open (r%dw%de%d).", pp->name,
596                             pp->acr, pp->acw, pp->ace);
597                         return (EBUSY);
598                 }
599         }
600
601         for (no = 0; no < sc->sc_ndisks; no++) {
602                 if (sc->sc_disks[no].d_consumer != NULL)
603                         g_concat_remove_disk(&sc->sc_disks[no]);
604         }
605
606         gp = sc->sc_geom;
607         gp->softc = NULL;
608         KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)",
609             gp->name));
610         free(sc->sc_disks, M_CONCAT);
611         free(sc, M_CONCAT);
612
613         pp = LIST_FIRST(&gp->provider);
614         if (pp == NULL || (pp->acr == 0 && pp->acw == 0 && pp->ace == 0))
615                 G_CONCAT_DEBUG(0, "Device %s destroyed.", gp->name);
616
617         g_wither_geom(gp, ENXIO);
618
619         return (0);
620 }
621
622 static int
623 g_concat_destroy_geom(struct gctl_req *req __unused,
624     struct g_class *mp __unused, struct g_geom *gp)
625 {
626         struct g_concat_softc *sc;
627
628         sc = gp->softc;
629         return (g_concat_destroy(sc, 0));
630 }
631
632 static struct g_geom *
633 g_concat_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
634 {
635         struct g_concat_metadata md;
636         struct g_concat_softc *sc;
637         struct g_consumer *cp;
638         struct g_geom *gp;
639         int error;
640
641         g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
642         g_topology_assert();
643
644         /* Skip providers that are already open for writing. */
645         if (pp->acw > 0)
646                 return (NULL);
647
648         G_CONCAT_DEBUG(3, "Tasting %s.", pp->name);
649
650         gp = g_new_geomf(mp, "concat:taste");
651         gp->start = g_concat_start;
652         gp->access = g_concat_access;
653         gp->orphan = g_concat_orphan;
654         cp = g_new_consumer(gp);
655         g_attach(cp, pp);
656         error = g_concat_read_metadata(cp, &md);
657         g_detach(cp);
658         g_destroy_consumer(cp);
659         g_destroy_geom(gp);
660         if (error != 0)
661                 return (NULL);
662         gp = NULL;
663
664         if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0)
665                 return (NULL);
666         if (md.md_version > G_CONCAT_VERSION) {
667                 printf("geom_concat.ko module is too old to handle %s.\n",
668                     pp->name);
669                 return (NULL);
670         }
671         /*
672          * Backward compatibility:
673          */
674         /* There was no md_provider field in earlier versions of metadata. */
675         if (md.md_version < 3)
676                 bzero(md.md_provider, sizeof(md.md_provider));
677         /* There was no md_provsize field in earlier versions of metadata. */
678         if (md.md_version < 4)
679                 md.md_provsize = pp->mediasize;
680
681         if (md.md_provider[0] != '\0' &&
682             !g_compare_names(md.md_provider, pp->name))
683                 return (NULL);
684         if (md.md_provsize != pp->mediasize)
685                 return (NULL);
686
687         /*
688          * Let's check if device already exists.
689          */
690         sc = NULL;
691         LIST_FOREACH(gp, &mp->geom, geom) {
692                 sc = gp->softc;
693                 if (sc == NULL)
694                         continue;
695                 if (sc->sc_type != G_CONCAT_TYPE_AUTOMATIC)
696                         continue;
697                 if (strcmp(md.md_name, sc->sc_name) != 0)
698                         continue;
699                 if (md.md_id != sc->sc_id)
700                         continue;
701                 break;
702         }
703         if (gp != NULL) {
704                 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name);
705                 error = g_concat_add_disk(sc, pp, md.md_no);
706                 if (error != 0) {
707                         G_CONCAT_DEBUG(0,
708                             "Cannot add disk %s to %s (error=%d).", pp->name,
709                             gp->name, error);
710                         return (NULL);
711                 }
712         } else {
713                 gp = g_concat_create(mp, &md, G_CONCAT_TYPE_AUTOMATIC);
714                 if (gp == NULL) {
715                         G_CONCAT_DEBUG(0, "Cannot create device %s.",
716                             md.md_name);
717                         return (NULL);
718                 }
719                 sc = gp->softc;
720                 G_CONCAT_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name);
721                 error = g_concat_add_disk(sc, pp, md.md_no);
722                 if (error != 0) {
723                         G_CONCAT_DEBUG(0,
724                             "Cannot add disk %s to %s (error=%d).", pp->name,
725                             gp->name, error);
726                         g_concat_destroy(sc, 1);
727                         return (NULL);
728                 }
729         }
730
731         return (gp);
732 }
733
734 static void
735 g_concat_ctl_create(struct gctl_req *req, struct g_class *mp)
736 {
737         u_int attached, no;
738         struct g_concat_metadata md;
739         struct g_provider *pp;
740         struct g_concat_softc *sc;
741         struct g_geom *gp;
742         struct sbuf *sb;
743         const char *name;
744         char param[16];
745         int *nargs;
746
747         g_topology_assert();
748         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
749         if (nargs == NULL) {
750                 gctl_error(req, "No '%s' argument.", "nargs");
751                 return;
752         }
753         if (*nargs < 2) {
754                 gctl_error(req, "Too few arguments.");
755                 return;
756         }
757
758         strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic));
759         md.md_version = G_CONCAT_VERSION;
760         name = gctl_get_asciiparam(req, "arg0");
761         if (name == NULL) {
762                 gctl_error(req, "No 'arg%u' argument.", 0);
763                 return;
764         }
765         strlcpy(md.md_name, name, sizeof(md.md_name));
766         md.md_id = arc4random();
767         md.md_no = 0;
768         md.md_all = *nargs - 1;
769         bzero(md.md_provider, sizeof(md.md_provider));
770         /* This field is not important here. */
771         md.md_provsize = 0;
772
773         /* Check all providers are valid */
774         for (no = 1; no < *nargs; no++) {
775                 snprintf(param, sizeof(param), "arg%u", no);
776                 name = gctl_get_asciiparam(req, param);
777                 if (name == NULL) {
778                         gctl_error(req, "No 'arg%u' argument.", no);
779                         return;
780                 }
781                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
782                         name += strlen("/dev/");
783                 pp = g_provider_by_name(name);
784                 if (pp == NULL) {
785                         G_CONCAT_DEBUG(1, "Disk %s is invalid.", name);
786                         gctl_error(req, "Disk %s is invalid.", name);
787                         return;
788                 }
789         }
790
791         gp = g_concat_create(mp, &md, G_CONCAT_TYPE_MANUAL);
792         if (gp == NULL) {
793                 gctl_error(req, "Can't configure %s.", md.md_name);
794                 return;
795         }
796
797         sc = gp->softc;
798         sb = sbuf_new_auto();
799         sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
800         for (attached = 0, no = 1; no < *nargs; no++) {
801                 snprintf(param, sizeof(param), "arg%u", no);
802                 name = gctl_get_asciiparam(req, param);
803                 if (name == NULL) {
804                         gctl_error(req, "No 'arg%d' argument.", no);
805                         return;
806                 }
807                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
808                         name += strlen("/dev/");
809                 pp = g_provider_by_name(name);
810                 KASSERT(pp != NULL, ("Provider %s disappear?!", name));
811                 if (g_concat_add_disk(sc, pp, no - 1) != 0) {
812                         G_CONCAT_DEBUG(1, "Disk %u (%s) not attached to %s.",
813                             no, pp->name, gp->name);
814                         sbuf_printf(sb, " %s", pp->name);
815                         continue;
816                 }
817                 attached++;
818         }
819         sbuf_finish(sb);
820         if (md.md_all != attached) {
821                 g_concat_destroy(gp->softc, 1);
822                 gctl_error(req, "%s", sbuf_data(sb));
823         }
824         sbuf_delete(sb);
825 }
826
827 static struct g_concat_softc *
828 g_concat_find_device(struct g_class *mp, const char *name)
829 {
830         struct g_concat_softc *sc;
831         struct g_geom *gp;
832
833         LIST_FOREACH(gp, &mp->geom, geom) {
834                 sc = gp->softc;
835                 if (sc == NULL)
836                         continue;
837                 if (strcmp(sc->sc_name, name) == 0)
838                         return (sc);
839         }
840         return (NULL);
841 }
842
843 static void
844 g_concat_ctl_destroy(struct gctl_req *req, struct g_class *mp)
845 {
846         struct g_concat_softc *sc;
847         int *force, *nargs, error;
848         const char *name;
849         char param[16];
850         u_int i;
851
852         g_topology_assert();
853
854         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
855         if (nargs == NULL) {
856                 gctl_error(req, "No '%s' argument.", "nargs");
857                 return;
858         }
859         if (*nargs <= 0) {
860                 gctl_error(req, "Missing device(s).");
861                 return;
862         }
863         force = gctl_get_paraml(req, "force", sizeof(*force));
864         if (force == NULL) {
865                 gctl_error(req, "No '%s' argument.", "force");
866                 return;
867         }
868
869         for (i = 0; i < (u_int)*nargs; i++) {
870                 snprintf(param, sizeof(param), "arg%u", i);
871                 name = gctl_get_asciiparam(req, param);
872                 if (name == NULL) {
873                         gctl_error(req, "No 'arg%u' argument.", i);
874                         return;
875                 }
876                 sc = g_concat_find_device(mp, name);
877                 if (sc == NULL) {
878                         gctl_error(req, "No such device: %s.", name);
879                         return;
880                 }
881                 error = g_concat_destroy(sc, *force);
882                 if (error != 0) {
883                         gctl_error(req, "Cannot destroy device %s (error=%d).",
884                             sc->sc_name, error);
885                         return;
886                 }
887         }
888 }
889
890 static void
891 g_concat_config(struct gctl_req *req, struct g_class *mp, const char *verb)
892 {
893         uint32_t *version;
894
895         g_topology_assert();
896
897         version = gctl_get_paraml(req, "version", sizeof(*version));
898         if (version == NULL) {
899                 gctl_error(req, "No '%s' argument.", "version");
900                 return;
901         }
902         if (*version != G_CONCAT_VERSION) {
903                 gctl_error(req, "Userland and kernel parts are out of sync.");
904                 return;
905         }
906
907         if (strcmp(verb, "create") == 0) {
908                 g_concat_ctl_create(req, mp);
909                 return;
910         } else if (strcmp(verb, "destroy") == 0 ||
911             strcmp(verb, "stop") == 0) {
912                 g_concat_ctl_destroy(req, mp);
913                 return;
914         }
915         gctl_error(req, "Unknown verb.");
916 }
917
918 static void
919 g_concat_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
920     struct g_consumer *cp, struct g_provider *pp)
921 {
922         struct g_concat_softc *sc;
923
924         g_topology_assert();
925         sc = gp->softc;
926         if (sc == NULL)
927                 return;
928         if (pp != NULL) {
929                 /* Nothing here. */
930         } else if (cp != NULL) {
931                 struct g_concat_disk *disk;
932
933                 disk = cp->private;
934                 if (disk == NULL)
935                         return;
936                 sbuf_printf(sb, "%s<End>%jd</End>\n", indent,
937                     (intmax_t)disk->d_end);
938                 sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent,
939                     (intmax_t)disk->d_start);
940         } else {
941                 sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id);
942                 sbuf_printf(sb, "%s<Type>", indent);
943                 switch (sc->sc_type) {
944                 case G_CONCAT_TYPE_AUTOMATIC:
945                         sbuf_printf(sb, "AUTOMATIC");
946                         break;
947                 case G_CONCAT_TYPE_MANUAL:
948                         sbuf_printf(sb, "MANUAL");
949                         break;
950                 default:
951                         sbuf_printf(sb, "UNKNOWN");
952                         break;
953                 }
954                 sbuf_printf(sb, "</Type>\n");
955                 sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n",
956                     indent, sc->sc_ndisks, g_concat_nvalid(sc));
957                 sbuf_printf(sb, "%s<State>", indent);
958                 if (sc->sc_provider != NULL && sc->sc_provider->error == 0)
959                         sbuf_printf(sb, "UP");
960                 else
961                         sbuf_printf(sb, "DOWN");
962                 sbuf_printf(sb, "</State>\n");
963         }
964 }
965
966 DECLARE_GEOM_CLASS(g_concat_class, g_concat);