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