Add "resize" verb to gmirror(8) and such functionality to geom_mirror(4).

Now it is easy to expand the size of the mirror when all its components
are replaced. Also add g_resize method to geom_mirror class. It will write
updated metadata to new last sector, when parent provider is resized.

Silence from:	geom@
MFC after:	1 month
This commit is contained in:
Andrey V. Elsukov 2013-11-19 22:55:17 +00:00
parent 19bc299f1c
commit 32cea4ca0f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=258357
4 changed files with 224 additions and 6 deletions

View File

@ -28,6 +28,7 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <err.h>
#include <errno.h>
#include <paths.h>
#include <stdio.h>
@ -53,6 +54,7 @@ static void mirror_activate(struct gctl_req *req);
static void mirror_clear(struct gctl_req *req);
static void mirror_dump(struct gctl_req *req);
static void mirror_label(struct gctl_req *req);
static void mirror_resize(struct gctl_req *req, unsigned flags);
struct g_command class_commands[] = {
{ "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
@ -112,6 +114,13 @@ struct g_command class_commands[] = {
{ "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
"[-v] name prov ..."
},
{ "resize", G_FLAG_VERBOSE, mirror_resize,
{
{ 's', "size", "*", G_TYPE_STRING },
G_OPT_SENTINEL
},
"[-s size] [-v] name"
},
{ "stop", G_FLAG_VERBOSE, NULL,
{
{ 'f', "force", NULL, G_TYPE_BOOL },
@ -376,3 +385,96 @@ mirror_activate(struct gctl_req *req)
printf("Provider %s activated.\n", path);
}
}
static struct gclass *
find_class(struct gmesh *mesh, const char *name)
{
struct gclass *classp;
LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
if (strcmp(classp->lg_name, name) == 0)
return (classp);
}
return (NULL);
}
static struct ggeom *
find_geom(struct gclass *classp, const char *name)
{
struct ggeom *gp;
LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
if (strcmp(gp->lg_name, name) == 0)
return (gp);
}
return (NULL);
}
static void
mirror_resize(struct gctl_req *req, unsigned flags __unused)
{
struct gmesh mesh;
struct gclass *classp;
struct ggeom *gp;
struct gprovider *pp;
struct gconsumer *cp;
off_t size;
int error, nargs;
const char *name;
char ssize[30];
nargs = gctl_get_int(req, "nargs");
if (nargs < 1) {
gctl_error(req, "Too few arguments.");
return;
}
error = geom_gettree(&mesh);
if (error)
errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
name = gctl_get_ascii(req, "class");
if (name == NULL)
abort();
classp = find_class(&mesh, name);
if (classp == NULL)
errx(EXIT_FAILURE, "Class %s not found.", name);
name = gctl_get_ascii(req, "arg0");
if (name == NULL)
abort();
gp = find_geom(classp, name);
if (gp == NULL)
errx(EXIT_FAILURE, "No such geom: %s.", name);
pp = LIST_FIRST(&gp->lg_provider);
if (pp == NULL)
errx(EXIT_FAILURE, "Provider of geom %s not found.", name);
size = pp->lg_mediasize;
name = gctl_get_ascii(req, "size");
if (name == NULL)
errx(EXIT_FAILURE, "The size is not specified.");
if (*name == '*') {
#define CSZ(c) ((c)->lg_provider->lg_mediasize - \
(c)->lg_provider->lg_sectorsize)
/* Find the maximum possible size */
LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
if (CSZ(cp) > size)
size = CSZ(cp);
}
LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
if (CSZ(cp) < size)
size = CSZ(cp);
}
#undef CSZ
if (size == pp->lg_mediasize)
errx(EXIT_FAILURE,
"Cannot expand provider %s\n",
pp->lg_name);
} else {
error = g_parse_lba(name, pp->lg_sectorsize, &size);
if (error)
errc(EXIT_FAILURE, error, "Invalid size param");
size *= pp->lg_sectorsize;
}
snprintf(ssize, sizeof(ssize), "%ju", (uintmax_t)size);
gctl_change_param(req, "size", -1, ssize);
geom_deletetree(&mesh);
gctl_issue(req);
}

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd December 8, 2009
.Dd November 20, 2013
.Dt GMIRROR 8
.Os
.Sh NAME
@ -60,6 +60,11 @@
.Ar name
.Ar prov ...
.Nm
.Cm resize
.Op Fl v
.Op Fl s Ar size
.Ar name
.Nm
.Cm insert
.Op Fl hiv
.Op Fl p Ar priority
@ -193,6 +198,16 @@ balance algorithm.
Rebuild the given mirror components forcibly.
If autosynchronization was not turned off for the given device, this command
should be unnecessary.
.It Cm resize
Change the size of the given mirror.
.Pp
Additional options include:
.Bl -tag -width ".Fl s Ar size"
.It Fl s Ar size
New size of the mirror is expressed in logical block numbers.
This option can be omitted, then it will be automatically calculated to
maximum available size.
.El
.It Cm insert
Add the given component(s) to the existing mirror.
.Pp

View File

