]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sbin/geom/class/virstor/geom_virstor.c
MFC r323314, r323338, r328849
[FreeBSD/stable/10.git] / sbin / geom / class / virstor / geom_virstor.c
1 /*-
2  * Copyright (c) 2005 Ivan Voras <ivoras@freebsd.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/param.h>
30 #include <errno.h>
31 #include <paths.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <libgeom.h>
40 #include <err.h>
41 #include <assert.h>
42
43 #include <core/geom.h>
44 #include <misc/subr.h>
45
46 #include <geom/virstor/g_virstor_md.h>
47 #include <geom/virstor/g_virstor.h>
48
49 uint32_t lib_version = G_LIB_VERSION;
50 uint32_t version = G_VIRSTOR_VERSION;
51
52 #define GVIRSTOR_CHUNK_SIZE     "4M"
53 #define GVIRSTOR_VIR_SIZE       "2T"
54
55 #if G_LIB_VERSION == 1
56 /* Support RELENG_6 */
57 #define G_TYPE_BOOL G_TYPE_NONE
58 #endif
59
60 /*
61  * virstor_main gets called by the geom(8) utility
62  */
63 static void virstor_main(struct gctl_req *req, unsigned flags);
64
65 struct g_command class_commands[] = {
66         { "clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS,
67             "[-v] prov ..."
68         },
69         { "dump", 0, virstor_main, G_NULL_OPTS,
70             "prov ..."
71         },
72         { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main,
73             {
74                 { 'h', "hardcode", NULL, G_TYPE_BOOL},
75                 { 'm', "chunk_size", GVIRSTOR_CHUNK_SIZE, G_TYPE_NUMBER},
76                 { 's', "vir_size", GVIRSTOR_VIR_SIZE, G_TYPE_NUMBER},
77                 G_OPT_SENTINEL
78             },
79             "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]"
80         },
81         { "destroy", G_FLAG_VERBOSE, NULL,
82             {
83                 { 'f', "force", NULL, G_TYPE_BOOL},
84                 G_OPT_SENTINEL
85             },
86             "[-fv] name ..."
87         },
88         { "stop", G_FLAG_VERBOSE, NULL,
89             {
90                 { 'f', "force", NULL, G_TYPE_BOOL},
91                 G_OPT_SENTINEL
92             },
93             "[-fv] name ... (alias for \"destroy\")"
94         },
95         { "add", G_FLAG_VERBOSE, NULL,
96             {
97                 { 'h', "hardcode", NULL, G_TYPE_BOOL},
98                 G_OPT_SENTINEL
99             },
100             "[-vh] name prov [prov ...]"
101         },
102         { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
103             "[-v] name ..."
104         },
105         G_CMD_SENTINEL
106 };
107
108 static int verbose = 0;
109
110 /* Helper functions' declarations */
111 static void virstor_clear(struct gctl_req *req);
112 static void virstor_dump(struct gctl_req *req);
113 static void virstor_label(struct gctl_req *req);
114
115 /* Dispatcher function (no real work done here, only verbose flag recorder) */
116 static void
117 virstor_main(struct gctl_req *req, unsigned flags)
118 {
119         const char *name;
120
121         if ((flags & G_FLAG_VERBOSE) != 0)
122                 verbose = 1;
123
124         name = gctl_get_ascii(req, "verb");
125         if (name == NULL) {
126                 gctl_error(req, "No '%s' argument.", "verb");
127                 return;
128         }
129         if (strcmp(name, "label") == 0)
130                 virstor_label(req);
131         else if (strcmp(name, "clear") == 0)
132                 virstor_clear(req);
133         else if (strcmp(name, "dump") == 0)
134                 virstor_dump(req);
135         else
136                 gctl_error(req, "%s: Unknown command: %s.", __func__, name);
137
138         /* No CTASSERT in userland
139         CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS);
140         */
141 }
142
143 static void
144 pathgen(const char *name, char *path, size_t size)
145 {
146
147         if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
148                 snprintf(path, size, "%s%s", _PATH_DEV, name);
149         else
150                 strlcpy(path, name, size);
151 }
152
153 static int
154 my_g_metadata_store(const char *name, u_char *md, size_t size)
155 {
156         char path[MAXPATHLEN];
157         unsigned sectorsize;
158         off_t mediasize;
159         u_char *sector;
160         int error, fd;
161
162         pathgen(name, path, sizeof(path));
163         sector = NULL;
164         error = 0;
165
166         fd = open(path, O_RDWR);
167         if (fd == -1)
168                 return (errno);
169         mediasize = g_get_mediasize(name);
170         if (mediasize == 0) {
171                 error = errno;
172                 goto out;
173         }
174         sectorsize = g_get_sectorsize(name);
175         if (sectorsize == 0) {
176                 error = errno;
177                 goto out;
178         }
179         assert(sectorsize >= size);
180         sector = malloc(sectorsize);
181         if (sector == NULL) {
182                 error = ENOMEM;
183                 goto out;
184         }
185         bcopy(md, sector, size);
186         bzero(sector + size, sectorsize - size);
187         if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
188             (ssize_t)sectorsize) {
189                 error = errno;
190                 goto out;
191         }
192 out:
193         if (sector != NULL)
194                 free(sector);
195         close(fd);
196         return (error);
197 }
198
199 /*
200  * Labels a new geom Meaning: parses and checks the parameters, calculates &
201  * writes metadata to the relevant providers so when the next round of
202  * "tasting" comes (which will be just after the provider(s) are closed) geom
203  * can be instantiated with the tasted metadata.
204  */
205 static void
206 virstor_label(struct gctl_req *req)
207 {
208         struct g_virstor_metadata md;
209         off_t msize;
210         unsigned char *sect;
211         unsigned int i;
212         size_t ssize, secsize;
213         const char *name;
214         char param[32];
215         int hardcode, nargs, error;
216         struct virstor_map_entry *map;
217         size_t total_chunks;    /* We'll run out of memory if
218                                    this needs to be bigger. */
219         unsigned int map_chunks; /* Chunks needed by the map (map size). */
220         size_t map_size;        /* In bytes. */
221         ssize_t written;
222         int fd;
223
224         nargs = gctl_get_int(req, "nargs");
225         if (nargs < 2) {
226                 gctl_error(req, "Too few arguments (%d): expecting: name "
227                     "provider0 [provider1 ...]", nargs);
228                 return;
229         }
230
231         hardcode = gctl_get_int(req, "hardcode");
232
233         /*
234          * Initialize constant parts of metadata: magic signature, version,
235          * name.
236          */
237         bzero(&md, sizeof(md));
238         strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
239         md.md_version = G_VIRSTOR_VERSION;
240         name = gctl_get_ascii(req, "arg0");
241         if (name == NULL) {
242                 gctl_error(req, "No 'arg%u' argument.", 0);
243                 return;
244         }
245         strlcpy(md.md_name, name, sizeof(md.md_name));
246
247         md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
248         md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
249         md.md_count = nargs - 1;
250
251         if (md.md_virsize == 0 || md.md_chunk_size == 0) {
252                 gctl_error(req, "Virtual size and chunk size must be non-zero");
253                 return;
254         }
255
256         if (md.md_chunk_size % MAXPHYS != 0) {
257                 /* XXX: This is not strictly needed, but it's convenient to
258                  * impose some limitations on it, so why not MAXPHYS. */
259                 size_t new_size = (md.md_chunk_size / MAXPHYS) * MAXPHYS;
260                 if (new_size < md.md_chunk_size)
261                         new_size += MAXPHYS;
262                 fprintf(stderr, "Resizing chunk size to be a multiple of "
263                     "MAXPHYS (%d kB).\n", MAXPHYS / 1024);
264                 fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
265                 md.md_chunk_size = new_size;
266         }
267
268         if (md.md_virsize % md.md_chunk_size != 0) {
269                 off_t chunk_count = md.md_virsize / md.md_chunk_size;
270                 md.md_virsize = chunk_count * md.md_chunk_size;
271                 fprintf(stderr, "Resizing virtual size to be a multiple of "
272                     "chunk size.\n");
273                 fprintf(stderr, "New virtual size: %zu MB\n",
274                     (size_t)(md.md_virsize/(1024 * 1024)));
275         }
276
277         msize = secsize = 0;
278         for (i = 1; i < (unsigned)nargs; i++) {
279                 snprintf(param, sizeof(param), "arg%u", i);
280                 name = gctl_get_ascii(req, "%s", param);
281                 ssize = g_get_sectorsize(name);
282                 if (ssize == 0)
283                         fprintf(stderr, "%s for %s\n", strerror(errno), name);
284                 msize += g_get_mediasize(name);
285                 if (secsize == 0)
286                         secsize = ssize;
287                 else if (secsize != ssize) {
288                         gctl_error(req, "Devices need to have same sector size "
289                             "(%u on %s needs to be %u).",
290                             (u_int)ssize, name, (u_int)secsize);
291                         return;
292                 }
293         }
294
295         if (secsize == 0) {
296                 gctl_error(req, "Device not specified");
297                 return;
298         }
299
300         if (md.md_chunk_size % secsize != 0) {
301                 fprintf(stderr, "Error: chunk size is not a multiple of sector "
302                     "size.");
303                 gctl_error(req, "Chunk size (in bytes) must be multiple of %u.",
304                     (unsigned int)secsize);
305                 return;
306         }
307
308         total_chunks = md.md_virsize / md.md_chunk_size;
309         map_size = total_chunks * sizeof(*map);
310         assert(md.md_virsize % md.md_chunk_size == 0);
311
312         ssize = map_size % secsize;
313         if (ssize != 0) {
314                 size_t add_chunks = (secsize - ssize) / sizeof(*map);
315                 total_chunks += add_chunks;
316                 md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
317                 map_size = total_chunks * sizeof(*map);
318                 fprintf(stderr, "Resizing virtual size to fit virstor "
319                     "structures.\n");
320                 fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
321                     (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
322         }
323
324         if (verbose)
325                 printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
326                     "virtual size.\n",
327                     total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
328                     md.md_virsize/(1024 * 1024));
329
330         if ((off_t)md.md_virsize < msize)
331                 fprintf(stderr, "WARNING: Virtual storage size < Physical "
332                     "available storage (%ju < %ju)\n", md.md_virsize, msize);
333
334         /* Clear last sector first to spoil all components if device exists. */
335         if (verbose)
336                 printf("Clearing metadata on");
337
338         for (i = 1; i < (unsigned)nargs; i++) {
339                 snprintf(param, sizeof(param), "arg%u", i);
340                 name = gctl_get_ascii(req, "%s", param);
341
342                 if (verbose)
343                         printf(" %s", name);
344
345                 msize = g_get_mediasize(name);
346                 ssize = g_get_sectorsize(name);
347                 if (msize == 0 || ssize == 0) {
348                         gctl_error(req, "Can't retrieve information about "
349                             "%s: %s.", name, strerror(errno));
350                         return;
351                 }
352                 if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
353                         gctl_error(req, "Device %s is too small", name);
354                 error = g_metadata_clear(name, NULL);
355                 if (error != 0) {
356                         gctl_error(req, "Can't clear metadata on %s: %s.", name,
357                             strerror(error));
358                         return;
359                 }
360         }
361
362
363         /* Write allocation table to the first provider - this needs to be done
364          * before metadata is written because when kernel tastes it it's too
365          * late */
366         name = gctl_get_ascii(req, "arg1"); /* device with metadata */
367         if (verbose)
368                 printf(".\nWriting allocation table to %s...", name);
369
370         /* How many chunks does the map occupy? */
371         map_chunks = map_size/md.md_chunk_size;
372         if (map_size % md.md_chunk_size != 0)
373                 map_chunks++;
374         if (verbose) {
375                 printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
376                 fflush(stdout);
377         }
378
379         if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
380                 fd = open(name, O_RDWR);
381         else {
382                 sprintf(param, "%s%s", _PATH_DEV, name);
383                 fd = open(param, O_RDWR);
384         }
385         if (fd < 0)
386                 gctl_error(req, "Cannot open provider %s to write map", name);
387
388         /* Do it with calloc because there might be a need to set up chunk flags
389          * in the future */
390         map = calloc(total_chunks, sizeof(*map));
391         if (map == NULL) {
392                 gctl_error(req,
393                     "Out of memory (need %zu bytes for allocation map)",
394                     map_size);
395         }
396
397         written = pwrite(fd, map, map_size, 0);
398         free(map);
399         if ((size_t)written != map_size) {
400                 if (verbose) {
401                         fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n",
402                             map_size, written, strerror(errno));
403                 }
404                 gctl_error(req, "Error writing out allocation map!");
405                 return;
406         }
407         close (fd);
408
409         if (verbose)
410                 printf("\nStoring metadata on ");
411
412         /*
413          * ID is randomly generated, unique for a geom. This is used to
414          * recognize all providers belonging to one geom.
415          */
416         md.md_id = arc4random();
417
418         /* Ok, store metadata. */
419         for (i = 1; i < (unsigned)nargs; i++) {
420                 snprintf(param, sizeof(param), "arg%u", i);
421                 name = gctl_get_ascii(req, "%s", param);
422
423                 msize = g_get_mediasize(name);
424                 ssize = g_get_sectorsize(name);
425
426                 if (verbose)
427                         printf("%s ", name);
428
429                 /* this provider's position/type in geom */
430                 md.no = i - 1;
431                 /* this provider's size */
432                 md.provsize = msize;
433                 /* chunk allocation info */
434                 md.chunk_count = md.provsize / md.md_chunk_size;
435                 if (verbose)
436                         printf("(%u chunks) ", md.chunk_count);
437                 /* Check to make sure last sector is unused */
438                 if ((off_t)(md.chunk_count * md.md_chunk_size) > (off_t)(msize-ssize))
439                     md.chunk_count--;
440                 md.chunk_next = 0;
441                 if (i != 1) {
442                         md.chunk_reserved = 0;
443                         md.flags = 0;
444                 } else {
445                         md.chunk_reserved = map_chunks * 2;
446                         md.flags = VIRSTOR_PROVIDER_ALLOCATED |
447                             VIRSTOR_PROVIDER_CURRENT;
448                         md.chunk_next = md.chunk_reserved;
449                         if (verbose)
450                                 printf("(%u reserved) ", md.chunk_reserved);
451                 }
452
453                 if (!hardcode)
454                         bzero(md.provider, sizeof(md.provider));
455                 else {
456                         /* convert "/dev/something" to "something" */
457                         if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) {
458                                 strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1,
459                                     sizeof(md.provider));
460                         } else
461                                 strlcpy(md.provider, name, sizeof(md.provider));
462                 }
463                 sect = malloc(ssize);
464                 if (sect == NULL)
465                         err(1, "Cannot allocate sector of %zu bytes", ssize);
466                 bzero(sect, ssize);
467                 virstor_metadata_encode(&md, sect);
468                 error = my_g_metadata_store(name, sect, ssize);
469                 free(sect);
470                 if (error != 0) {
471                         if (verbose)
472                                 printf("\n");
473                         fprintf(stderr, "Can't store metadata on %s: %s.\n",
474                             name, strerror(error));
475                         gctl_error(req,
476                             "Not fully done (error storing metadata).");
477                         return;
478                 }
479         }
480 #if 0
481         if (verbose)
482                 printf("\n");
483 #endif
484 }
485
486 /* Clears metadata on given provider(s) IF it's owned by us */
487 static void
488 virstor_clear(struct gctl_req *req)
489 {
490         const char *name;
491         char param[32];
492         unsigned i;
493         int nargs, error;
494         int fd;
495
496         nargs = gctl_get_int(req, "nargs");
497         if (nargs < 1) {
498                 gctl_error(req, "Too few arguments.");
499                 return;
500         }
501         for (i = 0; i < (unsigned)nargs; i++) {
502                 snprintf(param, sizeof(param), "arg%u", i);
503                 name = gctl_get_ascii(req, "%s", param);
504
505                 error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
506                 if (error != 0) {
507                         fprintf(stderr, "Can't clear metadata on %s: %s "
508                             "(do I own it?)\n", name, strerror(error));
509                         gctl_error(req,
510                             "Not fully done (can't clear metadata).");
511                         continue;
512                 }
513                 if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
514                         fd = open(name, O_RDWR);
515                 else {
516                         sprintf(param, "%s%s", _PATH_DEV, name);
517                         fd = open(param, O_RDWR);
518                 }
519                 if (fd < 0) {
520                         gctl_error(req, "Cannot clear header sector for %s",
521                             name);
522                         continue;
523                 }
524                 if (verbose)
525                         printf("Metadata cleared on %s.\n", name);
526         }
527 }
528
529 /* Print some metadata information */
530 static void
531 virstor_metadata_dump(const struct g_virstor_metadata *md)
532 {
533         printf("          Magic string: %s\n", md->md_magic);
534         printf("      Metadata version: %u\n", (u_int) md->md_version);
535         printf("           Device name: %s\n", md->md_name);
536         printf("             Device ID: %u\n", (u_int) md->md_id);
537         printf("        Provider index: %u\n", (u_int) md->no);
538         printf("      Active providers: %u\n", (u_int) md->md_count);
539         printf("    Hardcoded provider: %s\n",
540             md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
541         printf("          Virtual size: %u MB\n",
542             (unsigned int)(md->md_virsize/(1024 * 1024)));
543         printf("            Chunk size: %u kB\n", md->md_chunk_size / 1024);
544         printf("    Chunks on provider: %u\n", md->chunk_count);
545         printf("           Chunks free: %u\n", md->chunk_count - md->chunk_next);
546         printf("       Reserved chunks: %u\n", md->chunk_reserved);
547 }
548
549 /* Called by geom(8) via gvirstor_main() to dump metadata information */
550 static void
551 virstor_dump(struct gctl_req *req)
552 {
553         struct g_virstor_metadata md;
554         u_char tmpmd[512];      /* temporary buffer */
555         const char *name;
556         char param[16];
557         int nargs, error, i;
558
559         assert(sizeof(tmpmd) >= sizeof(md));
560
561         nargs = gctl_get_int(req, "nargs");
562         if (nargs < 1) {
563                 gctl_error(req, "Too few arguments.");
564                 return;
565         }
566         for (i = 0; i < nargs; i++) {
567                 snprintf(param, sizeof(param), "arg%u", i);
568                 name = gctl_get_ascii(req, "%s", param);
569
570                 error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
571                     G_VIRSTOR_MAGIC);
572                 if (error != 0) {
573                         fprintf(stderr, "Can't read metadata from %s: %s.\n",
574                             name, strerror(error));
575                         gctl_error(req,
576                             "Not fully done (error reading metadata).");
577                         continue;
578                 }
579                 virstor_metadata_decode((u_char *) & tmpmd, &md);
580                 printf("Metadata on %s:\n", name);
581                 virstor_metadata_dump(&md);
582                 printf("\n");
583         }
584 }