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