xref: /freebsd/lib/geom/virstor/geom_virstor.c (revision 6829dae12bb055451fa467da4589c43bd03b1e64)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2005 Ivan Voras <ivoras@freebsd.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <errno.h>
33 #include <paths.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdint.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <libgeom.h>
42 #include <err.h>
43 #include <assert.h>
44 
45 #include <core/geom.h>
46 #include <misc/subr.h>
47 
48 #include <geom/virstor/g_virstor_md.h>
49 #include <geom/virstor/g_virstor.h>
50 
51 uint32_t lib_version = G_LIB_VERSION;
52 uint32_t version = G_VIRSTOR_VERSION;
53 
54 #define	GVIRSTOR_CHUNK_SIZE	"4M"
55 #define	GVIRSTOR_VIR_SIZE	"2T"
56 
57 #if G_LIB_VERSION == 1
58 /* Support RELENG_6 */
59 #define G_TYPE_BOOL G_TYPE_NONE
60 #endif
61 
62 /*
63  * virstor_main gets called by the geom(8) utility
64  */
65 static void virstor_main(struct gctl_req *req, unsigned flags);
66 
67 struct g_command class_commands[] = {
68 	{ "clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS,
69 	    "[-v] prov ..."
70 	},
71 	{ "dump", 0, virstor_main, G_NULL_OPTS,
72 	    "prov ..."
73 	},
74 	{ "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main,
75 	    {
76 		{ 'h', "hardcode", NULL, G_TYPE_BOOL},
77 		{ 'm', "chunk_size", GVIRSTOR_CHUNK_SIZE, G_TYPE_NUMBER},
78 		{ 's', "vir_size", GVIRSTOR_VIR_SIZE, G_TYPE_NUMBER},
79 		G_OPT_SENTINEL
80 	    },
81 	    "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]"
82 	},
83 	{ "destroy", G_FLAG_VERBOSE, NULL,
84 	    {
85 		{ 'f', "force", NULL, G_TYPE_BOOL},
86 		G_OPT_SENTINEL
87 	    },
88 	    "[-fv] name ..."
89 	},
90 	{ "stop", G_FLAG_VERBOSE, NULL,
91 	    {
92 		{ 'f', "force", NULL, G_TYPE_BOOL},
93 		G_OPT_SENTINEL
94 	    },
95 	    "[-fv] name ... (alias for \"destroy\")"
96 	},
97 	{ "add", G_FLAG_VERBOSE, NULL,
98 	    {
99 		{ 'h', "hardcode", NULL, G_TYPE_BOOL},
100 		G_OPT_SENTINEL
101 	    },
102 	    "[-vh] name prov [prov ...]"
103 	},
104 	{ "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
105 	    "[-v] name ..."
106 	},
107 	G_CMD_SENTINEL
108 };
109 
110 static int verbose = 0;
111 
112 /* Helper functions' declarations */
113 static void virstor_clear(struct gctl_req *req);
114 static void virstor_dump(struct gctl_req *req);
115 static void virstor_label(struct gctl_req *req);
116 
117 /* Dispatcher function (no real work done here, only verbose flag recorder) */
118 static void
119 virstor_main(struct gctl_req *req, unsigned flags)
120 {
121 	const char *name;
122 
123 	if ((flags & G_FLAG_VERBOSE) != 0)
124 		verbose = 1;
125 
126 	name = gctl_get_ascii(req, "verb");
127 	if (name == NULL) {
128 		gctl_error(req, "No '%s' argument.", "verb");
129 		return;
130 	}
131 	if (strcmp(name, "label") == 0)
132 		virstor_label(req);
133 	else if (strcmp(name, "clear") == 0)
134 		virstor_clear(req);
135 	else if (strcmp(name, "dump") == 0)
136 		virstor_dump(req);
137 	else
138 		gctl_error(req, "%s: Unknown command: %s.", __func__, name);
139 
140 	/* No CTASSERT in userland
141 	CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS);
142 	*/
143 }
144 
145 /*
146  * Labels a new geom Meaning: parses and checks the parameters, calculates &
147  * writes metadata to the relevant providers so when the next round of
148  * "tasting" comes (which will be just after the provider(s) are closed) geom
149  * can be instantiated with the tasted metadata.
150  */
151 static void
152 virstor_label(struct gctl_req *req)
153 {
154 	struct g_virstor_metadata md;
155 	off_t msize;
156 	unsigned char *sect;
157 	unsigned int i;
158 	size_t ssize, secsize;
159 	const char *name;
160 	char param[32];
161 	int hardcode, nargs, error;
162 	struct virstor_map_entry *map;
163 	size_t total_chunks;	/* We'll run out of memory if
164 				   this needs to be bigger. */
165 	unsigned int map_chunks; /* Chunks needed by the map (map size). */
166 	size_t map_size;	/* In bytes. */
167 	ssize_t written;
168 	int fd;
169 
170 	nargs = gctl_get_int(req, "nargs");
171 	if (nargs < 2) {
172 		gctl_error(req, "Too few arguments (%d): expecting: name "
173 		    "provider0 [provider1 ...]", nargs);
174 		return;
175 	}
176 
177 	hardcode = gctl_get_int(req, "hardcode");
178 
179 	/*
180 	 * Initialize constant parts of metadata: magic signature, version,
181 	 * name.
182 	 */
183 	bzero(&md, sizeof(md));
184 	strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
185 	md.md_version = G_VIRSTOR_VERSION;
186 	name = gctl_get_ascii(req, "arg0");
187 	if (name == NULL) {
188 		gctl_error(req, "No 'arg%u' argument.", 0);
189 		return;
190 	}
191 	strlcpy(md.md_name, name, sizeof(md.md_name));
192 
193 	md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
194 	md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
195 	md.md_count = nargs - 1;
196 
197 	if (md.md_virsize == 0 || md.md_chunk_size == 0) {
198 		gctl_error(req, "Virtual size and chunk size must be non-zero");
199 		return;
200 	}
201 
202 	if (md.md_chunk_size % MAXPHYS != 0) {
203 		/* XXX: This is not strictly needed, but it's convenient to
204 		 * impose some limitations on it, so why not MAXPHYS. */
205 		size_t new_size = rounddown(md.md_chunk_size, MAXPHYS);
206 		if (new_size < md.md_chunk_size)
207 			new_size += MAXPHYS;
208 		fprintf(stderr, "Resizing chunk size to be a multiple of "
209 		    "MAXPHYS (%d kB).\n", MAXPHYS / 1024);
210 		fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
211 		md.md_chunk_size = new_size;
212 	}
213 
214 	if (md.md_virsize % md.md_chunk_size != 0) {
215 		off_t chunk_count = md.md_virsize / md.md_chunk_size;
216 		md.md_virsize = chunk_count * md.md_chunk_size;
217 		fprintf(stderr, "Resizing virtual size to be a multiple of "
218 		    "chunk size.\n");
219 		fprintf(stderr, "New virtual size: %zu MB\n",
220 		    (size_t)(md.md_virsize/(1024 * 1024)));
221 	}
222 
223 	msize = secsize = 0;
224 	for (i = 1; i < (unsigned)nargs; i++) {
225 		snprintf(param, sizeof(param), "arg%u", i);
226 		name = gctl_get_ascii(req, "%s", param);
227 		ssize = g_get_sectorsize(name);
228 		if (ssize == 0)
229 			fprintf(stderr, "%s for %s\n", strerror(errno), name);
230 		msize += g_get_mediasize(name);
231 		if (secsize == 0)
232 			secsize = ssize;
233 		else if (secsize != ssize) {
234 			gctl_error(req, "Devices need to have same sector size "
235 			    "(%u on %s needs to be %u).",
236 			    (u_int)ssize, name, (u_int)secsize);
237 			return;
238 		}
239 	}
240 
241 	if (secsize == 0) {
242 		gctl_error(req, "Device not specified");
243 		return;
244 	}
245 
246 	if (md.md_chunk_size % secsize != 0) {
247 		fprintf(stderr, "Error: chunk size is not a multiple of sector "
248 		    "size.");
249 		gctl_error(req, "Chunk size (in bytes) must be multiple of %u.",
250 		    (unsigned int)secsize);
251 		return;
252 	}
253 
254 	total_chunks = md.md_virsize / md.md_chunk_size;
255 	map_size = total_chunks * sizeof(*map);
256 	assert(md.md_virsize % md.md_chunk_size == 0);
257 
258 	ssize = map_size % secsize;
259 	if (ssize != 0) {
260 		size_t add_chunks = (secsize - ssize) / sizeof(*map);
261 		total_chunks += add_chunks;
262 		md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
263 		map_size = total_chunks * sizeof(*map);
264 		fprintf(stderr, "Resizing virtual size to fit virstor "
265 		    "structures.\n");
266 		fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
267 		    (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
268 	}
269 
270 	if (verbose)
271 		printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
272 		    "virtual size.\n",
273 		    total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
274 		    md.md_virsize/(1024 * 1024));
275 
276 	if ((off_t)md.md_virsize < msize)
277 		fprintf(stderr, "WARNING: Virtual storage size < Physical "
278 		    "available storage (%ju < %ju)\n", md.md_virsize, msize);
279 
280 	/* Clear last sector first to spoil all components if device exists. */
281 	if (verbose)
282 		printf("Clearing metadata on");
283 
284 	for (i = 1; i < (unsigned)nargs; i++) {
285 		snprintf(param, sizeof(param), "arg%u", i);
286 		name = gctl_get_ascii(req, "%s", param);
287 
288 		if (verbose)
289 			printf(" %s", name);
290 
291 		msize = g_get_mediasize(name);
292 		ssize = g_get_sectorsize(name);
293 		if (msize == 0 || ssize == 0) {
294 			gctl_error(req, "Can't retrieve information about "
295 			    "%s: %s.", name, strerror(errno));
296 			return;
297 		}
298 		if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
299 			gctl_error(req, "Device %s is too small", name);
300 		error = g_metadata_clear(name, NULL);
301 		if (error != 0) {
302 			gctl_error(req, "Can't clear metadata on %s: %s.", name,
303 			    strerror(error));
304 			return;
305 		}
306 	}
307 
308 
309 	/* Write allocation table to the first provider - this needs to be done
310 	 * before metadata is written because when kernel tastes it it's too
311 	 * late */
312 	name = gctl_get_ascii(req, "arg1"); /* device with metadata */
313 	if (verbose)
314 		printf(".\nWriting allocation table to %s...", name);
315 
316 	/* How many chunks does the map occupy? */
317 	map_chunks = map_size/md.md_chunk_size;
318 	if (map_size % md.md_chunk_size != 0)
319 		map_chunks++;
320 	if (verbose) {
321 		printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
322 		fflush(stdout);
323 	}
324 
325 	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
326 		fd = open(name, O_RDWR);
327 	else {
328 		sprintf(param, "%s%s", _PATH_DEV, name);
329 		fd = open(param, O_RDWR);
330 	}
331 	if (fd < 0)
332 		gctl_error(req, "Cannot open provider %s to write map", name);
333 
334 	/* Do it with calloc because there might be a need to set up chunk flags
335 	 * in the future */
336 	map = calloc(total_chunks, sizeof(*map));
337 	if (map == NULL) {
338 		gctl_error(req,
339 		    "Out of memory (need %zu bytes for allocation map)",
340 		    map_size);
341 	}
342 
343 	written = pwrite(fd, map, map_size, 0);
344 	free(map);
345 	if ((size_t)written != map_size) {
346 		if (verbose) {
347 			fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n",
348 			    map_size, written, strerror(errno));
349 		}
350 		gctl_error(req, "Error writing out allocation map!");
351 		return;
352 	}
353 	close (fd);
354 
355 	if (verbose)
356 		printf("\nStoring metadata on ");
357 
358 	/*
359 	 * ID is randomly generated, unique for a geom. This is used to
360 	 * recognize all providers belonging to one geom.
361 	 */
362 	md.md_id = arc4random();
363 
364 	/* Ok, store metadata. */
365 	for (i = 1; i < (unsigned)nargs; i++) {
366 		snprintf(param, sizeof(param), "arg%u", i);
367 		name = gctl_get_ascii(req, "%s", param);
368 
369 		msize = g_get_mediasize(name);
370 		ssize = g_get_sectorsize(name);
371 
372 		if (verbose)
373 			printf("%s ", name);
374 
375 		/* this provider's position/type in geom */
376 		md.no = i - 1;
377 		/* this provider's size */
378 		md.provsize = msize;
379 		/* chunk allocation info */
380 		md.chunk_count = md.provsize / md.md_chunk_size;
381 		if (verbose)
382 			printf("(%u chunks) ", md.chunk_count);
383 		/* Check to make sure last sector is unused */
384 		if ((off_t)(md.chunk_count * md.md_chunk_size) > (off_t)(msize-ssize))
385 		    md.chunk_count--;
386 		md.chunk_next = 0;
387 		if (i != 1) {
388 			md.chunk_reserved = 0;
389 			md.flags = 0;
390 		} else {
391 			md.chunk_reserved = map_chunks * 2;
392 			md.flags = VIRSTOR_PROVIDER_ALLOCATED |
393 			    VIRSTOR_PROVIDER_CURRENT;
394 			md.chunk_next = md.chunk_reserved;
395 			if (verbose)
396 				printf("(%u reserved) ", md.chunk_reserved);
397 		}
398 
399 		if (!hardcode)
400 			bzero(md.provider, sizeof(md.provider));
401 		else {
402 			/* convert "/dev/something" to "something" */
403 			if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) {
404 				strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1,
405 				    sizeof(md.provider));
406 			} else
407 				strlcpy(md.provider, name, sizeof(md.provider));
408 		}
409 		sect = malloc(ssize);
410 		if (sect == NULL)
411 			err(1, "Cannot allocate sector of %zu bytes", ssize);
412 		bzero(sect, ssize);
413 		virstor_metadata_encode(&md, sect);
414 		error = g_metadata_store(name, sect, ssize);
415 		free(sect);
416 		if (error != 0) {
417 			if (verbose)
418 				printf("\n");
419 			fprintf(stderr, "Can't store metadata on %s: %s.\n",
420 			    name, strerror(error));
421 			gctl_error(req,
422 			    "Not fully done (error storing metadata).");
423 			return;
424 		}
425 	}
426 #if 0
427 	if (verbose)
428 		printf("\n");
429 #endif
430 }
431 
432 /* Clears metadata on given provider(s) IF it's owned by us */
433 static void
434 virstor_clear(struct gctl_req *req)
435 {
436 	const char *name;
437 	char param[32];
438 	unsigned i;
439 	int nargs, error;
440 	int fd;
441 
442 	nargs = gctl_get_int(req, "nargs");
443 	if (nargs < 1) {
444 		gctl_error(req, "Too few arguments.");
445 		return;
446 	}
447 	for (i = 0; i < (unsigned)nargs; i++) {
448 		snprintf(param, sizeof(param), "arg%u", i);
449 		name = gctl_get_ascii(req, "%s", param);
450 
451 		error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
452 		if (error != 0) {
453 			fprintf(stderr, "Can't clear metadata on %s: %s "
454 			    "(do I own it?)\n", name, strerror(error));
455 			gctl_error(req,
456 			    "Not fully done (can't clear metadata).");
457 			continue;
458 		}
459 		if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
460 			fd = open(name, O_RDWR);
461 		else {
462 			sprintf(param, "%s%s", _PATH_DEV, name);
463 			fd = open(param, O_RDWR);
464 		}
465 		if (fd < 0) {
466 			gctl_error(req, "Cannot clear header sector for %s",
467 			    name);
468 			continue;
469 		}
470 		if (verbose)
471 			printf("Metadata cleared on %s.\n", name);
472 	}
473 }
474 
475 /* Print some metadata information */
476 static void
477 virstor_metadata_dump(const struct g_virstor_metadata *md)
478 {
479 	printf("          Magic string: %s\n", md->md_magic);
480 	printf("      Metadata version: %u\n", (u_int) md->md_version);
481 	printf("           Device name: %s\n", md->md_name);
482 	printf("             Device ID: %u\n", (u_int) md->md_id);
483 	printf("        Provider index: %u\n", (u_int) md->no);
484 	printf("      Active providers: %u\n", (u_int) md->md_count);
485 	printf("    Hardcoded provider: %s\n",
486 	    md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
487 	printf("          Virtual size: %u MB\n",
488 	    (unsigned int)(md->md_virsize/(1024 * 1024)));
489 	printf("            Chunk size: %u kB\n", md->md_chunk_size / 1024);
490 	printf("    Chunks on provider: %u\n", md->chunk_count);
491 	printf("           Chunks free: %u\n", md->chunk_count - md->chunk_next);
492 	printf("       Reserved chunks: %u\n", md->chunk_reserved);
493 }
494 
495 /* Called by geom(8) via gvirstor_main() to dump metadata information */
496 static void
497 virstor_dump(struct gctl_req *req)
498 {
499 	struct g_virstor_metadata md;
500 	u_char tmpmd[512];	/* temporary buffer */
501 	const char *name;
502 	char param[16];
503 	int nargs, error, i;
504 
505 	assert(sizeof(tmpmd) >= sizeof(md));
506 
507 	nargs = gctl_get_int(req, "nargs");
508 	if (nargs < 1) {
509 		gctl_error(req, "Too few arguments.");
510 		return;
511 	}
512 	for (i = 0; i < nargs; i++) {
513 		snprintf(param, sizeof(param), "arg%u", i);
514 		name = gctl_get_ascii(req, "%s", param);
515 
516 		error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
517 		    G_VIRSTOR_MAGIC);
518 		if (error != 0) {
519 			fprintf(stderr, "Can't read metadata from %s: %s.\n",
520 			    name, strerror(error));
521 			gctl_error(req,
522 			    "Not fully done (error reading metadata).");
523 			continue;
524 		}
525 		virstor_metadata_decode((u_char *) & tmpmd, &md);
526 		printf("Metadata on %s:\n", name);
527 		virstor_metadata_dump(&md);
528 		printf("\n");
529 	}
530 }
531