]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/vinum/geom_vinum_create.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / sys / geom / vinum / geom_vinum_create.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2007 Lukas Ertl
5  * Copyright (c) 2007, 2009 Ulf Lilleengen
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/bio.h>
35 #include <sys/conf.h>
36 #include <sys/jail.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/systm.h>
40
41 #include <geom/geom.h>
42 #include <geom/geom_dbg.h>
43 #include <geom/vinum/geom_vinum_var.h>
44 #include <geom/vinum/geom_vinum.h>
45
46 #define DEFAULT_STRIPESIZE      262144
47
48 /*
49  * Create a new drive object, either by user request, during taste of the drive
50  * itself, or because it was referenced by a subdisk during taste.
51  */
52 int
53 gv_create_drive(struct gv_softc *sc, struct gv_drive *d)
54 {
55         struct g_geom *gp;
56         struct g_provider *pp;
57         struct g_consumer *cp, *cp2;
58         struct gv_drive *d2;
59         struct gv_hdr *hdr;
60         struct gv_freelist *fl;
61
62         KASSERT(d != NULL, ("gv_create_drive: NULL d"));
63
64         gp = sc->geom;
65
66         pp = NULL;
67         cp = cp2 = NULL;
68
69         /* The drive already has a consumer if it was tasted before. */
70         if (d->consumer != NULL) {
71                 cp = d->consumer;
72                 cp->private = d;
73                 pp = cp->provider;
74         } else if (!(d->flags & GV_DRIVE_REFERENCED)) {
75                 if (gv_find_drive(sc, d->name) != NULL) {
76                         G_VINUM_DEBUG(0, "drive '%s' already exists", d->name);
77                         g_free(d);
78                         return (GV_ERR_CREATE);
79                 }
80
81                 if (gv_find_drive_device(sc, d->device) != NULL) {
82                         G_VINUM_DEBUG(0, "provider '%s' already in use by "
83                             "gvinum", d->device);
84                         return (GV_ERR_CREATE);
85                 }
86
87                 pp = g_provider_by_name(d->device);
88                 if (pp == NULL) {
89                         G_VINUM_DEBUG(0, "create '%s': device '%s' disappeared",
90                             d->name, d->device);
91                         g_free(d);
92                         return (GV_ERR_CREATE);
93                 }
94
95                 g_topology_lock();
96                 cp = g_new_consumer(gp);
97                 if (g_attach(cp, pp) != 0) {
98                         g_destroy_consumer(cp);
99                         g_topology_unlock();
100                         G_VINUM_DEBUG(0, "create drive '%s': unable to attach",
101                             d->name);
102                         g_free(d);
103                         return (GV_ERR_CREATE);
104                 }
105                 g_topology_unlock();
106
107                 d->consumer = cp;
108                 cp->private = d;
109         }
110
111         /*
112          * If this was just a "referenced" drive, we're almost finished, but
113          * insert this drive not on the head of the drives list, as
114          * gv_drive_is_newer() expects a "real" drive from LIST_FIRST().
115          */
116         if (d->flags & GV_DRIVE_REFERENCED) {
117                 snprintf(d->device, sizeof(d->device), "???");
118                 d2 = LIST_FIRST(&sc->drives);
119                 if (d2 == NULL)
120                         LIST_INSERT_HEAD(&sc->drives, d, drive);
121                 else
122                         LIST_INSERT_AFTER(d2, d, drive);
123                 return (0);
124         }
125
126         /*
127          * Update access counts of the new drive to those of an already
128          * existing drive.
129          */
130         LIST_FOREACH(d2, &sc->drives, drive) {
131                 if ((d == d2) || (d2->consumer == NULL))
132                         continue;
133
134                 cp2 = d2->consumer;
135                 g_topology_lock();
136                 if ((cp2->acr || cp2->acw || cp2->ace) &&
137                     (g_access(cp, cp2->acr, cp2->acw, cp2->ace) != 0)) {
138                         g_detach(cp);
139                         g_destroy_consumer(cp);
140                         g_topology_unlock();
141                         G_VINUM_DEBUG(0, "create drive '%s': unable to update "
142                             "access counts", d->name);
143                         if (d->hdr != NULL)
144                                 g_free(d->hdr);
145                         g_free(d);
146                         return (GV_ERR_CREATE);
147                 }
148                 g_topology_unlock();
149                 break;
150         }
151
152         d->size = pp->mediasize - GV_DATA_START;
153         d->avail = d->size;
154         d->vinumconf = sc;
155         LIST_INIT(&d->subdisks);
156         LIST_INIT(&d->freelist);
157
158         /* The header might have been set during taste. */
159         if (d->hdr == NULL) {
160                 hdr = g_malloc(sizeof(*hdr), M_WAITOK | M_ZERO);
161                 hdr->magic = GV_MAGIC;
162                 hdr->config_length = GV_CFG_LEN;
163                 getcredhostname(NULL, hdr->label.sysname, GV_HOSTNAME_LEN);
164                 strlcpy(hdr->label.name, d->name, sizeof(hdr->label.name));
165                 microtime(&hdr->label.date_of_birth);
166                 d->hdr = hdr;
167         }
168
169         /* We also need a freelist entry. */
170         fl = g_malloc(sizeof(struct gv_freelist), M_WAITOK | M_ZERO);
171         fl->offset = GV_DATA_START;
172         fl->size = d->avail;
173         LIST_INSERT_HEAD(&d->freelist, fl, freelist);
174         d->freelist_entries = 1;
175
176         if (gv_find_drive(sc, d->name) == NULL)
177                 LIST_INSERT_HEAD(&sc->drives, d, drive);
178
179         gv_set_drive_state(d, GV_DRIVE_UP, 0);
180         return (0);
181 }
182
183 int
184 gv_create_volume(struct gv_softc *sc, struct gv_volume *v)
185 {
186         KASSERT(v != NULL, ("gv_create_volume: NULL v"));
187
188         v->vinumconf = sc;
189         v->flags |= GV_VOL_NEWBORN;
190         LIST_INIT(&v->plexes);
191         LIST_INSERT_HEAD(&sc->volumes, v, volume);
192         v->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
193         bioq_init(v->wqueue);
194         return (0);
195 }
196
197 int
198 gv_create_plex(struct gv_softc *sc, struct gv_plex *p)
199 {
200         struct gv_volume *v;
201
202         KASSERT(p != NULL, ("gv_create_plex: NULL p"));
203
204         /* Find the volume this plex should be attached to. */
205         v = gv_find_vol(sc, p->volume);
206         if (v == NULL) {
207                 G_VINUM_DEBUG(0, "create plex '%s': volume '%s' not found",
208                     p->name, p->volume);
209                 g_free(p);
210                 return (GV_ERR_CREATE);
211         }
212         if (!(v->flags & GV_VOL_NEWBORN))
213                 p->flags |= GV_PLEX_ADDED;
214         p->vol_sc = v;
215         v->plexcount++;
216         p->vinumconf = sc;
217         p->synced = 0;
218         p->flags |= GV_PLEX_NEWBORN;
219         LIST_INSERT_HEAD(&v->plexes, p, in_volume);
220         LIST_INIT(&p->subdisks);
221         TAILQ_INIT(&p->packets);
222         LIST_INSERT_HEAD(&sc->plexes, p, plex);
223         p->bqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
224         bioq_init(p->bqueue);
225         p->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
226         bioq_init(p->wqueue);
227         p->rqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
228         bioq_init(p->rqueue);
229         return (0);
230 }
231
232 int
233 gv_create_sd(struct gv_softc *sc, struct gv_sd *s)
234 {
235         struct gv_plex *p;
236         struct gv_drive *d;
237
238         KASSERT(s != NULL, ("gv_create_sd: NULL s"));
239
240         /* Find the drive where this subdisk should be put on. */
241         d = gv_find_drive(sc, s->drive);
242         if (d == NULL) {
243                 /*
244                  * It's possible that the subdisk references a drive that
245                  * doesn't exist yet (during the taste process), so create a
246                  * practically empty "referenced" drive.
247                  */
248                 if (s->flags & GV_SD_TASTED) {
249                         d = g_malloc(sizeof(struct gv_drive),
250                             M_WAITOK | M_ZERO);
251                         d->flags |= GV_DRIVE_REFERENCED;
252                         strlcpy(d->name, s->drive, sizeof(d->name));
253                         gv_create_drive(sc, d);
254                 } else {
255                         G_VINUM_DEBUG(0, "create sd '%s': drive '%s' not found",
256                             s->name, s->drive);
257                         g_free(s);
258                         return (GV_ERR_CREATE);
259                 }
260         }
261
262         /* Find the plex where this subdisk belongs to. */
263         p = gv_find_plex(sc, s->plex);
264         if (p == NULL) {
265                 G_VINUM_DEBUG(0, "create sd '%s': plex '%s' not found",
266                     s->name, s->plex);
267                 g_free(s);
268                 return (GV_ERR_CREATE);
269         }
270
271         /*
272          * First we give the subdisk to the drive, to handle autosized
273          * values ...
274          */
275         if (gv_sd_to_drive(s, d) != 0) {
276                 g_free(s);
277                 return (GV_ERR_CREATE);
278         }
279
280         /*
281          * Then, we give the subdisk to the plex; we check if the
282          * given values are correct and maybe adjust them.
283          */
284         if (gv_sd_to_plex(s, p) != 0) {
285                 G_VINUM_DEBUG(0, "unable to give sd '%s' to plex '%s'",
286                     s->name, p->name);
287                 if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED))
288                         LIST_REMOVE(s, from_drive);
289                 gv_free_sd(s);
290                 g_free(s);
291                 /*
292                  * If this subdisk can't be created, we won't create
293                  * the attached plex either, if it is also a new one.
294                  */
295                 if (!(p->flags & GV_PLEX_NEWBORN))
296                         return (GV_ERR_CREATE);
297                 gv_rm_plex(sc, p);
298                 return (GV_ERR_CREATE);
299         }
300         s->flags |= GV_SD_NEWBORN;
301
302         s->vinumconf = sc;
303         LIST_INSERT_HEAD(&sc->subdisks, s, sd);
304
305         return (0);
306 }
307
308 /*
309  * Create a concatenated volume from specified drives or drivegroups.
310  */
311 void
312 gv_concat(struct g_geom *gp, struct gctl_req *req)
313 {
314         struct gv_drive *d;
315         struct gv_sd *s;
316         struct gv_volume *v;
317         struct gv_plex *p;
318         struct gv_softc *sc;
319         char *drive, buf[30], *vol;
320         int *drives, dcount;
321
322         sc = gp->softc;
323         dcount = 0;
324         vol = gctl_get_param(req, "name", NULL);
325         if (vol == NULL) {
326                 gctl_error(req, "volume name not given");       
327                 return;
328         }
329
330         drives = gctl_get_paraml(req, "drives", sizeof(*drives));
331
332         if (drives == NULL) { 
333                 gctl_error(req, "drive names not given");
334                 return;
335         }
336
337         /* First we create the volume. */
338         v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
339         strlcpy(v->name, vol, sizeof(v->name));
340         v->state = GV_VOL_UP;
341         gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
342
343         /* Then we create the plex. */
344         p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
345         snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
346         strlcpy(p->volume, v->name, sizeof(p->volume));
347         p->org = GV_PLEX_CONCAT;
348         p->stripesize = 0;
349         gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
350
351         /* Drives are first (right now) priority */
352         for (dcount = 0; dcount < *drives; dcount++) {
353                 snprintf(buf, sizeof(buf), "drive%d", dcount);
354                 drive = gctl_get_param(req, buf, NULL);
355                 d = gv_find_drive(sc, drive);
356                 if (d == NULL) {
357                         gctl_error(req, "No such drive '%s'", drive);
358                         continue;
359                 }
360                 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
361                 snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
362                 strlcpy(s->plex, p->name, sizeof(s->plex));
363                 strlcpy(s->drive, drive, sizeof(s->drive));
364                 s->plex_offset = -1;
365                 s->drive_offset = -1;
366                 s->size = -1;
367                 gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
368         }
369         gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
370         gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
371 }
372
373 /*
374  * Create a mirrored volume from specified drives or drivegroups.
375  */
376 void
377 gv_mirror(struct g_geom *gp, struct gctl_req *req)
378 {
379         struct gv_drive *d;
380         struct gv_sd *s;
381         struct gv_volume *v;
382         struct gv_plex *p;
383         struct gv_softc *sc;
384         char *drive, buf[30], *vol;
385         int *drives, *flags, dcount, pcount, scount;
386
387         sc = gp->softc;
388         dcount = 0;
389         scount = 0;
390         pcount = 0;
391         vol = gctl_get_param(req, "name", NULL);
392         if (vol == NULL) {
393                 gctl_error(req, "volume name not given");       
394                 return;
395         }
396
397         flags = gctl_get_paraml(req, "flags", sizeof(*flags));
398         drives = gctl_get_paraml(req, "drives", sizeof(*drives));
399
400         if (drives == NULL) { 
401                 gctl_error(req, "drive names not given");
402                 return;
403         }
404
405         /* We must have an even number of drives. */
406         if (*drives % 2 != 0) {
407                 gctl_error(req, "mirror organization must have an even number "
408                     "of drives");
409                 return;
410         }
411         if (*flags & GV_FLAG_S && *drives < 4) {
412                 gctl_error(req, "must have at least 4 drives for striped plex");
413                 return;
414         }
415
416         /* First we create the volume. */
417         v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
418         strlcpy(v->name, vol, sizeof(v->name));
419         v->state = GV_VOL_UP;
420         gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
421
422         /* Then we create the plexes. */
423         for (pcount = 0; pcount < 2; pcount++) {
424                 p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
425                 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name,
426                     pcount);
427                 strlcpy(p->volume, v->name, sizeof(p->volume));
428                 if (*flags & GV_FLAG_S) {
429                         p->org = GV_PLEX_STRIPED;
430                         p->stripesize = DEFAULT_STRIPESIZE;
431                 } else {
432                         p->org = GV_PLEX_CONCAT;
433                         p->stripesize = -1;
434                 }
435                 gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
436
437                 /*
438                  * We just gives each even drive to plex one, and each odd to
439                  * plex two.
440                  */
441                 scount = 0;
442                 for (dcount = pcount; dcount < *drives; dcount += 2) {
443                         snprintf(buf, sizeof(buf), "drive%d", dcount);
444                         drive = gctl_get_param(req, buf, NULL);
445                         d = gv_find_drive(sc, drive);
446                         if (d == NULL) {
447                                 gctl_error(req, "No such drive '%s', aborting",
448                                     drive);
449                                 scount++;
450                                 break;
451                         }
452                         s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
453                         snprintf(s->name, sizeof(s->name), "%s.s%d", p->name,
454                             scount);
455                         strlcpy(s->plex, p->name, sizeof(s->plex));
456                         strlcpy(s->drive, drive, sizeof(s->drive));
457                         s->plex_offset = -1;
458                         s->drive_offset = -1;
459                         s->size = -1;
460                         gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
461                         scount++;
462                 }
463         }
464         gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
465         gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
466 }
467
468 void
469 gv_raid5(struct g_geom *gp, struct gctl_req *req)
470 {
471         struct gv_softc *sc;
472         struct gv_drive *d;
473         struct gv_volume *v;
474         struct gv_plex *p;
475         struct gv_sd *s;
476         int *drives, *flags, dcount;
477         char *vol, *drive, buf[30];
478         off_t *stripesize;
479
480         sc = gp->softc;
481
482         vol = gctl_get_param(req, "name", NULL);
483         if (vol == NULL) {
484                 gctl_error(req, "volume name not given");       
485                 return;
486         }
487         flags = gctl_get_paraml(req, "flags", sizeof(*flags));
488         drives = gctl_get_paraml(req, "drives", sizeof(*drives));
489         stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize));
490
491         if (stripesize == NULL) {
492                 gctl_error(req, "no stripesize given");
493                 return;
494         }
495
496         if (drives == NULL) {
497                 gctl_error(req, "drive names not given");
498                 return;
499         }
500
501         /* We must have at least three drives. */
502         if (*drives < 3) {
503                 gctl_error(req, "must have at least three drives for this "
504                     "plex organisation");
505                 return;
506         }
507         /* First we create the volume. */
508         v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
509         strlcpy(v->name, vol, sizeof(v->name));
510         v->state = GV_VOL_UP;
511         gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
512
513         /* Then we create the plex. */
514         p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
515         snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
516         strlcpy(p->volume, v->name, sizeof(p->volume));
517         p->org = GV_PLEX_RAID5;
518         p->stripesize = *stripesize;
519         gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
520
521         /* Create subdisks on drives. */
522         for (dcount = 0; dcount < *drives; dcount++) {
523                 snprintf(buf, sizeof(buf), "drive%d", dcount);
524                 drive = gctl_get_param(req, buf, NULL);
525                 d = gv_find_drive(sc, drive);
526                 if (d == NULL) {
527                         gctl_error(req, "No such drive '%s'", drive);
528                         continue;
529                 }
530                 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
531                 snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
532                 strlcpy(s->plex, p->name, sizeof(s->plex));
533                 strlcpy(s->drive, drive, sizeof(s->drive));
534                 s->plex_offset = -1;
535                 s->drive_offset = -1;
536                 s->size = -1;
537                 gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
538         }
539         gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
540         gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
541 }
542
543 /*
544  * Create a striped volume from specified drives or drivegroups.
545  */
546 void
547 gv_stripe(struct g_geom *gp, struct gctl_req *req)
548 {
549         struct gv_drive *d;
550         struct gv_sd *s;
551         struct gv_volume *v;
552         struct gv_plex *p;
553         struct gv_softc *sc;
554         char *drive, buf[30], *vol;
555         int *drives, *flags, dcount, pcount;
556
557         sc = gp->softc;
558         dcount = 0;
559         pcount = 0;
560         vol = gctl_get_param(req, "name", NULL);
561         if (vol == NULL) {
562                 gctl_error(req, "volume name not given");       
563                 return;
564         }
565         flags = gctl_get_paraml(req, "flags", sizeof(*flags));
566         drives = gctl_get_paraml(req, "drives", sizeof(*drives));
567
568         if (drives == NULL) { 
569                 gctl_error(req, "drive names not given");
570                 return;
571         }
572
573         /* We must have at least two drives. */
574         if (*drives < 2) {
575                 gctl_error(req, "must have at least 2 drives");
576                 return;
577         }
578
579         /* First we create the volume. */
580         v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
581         strlcpy(v->name, vol, sizeof(v->name));
582         v->state = GV_VOL_UP;
583         gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
584
585         /* Then we create the plex. */
586         p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
587         snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
588         strlcpy(p->volume, v->name, sizeof(p->volume));
589         p->org = GV_PLEX_STRIPED;
590         p->stripesize = 262144;
591         gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
592
593         /* Create subdisks on drives. */
594         for (dcount = 0; dcount < *drives; dcount++) {
595                 snprintf(buf, sizeof(buf), "drive%d", dcount);
596                 drive = gctl_get_param(req, buf, NULL);
597                 d = gv_find_drive(sc, drive);
598                 if (d == NULL) {
599                         gctl_error(req, "No such drive '%s'", drive);
600                         continue;
601                 }
602                 s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
603                 snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
604                 strlcpy(s->plex, p->name, sizeof(s->plex));
605                 strlcpy(s->drive, drive, sizeof(s->drive));
606                 s->plex_offset = -1;
607                 s->drive_offset = -1;
608                 s->size = -1;
609                 gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
610         }
611         gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
612         gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
613 }