geom_raid3.c revision 142727
1139743Simp/*-
254122Smarcel * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
354122Smarcel * All rights reserved.
454122Smarcel *
554122Smarcel * Redistribution and use in source and binary forms, with or without
654122Smarcel * modification, are permitted provided that the following conditions
754122Smarcel * are met:
854122Smarcel * 1. Redistributions of source code must retain the above copyright
954122Smarcel *    notice, this list of conditions and the following disclaimer.
1054122Smarcel * 2. Redistributions in binary form must reproduce the above copyright
1154122Smarcel *    notice, this list of conditions and the following disclaimer in the
1254122Smarcel *    documentation and/or other materials provided with the distribution.
1354122Smarcel *
1454122Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1565067Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1654122Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1754122Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
1854122Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1954122Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2054122Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2154122Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2254122Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2354122Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2454122Smarcel * SUCH DAMAGE.
2554122Smarcel */
2654122Smarcel
2754122Smarcel#include <sys/cdefs.h>
2854122Smarcel__FBSDID("$FreeBSD: head/sbin/geom/class/raid3/geom_raid3.c 142727 2005-02-27 23:07:47Z pjd $");
2954122Smarcel
3054122Smarcel#include <sys/param.h>
3154122Smarcel#include <errno.h>
3254122Smarcel#include <paths.h>
3354122Smarcel#include <stdio.h>
3454122Smarcel#include <stdlib.h>
35221426Snetchild#include <stdint.h>
36221426Snetchild#include <string.h>
37221426Snetchild#include <strings.h>
38221426Snetchild#include <assert.h>
39221426Snetchild#include <libgeom.h>
40221426Snetchild#include <geom/raid3/g_raid3.h>
41221426Snetchild#include <core/geom.h>
42221426Snetchild#include <misc/subr.h>
43221426Snetchild
44221426Snetchild
45221426Snetchilduint32_t lib_version = G_LIB_VERSION;
4657858Snsayeruint32_t version = G_RAID3_VERSION;
4757858Snsayer
4857998Snsayerstatic void raid3_main(struct gctl_req *req, unsigned f);
4957998Snsayerstatic void raid3_clear(struct gctl_req *req);
5057998Snsayerstatic void raid3_dump(struct gctl_req *req);
5157998Snsayerstatic void raid3_label(struct gctl_req *req);
5257998Snsayer
5357998Snsayerstruct g_command class_commands[] = {
5457998Snsayer	{ "clear", G_FLAG_VERBOSE, raid3_main, G_NULL_OPTS },
5557998Snsayer	{ "configure", G_FLAG_VERBOSE, NULL,
5657998Snsayer	    {
5757998Snsayer		{ 'a', "autosync", NULL, G_TYPE_NONE },
5857998Snsayer		{ 'd', "dynamic", NULL, G_TYPE_NONE },
5957998Snsayer		{ 'h', "hardcode", NULL, G_TYPE_NONE },
6057858Snsayer		{ 'n', "noautosync", NULL, G_TYPE_NONE },
61130453Sphk		{ 'r', "round_robin", NULL, G_TYPE_NONE },
62130453Sphk		{ 'R', "noround_robin", NULL, G_TYPE_NONE },
6357858Snsayer		{ 'w', "verify", NULL, G_TYPE_NONE },
6457858Snsayer		{ 'W', "noverify", NULL, G_TYPE_NONE },
65130453Sphk		G_OPT_SENTINEL
66130453Sphk	    }
67130453Sphk	},
68130453Sphk	{ "dump", 0, raid3_main, G_NULL_OPTS },
69130453Sphk	{ "insert", G_FLAG_VERBOSE, NULL,
70130453Sphk	    {
71130453Sphk		{ 'h', "hardcode", NULL, G_TYPE_NONE },
72130453Sphk		{ 'n', "number", NULL, G_TYPE_NUMBER },
73130453Sphk		G_OPT_SENTINEL
74130453Sphk	    }
7554122Smarcel	},
7654122Smarcel	{ "label", G_FLAG_VERBOSE, raid3_main,
7786555Smarcel	    {
7886555Smarcel		{ 'h', "hardcode", NULL, G_TYPE_NONE },
7986555Smarcel		{ 'n', "noautosync", NULL, G_TYPE_NONE },
8086555Smarcel		{ 'r', "round_robin", NULL, G_TYPE_NONE },
8186555Smarcel		{ 'w', "verify", NULL, G_TYPE_NONE },
8286555Smarcel		G_OPT_SENTINEL
8386555Smarcel	    }
8486555Smarcel	},
8586555Smarcel	{ "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS },
8686555Smarcel	{ "remove", G_FLAG_VERBOSE, NULL,
8786555Smarcel	    {
8886555Smarcel		{ 'n', "number", NULL, G_TYPE_NUMBER },
8986555Smarcel		G_OPT_SENTINEL
9086555Smarcel	    }
9186555Smarcel	},
9286555Smarcel	{ "stop", G_FLAG_VERBOSE, NULL,
9386555Smarcel	    {
9486555Smarcel		{ 'f', "force", NULL, G_TYPE_NONE },
9586555Smarcel		G_OPT_SENTINEL
9686555Smarcel	    }
9786555Smarcel	},
9886555Smarcel	G_CMD_SENTINEL
9986555Smarcel};
10086555Smarcel
10186555Smarcelstatic int verbose = 0;
10286555Smarcel
10386555Smarcelvoid usage(const char *);
10486555Smarcelvoid
10586555Smarcelusage(const char *comm)
10686555Smarcel{
10786555Smarcel	fprintf(stderr,
10886555Smarcel	    "usage: %s label [-hnrvw] name prov prov prov ...\n"
10986555Smarcel	    "       %s clear [-v] prov ...\n"
11086555Smarcel	    "       %s dump prov ...\n"
11186555Smarcel	    "       %s configure [-adhnrRvwW] name\n"
11286555Smarcel	    "       %s rebuild [-v] name prov\n"
11386555Smarcel	    "       %s insert [-hv] <-n number> name prov\n"
11486555Smarcel	    "       %s remove [-v] <-n number> name\n"
11586555Smarcel	    "       %s stop [-fv] name ...\n",
11686555Smarcel	    comm, comm, comm, comm, comm, comm, comm, comm);
117168477Sscottl}
118168477Sscottl
11986555Smarcelstatic void
12086555Smarcelraid3_main(struct gctl_req *req, unsigned flags)
12186555Smarcel{
12286555Smarcel	const char *name;
12386555Smarcel
12486555Smarcel	if ((flags & G_FLAG_VERBOSE) != 0)
12554122Smarcel		verbose = 1;
12654122Smarcel
12786555Smarcel	name = gctl_get_asciiparam(req, "verb");
12854122Smarcel	if (name == NULL) {
12954122Smarcel		gctl_error(req, "No '%s' argument.", "verb");
13054122Smarcel		return;
13154122Smarcel	}
13286555Smarcel	if (strcmp(name, "label") == 0)
13386555Smarcel		raid3_label(req);
13486555Smarcel	else if (strcmp(name, "clear") == 0)
13586555Smarcel		raid3_clear(req);
13686555Smarcel	else if (strcmp(name, "dump") == 0)
13786555Smarcel		raid3_dump(req);
13886555Smarcel	else
13986555Smarcel		gctl_error(req, "Unknown command: %s.", name);
14086555Smarcel}
14186555Smarcel
14286555Smarcelstatic void
14386555Smarcelraid3_label(struct gctl_req *req)
14486555Smarcel{
14554122Smarcel	struct g_raid3_metadata md;
146168477Sscottl	u_char sector[512];
147168477Sscottl	const char *str;
148168602Sscottl	char param[16];
149168602Sscottl	int *hardcode, *nargs, *noautosync, *round_robin, *verify;
150168602Sscottl	int error, i;
151168602Sscottl	unsigned sectorsize, ssize;
152168602Sscottl	off_t mediasize, msize;
153168602Sscottl
154168602Sscottl	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
155168602Sscottl	if (nargs == NULL) {
156168602Sscottl		gctl_error(req, "No '%s' argument.", "nargs");
157168602Sscottl		return;
158168602Sscottl	}
159168602Sscottl	if (*nargs < 4) {
160168602Sscottl		gctl_error(req, "Too few arguments.");
161168602Sscottl		return;
162168602Sscottl	}
163168602Sscottl#ifndef BITCOUNT
164168602Sscottl#define	BITCOUNT(x)	(((BX_(x) + (BX_(x) >> 4)) & 0x0F0F0F0F) % 255)
165168602Sscottl#define	BX_(x)		((x) - (((x) >> 1) & 0x77777777) -		\
166168602Sscottl			 (((x) >> 2) & 0x33333333) - (((x) >> 3) & 0x11111111))
167168602Sscottl#endif
168168602Sscottl	if (BITCOUNT(*nargs - 2) != 1) {
169168602Sscottl		gctl_error(req, "Invalid number of components.");
170168602Sscottl		return;
171168602Sscottl	}
172168602Sscottl
173168477Sscottl	strlcpy(md.md_magic, G_RAID3_MAGIC, sizeof(md.md_magic));
174168602Sscottl	md.md_version = G_RAID3_VERSION;
175168602Sscottl	str = gctl_get_asciiparam(req, "arg0");
176168477Sscottl	if (str == NULL) {
177168477Sscottl		gctl_error(req, "No 'arg%u' argument.", 0);
178104893Ssobomax		return;
179104893Ssobomax	}
180104893Ssobomax	strlcpy(md.md_name, str, sizeof(md.md_name));
181104893Ssobomax	md.md_all = *nargs - 1;
182104893Ssobomax	md.md_mflags = 0;
183104893Ssobomax	md.md_dflags = 0;
184104893Ssobomax	md.md_genid = 0;
185104893Ssobomax	md.md_syncid = 1;
18654122Smarcel	md.md_sync_offset = 0;
18754122Smarcel	noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync));
18854122Smarcel	if (noautosync == NULL) {
18954122Smarcel		gctl_error(req, "No '%s' argument.", "noautosync");
19054122Smarcel		return;
19154122Smarcel	}
19254122Smarcel	if (*noautosync)
19354122Smarcel		md.md_mflags |= G_RAID3_DEVICE_FLAG_NOAUTOSYNC;
19454122Smarcel	round_robin = gctl_get_paraml(req, "round_robin", sizeof(*round_robin));
19554122Smarcel	if (round_robin == NULL) {
19654122Smarcel		gctl_error(req, "No '%s' argument.", "round_robin");
19754122Smarcel		return;
19854122Smarcel	}
19954122Smarcel	if (*round_robin)
20054122Smarcel		md.md_mflags |= G_RAID3_DEVICE_FLAG_ROUND_ROBIN;
20154122Smarcel	verify = gctl_get_paraml(req, "verify", sizeof(*verify));
20254122Smarcel	if (verify == NULL) {
20354122Smarcel		gctl_error(req, "No '%s' argument.", "verify");
20454122Smarcel		return;
20554122Smarcel	}
20654122Smarcel	if (*verify)
20754122Smarcel		md.md_mflags |= G_RAID3_DEVICE_FLAG_VERIFY;
20854122Smarcel	if (*round_robin && *verify) {
20954122Smarcel		gctl_error(req, "Both '%c' and '%c' options given.", 'r', 'w');
21054122Smarcel		return;
21154122Smarcel	}
21254122Smarcel	hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode));
21354122Smarcel	if (hardcode == NULL) {
21454122Smarcel		gctl_error(req, "No '%s' argument.", "hardcode");
21554122Smarcel		return;
21654122Smarcel	}
21754122Smarcel
21854122Smarcel	/*
21954122Smarcel	 * Calculate sectorsize by finding least common multiple from
22054122Smarcel	 * sectorsizes of every disk and find the smallest mediasize.
22154122Smarcel	 */
22254122Smarcel	mediasize = 0;
22354122Smarcel	sectorsize = 0;
22454122Smarcel	for (i = 1; i < *nargs; i++) {
22554122Smarcel		snprintf(param, sizeof(param), "arg%u", i);
22654122Smarcel		str = gctl_get_asciiparam(req, param);
22754122Smarcel
22854122Smarcel		msize = g_get_mediasize(str);
22954122Smarcel		ssize = g_get_sectorsize(str);
23054122Smarcel		if (msize == 0 || ssize == 0) {
23154122Smarcel			gctl_error(req, "Can't get informations about %s: %s.",
23286607Siedowse			    str, strerror(errno));
23354122Smarcel			return;
23454122Smarcel		}
23554122Smarcel		msize -= ssize;
23685203Sdes		if (mediasize == 0 || (mediasize > 0 && msize < mediasize))
23785203Sdes			mediasize = msize;
23885203Sdes		if (sectorsize == 0)
23985203Sdes			sectorsize = ssize;
24085203Sdes		else
24154122Smarcel			sectorsize = g_lcm(sectorsize, ssize);
24254122Smarcel	}
24354122Smarcel	md.md_mediasize = mediasize * (*nargs - 2);
244173422Skib	md.md_sectorsize = sectorsize * (*nargs - 2);
245173422Skib
246173422Skib	/*
24754122Smarcel	 * Clear last sector first, to spoil all components if device exists.
24854122Smarcel	 */
249173422Skib	for (i = 1; i < *nargs; i++) {
25054122Smarcel		snprintf(param, sizeof(param), "arg%u", i);
25154122Smarcel		str = gctl_get_asciiparam(req, param);
25285127Sdes
25385127Sdes		error = g_metadata_clear(str, NULL);
25485127Sdes		if (error != 0) {
25585127Sdes			gctl_error(req, "Can't store metadata on %s: %s.", str,
25685127Sdes			    strerror(error));
25785127Sdes			return;
25885127Sdes		}
25954122Smarcel	}
26054122Smarcel
26154122Smarcel	/*
26254122Smarcel	 * Ok, store metadata (use disk number as priority).
26354122Smarcel	 */
26454122Smarcel	for (i = 1; i < *nargs; i++) {
26554122Smarcel		snprintf(param, sizeof(param), "arg%u", i);
26654122Smarcel		str = gctl_get_asciiparam(req, param);
26754122Smarcel
26854122Smarcel		msize = g_get_mediasize(str);
26954122Smarcel		ssize = g_get_sectorsize(str);
27054122Smarcel		if (mediasize < msize - ssize) {
27154122Smarcel			fprintf(stderr,
27254122Smarcel			    "warning: %s: only %jd bytes from %jd bytes used.\n",
27354122Smarcel			    str, (intmax_t)mediasize, (intmax_t)(msize - ssize));
27454122Smarcel		}
27554122Smarcel
27654122Smarcel		md.md_no = i - 1;
27754122Smarcel		md.md_provsize = msize;
278130689Sbms		if (!*hardcode)
27954122Smarcel			bzero(md.md_provider, sizeof(md.md_provider));
280104893Ssobomax		else {
281216813Sscf			if (strncmp(str, _PATH_DEV, strlen(_PATH_DEV)) == 0)
282130691Sbms				str += strlen(_PATH_DEV);
28354122Smarcel			strlcpy(md.md_provider, str, sizeof(md.md_provider));
28486540Smarcel		}
28554122Smarcel		if (*verify && md.md_no == md.md_all - 1) {
28654122Smarcel			/*
28754122Smarcel			 * In "verify" mode, force synchronization of parity
28854122Smarcel			 * component on start.
28954122Smarcel			 */
29054122Smarcel			md.md_syncid = 0;
29154122Smarcel		}
29254122Smarcel		raid3_metadata_encode(&md, sector);
29354122Smarcel		error = g_metadata_store(str, sector, sizeof(sector));
29454122Smarcel		if (error != 0) {
29554122Smarcel			fprintf(stderr, "Can't store metadata on %s: %s.\n",
29654122Smarcel			    str, strerror(error));
29754122Smarcel			gctl_error(req, "Not fully done.");
29854122Smarcel			continue;
29954122Smarcel		}
30054122Smarcel		if (verbose)
30154122Smarcel			printf("Metadata value stored on %s.\n", str);
30254122Smarcel	}
30354122Smarcel}
30454122Smarcel
30554122Smarcelstatic void
306131461Snetchildraid3_clear(struct gctl_req *req)
30754122Smarcel{
30854122Smarcel	const char *name;
30954122Smarcel	char param[16];
31054122Smarcel	int *nargs, error, i;
31154122Smarcel
31254122Smarcel	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
31354122Smarcel	if (nargs == NULL) {
31454122Smarcel		gctl_error(req, "No '%s' argument.", "nargs");
31554122Smarcel		return;
31654122Smarcel	}
31754122Smarcel	if (*nargs < 1) {
31854122Smarcel		gctl_error(req, "Too few arguments.");
31954122Smarcel		return;
32054122Smarcel	}
32154122Smarcel
32254122Smarcel	for (i = 0; i < *nargs; i++) {
32354122Smarcel		snprintf(param, sizeof(param), "arg%u", i);
32454122Smarcel		name = gctl_get_asciiparam(req, param);
32554122Smarcel
32654122Smarcel		error = g_metadata_clear(name, G_RAID3_MAGIC);
32754122Smarcel		if (error != 0) {
32854122Smarcel			fprintf(stderr, "Can't clear metadata on %s: %s.\n",
32954122Smarcel			    name, strerror(error));
33054122Smarcel			gctl_error(req, "Not fully done.");
33154122Smarcel			continue;
33254122Smarcel		}
33354122Smarcel		if (verbose)
33454122Smarcel			printf("Metadata cleared on %s.\n", name);
33554122Smarcel	}
33654122Smarcel}
33754122Smarcel
33854122Smarcelstatic void
33954122Smarcelraid3_dump(struct gctl_req *req)
34054122Smarcel{
34185139Smarcel	struct g_raid3_metadata md, tmpmd;
34254122Smarcel	const char *name;
34354122Smarcel	char param[16];
34454122Smarcel	int *nargs, error, i;
34585139Smarcel
34654122Smarcel	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
34754122Smarcel	if (nargs == NULL) {
34885139Smarcel		gctl_error(req, "No '%s' argument.", "nargs");
34954122Smarcel		return;
35054122Smarcel	}
35185139Smarcel	if (*nargs < 1) {
35254122Smarcel		gctl_error(req, "Too few arguments.");
35354122Smarcel		return;
35485139Smarcel	}
35554122Smarcel
35654122Smarcel	for (i = 0; i < *nargs; i++) {
35754122Smarcel		snprintf(param, sizeof(param), "arg%u", i);
35854122Smarcel		name = gctl_get_asciiparam(req, param);
35954122Smarcel
36054122Smarcel		error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
36185139Smarcel		    G_RAID3_MAGIC);
36254122Smarcel		if (error != 0) {
36385139Smarcel			fprintf(stderr, "Can't read metadata from %s: %s.\n",
36454122Smarcel			    name, strerror(error));
36554122Smarcel			gctl_error(req, "Not fully done.");
36654122Smarcel			continue;
36754122Smarcel		}
36854122Smarcel		if (raid3_metadata_decode((u_char *)&tmpmd, &md) != 0) {
36954122Smarcel			fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
37085139Smarcel			    name);
37154122Smarcel			gctl_error(req, "Not fully done.");
37285139Smarcel			continue;
37354122Smarcel		}
37454122Smarcel		printf("Metadata on %s:\n", name);
37554122Smarcel		raid3_metadata_dump(&md);
37654122Smarcel		printf("\n");
37754122Smarcel	}
37885139Smarcel}
379125997Sbms