]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/geom/class/mirror/geom_mirror.c
MFC r326276:
[FreeBSD/FreeBSD.git] / sbin / geom / class / mirror / geom_mirror.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <paths.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <assert.h>
42 #include <libgeom.h>
43 #include <geom/mirror/g_mirror.h>
44 #include <core/geom.h>
45 #include <misc/subr.h>
46
47 uint32_t lib_version = G_LIB_VERSION;
48 uint32_t version = G_MIRROR_VERSION;
49
50 #define GMIRROR_BALANCE         "load"
51 #define GMIRROR_SLICE           "4096"
52 #define GMIRROR_PRIORITY        "0"
53
54 static void mirror_main(struct gctl_req *req, unsigned flags);
55 static void mirror_activate(struct gctl_req *req);
56 static void mirror_clear(struct gctl_req *req);
57 static void mirror_dump(struct gctl_req *req);
58 static void mirror_label(struct gctl_req *req);
59 static void mirror_resize(struct gctl_req *req, unsigned flags);
60
61 struct g_command class_commands[] = {
62         { "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
63             "[-v] name prov ..."
64         },
65         { "clear", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
66             "[-v] prov ..."
67         },
68         { "configure", G_FLAG_VERBOSE, NULL,
69             {
70                 { 'a', "autosync", NULL, G_TYPE_BOOL },
71                 { 'b', "balance", "", G_TYPE_STRING },
72                 { 'd', "dynamic", NULL, G_TYPE_BOOL },
73                 { 'f', "failsync", NULL, G_TYPE_BOOL },
74                 { 'F', "nofailsync", NULL, G_TYPE_BOOL },
75                 { 'h', "hardcode", NULL, G_TYPE_BOOL },
76                 { 'n', "noautosync", NULL, G_TYPE_BOOL },
77                 { 'p', "priority", "-1", G_TYPE_NUMBER },
78                 { 's', "slice", "-1", G_TYPE_NUMBER },
79                 G_OPT_SENTINEL
80             },
81             "[-adfFhnv] [-b balance] [-s slice] name\n"
82             "[-v] -p priority name prov"
83         },
84         { "create", G_FLAG_VERBOSE, NULL,
85             {
86                 { 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING },
87                 { 'F', "nofailsync", NULL, G_TYPE_BOOL },
88                 { 'n', "noautosync", NULL, G_TYPE_BOOL },
89                 { 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER },
90                 G_OPT_SENTINEL
91             },
92             "[-Fnv] [-b balance] [-s slice] name prov ..."
93         },
94         { "deactivate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
95             "[-v] name prov ..."
96         },
97         { "destroy", G_FLAG_VERBOSE, NULL,
98             {
99                 { 'f', "force", NULL, G_TYPE_BOOL },
100                 G_OPT_SENTINEL
101             },
102             "[-fv] name ..."
103         },
104         { "dump", 0, mirror_main, G_NULL_OPTS,
105             "prov ..."
106         },
107         { "forget", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
108             "name ..."
109         },
110         { "label", G_FLAG_VERBOSE, mirror_main,
111             {
112                 { 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING },
113                 { 'F', "nofailsync", NULL, G_TYPE_BOOL },
114                 { 'h', "hardcode", NULL, G_TYPE_BOOL },
115                 { 'n', "noautosync", NULL, G_TYPE_BOOL },
116                 { 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER },
117                 G_OPT_SENTINEL
118             },
119             "[-Fhnv] [-b balance] [-s slice] name prov ..."
120         },
121         { "insert", G_FLAG_VERBOSE, NULL,
122             {
123                 { 'h', "hardcode", NULL, G_TYPE_BOOL },
124                 { 'i', "inactive", NULL, G_TYPE_BOOL },
125                 { 'p', "priority", GMIRROR_PRIORITY, G_TYPE_NUMBER },
126                 G_OPT_SENTINEL
127             },
128             "[-hiv] [-p priority] name prov ..."
129         },
130         { "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
131             "[-v] name prov ..."
132         },
133         { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
134             "[-v] name prov ..."
135         },
136         { "resize", G_FLAG_VERBOSE, mirror_resize,
137             {
138                 { 's', "size", "*", G_TYPE_STRING },
139                 G_OPT_SENTINEL
140             },
141             "[-s size] [-v] name"
142         },
143         { "stop", G_FLAG_VERBOSE, NULL,
144             {
145                 { 'f', "force", NULL, G_TYPE_BOOL },
146                 G_OPT_SENTINEL
147             },
148             "[-fv] name ..."
149         },
150         G_CMD_SENTINEL
151 };
152
153 static int verbose = 0;
154
155 static void
156 mirror_main(struct gctl_req *req, unsigned flags)
157 {
158         const char *name;
159
160         if ((flags & G_FLAG_VERBOSE) != 0)
161                 verbose = 1;
162
163         name = gctl_get_ascii(req, "verb");
164         if (name == NULL) {
165                 gctl_error(req, "No '%s' argument.", "verb");
166                 return;
167         }
168         if (strcmp(name, "label") == 0)
169                 mirror_label(req);
170         else if (strcmp(name, "clear") == 0)
171                 mirror_clear(req);
172         else if (strcmp(name, "dump") == 0)
173                 mirror_dump(req);
174         else if (strcmp(name, "activate") == 0)
175                 mirror_activate(req);
176         else
177                 gctl_error(req, "Unknown command: %s.", name);
178 }
179
180 static void
181 mirror_label(struct gctl_req *req)
182 {
183         struct g_mirror_metadata md;
184         u_char sector[512];
185         const char *str;
186         unsigned sectorsize;
187         off_t mediasize;
188         intmax_t val;
189         int error, i, nargs, bal, hardcode;
190
191         nargs = gctl_get_int(req, "nargs");
192         if (nargs < 2) {
193                 gctl_error(req, "Too few arguments.");
194                 return;
195         }
196
197         strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic));
198         md.md_version = G_MIRROR_VERSION;
199         str = gctl_get_ascii(req, "arg0");
200         strlcpy(md.md_name, str, sizeof(md.md_name));
201         md.md_mid = arc4random();
202         md.md_all = nargs - 1;
203         md.md_mflags = 0;
204         md.md_dflags = 0;
205         md.md_genid = 0;
206         md.md_syncid = 1;
207         md.md_sync_offset = 0;
208         val = gctl_get_intmax(req, "slice");
209         md.md_slice = val;
210         str = gctl_get_ascii(req, "balance");
211         bal = balance_id(str);
212         if (bal == -1) {
213                 gctl_error(req, "Invalid balance algorithm.");
214                 return;
215         }
216         md.md_balance = bal;
217         if (gctl_get_int(req, "noautosync"))
218                 md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
219         if (gctl_get_int(req, "nofailsync"))
220                 md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
221         hardcode = gctl_get_int(req, "hardcode");
222
223         /*
224          * Calculate sectorsize by finding least common multiple from
225          * sectorsizes of every disk and find the smallest mediasize.
226          */
227         mediasize = 0;
228         sectorsize = 0;
229         for (i = 1; i < nargs; i++) {
230                 unsigned ssize;
231                 off_t msize;
232
233                 str = gctl_get_ascii(req, "arg%d", i);
234                 msize = g_get_mediasize(str);
235                 ssize = g_get_sectorsize(str);
236                 if (msize == 0 || ssize == 0) {
237                         gctl_error(req, "Can't get informations about %s: %s.",
238                             str, strerror(errno));
239                         return;
240                 }
241                 msize -= ssize;
242                 if (mediasize == 0 || (mediasize > 0 && msize < mediasize))
243                         mediasize = msize;
244                 if (sectorsize == 0)
245                         sectorsize = ssize;
246                 else
247                         sectorsize = g_lcm(sectorsize, ssize);
248         }
249         md.md_mediasize = mediasize;
250         md.md_sectorsize = sectorsize;
251         md.md_mediasize -= (md.md_mediasize % md.md_sectorsize);
252
253         /*
254          * Clear last sector first, to spoil all components if device exists.
255          */
256         for (i = 1; i < nargs; i++) {
257                 str = gctl_get_ascii(req, "arg%d", i);
258                 error = g_metadata_clear(str, NULL);
259                 if (error != 0) {
260                         gctl_error(req, "Can't store metadata on %s: %s.", str,
261                             strerror(error));
262                         return;
263                 }
264         }
265
266         /*
267          * Ok, store metadata (use disk number as priority).
268          */
269         for (i = 1; i < nargs; i++) {
270                 str = gctl_get_ascii(req, "arg%d", i);
271                 md.md_did = arc4random();
272                 md.md_priority = i - 1;
273                 md.md_provsize = g_get_mediasize(str);
274                 assert(md.md_provsize != 0);
275                 if (!hardcode)
276                         bzero(md.md_provider, sizeof(md.md_provider));
277                 else {
278                         if (strncmp(str, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
279                                 str += sizeof(_PATH_DEV) - 1;
280                         strlcpy(md.md_provider, str, sizeof(md.md_provider));
281                 }
282                 mirror_metadata_encode(&md, sector);
283                 error = g_metadata_store(str, sector, sizeof(sector));
284                 if (error != 0) {
285                         fprintf(stderr, "Can't store metadata on %s: %s.\n",
286                             str, strerror(error));
287                         gctl_error(req, "Not fully done.");
288                         continue;
289                 }
290                 if (verbose)
291                         printf("Metadata value stored on %s.\n", str);
292         }
293 }
294
295 static void
296 mirror_clear(struct gctl_req *req)
297 {
298         const char *name;
299         int error, i, nargs;
300
301         nargs = gctl_get_int(req, "nargs");
302         if (nargs < 1) {
303                 gctl_error(req, "Too few arguments.");
304                 return;
305         }
306
307         for (i = 0; i < nargs; i++) {
308                 name = gctl_get_ascii(req, "arg%d", i);
309                 error = g_metadata_clear(name, G_MIRROR_MAGIC);
310                 if (error != 0) {
311                         fprintf(stderr, "Can't clear metadata on %s: %s.\n",
312                             name, strerror(error));
313                         gctl_error(req, "Not fully done.");
314                         continue;
315                 }
316                 if (verbose)
317                         printf("Metadata cleared on %s.\n", name);
318         }
319 }
320
321 static void
322 mirror_dump(struct gctl_req *req)
323 {
324         struct g_mirror_metadata md, tmpmd;
325         const char *name;
326         int error, i, nargs;
327
328         nargs = gctl_get_int(req, "nargs");
329         if (nargs < 1) {
330                 gctl_error(req, "Too few arguments.");
331                 return;
332         }
333
334         for (i = 0; i < nargs; i++) {
335                 name = gctl_get_ascii(req, "arg%d", i);
336                 error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
337                     G_MIRROR_MAGIC);
338                 if (error != 0) {
339                         fprintf(stderr, "Can't read metadata from %s: %s.\n",
340                             name, strerror(error));
341                         gctl_error(req, "Not fully done.");
342                         continue;
343                 }
344                 if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
345                         fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
346                             name);
347                         gctl_error(req, "Not fully done.");
348                         continue;
349                 }
350                 printf("Metadata on %s:\n", name);
351                 mirror_metadata_dump(&md);
352                 printf("\n");
353         }
354 }
355
356 static void
357 mirror_activate(struct gctl_req *req)
358 {
359         struct g_mirror_metadata md, tmpmd;
360         const char *name, *path;
361         int error, i, nargs;
362
363         nargs = gctl_get_int(req, "nargs");
364         if (nargs < 2) {
365                 gctl_error(req, "Too few arguments.");
366                 return;
367         }
368         name = gctl_get_ascii(req, "arg0");
369
370         for (i = 1; i < nargs; i++) {
371                 path = gctl_get_ascii(req, "arg%d", i);
372                 error = g_metadata_read(path, (u_char *)&tmpmd, sizeof(tmpmd),
373                     G_MIRROR_MAGIC);
374                 if (error != 0) {
375                         fprintf(stderr, "Cannot read metadata from %s: %s.\n",
376                             path, strerror(error));
377                         gctl_error(req, "Not fully done.");
378                         continue;
379                 }
380                 if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
381                         fprintf(stderr,
382                             "MD5 hash mismatch for provider %s, skipping.\n",
383                             path);
384                         gctl_error(req, "Not fully done.");
385                         continue;
386                 }
387                 if (strcmp(md.md_name, name) != 0) {
388                         fprintf(stderr,
389                             "Provider %s is not the mirror %s component.\n",
390                             path, name);
391                         gctl_error(req, "Not fully done.");
392                         continue;
393                 }
394                 md.md_dflags &= ~G_MIRROR_DISK_FLAG_INACTIVE;
395                 mirror_metadata_encode(&md, (u_char *)&tmpmd);
396                 error = g_metadata_store(path, (u_char *)&tmpmd, sizeof(tmpmd));
397                 if (error != 0) {
398                         fprintf(stderr, "Cannot write metadata from %s: %s.\n",
399                             path, strerror(error));
400                         gctl_error(req, "Not fully done.");
401                         continue;
402                 }
403                 if (verbose)
404                         printf("Provider %s activated.\n", path);
405         }
406 }
407
408 static struct gclass *
409 find_class(struct gmesh *mesh, const char *name)
410 {
411         struct gclass *classp;
412
413         LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
414                 if (strcmp(classp->lg_name, name) == 0)
415                         return (classp);
416         }
417         return (NULL);
418 }
419
420 static struct ggeom *
421 find_geom(struct gclass *classp, const char *name)
422 {
423         struct ggeom *gp;
424
425         LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
426                 if (strcmp(gp->lg_name, name) == 0)
427                         return (gp);
428         }
429         return (NULL);
430 }
431
432 static void
433 mirror_resize(struct gctl_req *req, unsigned flags __unused)
434 {
435         struct gmesh mesh;
436         struct gclass *classp;
437         struct ggeom *gp;
438         struct gprovider *pp;
439         struct gconsumer *cp;
440         off_t size;
441         int error, nargs;
442         const char *name;
443         char ssize[30];
444
445         nargs = gctl_get_int(req, "nargs");
446         if (nargs < 1) {
447                 gctl_error(req, "Too few arguments.");
448                 return;
449         }
450         error = geom_gettree(&mesh);
451         if (error)
452                 errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
453         name = gctl_get_ascii(req, "class");
454         if (name == NULL)
455                 abort();
456         classp = find_class(&mesh, name);
457         if (classp == NULL)
458                 errx(EXIT_FAILURE, "Class %s not found.", name);
459         name = gctl_get_ascii(req, "arg0");
460         if (name == NULL)
461                 abort();
462         gp = find_geom(classp, name);
463         if (gp == NULL)
464                 errx(EXIT_FAILURE, "No such geom: %s.", name);
465         pp = LIST_FIRST(&gp->lg_provider);
466         if (pp == NULL)
467                 errx(EXIT_FAILURE, "Provider of geom %s not found.", name);
468         size = pp->lg_mediasize;
469         name = gctl_get_ascii(req, "size");
470         if (name == NULL)
471                 errx(EXIT_FAILURE, "The size is not specified.");
472         if (*name == '*') {
473 #define CSZ(c)  ((c)->lg_provider->lg_mediasize - \
474     (c)->lg_provider->lg_sectorsize)
475                 /* Find the maximum possible size */
476                 LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
477                         if (CSZ(cp) > size)
478                                 size = CSZ(cp);
479                 }
480                 LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
481                         if (CSZ(cp) < size)
482                                 size = CSZ(cp);
483                 }
484 #undef CSZ
485                 if (size == pp->lg_mediasize)
486                         errx(EXIT_FAILURE,
487                             "Cannot expand provider %s\n",
488                             pp->lg_name);
489         } else {
490                 error = g_parse_lba(name, pp->lg_sectorsize, &size);
491                 if (error)
492                         errc(EXIT_FAILURE, error, "Invalid size param");
493                 size *= pp->lg_sectorsize;
494         }
495         snprintf(ssize, sizeof(ssize), "%ju", (uintmax_t)size);
496         gctl_change_param(req, "size", -1, ssize);
497         geom_deletetree(&mesh);
498         gctl_issue(req);
499 }