diff options
author | ae <ae@FreeBSD.org> | 2013-11-19 22:55:17 +0000 |
---|---|---|
committer | ae <ae@FreeBSD.org> | 2013-11-19 22:55:17 +0000 |
commit | 0b2e83403215eb3424720529daa9c2e9c761a429 (patch) | |
tree | 11c6716794ee96124e2992ca31dee0abd3755476 /sys/geom/mirror | |
parent | 9f5fb529a581ce23bce262a7638f4c7a9d0fec93 (diff) | |
download | FreeBSD-src-0b2e83403215eb3424720529daa9c2e9c761a429.zip FreeBSD-src-0b2e83403215eb3424720529daa9c2e9c761a429.tar.gz |
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
Diffstat (limited to 'sys/geom/mirror')
-rw-r--r-- | sys/geom/mirror/g_mirror.c | 38 | ||||
-rw-r--r-- | sys/geom/mirror/g_mirror_ctl.c | 73 |
2 files changed, 106 insertions, 5 deletions
diff --git a/sys/geom/mirror/g_mirror.c b/sys/geom/mirror/g_mirror.c index b4be912..37d52c5 100644 --- a/sys/geom/mirror/g_mirror.c +++ b/sys/geom/mirror/g_mirror.c @@ -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) diff --git a/sys/geom/mirror/g_mirror_ctl.c b/sys/geom/mirror/g_mirror_ctl.c index c024bd9..1748d7b 100644 --- a/sys/geom/mirror/g_mirror_ctl.c +++ b/sys/geom/mirror/g_mirror_ctl.c @@ -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> @@ -617,6 +619,75 @@ g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp) } 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) { struct g_mirror_softc *sc; @@ -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) |