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:
parent
19bc299f1c
commit
32cea4ca0f
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=258357
@ -28,6 +28,7 @@
|
|||||||
__FBSDID("$FreeBSD$");
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <stdio.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_clear(struct gctl_req *req);
|
||||||
static void mirror_dump(struct gctl_req *req);
|
static void mirror_dump(struct gctl_req *req);
|
||||||
static void mirror_label(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[] = {
|
struct g_command class_commands[] = {
|
||||||
{ "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
|
{ "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,
|
{ "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
|
||||||
"[-v] name prov ..."
|
"[-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,
|
{ "stop", G_FLAG_VERBOSE, NULL,
|
||||||
{
|
{
|
||||||
{ 'f', "force", NULL, G_TYPE_BOOL },
|
{ 'f', "force", NULL, G_TYPE_BOOL },
|
||||||
@ -376,3 +385,96 @@ mirror_activate(struct gctl_req *req)
|
|||||||
printf("Provider %s activated.\n", path);
|
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);
|
||||||
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd December 8, 2009
|
.Dd November 20, 2013
|
||||||
.Dt GMIRROR 8
|
.Dt GMIRROR 8
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -60,6 +60,11 @@
|
|||||||
.Ar name
|
.Ar name
|
||||||
.Ar prov ...
|
.Ar prov ...
|
||||||
.Nm
|
.Nm
|
||||||
|
.Cm resize
|
||||||
|
.Op Fl v
|
||||||
|
.Op Fl s Ar size
|
||||||
|
.Ar name
|
||||||
|
.Nm
|
||||||
.Cm insert
|
.Cm insert
|
||||||
.Op Fl hiv
|
.Op Fl hiv
|
||||||
.Op Fl p Ar priority
|
.Op Fl p Ar priority
|
||||||
@ -193,6 +198,16 @@ balance algorithm.
|
|||||||
Rebuild the given mirror components forcibly.
|
Rebuild the given mirror components forcibly.
|
||||||
If autosynchronization was not turned off for the given device, this command
|
If autosynchronization was not turned off for the given device, this command
|
||||||
should be unnecessary.
|
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
|
.It Cm insert
|
||||||
Add the given component(s) to the existing mirror.
|
Add the given component(s) to the existing mirror.
|
||||||
.Pp
|
.Pp
|
||||||
|
@ -87,6 +87,7 @@ static int g_mirror_shutdown = 0;
|
|||||||
static int g_mirror_destroy_geom(struct gctl_req *req, struct g_class *mp,
|
static int g_mirror_destroy_geom(struct gctl_req *req, struct g_class *mp,
|
||||||
struct g_geom *gp);
|
struct g_geom *gp);
|
||||||
static g_taste_t g_mirror_taste;
|
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_init(struct g_class *mp);
|
||||||
static void g_mirror_fini(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,
|
.taste = g_mirror_taste,
|
||||||
.destroy_geom = g_mirror_destroy_geom,
|
.destroy_geom = g_mirror_destroy_geom,
|
||||||
.init = g_mirror_init,
|
.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;
|
length = cp->provider->sectorsize;
|
||||||
offset = cp->provider->mediasize - length;
|
offset = cp->provider->mediasize - length;
|
||||||
sector = malloc((size_t)length, M_MIRROR, M_WAITOK | M_ZERO);
|
sector = malloc((size_t)length, M_MIRROR, M_WAITOK | M_ZERO);
|
||||||
if (md != NULL)
|
if (md != NULL) {
|
||||||
mirror_metadata_encode(md, sector);
|
/*
|
||||||
error = g_write_data(cp, offset, sector, length);
|
* 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);
|
free(sector, M_MIRROR);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
if ((disk->d_flags & G_MIRROR_DISK_FLAG_BROKEN) == 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.");
|
G_MIRROR_LOGREQ(3, bp, "Synchronization request finished.");
|
||||||
sync = &disk->d_sync;
|
sync = &disk->d_sync;
|
||||||
if (sync->ds_offset == sc->sc_mediasize ||
|
if (sync->ds_offset >= sc->sc_mediasize ||
|
||||||
sync->ds_consumer == NULL ||
|
sync->ds_consumer == NULL ||
|
||||||
(sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
|
(sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
|
||||||
/* Don't send more synchronization requests. */
|
/* 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);
|
"md_balance", pp->name, sc->sc_name);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
if (md->md_mediasize != sc->sc_mediasize) {
|
if (md->md_mediasize != sc->sc_mediasize) {
|
||||||
G_MIRROR_DEBUG(1,
|
G_MIRROR_DEBUG(1,
|
||||||
"Invalid '%s' field on disk %s (device %s), skipping.",
|
"Invalid '%s' field on disk %s (device %s), skipping.",
|
||||||
"md_mediasize", pp->name, sc->sc_name);
|
"md_mediasize", pp->name, sc->sc_name);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (sc->sc_mediasize > pp->mediasize) {
|
if (sc->sc_mediasize > pp->mediasize) {
|
||||||
G_MIRROR_DEBUG(1,
|
G_MIRROR_DEBUG(1,
|
||||||
"Invalid size of disk %s (device %s), skipping.", pp->name,
|
"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);
|
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
|
static int
|
||||||
g_mirror_destroy_geom(struct gctl_req *req __unused,
|
g_mirror_destroy_geom(struct gctl_req *req __unused,
|
||||||
struct g_class *mp __unused, struct g_geom *gp)
|
struct g_class *mp __unused, struct g_geom *gp)
|
||||||
|
@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
#include <sys/kernel.h>
|
#include <sys/kernel.h>
|
||||||
#include <sys/module.h>
|
#include <sys/module.h>
|
||||||
|
#include <sys/limits.h>
|
||||||
#include <sys/lock.h>
|
#include <sys/lock.h>
|
||||||
#include <sys/mutex.h>
|
#include <sys/mutex.h>
|
||||||
#include <sys/bio.h>
|
#include <sys/bio.h>
|
||||||
@ -40,6 +41,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <vm/uma.h>
|
#include <vm/uma.h>
|
||||||
#include <machine/atomic.h>
|
#include <machine/atomic.h>
|
||||||
#include <geom/geom.h>
|
#include <geom/geom.h>
|
||||||
|
#include <geom/geom_int.h>
|
||||||
#include <sys/proc.h>
|
#include <sys/proc.h>
|
||||||
#include <sys/kthread.h>
|
#include <sys/kthread.h>
|
||||||
#include <geom/mirror/g_mirror.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);
|
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
|
static void
|
||||||
g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
|
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);
|
g_mirror_ctl_insert(req, mp);
|
||||||
else if (strcmp(verb, "remove") == 0)
|
else if (strcmp(verb, "remove") == 0)
|
||||||
g_mirror_ctl_remove(req, mp);
|
g_mirror_ctl_remove(req, mp);
|
||||||
|
else if (strcmp(verb, "resize") == 0)
|
||||||
|
g_mirror_ctl_resize(req, mp);
|
||||||
else if (strcmp(verb, "deactivate") == 0)
|
else if (strcmp(verb, "deactivate") == 0)
|
||||||
g_mirror_ctl_deactivate(req, mp);
|
g_mirror_ctl_deactivate(req, mp);
|
||||||
else if (strcmp(verb, "forget") == 0)
|
else if (strcmp(verb, "forget") == 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user