aboutsummaryrefslogtreecommitdiff
path: root/lib/geom/virstor
diff options
context:
space:
mode:
Diffstat (limited to 'lib/geom/virstor')
-rw-r--r--lib/geom/virstor/Makefile9
-rw-r--r--lib/geom/virstor/Makefile.depend16
-rw-r--r--lib/geom/virstor/geom_virstor.c541
-rw-r--r--lib/geom/virstor/gvirstor.8297
4 files changed, 863 insertions, 0 deletions
diff --git a/lib/geom/virstor/Makefile b/lib/geom/virstor/Makefile
new file mode 100644
index 000000000000..63ea8e5ad31c
--- /dev/null
+++ b/lib/geom/virstor/Makefile
@@ -0,0 +1,9 @@
+PACKAGE=geom
+.PATH: ${SRCTOP}/sys/geom/virstor
+
+GEOM_CLASS= virstor
+
+SRCS+= binstream.c
+SRCS+= g_virstor_md.c
+
+.include <bsd.lib.mk>
diff --git a/lib/geom/virstor/Makefile.depend b/lib/geom/virstor/Makefile.depend
new file mode 100644
index 000000000000..0dd05cace3c0
--- /dev/null
+++ b/lib/geom/virstor/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libgeom \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/geom/virstor/geom_virstor.c b/lib/geom/virstor/geom_virstor.c
new file mode 100644
index 000000000000..5f5087e99213
--- /dev/null
+++ b/lib/geom/virstor/geom_virstor.c
@@ -0,0 +1,541 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2005 Ivan Voras <ivoras@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgeom.h>
+#include <err.h>
+#include <assert.h>
+
+#include <core/geom.h>
+#include <misc/subr.h>
+
+#include <geom/virstor/g_virstor_md.h>
+#include <geom/virstor/g_virstor.h>
+
+uint32_t lib_version = G_LIB_VERSION;
+uint32_t version = G_VIRSTOR_VERSION;
+
+#define GVIRSTOR_CHUNK_SIZE "4M"
+#define GVIRSTOR_VIR_SIZE "2T"
+
+#if G_LIB_VERSION == 1
+/* Support RELENG_6 */
+#define G_TYPE_BOOL G_TYPE_NONE
+#endif
+
+/*
+ * virstor_main gets called by the geom(8) utility
+ */
+static void virstor_main(struct gctl_req *req, unsigned flags);
+
+struct g_command class_commands[] = {
+ { "clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ { "dump", 0, virstor_main, G_NULL_OPTS,
+ "prov ..."
+ },
+ { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_BOOL},
+ { 'm', "chunk_size", GVIRSTOR_CHUNK_SIZE, G_TYPE_NUMBER},
+ { 's', "vir_size", GVIRSTOR_VIR_SIZE, G_TYPE_NUMBER},
+ G_OPT_SENTINEL
+ },
+ "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]"
+ },
+ { "destroy", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_BOOL},
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ..."
+ },
+ { "stop", G_FLAG_VERBOSE, NULL,
+ {
+ { 'f', "force", NULL, G_TYPE_BOOL},
+ G_OPT_SENTINEL
+ },
+ "[-fv] name ... (alias for \"destroy\")"
+ },
+ { "add", G_FLAG_VERBOSE, NULL,
+ {
+ { 'h', "hardcode", NULL, G_TYPE_BOOL},
+ G_OPT_SENTINEL
+ },
+ "[-vh] name prov [prov ...]"
+ },
+ { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
+ "[-v] name ..."
+ },
+ G_CMD_SENTINEL
+};
+
+static int verbose = 0;
+
+/* Helper functions' declarations */
+static void virstor_clear(struct gctl_req *req);
+static void virstor_dump(struct gctl_req *req);
+static void virstor_label(struct gctl_req *req);
+
+/* Dispatcher function (no real work done here, only verbose flag recorder) */
+static void
+virstor_main(struct gctl_req *req, unsigned flags)
+{
+ const char *name;
+
+ if ((flags & G_FLAG_VERBOSE) != 0)
+ verbose = 1;
+
+ name = gctl_get_ascii(req, "verb");
+ if (name == NULL) {
+ gctl_error(req, "No '%s' argument.", "verb");
+ return;
+ }
+ if (strcmp(name, "label") == 0)
+ virstor_label(req);
+ else if (strcmp(name, "clear") == 0)
+ virstor_clear(req);
+ else if (strcmp(name, "dump") == 0)
+ virstor_dump(req);
+ else
+ gctl_error(req, "%s: Unknown command: %s.", __func__, name);
+
+ /* No CTASSERT in userland
+ CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS);
+ */
+}
+
+/*
+ * Labels a new geom Meaning: parses and checks the parameters, calculates &
+ * writes metadata to the relevant providers so when the next round of
+ * "tasting" comes (which will be just after the provider(s) are closed) geom
+ * can be instantiated with the tasted metadata.
+ */
+static void
+virstor_label(struct gctl_req *req)
+{
+ struct g_virstor_metadata md;
+ off_t msize;
+ unsigned char *sect;
+ unsigned int i;
+ size_t ssize, secsize;
+ const char *name;
+ char param[32];
+ int hardcode, nargs, error;
+ struct virstor_map_entry *map;
+ size_t total_chunks, write_max_map_entries;
+ unsigned int map_chunks; /* Chunks needed by the map (map size). */
+ size_t map_size; /* In bytes. */
+ ssize_t written;
+ int fd;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 2) {
+ gctl_error(req, "Too few arguments (%d): expecting: name "
+ "provider0 [provider1 ...]", nargs);
+ return;
+ }
+
+ hardcode = gctl_get_int(req, "hardcode");
+
+ /*
+ * Initialize constant parts of metadata: magic signature, version,
+ * name.
+ */
+ bzero(&md, sizeof(md));
+ strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
+ md.md_version = G_VIRSTOR_VERSION;
+ name = gctl_get_ascii(req, "arg0");
+ if (name == NULL) {
+ gctl_error(req, "No 'arg%u' argument.", 0);
+ return;
+ }
+ strlcpy(md.md_name, name, sizeof(md.md_name));
+
+ md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
+ md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
+ md.md_count = nargs - 1;
+
+ if (md.md_virsize == 0 || md.md_chunk_size == 0) {
+ gctl_error(req, "Virtual size and chunk size must be non-zero");
+ return;
+ }
+
+ msize = secsize = 0;
+ for (i = 1; i < (unsigned)nargs; i++) {
+ snprintf(param, sizeof(param), "arg%u", i);
+ name = gctl_get_ascii(req, "%s", param);
+ ssize = g_get_sectorsize(name);
+ if (ssize == 0)
+ fprintf(stderr, "%s for %s\n", strerror(errno), name);
+ msize += g_get_mediasize(name);
+ if (secsize == 0)
+ secsize = ssize;
+ else if (secsize != ssize) {
+ gctl_error(req, "Devices need to have same sector size "
+ "(%u on %s needs to be %u).",
+ (u_int)ssize, name, (u_int)secsize);
+ return;
+ }
+ }
+
+ if (secsize == 0) {
+ gctl_error(req, "Device not specified");
+ return;
+ }
+
+ if (md.md_chunk_size % secsize != 0) {
+ size_t new_size = roundup(md.md_chunk_size, secsize);
+ fprintf(stderr, "Resizing chunk size to be a multiple of "
+ "sector size (%zu bytes).\n", secsize);
+ fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
+ md.md_chunk_size = new_size;
+ }
+
+ if (md.md_virsize % md.md_chunk_size != 0) {
+ off_t chunk_count = md.md_virsize / md.md_chunk_size;
+ md.md_virsize = chunk_count * md.md_chunk_size;
+ fprintf(stderr, "Resizing virtual size to be a multiple of "
+ "chunk size.\n");
+ fprintf(stderr, "New virtual size: %zu MB\n",
+ (size_t)(md.md_virsize / (1024 * 1024)));
+ }
+
+ total_chunks = md.md_virsize / md.md_chunk_size;
+ map_size = total_chunks * sizeof(*map);
+ assert(md.md_virsize % md.md_chunk_size == 0);
+
+ ssize = map_size % secsize;
+ if (ssize != 0) {
+ size_t add_chunks = (secsize - ssize) / sizeof(*map);
+ total_chunks += add_chunks;
+ md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
+ map_size = total_chunks * sizeof(*map);
+ fprintf(stderr, "Resizing virtual size to fit virstor "
+ "structures.\n");
+ fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
+ (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
+ }
+
+ if (verbose)
+ printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
+ "virtual size.\n",
+ total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
+ md.md_virsize/(1024 * 1024));
+
+ if ((off_t)md.md_virsize < msize)
+ fprintf(stderr, "WARNING: Virtual storage size < Physical "
+ "available storage (%ju < %ju)\n", md.md_virsize, msize);
+
+ /* Clear last sector first to spoil all components if device exists. */
+ if (verbose)
+ printf("Clearing metadata on");
+
+ for (i = 1; i < (unsigned)nargs; i++) {
+ snprintf(param, sizeof(param), "arg%u", i);
+ name = gctl_get_ascii(req, "%s", param);
+
+ if (verbose)
+ printf(" %s", name);
+
+ msize = g_get_mediasize(name);
+ ssize = g_get_sectorsize(name);
+ if (msize == 0 || ssize == 0) {
+ gctl_error(req, "Can't retrieve information about "
+ "%s: %s.", name, strerror(errno));
+ return;
+ }
+ if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
+ gctl_error(req, "Device %s is too small", name);
+ error = g_metadata_clear(name, NULL);
+ if (error != 0) {
+ gctl_error(req, "Can't clear metadata on %s: %s.", name,
+ strerror(error));
+ return;
+ }
+ }
+
+
+ /* Write allocation table to the first provider - this needs to be done
+ * before metadata is written because when kernel tastes it it's too
+ * late */
+ name = gctl_get_ascii(req, "arg1"); /* device with metadata */
+ if (verbose)
+ printf(".\nWriting allocation table to %s...", name);
+
+ /* How many chunks does the map occupy? */
+ map_chunks = map_size/md.md_chunk_size;
+ if (map_size % md.md_chunk_size != 0)
+ map_chunks++;
+ if (verbose) {
+ printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
+ fflush(stdout);
+ }
+
+ if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
+ fd = open(name, O_RDWR);
+ else {
+ sprintf(param, "%s%s", _PATH_DEV, name);
+ fd = open(param, O_RDWR);
+ }
+ if (fd < 0) {
+ gctl_error(req, "Cannot open provider %s to write map", name);
+ return;
+ }
+
+ /*
+ * Initialize and write the map. Don't malloc the whole map at once,
+ * in case it's large. Use calloc because there might be a need to set
+ * up chunk flags in the future.
+ */
+ write_max_map_entries = 1024 * 1024 / sizeof(*map);
+ if (write_max_map_entries > total_chunks)
+ write_max_map_entries = total_chunks;
+ map = calloc(write_max_map_entries, sizeof(*map));
+ if (map == NULL) {
+ gctl_error(req,
+ "Out of memory (need %zu bytes for allocation map)",
+ write_max_map_entries * sizeof(*map));
+ close(fd);
+ return;
+ }
+ for (size_t chunk = 0; chunk < total_chunks;
+ chunk += write_max_map_entries) {
+ size_t bytes_to_write, entries_to_write;
+
+ entries_to_write = total_chunks - chunk;
+ if (entries_to_write > write_max_map_entries)
+ entries_to_write = write_max_map_entries;
+ bytes_to_write = entries_to_write * sizeof(*map);
+ for (size_t off = 0; off < bytes_to_write; off += written) {
+ written = write(fd, ((char *)map) + off,
+ bytes_to_write - off);
+ if (written < 0) {
+ if (verbose) {
+ fprintf(stderr,
+ "\nError writing map at offset "
+ "%zu of %zu: %s\n",
+ chunk * sizeof(*map) + off,
+ map_size, strerror(errno));
+ }
+ gctl_error(req,
+ "Error writing out allocation map!");
+ free(map);
+ close(fd);
+ return;
+ }
+ }
+ }
+ free(map);
+ map = NULL;
+ close (fd);
+
+ if (verbose)
+ printf("\nStoring metadata on ");
+
+ /*
+ * ID is randomly generated, unique for a geom. This is used to
+ * recognize all providers belonging to one geom.
+ */
+ md.md_id = arc4random();
+
+ /* Ok, store metadata. */
+ for (i = 1; i < (unsigned)nargs; i++) {
+ snprintf(param, sizeof(param), "arg%u", i);
+ name = gctl_get_ascii(req, "%s", param);
+
+ msize = g_get_mediasize(name);
+ ssize = g_get_sectorsize(name);
+
+ if (verbose)
+ printf("%s ", name);
+
+ /* this provider's position/type in geom */
+ md.no = i - 1;
+ /* this provider's size */
+ md.provsize = msize;
+ /* chunk allocation info */
+ md.chunk_count = md.provsize / md.md_chunk_size;
+ if (verbose)
+ printf("(%u chunks) ", md.chunk_count);
+ /* Check to make sure last sector is unused */
+ if ((off_t)(md.chunk_count * md.md_chunk_size) > (off_t)(msize-ssize))
+ md.chunk_count--;
+ md.chunk_next = 0;
+ if (i != 1) {
+ md.chunk_reserved = 0;
+ md.flags = 0;
+ } else {
+ md.chunk_reserved = map_chunks * 2;
+ md.flags = VIRSTOR_PROVIDER_ALLOCATED |
+ VIRSTOR_PROVIDER_CURRENT;
+ md.chunk_next = md.chunk_reserved;
+ if (verbose)
+ printf("(%u reserved) ", md.chunk_reserved);
+ }
+
+ if (!hardcode)
+ bzero(md.provider, sizeof(md.provider));
+ else {
+ /* convert "/dev/something" to "something" */
+ if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) {
+ strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1,
+ sizeof(md.provider));
+ } else
+ strlcpy(md.provider, name, sizeof(md.provider));
+ }
+ sect = calloc(ssize, sizeof(unsigned char));
+ if (sect == NULL)
+ err(1, "Cannot allocate sector of %zu bytes", ssize);
+ virstor_metadata_encode(&md, sect);
+ error = g_metadata_store(name, sect, ssize);
+ free(sect);
+ if (error != 0) {
+ if (verbose)
+ printf("\n");
+ fprintf(stderr, "Can't store metadata on %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req,
+ "Not fully done (error storing metadata).");
+ return;
+ }
+ }
+#if 0
+ if (verbose)
+ printf("\n");
+#endif
+}
+
+/* Clears metadata on given provider(s) IF it's owned by us */
+static void
+virstor_clear(struct gctl_req *req)
+{
+ const char *name;
+ char param[32];
+ unsigned i;
+ int nargs, error;
+ int fd;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ for (i = 0; i < (unsigned)nargs; i++) {
+ snprintf(param, sizeof(param), "arg%u", i);
+ name = gctl_get_ascii(req, "%s", param);
+
+ error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't clear metadata on %s: %s "
+ "(do I own it?)\n", name, strerror(error));
+ gctl_error(req,
+ "Not fully done (can't clear metadata).");
+ continue;
+ }
+ if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
+ fd = open(name, O_RDWR);
+ else {
+ sprintf(param, "%s%s", _PATH_DEV, name);
+ fd = open(param, O_RDWR);
+ }
+ if (fd < 0) {
+ gctl_error(req, "Cannot clear header sector for %s",
+ name);
+ continue;
+ }
+ if (verbose)
+ printf("Metadata cleared on %s.\n", name);
+ }
+}
+
+/* Print some metadata information */
+static void
+virstor_metadata_dump(const struct g_virstor_metadata *md)
+{
+ printf(" Magic string: %s\n", md->md_magic);
+ printf(" Metadata version: %u\n", (u_int) md->md_version);
+ printf(" Device name: %s\n", md->md_name);
+ printf(" Device ID: %u\n", (u_int) md->md_id);
+ printf(" Provider index: %u\n", (u_int) md->no);
+ printf(" Active providers: %u\n", (u_int) md->md_count);
+ printf(" Hardcoded provider: %s\n",
+ md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
+ printf(" Virtual size: %u MB\n",
+ (unsigned int)(md->md_virsize/(1024 * 1024)));
+ printf(" Chunk size: %u kB\n", md->md_chunk_size / 1024);
+ printf(" Chunks on provider: %u\n", md->chunk_count);
+ printf(" Chunks free: %u\n", md->chunk_count - md->chunk_next);
+ printf(" Reserved chunks: %u\n", md->chunk_reserved);
+}
+
+/* Called by geom(8) via gvirstor_main() to dump metadata information */
+static void
+virstor_dump(struct gctl_req *req)
+{
+ struct g_virstor_metadata md;
+ u_char tmpmd[512]; /* temporary buffer */
+ const char *name;
+ char param[16];
+ int nargs, error, i;
+
+ assert(sizeof(tmpmd) >= sizeof(md));
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs < 1) {
+ gctl_error(req, "Too few arguments.");
+ return;
+ }
+ for (i = 0; i < nargs; i++) {
+ snprintf(param, sizeof(param), "arg%u", i);
+ name = gctl_get_ascii(req, "%s", param);
+
+ error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
+ G_VIRSTOR_MAGIC);
+ if (error != 0) {
+ fprintf(stderr, "Can't read metadata from %s: %s.\n",
+ name, strerror(error));
+ gctl_error(req,
+ "Not fully done (error reading metadata).");
+ continue;
+ }
+ virstor_metadata_decode((u_char *) & tmpmd, &md);
+ printf("Metadata on %s:\n", name);
+ virstor_metadata_dump(&md);
+ printf("\n");
+ }
+}
diff --git a/lib/geom/virstor/gvirstor.8 b/lib/geom/virstor/gvirstor.8
new file mode 100644
index 000000000000..436b60bef02c
--- /dev/null
+++ b/lib/geom/virstor/gvirstor.8
@@ -0,0 +1,297 @@
+.\" Copyright (c) 2006-2011 Ivan Voras <ivoras@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd October 1, 2013
+.Dt GVIRSTOR 8
+.Os
+.Sh NAME
+.Nm gvirstor
+.Nd "control utility for virtual data storage devices"
+.Sh SYNOPSIS
+.Nm
+.Cm label
+.Op Fl hv
+.Op Fl s Ar virsize
+.Op Fl m Ar chunksize
+.Ar name
+.Ar prov ...
+.Nm
+.Cm stop
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm destroy
+.Op Fl fv
+.Ar name ...
+.Nm
+.Cm add
+.Op Fl vh
+.Ar name prov ...
+.Nm
+.Cm remove
+.Op Fl v
+.Ar name prov ...
+.Nm
+.Cm clear
+.Op Fl v
+.Ar prov ...
+.Nm
+.Cm dump
+.Ar prov ...
+.Nm
+.Cm list
+.Nm
+.Cm status
+.Nm
+.Cm load
+.Nm
+.Cm unload
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for setting up a virtual storage device of arbitrary
+large size
+.Pq for example, several TB ,
+consisting of an arbitrary number of physical storage devices with the
+total size which is equal to or smaller than the virtual size.
+Data for the virtual devices will be allocated from physical devices on
+demand.
+The idea behind
+.Nm
+is similar to the concept of Virtual Memory in operating systems,
+effectively allowing users to overcommit on storage
+.Pq free file system space .
+The concept is also known as "thin provisioning" in virtualization
+environments, only here it is implemented on the level of physical storage
+devices.
+.Pp
+The first argument to
+.Nm
+indicates an action to be performed:
+.Bl -tag -width ".Cm remove"
+.It Cm label
+Set up a virtual device from the given components with the specified
+.Ar name .
+Metadata is stored in the last sector of every component.
+Argument
+.Fl s Ar virsize
+is the size of new virtual device, with default being set to 2 TiB
+.Pq 2097152 MiB .
+Argument
+.Fl m Ar chunksize
+is the chunk size, with default being set to 4 MiB
+.Pq 4096 KiB .
+The default arguments are thus
+.Qq Fl s Ar 2097152 Fl m Ar 4096 .
+.It Cm stop
+Turn off an existing virtual device with the given
+.Ar name .
+This command does not touch on-disk metadata.
+As with other GEOM classes, stopped geoms cannot be started manually.
+.It Cm destroy
+Same as
+.Cm stop.
+.It Cm add
+Adds new components to existing virtual device with the given
+.Ar name .
+The specified virstor device must exist and be active
+.Pq i.e. module loaded, device present in Pa /dev .
+This action can be safely performed while the virstor device is in use
+.Pq Qo hot Qc operation .
+.It Cm remove
+Removes components from existing virtual device with the given
+.Ar name .
+Only unallocated providers can be removed.
+.It Cm clear
+Clear metadata on the given providers.
+.It Cm dump
+Dump metadata stored on the given providers.
+.It Cm list
+See
+.Xr geom 8 .
+.It Cm status
+See
+.Xr geom 8 .
+.It Cm load
+See
+.Xr geom 8 .
+.It Cm unload
+See
+.Xr geom 8 .
+.El
+.Pp
+Additional options:
+.Bl -tag -width ".Fl f"
+.It Fl f
+Force the removal of the specified virtual device.
+.It Fl h
+Hardcode providers' names in metadata.
+.It Fl v
+Be more verbose.
+.El
+.Sh EXAMPLES
+The following example shows how to create a virtual device of default size
+.Pq 2 TiB ,
+of default chunk
+.Pq extent
+size
+.Pq 4 MiB ,
+with two physical devices for backing storage.
+.Bd -literal -offset indent
+.No gvirstor label -v Ar mydata Ar /dev/ada4 Ar /dev/ada6
+.No newfs Ar /dev/virstor/mydata
+.Ed
+.Pp
+From now on, the virtual device will be available via the
+.Pa /dev/virstor/mydata
+device entry.
+To add a new physical device / component to an active virstor device:
+.Bd -literal -offset indent
+.No gvirstor add Ar mydata Ar ada8
+.Ed
+.Pp
+This will add physical storage of
+.Ar ada8
+to
+.Pa /dev/virstor/mydata
+device.
+.Pp
+To see the device status information
+.Pq including how much physical storage is still available for the virtual device ,
+use:
+.Bd -literal -offset indent
+gvirstor list
+.Ed
+.Pp
+All standard
+.Xr geom 8
+subcommands
+.Pq e.g. Cm status , Cm help
+are also supported.
+.Sh SYSCTL VARIABLES
+.Nm
+has several
+.Xr sysctl 8
+tunable variables.
+.Bd -literal -offset indent
+.Va int kern.geom.virstor.debug
+.Ed
+.Pp
+This sysctl controls verbosity of the kernel module, in the range
+1 to 15.
+Messages that are marked with higher verbosity levels than this are
+suppressed.
+Default value is 5 and it is not recommended to set this tunable to less
+than 2, because level 1 messages are error events, and level 2 messages
+are system warnings.
+.Bd -literal -offset indent
+.Va int kern.geom.virstor.chunk_watermark
+.Ed
+.Pp
+Value in this sysctl sets warning watermark level for physical chunk
+usage on a single component.
+The warning is issued when a virstor component has less than this many
+free chunks
+.Pq default 100 .
+.Bd -literal -offset indent
+.Va int kern.geom.virstor.component_watermark
+.Ed
+.Pp
+Value in this sysctl sets warning watermark level for component usage.
+The warning is issued when there are less than this many unallocated
+components
+.Pq default is 1 .
+.Pp
+All these sysctls are also available as
+.Xr loader 8
+tunables.
+.Sh DIAGNOSTICS
+.Ex -std
+.Pp
+.Nm
+kernel module issues log messages with prefixes in standardized format,
+which is useful for log message filtering and dispatching.
+Each message line begins with
+.Bd -literal -offset indent
+.Li GEOM_VIRSTOR[%d]:
+.Ed
+.Pp
+The number
+.Pq %d
+is message verbosity / importance level, in the range 1 to 15.
+If a message filtering, dispatching or operator alert system is used, it
+is recommended that messages with levels 1 and 2 be taken seriously
+.Pq for example, to catch out-of-space conditions as set by watermark
+sysctls.
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr fstab 5 ,
+.Xr geom 8 ,
+.Xr glabel 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+.An Ivan Voras Aq Mt ivoras@FreeBSD.org
+.Pp
+Sponsored by Google Summer of Code 2006.
+.Sh BUGS
+Commands
+.Cm add
+and
+.Cm remove
+contain unavoidable critical sections which may make the virstor
+device unusable if a power failure
+.Pq or other disruptive event
+happens during their execution.
+It is recommended to run them when the system is quiescent.
+.Sh ASSUMPTIONS AND INTERACTION WITH FILE SYSTEMS
+There are several assumptions that
+.Nm
+has in its operation: that the size of the virtual storage device will not
+change once it is set, and that the sizes of individual physical storage
+components will always remain constant during their existence.
+For alternative ways to implement virtual or resizable file systems see
+.Xr zfs 1M ,
+.Xr gconcat 8
+and
+.Xr growfs 8 .
+.Pp
+Note that
+.Nm
+has nontrivial interaction with file systems which initialize a large
+number of on-disk structures during newfs.
+If such file systems attempt to spread their structures across the drive
+media
+.Pq like UFS/UFS2 does ,
+their efforts will be effectively foiled by sequential allocation of
+chunks in
+.Nm
+and all their structures will be physically allocated at the start
+of the first virstor component.
+This could have a significant impact on file system performance
+.Pq which can in some rare cases be even positive .