geom_virstor.c revision 213662
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: head/sbin/geom/class/virstor/geom_virstor.c 213662 2010-10-09 20:20:27Z ae $");
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	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
187	    (ssize_t)sectorsize) {
188		error = errno;
189		goto out;
190	}
191out:
192	if (sector != NULL)
193		free(sector);
194	close(fd);
195	return (error);
196}
197
198/*
199 * Labels a new geom Meaning: parses and checks the parameters, calculates &
200 * writes metadata to the relevant providers so when the next round of
201 * "tasting" comes (which will be just after the provider(s) are closed) geom
202 * can be instantiated with the tasted metadata.
203 */
204static void
205virstor_label(struct gctl_req *req)
206{
207	struct g_virstor_metadata md;
208	off_t msize;
209	unsigned char *sect;
210	unsigned int i;
211	size_t ssize, secsize;
212	const char *name;
213	char param[32];
214	int hardcode, nargs, error;
215	struct virstor_map_entry *map;
216	size_t total_chunks;	/* We'll run out of memory if
217				   this needs to be bigger. */
218	unsigned int map_chunks; /* Chunks needed by the map (map size). */
219	size_t map_size;	/* In bytes. */
220	ssize_t written;
221	int fd;
222
223	nargs = gctl_get_int(req, "nargs");
224	if (nargs < 2) {
225		gctl_error(req, "Too few arguments (%d): expecting: name "
226		    "provider0 [provider1 ...]", nargs);
227		return;
228	}
229
230	hardcode = gctl_get_int(req, "hardcode");
231
232	/*
233	 * Initialize constant parts of metadata: magic signature, version,
234	 * name.
235	 */
236	bzero(&md, sizeof(md));
237	strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
238	md.md_version = G_VIRSTOR_VERSION;
239	name = gctl_get_ascii(req, "arg0");
240	if (name == NULL) {
241		gctl_error(req, "No 'arg%u' argument.", 0);
242		return;
243	}
244	strlcpy(md.md_name, name, sizeof(md.md_name));
245
246	md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
247	md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
248	md.md_count = nargs - 1;
249
250	if (md.md_virsize == 0 || md.md_chunk_size == 0) {
251		gctl_error(req, "Virtual size and chunk size must be non-zero");
252		return;
253	}
254
255	if (md.md_chunk_size % MAXPHYS != 0) {
256		/* XXX: This is not strictly needed, but it's convenient to
257		 * impose some limitations on it, so why not MAXPHYS. */
258		size_t new_size = (md.md_chunk_size / MAXPHYS) * MAXPHYS;
259		if (new_size < md.md_chunk_size)
260			new_size += MAXPHYS;
261		fprintf(stderr, "Resizing chunk size to be a multiple of "
262		    "MAXPHYS (%d kB).\n", MAXPHYS / 1024);
263		fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
264		md.md_chunk_size = new_size;
265	}
266
267	if (md.md_virsize % md.md_chunk_size != 0) {
268		off_t chunk_count = md.md_virsize / md.md_chunk_size;
269		md.md_virsize = chunk_count * md.md_chunk_size;
270		fprintf(stderr, "Resizing virtual size to be a multiple of "
271		    "chunk size.\n");
272		fprintf(stderr, "New virtual size: %zu MB\n",
273		    (size_t)(md.md_virsize/(1024 * 1024)));
274	}
275
276	msize = secsize = 0;
277	for (i = 1; i < (unsigned)nargs; i++) {
278		snprintf(param, sizeof(param), "arg%u", i);
279		name = gctl_get_ascii(req, param);
280		ssize = g_get_sectorsize(name);
281		if (ssize == 0)
282			fprintf(stderr, "%s for %s\n", strerror(errno), name);
283		msize += g_get_mediasize(name);
284		if (secsize == 0)
285			secsize = ssize;
286		else if (secsize != ssize) {
287			gctl_error(req, "Devices need to have same sector size "
288			    "(%u on %s needs to be %u).",
289			    (u_int)ssize, name, (u_int)secsize);
290			return;
291		}
292	}
293
294	if (secsize == 0) {
295		gctl_error(req, "Device not specified");
296		return;
297	}
298
299	if (md.md_chunk_size % secsize != 0) {
300		fprintf(stderr, "Error: chunk size is not a multiple of sector "
301		    "size.");
302		gctl_error(req, "Chunk size (in bytes) must be multiple of %u.",
303		    (unsigned int)secsize);
304		return;
305	}
306
307	total_chunks = md.md_virsize / md.md_chunk_size;
308	map_size = total_chunks * sizeof(*map);
309	assert(md.md_virsize % md.md_chunk_size == 0);
310
311	ssize = map_size % secsize;
312	if (ssize != 0) {
313		size_t add_chunks = (secsize - ssize) / sizeof(*map);
314		total_chunks += add_chunks;
315		md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
316		map_size = total_chunks * sizeof(*map);
317		fprintf(stderr, "Resizing virtual size to fit virstor "
318		    "structures.\n");
319		fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
320		    (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
321	}
322
323	if (verbose)
324		printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
325		    "virtual size.\n",
326		    total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
327		    md.md_virsize/(1024 * 1024));
328
329	if ((off_t)md.md_virsize < msize)
330		fprintf(stderr, "WARNING: Virtual storage size < Physical "
331		    "available storage (%ju < %ju)\n", md.md_virsize, msize);
332
333	/* Clear last sector first to spoil all components if device exists. */
334	if (verbose)
335		printf("Clearing metadata on");
336
337	for (i = 1; i < (unsigned)nargs; i++) {
338		snprintf(param, sizeof(param), "arg%u", i);
339		name = gctl_get_ascii(req, param);
340
341		if (verbose)
342			printf(" %s", name);
343
344		msize = g_get_mediasize(name);
345		ssize = g_get_sectorsize(name);
346		if (msize == 0 || ssize == 0) {
347			gctl_error(req, "Can't retrieve information about "
348			    "%s: %s.", name, strerror(errno));
349			return;
350		}
351		if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
352			gctl_error(req, "Device %s is too small", name);
353		error = g_metadata_clear(name, NULL);
354		if (error != 0) {
355			gctl_error(req, "Can't clear metadata on %s: %s.", name,
356			    strerror(error));
357			return;
358		}
359	}
360
361
362	/* Write allocation table to the first provider - this needs to be done
363	 * before metadata is written because when kernel tastes it it's too
364	 * late */
365	name = gctl_get_ascii(req, "arg1"); /* device with metadata */
366	if (verbose)
367		printf(".\nWriting allocation table to %s...", name);
368
369	/* How many chunks does the map occupy? */
370	map_chunks = map_size/md.md_chunk_size;
371	if (map_size % md.md_chunk_size != 0)
372		map_chunks++;
373	if (verbose) {
374		printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
375		fflush(stdout);
376	}
377
378	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
379		fd = open(name, O_RDWR);
380	else {
381		sprintf(param, "%s%s", _PATH_DEV, name);
382		fd = open(param, O_RDWR);
383	}
384	if (fd < 0)
385		gctl_error(req, "Cannot open provider %s to write map", name);
386
387	/* Do it with calloc because there might be a need to set up chunk flags
388	 * in the future */
389	map = calloc(total_chunks, sizeof(*map));
390	if (map == NULL) {
391		gctl_error(req,
392		    "Out of memory (need %zu bytes for allocation map)",
393		    map_size);
394	}
395
396	written = pwrite(fd, map, map_size, 0);
397	free(map);
398	if ((size_t)written != map_size) {
399		if (verbose) {
400			fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n",
401			    map_size, written, strerror(errno));
402		}
403		gctl_error(req, "Error writing out allocation map!");
404		return;
405	}
406	close (fd);
407
408	if (verbose)
409		printf("\nStoring metadata on ");
410
411	/*
412	 * ID is randomly generated, unique for a geom. This is used to
413	 * recognize all providers belonging to one geom.
414	 */
415	md.md_id = arc4random();
416
417	/* Ok, store metadata. */
418	for (i = 1; i < (unsigned)nargs; i++) {
419		snprintf(param, sizeof(param), "arg%u", i);
420		name = gctl_get_ascii(req, param);
421
422		msize = g_get_mediasize(name);
423		ssize = g_get_sectorsize(name);
424
425		if (verbose)
426			printf("%s ", name);
427
428		/* this provider's position/type in geom */
429		md.no = i - 1;
430		/* this provider's size */
431		md.provsize = msize;
432		/* chunk allocation info */
433		md.chunk_count = md.provsize / md.md_chunk_size;
434		if (verbose)
435			printf("(%u chunks) ", md.chunk_count);
436		/* Check to make sure last sector is unused */
437		if ((off_t)(md.chunk_count * md.md_chunk_size) > msize-ssize)
438		    md.chunk_count--;
439		md.chunk_next = 0;
440		if (i != 1) {
441			md.chunk_reserved = 0;
442			md.flags = 0;
443		} else {
444			md.chunk_reserved = map_chunks * 2;
445			md.flags = VIRSTOR_PROVIDER_ALLOCATED |
446			    VIRSTOR_PROVIDER_CURRENT;
447			md.chunk_next = md.chunk_reserved;
448			if (verbose)
449				printf("(%u reserved) ", md.chunk_reserved);
450		}
451
452		if (!hardcode)
453			bzero(md.provider, sizeof(md.provider));
454		else {
455			/* convert "/dev/something" to "something" */
456			if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) {
457				strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1,
458				    sizeof(md.provider));
459			} else
460				strlcpy(md.provider, name, sizeof(md.provider));
461		}
462		sect = malloc(ssize);
463		if (sect == NULL)
464			err(1, "Cannot allocate sector of %zu bytes", ssize);
465		bzero(sect, ssize);
466		virstor_metadata_encode(&md, sect);
467		error = my_g_metadata_store(name, sect, ssize);
468		free(sect);
469		if (error != 0) {
470			if (verbose)
471				printf("\n");
472			fprintf(stderr, "Can't store metadata on %s: %s.\n",
473			    name, strerror(error));
474			gctl_error(req,
475			    "Not fully done (error storing metadata).");
476			return;
477		}
478	}
479#if 0
480	if (verbose)
481		printf("\n");
482#endif
483}
484
485/* Clears metadata on given provider(s) IF it's owned by us */
486static void
487virstor_clear(struct gctl_req *req)
488{
489	const char *name;
490	char param[32];
491	unsigned i;
492	int nargs, error;
493	int fd;
494
495	nargs = gctl_get_int(req, "nargs");
496	if (nargs < 1) {
497		gctl_error(req, "Too few arguments.");
498		return;
499	}
500	for (i = 0; i < (unsigned)nargs; i++) {
501		snprintf(param, sizeof(param), "arg%u", i);
502		name = gctl_get_ascii(req, param);
503
504		error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
505		if (error != 0) {
506			fprintf(stderr, "Can't clear metadata on %s: %s "
507			    "(do I own it?)\n", name, strerror(error));
508			gctl_error(req,
509			    "Not fully done (can't clear metadata).");
510			continue;
511		}
512		if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
513			fd = open(name, O_RDWR);
514		else {
515			sprintf(param, "%s%s", _PATH_DEV, name);
516			fd = open(param, O_RDWR);
517		}
518		if (fd < 0) {
519			gctl_error(req, "Cannot clear header sector for %s",
520			    name);
521			continue;
522		}
523		if (verbose)
524			printf("Metadata cleared on %s.\n", name);
525	}
526}
527
528/* Print some metadata information */
529static void
530virstor_metadata_dump(const struct g_virstor_metadata *md)
531{
532	printf("          Magic string: %s\n", md->md_magic);
533	printf("      Metadata version: %u\n", (u_int) md->md_version);
534	printf("           Device name: %s\n", md->md_name);
535	printf("             Device ID: %u\n", (u_int) md->md_id);
536	printf("        Provider index: %u\n", (u_int) md->no);
537	printf("      Active providers: %u\n", (u_int) md->md_count);
538	printf("    Hardcoded provider: %s\n",
539	    md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
540	printf("          Virtual size: %u MB\n",
541	    (unsigned int)(md->md_virsize/(1024 * 1024)));
542	printf("            Chunk size: %u kB\n", md->md_chunk_size / 1024);
543	printf("    Chunks on provider: %u\n", md->chunk_count);
544	printf("           Chunks free: %u\n", md->chunk_count - md->chunk_next);
545	printf("       Reserved chunks: %u\n", md->chunk_reserved);
546}
547
548/* Called by geom(8) via gvirstor_main() to dump metadata information */
549static void
550virstor_dump(struct gctl_req *req)
551{
552	struct g_virstor_metadata md;
553	u_char tmpmd[512];	/* temporary buffer */
554	const char *name;
555	char param[16];
556	int nargs, error, i;
557
558	assert(sizeof(tmpmd) >= sizeof(md));
559
560	nargs = gctl_get_int(req, "nargs");
561	if (nargs < 1) {
562		gctl_error(req, "Too few arguments.");
563		return;
564	}
565	for (i = 0; i < nargs; i++) {
566		snprintf(param, sizeof(param), "arg%u", i);
567		name = gctl_get_ascii(req, param);
568
569		error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
570		    G_VIRSTOR_MAGIC);
571		if (error != 0) {
572			fprintf(stderr, "Can't read metadata from %s: %s.\n",
573			    name, strerror(error));
574			gctl_error(req,
575			    "Not fully done (error reading metadata).");
576			continue;
577		}
578		virstor_metadata_decode((u_char *) & tmpmd, &md);
579		printf("Metadata on %s:\n", name);
580		virstor_metadata_dump(&md);
581		printf("\n");
582	}
583}
584