aboutsummaryrefslogtreecommitdiff
path: root/sys/geom/mirror
diff options
context:
space:
mode:
authorAndrey V. Elsukov <ae@FreeBSD.org>2013-11-19 22:55:17 +0000
committerAndrey V. Elsukov <ae@FreeBSD.org>2013-11-19 22:55:17 +0000
commit32cea4ca0f756681c337138935512fac04a84579 (patch)
tree11c6716794ee96124e2992ca31dee0abd3755476 /sys/geom/mirror
parent19bc299f1c52efd11c93c651136033481b02ddf0 (diff)
downloadsrc-32cea4ca0f756681c337138935512fac04a84579.tar.gz
src-32cea4ca0f756681c337138935512fac04a84579.zip
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
Notes
Notes: svn path=/head/; revision=258357
Diffstat (limited to 'sys/geom/mirror')
-rw-r--r--sys/geom/mirror/g_mirror.c38
-rw-r--r--sys/geom/mirror/g_mirror_ctl.c73
2 files changed, 106 insertions, 5 deletions
diff --git a/sys/geom/mirror/g_mirror.c b/sys/geom/mirror/g_mirror.c
index b4be912a2732..37d52c52f94e 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 c024bd9d3181..1748d7b9d6b1 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)