svn commit: r258357 - in head: sbin/geom/class/mirror sys/geom/mirror

Andrey V. Elsukov ae at FreeBSD.org
Tue Nov 19 22:55:18 UTC 2013


Author: ae
Date: Tue Nov 19 22:55:17 2013
New Revision: 258357
URL: http://svnweb.freebsd.org/changeset/base/258357

Log:
  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

Modified:
  head/sbin/geom/class/mirror/geom_mirror.c
  head/sbin/geom/class/mirror/gmirror.8
  head/sys/geom/mirror/g_mirror.c
  head/sys/geom/mirror/g_mirror_ctl.c

Modified: head/sbin/geom/class/mirror/geom_mirror.c
==============================================================================
--- head/sbin/geom/class/mirror/geom_mirror.c	Tue Nov 19 22:14:35 2013	(r258356)
+++ head/sbin/geom/class/mirror/geom_mirror.c	Tue Nov 19 22:55:17 2013	(r258357)
@@ -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_
 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);
+}

Modified: head/sbin/geom/class/mirror/gmirror.8
==============================================================================
--- head/sbin/geom/class/mirror/gmirror.8	Tue Nov 19 22:14:35 2013	(r258356)
+++ head/sbin/geom/class/mirror/gmirror.8	Tue Nov 19 22:55:17 2013	(r258357)
@@ -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

Modified: head/sys/geom/mirror/g_mirror.c
==============================================================================
--- head/sys/geom/mirror/g_mirror.c	Tue Nov 19 22:14:35 2013	(r258356)
+++ head/sys/geom/mirror/g_mirror.c	Tue Nov 19 22:55:17 2013	(r258357)
@@ -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_
 	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_
 		    "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, struc
 	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)

Modified: head/sys/geom/mirror/g_mirror_ctl.c
==============================================================================
--- head/sys/geom/mirror/g_mirror_ctl.c	Tue Nov 19 22:14:35 2013	(r258356)
+++ head/sys/geom/mirror/g_mirror_ctl.c	Tue Nov 19 22:55:17 2013	(r258357)
@@ -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
 }
 
 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, st
 		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)


More information about the svn-src-head mailing list