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