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