geom_virstor.c revision 330737
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: stable/10/sbin/geom/class/virstor/geom_virstor.c 330737 2018-03-10 04:17:01Z asomers $"); 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 49uint32_t lib_version = G_LIB_VERSION; 50uint32_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 */ 63static void virstor_main(struct gctl_req *req, unsigned flags); 64 65struct 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 108static int verbose = 0; 109 110/* Helper functions' declarations */ 111static void virstor_clear(struct gctl_req *req); 112static void virstor_dump(struct gctl_req *req); 113static void virstor_label(struct gctl_req *req); 114 115/* Dispatcher function (no real work done here, only verbose flag recorder) */ 116static void 117virstor_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 143static void 144pathgen(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 153static int 154my_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 } 192out: 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 */ 205static void 206virstor_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 */ 487static void 488virstor_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 */ 530static void 531virstor_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 */ 550static void 551virstor_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} 585