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