@ -87,6 +87,7 @@ static int g_mirror_shutdown = 0;
static int g_mirror_destroy_geom(struct gctl_req *req, struct g_class *mp,
struct g_geom *gp);
static g_taste_t g_mirror_taste;
static g_resize_t g_mirror_resize;
static void g_mirror_init(struct g_class *mp);
static void g_mirror_fini(struct g_class *mp);
@ -97,7 +98,8 @@ struct g_class g_mirror_class = {
.taste = g_mirror_taste,
.destroy_geom = g_mirror_destroy_geom,
.init = g_mirror_init,
.fini = g_mirror_fini
.fini = g_mirror_fini,
.resize = g_mirror_resize
};
@ -640,9 +642,17 @@ g_mirror_write_metadata(struct g_mirror_disk *disk,
length = cp->provider->sectorsize;
offset = cp->provider->mediasize - length;
sector = malloc((size_t)length, M_MIRROR, M_WAITOK | M_ZERO);
if (md != NULL)
mirror_metadata_encode(md, sector);
error = g_write_data(cp, offset, sector, length);
if (md != NULL) {
/*
* Handle the case, when the size of parent provider reduced.
*/
if (offset < md->md_mediasize)
error = ENOSPC;
else
mirror_metadata_encode(md, sector);
}
if (error == 0)
error = g_write_data(cp, offset, sector, length);
free(sector, M_MIRROR);
if (error != 0) {
if ((disk->d_flags & G_MIRROR_DISK_FLAG_BROKEN) == 0) {
@ -1323,7 +1333,7 @@ g_mirror_sync_request(struct bio *bp)
}
G_MIRROR_LOGREQ(3, bp, "Synchronization request finished.");
sync = &disk->d_sync;
if (sync->ds_offset == sc->sc_mediasize ||
if (sync->ds_offset >= sc->sc_mediasize ||
sync->ds_consumer == NULL ||
(sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
/* Don't send more synchronization requests. */
@ -2717,12 +2727,14 @@ g_mirror_check_metadata(struct g_mirror_softc *sc, struct g_provider *pp,
"md_balance", pp->name, sc->sc_name);
return (EINVAL);
}
#if 0
if (md->md_mediasize != sc->sc_mediasize) {
G_MIRROR_DEBUG(1,
"Invalid '%s' field on disk %s (device %s), skipping.",
"md_mediasize", pp->name, sc->sc_name);
return (EINVAL);
}
#endif
if (sc->sc_mediasize > pp->mediasize) {
G_MIRROR_DEBUG(1,
"Invalid size of disk %s (device %s), skipping.", pp->name,
@ -3115,6 +3127,22 @@ g_mirror_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
return (gp);
}
static void
g_mirror_resize(struct g_consumer *cp)
{
struct g_mirror_disk *disk;
g_topology_assert();
g_trace(G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name);
disk = cp->private;
if (disk == NULL)
return;
g_topology_unlock();
g_mirror_update_metadata(disk);
g_topology_lock();
}
static int
g_mirror_destroy_geom(struct gctl_req *req __unused,
struct g_class *mp __unused, struct g_geom *gp)

View File

@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/bio.h>
@ -40,6 +41,7 @@ __FBSDID("$FreeBSD$");
#include <vm/uma.h>
#include <machine/atomic.h>
#include <geom/geom.h>
#include <geom/geom_int.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <geom/mirror/g_mirror.h>
@ -616,6 +618,75 @@ g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp)
sx_xunlock(&sc->sc_lock);
}
static void
g_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp)
{
struct g_mirror_softc *sc;
struct g_mirror_disk *disk;
uint64_t mediasize;
const char *name, *s;
char *x;
int *nargs;
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
if (nargs == NULL) {
gctl_error(req, "No '%s' argument.", "nargs");
return;
}
if (*nargs != 1) {
gctl_error(req, "Missing device.");
return;
}
name = gctl_get_asciiparam(req, "arg0");
if (name == NULL) {
gctl_error(req, "No 'arg%u' argument.", 0);
return;
}
s = gctl_get_asciiparam(req, "size");
if (s == NULL) {
gctl_error(req, "No '%s' argument.", "size");
return;
}
mediasize = strtouq(s, &x, 0);
if (*x != '\0' || mediasize == 0) {
gctl_error(req, "Invalid '%s' argument.", "size");
return;
}
sc = g_mirror_find_device(mp, name);
if (sc == NULL) {
gctl_error(req, "No such device: %s.", name);
return;
}
/* Deny shrinking of an opened provider */
if ((g_debugflags & 16) == 0 && (sc->sc_provider->acr > 0 ||
sc->sc_provider->acw > 0 || sc->sc_provider->ace > 0)) {
if (sc->sc_mediasize > mediasize) {
gctl_error(req, "Device %s is busy.",
sc->sc_provider->name);
sx_xunlock(&sc->sc_lock);
return;
}
}
LIST_FOREACH(disk, &sc->sc_disks, d_next) {
if (mediasize > disk->d_consumer->provider->mediasize -
disk->d_consumer->provider->sectorsize) {
gctl_error(req, "Provider %s is too small.",
disk->d_name);
sx_xunlock(&sc->sc_lock);
return;
}
}
/* Update the size. */
sc->sc_mediasize = mediasize;
LIST_FOREACH(disk, &sc->sc_disks, d_next) {
g_mirror_update_metadata(disk);
}
g_topology_lock();
g_resize_provider(sc->sc_provider, mediasize);
g_topology_unlock();
sx_xunlock(&sc->sc_lock);
}
static void
g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
{
@ -793,6 +864,8 @@ g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb)
g_mirror_ctl_insert(req, mp);
else if (strcmp(verb, "remove") == 0)
g_mirror_ctl_remove(req, mp);
else if (strcmp(verb, "resize") == 0)
g_mirror_ctl_resize(req, mp);
else if (strcmp(verb, "deactivate") == 0)
g_mirror_ctl_deactivate(req, mp);
else if (strcmp(verb, "forget") == 0)