xref: /freebsd-src/sys/geom/union/g_union.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
1c7996ddfSKirk McKusick /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3c7996ddfSKirk McKusick  *
4c7996ddfSKirk McKusick  * Copyright (c) 2022 Marshall Kirk McKusick <mckusick@mckusick.com>
5c7996ddfSKirk McKusick  *
6c7996ddfSKirk McKusick  * Redistribution and use in source and binary forms, with or without
7c7996ddfSKirk McKusick  * modification, are permitted provided that the following conditions
8c7996ddfSKirk McKusick  * are met:
9c7996ddfSKirk McKusick  * 1. Redistributions of source code must retain the above copyright
10c7996ddfSKirk McKusick  *    notice, this list of conditions and the following disclaimer.
11c7996ddfSKirk McKusick  * 2. Redistributions in binary form must reproduce the above copyright
12c7996ddfSKirk McKusick  *    notice, this list of conditions and the following disclaimer in the
13c7996ddfSKirk McKusick  *    documentation and/or other materials provided with the distribution.
14c7996ddfSKirk McKusick  *
15c7996ddfSKirk McKusick  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16c7996ddfSKirk McKusick  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17c7996ddfSKirk McKusick  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18c7996ddfSKirk McKusick  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19c7996ddfSKirk McKusick  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c7996ddfSKirk McKusick  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21c7996ddfSKirk McKusick  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c7996ddfSKirk McKusick  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c7996ddfSKirk McKusick  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c7996ddfSKirk McKusick  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c7996ddfSKirk McKusick  * SUCH DAMAGE.
26c7996ddfSKirk McKusick  */
27c7996ddfSKirk McKusick 
28c7996ddfSKirk McKusick #include <sys/param.h>
29c7996ddfSKirk McKusick #include <sys/bio.h>
30c7996ddfSKirk McKusick #include <sys/buf.h>
31c7996ddfSKirk McKusick #include <sys/ctype.h>
32c7996ddfSKirk McKusick #include <sys/kernel.h>
33c7996ddfSKirk McKusick #include <sys/lock.h>
34c7996ddfSKirk McKusick #include <sys/malloc.h>
35c7996ddfSKirk McKusick #include <sys/module.h>
36c7996ddfSKirk McKusick #include <sys/reboot.h>
37c7996ddfSKirk McKusick #include <sys/rwlock.h>
38c7996ddfSKirk McKusick #include <sys/sbuf.h>
39c7996ddfSKirk McKusick #include <sys/sysctl.h>
40c7996ddfSKirk McKusick 
41c7996ddfSKirk McKusick #include <geom/geom.h>
42c7996ddfSKirk McKusick #include <geom/geom_dbg.h>
43c7996ddfSKirk McKusick #include <geom/union/g_union.h>
44c7996ddfSKirk McKusick 
45c7996ddfSKirk McKusick SYSCTL_DECL(_kern_geom);
46c7996ddfSKirk McKusick static SYSCTL_NODE(_kern_geom, OID_AUTO, union, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
47c7996ddfSKirk McKusick     "GEOM_UNION stuff");
48c7996ddfSKirk McKusick static u_int g_union_debug = 0;
49c7996ddfSKirk McKusick SYSCTL_UINT(_kern_geom_union, OID_AUTO, debug, CTLFLAG_RW, &g_union_debug, 0,
50c7996ddfSKirk McKusick     "Debug level");
51c7996ddfSKirk McKusick 
52c7996ddfSKirk McKusick static void g_union_config(struct gctl_req *req, struct g_class *mp,
53c7996ddfSKirk McKusick     const char *verb);
54c7996ddfSKirk McKusick static g_access_t g_union_access;
55c7996ddfSKirk McKusick static g_start_t g_union_start;
56c7996ddfSKirk McKusick static g_dumpconf_t g_union_dumpconf;
57c7996ddfSKirk McKusick static g_orphan_t g_union_orphan;
58c7996ddfSKirk McKusick static int g_union_destroy_geom(struct gctl_req *req, struct g_class *mp,
59c7996ddfSKirk McKusick     struct g_geom *gp);
60c7996ddfSKirk McKusick static g_provgone_t g_union_providergone;
61c7996ddfSKirk McKusick static g_resize_t g_union_resize;
62c7996ddfSKirk McKusick 
63c7996ddfSKirk McKusick struct g_class g_union_class = {
64c7996ddfSKirk McKusick 	.name = G_UNION_CLASS_NAME,
65c7996ddfSKirk McKusick 	.version = G_VERSION,
66c7996ddfSKirk McKusick 	.ctlreq = g_union_config,
67c7996ddfSKirk McKusick 	.access = g_union_access,
68c7996ddfSKirk McKusick 	.start = g_union_start,
69c7996ddfSKirk McKusick 	.dumpconf = g_union_dumpconf,
70c7996ddfSKirk McKusick 	.orphan = g_union_orphan,
71c7996ddfSKirk McKusick 	.destroy_geom = g_union_destroy_geom,
72c7996ddfSKirk McKusick 	.providergone = g_union_providergone,
73c7996ddfSKirk McKusick 	.resize = g_union_resize,
74c7996ddfSKirk McKusick };
75c7996ddfSKirk McKusick 
76c7996ddfSKirk McKusick static void g_union_ctl_create(struct gctl_req *req, struct g_class *mp, bool);
77c7996ddfSKirk McKusick static intmax_t g_union_fetcharg(struct gctl_req *req, const char *name);
78c7996ddfSKirk McKusick static bool g_union_verify_nprefix(const char *name);
79c7996ddfSKirk McKusick static void g_union_ctl_destroy(struct gctl_req *req, struct g_class *mp, bool);
80c7996ddfSKirk McKusick static struct g_geom *g_union_find_geom(struct g_class *mp, const char *name);
81c7996ddfSKirk McKusick static void g_union_ctl_reset(struct gctl_req *req, struct g_class *mp, bool);
82c7996ddfSKirk McKusick static void g_union_ctl_revert(struct gctl_req *req, struct g_class *mp, bool);
83c7996ddfSKirk McKusick static void g_union_revert(struct g_union_softc *sc);
84c7996ddfSKirk McKusick static void g_union_doio(struct g_union_wip *wip);
85c7996ddfSKirk McKusick static void g_union_ctl_commit(struct gctl_req *req, struct g_class *mp, bool);
86c7996ddfSKirk McKusick static void g_union_setmap(struct bio *bp, struct g_union_softc *sc);
87c7996ddfSKirk McKusick static bool g_union_getmap(struct bio *bp, struct g_union_softc *sc,
88c7996ddfSKirk McKusick 	off_t *len2read);
89c7996ddfSKirk McKusick static void g_union_done(struct bio *bp);
90c7996ddfSKirk McKusick static void g_union_kerneldump(struct bio *bp, struct g_union_softc *sc);
91489ba222SMitchell Horne static int g_union_dumper(void *, void *, off_t, size_t);
92c7996ddfSKirk McKusick static int g_union_destroy(struct gctl_req *req, struct g_geom *gp, bool force);
93c7996ddfSKirk McKusick 
94c7996ddfSKirk McKusick /*
95c7996ddfSKirk McKusick  * Operate on union-specific configuration commands.
96c7996ddfSKirk McKusick  */
97c7996ddfSKirk McKusick static void
g_union_config(struct gctl_req * req,struct g_class * mp,const char * verb)98c7996ddfSKirk McKusick g_union_config(struct gctl_req *req, struct g_class *mp, const char *verb)
99c7996ddfSKirk McKusick {
100c7996ddfSKirk McKusick 	uint32_t *version, *verbose;
101c7996ddfSKirk McKusick 
102c7996ddfSKirk McKusick 	g_topology_assert();
103c7996ddfSKirk McKusick 
104c7996ddfSKirk McKusick 	version = gctl_get_paraml(req, "version", sizeof(*version));
105c7996ddfSKirk McKusick 	if (version == NULL) {
106c7996ddfSKirk McKusick 		gctl_error(req, "No '%s' argument.", "version");
107c7996ddfSKirk McKusick 		return;
108c7996ddfSKirk McKusick 	}
109c7996ddfSKirk McKusick 	if (*version != G_UNION_VERSION) {
110c7996ddfSKirk McKusick 		gctl_error(req, "Userland and kernel parts are out of sync.");
111c7996ddfSKirk McKusick 		return;
112c7996ddfSKirk McKusick 	}
113c7996ddfSKirk McKusick 	verbose = gctl_get_paraml(req, "verbose", sizeof(*verbose));
114c7996ddfSKirk McKusick 	if (verbose == NULL) {
115c7996ddfSKirk McKusick 		gctl_error(req, "No '%s' argument.", "verbose");
116c7996ddfSKirk McKusick 		return;
117c7996ddfSKirk McKusick 	}
118c7996ddfSKirk McKusick 	if (strcmp(verb, "create") == 0) {
119c7996ddfSKirk McKusick 		g_union_ctl_create(req, mp, *verbose);
120c7996ddfSKirk McKusick 		return;
121c7996ddfSKirk McKusick 	} else if (strcmp(verb, "destroy") == 0) {
122c7996ddfSKirk McKusick 		g_union_ctl_destroy(req, mp, *verbose);
123c7996ddfSKirk McKusick 		return;
124c7996ddfSKirk McKusick 	} else if (strcmp(verb, "reset") == 0) {
125c7996ddfSKirk McKusick 		g_union_ctl_reset(req, mp, *verbose);
126c7996ddfSKirk McKusick 		return;
127c7996ddfSKirk McKusick 	} else if (strcmp(verb, "revert") == 0) {
128c7996ddfSKirk McKusick 		g_union_ctl_revert(req, mp, *verbose);
129c7996ddfSKirk McKusick 		return;
130c7996ddfSKirk McKusick 	} else if (strcmp(verb, "commit") == 0) {
131c7996ddfSKirk McKusick 		g_union_ctl_commit(req, mp, *verbose);
132c7996ddfSKirk McKusick 		return;
133c7996ddfSKirk McKusick 	}
134c7996ddfSKirk McKusick 
135c7996ddfSKirk McKusick 	gctl_error(req, "Unknown verb.");
136c7996ddfSKirk McKusick }
137c7996ddfSKirk McKusick 
138c7996ddfSKirk McKusick /*
139c7996ddfSKirk McKusick  * Create a union device.
140c7996ddfSKirk McKusick  */
141c7996ddfSKirk McKusick static void
g_union_ctl_create(struct gctl_req * req,struct g_class * mp,bool verbose)142c7996ddfSKirk McKusick g_union_ctl_create(struct gctl_req *req, struct g_class *mp, bool verbose)
143c7996ddfSKirk McKusick {
144c7996ddfSKirk McKusick 	struct g_provider *upperpp, *lowerpp, *newpp;
145c7996ddfSKirk McKusick 	struct g_consumer *uppercp, *lowercp;
146c7996ddfSKirk McKusick 	struct g_union_softc *sc;
147c7996ddfSKirk McKusick 	struct g_geom_alias *gap;
148c7996ddfSKirk McKusick 	struct g_geom *gp;
149c7996ddfSKirk McKusick 	intmax_t offset, secsize, size, needed;
150c7996ddfSKirk McKusick 	const char *gunionname;
151c7996ddfSKirk McKusick 	int *nargs, error, i, n;
152c7996ddfSKirk McKusick 	char name[64];
153c7996ddfSKirk McKusick 
154c7996ddfSKirk McKusick 	g_topology_assert();
155c7996ddfSKirk McKusick 
156c7996ddfSKirk McKusick 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
157c7996ddfSKirk McKusick 	if (nargs == NULL) {
158c7996ddfSKirk McKusick 		gctl_error(req, "No '%s' argument.", "nargs");
159c7996ddfSKirk McKusick 		return;
160c7996ddfSKirk McKusick 	}
161c7996ddfSKirk McKusick 	if (*nargs < 2) {
162c7996ddfSKirk McKusick 		gctl_error(req, "Missing device(s).");
163c7996ddfSKirk McKusick 		return;
164c7996ddfSKirk McKusick 	}
165c7996ddfSKirk McKusick 	if (*nargs > 2) {
166c7996ddfSKirk McKusick 		gctl_error(req, "Extra device(s).");
167c7996ddfSKirk McKusick 		return;
168c7996ddfSKirk McKusick 	}
169c7996ddfSKirk McKusick 
170c7996ddfSKirk McKusick 	offset = g_union_fetcharg(req, "offset");
171c7996ddfSKirk McKusick 	size = g_union_fetcharg(req, "size");
172c7996ddfSKirk McKusick 	secsize = g_union_fetcharg(req, "secsize");
173c7996ddfSKirk McKusick 	gunionname = gctl_get_asciiparam(req, "gunionname");
174c7996ddfSKirk McKusick 
175c7996ddfSKirk McKusick 	upperpp = gctl_get_provider(req, "arg0");
176c7996ddfSKirk McKusick 	lowerpp = gctl_get_provider(req, "arg1");
177c7996ddfSKirk McKusick 	if (upperpp == NULL || lowerpp == NULL)
178c7996ddfSKirk McKusick 		/* error message provided by gctl_get_provider() */
179c7996ddfSKirk McKusick 		return;
180c7996ddfSKirk McKusick 	/* Create the union */
181c7996ddfSKirk McKusick 	if (secsize == 0)
182c7996ddfSKirk McKusick 		secsize = lowerpp->sectorsize;
183c7996ddfSKirk McKusick 	else if ((secsize % lowerpp->sectorsize) != 0) {
184c7996ddfSKirk McKusick 		gctl_error(req, "Sector size %jd is not a multiple of lower "
185c7996ddfSKirk McKusick 		    "provider %s's %jd sector size.", (intmax_t)secsize,
186c7996ddfSKirk McKusick 		    lowerpp->name, (intmax_t)lowerpp->sectorsize);
187c7996ddfSKirk McKusick 		return;
188c7996ddfSKirk McKusick 	}
189c7996ddfSKirk McKusick 	if (secsize > maxphys) {
190c7996ddfSKirk McKusick 		gctl_error(req, "Too big secsize %jd for lower provider %s.",
191c7996ddfSKirk McKusick 		    (intmax_t)secsize, lowerpp->name);
192c7996ddfSKirk McKusick 		return;
193c7996ddfSKirk McKusick 	}
194c7996ddfSKirk McKusick 	if (secsize % upperpp->sectorsize != 0) {
195c7996ddfSKirk McKusick 		gctl_error(req, "Sector size %jd is not a multiple of upper "
196c7996ddfSKirk McKusick 		    "provider %s's %jd sector size.", (intmax_t)secsize,
197c7996ddfSKirk McKusick 		    upperpp->name, (intmax_t)upperpp->sectorsize);
198c7996ddfSKirk McKusick 		return;
199c7996ddfSKirk McKusick 	}
200c7996ddfSKirk McKusick 	if ((offset % secsize) != 0) {
201c7996ddfSKirk McKusick 		gctl_error(req, "Offset %jd is not a multiple of lower "
202c7996ddfSKirk McKusick 		    "provider %s's %jd sector size.", (intmax_t)offset,
203c7996ddfSKirk McKusick 		    lowerpp->name, (intmax_t)lowerpp->sectorsize);
204c7996ddfSKirk McKusick 		return;
205c7996ddfSKirk McKusick 	}
206c7996ddfSKirk McKusick 	if (size == 0)
207c7996ddfSKirk McKusick 		size = lowerpp->mediasize - offset;
208c7996ddfSKirk McKusick 	else
209c7996ddfSKirk McKusick 		size -= offset;
210c7996ddfSKirk McKusick 	if ((size % secsize) != 0) {
211c7996ddfSKirk McKusick 		gctl_error(req, "Size %jd is not a multiple of sector size "
212c7996ddfSKirk McKusick 		    "%jd.", (intmax_t)size, (intmax_t)secsize);
213c7996ddfSKirk McKusick 		return;
214c7996ddfSKirk McKusick 	}
215c7996ddfSKirk McKusick 	if (offset + size < lowerpp->mediasize) {
216c7996ddfSKirk McKusick 		gctl_error(req, "Size %jd is too small for lower provider %s, "
217c7996ddfSKirk McKusick 		    "needs %jd.", (intmax_t)(offset + size), lowerpp->name,
218c7996ddfSKirk McKusick 		    lowerpp->mediasize);
219c7996ddfSKirk McKusick 		return;
220c7996ddfSKirk McKusick 	}
221c7996ddfSKirk McKusick 	if (size > upperpp->mediasize) {
222c7996ddfSKirk McKusick 		gctl_error(req, "Upper provider %s size (%jd) is too small, "
223c7996ddfSKirk McKusick 		    "needs %jd.", upperpp->name, (intmax_t)upperpp->mediasize,
224c7996ddfSKirk McKusick 		    (intmax_t)size);
225c7996ddfSKirk McKusick 		return;
226c7996ddfSKirk McKusick 	}
227c7996ddfSKirk McKusick 	if (gunionname != NULL && !g_union_verify_nprefix(gunionname)) {
228c7996ddfSKirk McKusick 		gctl_error(req, "Gunion name %s must be alphanumeric.",
229c7996ddfSKirk McKusick 		    gunionname);
230c7996ddfSKirk McKusick 		return;
231c7996ddfSKirk McKusick 	}
232c7996ddfSKirk McKusick 	if (gunionname != NULL) {
233c7996ddfSKirk McKusick 		n = snprintf(name, sizeof(name), "%s%s", gunionname,
234c7996ddfSKirk McKusick 		    G_UNION_SUFFIX);
235c7996ddfSKirk McKusick 	} else {
236c7996ddfSKirk McKusick 		n = snprintf(name, sizeof(name), "%s-%s%s", upperpp->name,
237c7996ddfSKirk McKusick 		    lowerpp->name, G_UNION_SUFFIX);
238c7996ddfSKirk McKusick 	}
239c7996ddfSKirk McKusick 	if (n <= 0 || n >= sizeof(name)) {
240c7996ddfSKirk McKusick 		gctl_error(req, "Invalid provider name.");
241c7996ddfSKirk McKusick 		return;
242c7996ddfSKirk McKusick 	}
243c7996ddfSKirk McKusick 	LIST_FOREACH(gp, &mp->geom, geom) {
244c7996ddfSKirk McKusick 		if (strcmp(gp->name, name) == 0) {
245c7996ddfSKirk McKusick 			gctl_error(req, "Provider %s already exists.", name);
246c7996ddfSKirk McKusick 			return;
247c7996ddfSKirk McKusick 		}
248c7996ddfSKirk McKusick 	}
249c7996ddfSKirk McKusick 	gp = g_new_geomf(mp, "%s", name);
250c7996ddfSKirk McKusick 	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
251c7996ddfSKirk McKusick 	rw_init(&sc->sc_rwlock, "gunion");
252c7996ddfSKirk McKusick 	TAILQ_INIT(&sc->sc_wiplist);
253c7996ddfSKirk McKusick 	sc->sc_offset = offset;
254c7996ddfSKirk McKusick 	sc->sc_size = size;
255c7996ddfSKirk McKusick 	sc->sc_sectorsize = secsize;
256c7996ddfSKirk McKusick 	sc->sc_reads = 0;
257c7996ddfSKirk McKusick 	sc->sc_writes = 0;
258c7996ddfSKirk McKusick 	sc->sc_deletes = 0;
259c7996ddfSKirk McKusick 	sc->sc_getattrs = 0;
260c7996ddfSKirk McKusick 	sc->sc_flushes = 0;
261c7996ddfSKirk McKusick 	sc->sc_speedups = 0;
262c7996ddfSKirk McKusick 	sc->sc_cmd0s = 0;
263c7996ddfSKirk McKusick 	sc->sc_cmd1s = 0;
264c7996ddfSKirk McKusick 	sc->sc_cmd2s = 0;
265c7996ddfSKirk McKusick 	sc->sc_readbytes = 0;
266c7996ddfSKirk McKusick 	sc->sc_wrotebytes = 0;
267c7996ddfSKirk McKusick 	sc->sc_writemap_memory = 0;
268c7996ddfSKirk McKusick 	gp->softc = sc;
269c7996ddfSKirk McKusick 
270c7996ddfSKirk McKusick 	newpp = g_new_providerf(gp, "%s", gp->name);
271c7996ddfSKirk McKusick 	newpp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
272c7996ddfSKirk McKusick 	newpp->mediasize = size;
273c7996ddfSKirk McKusick 	newpp->sectorsize = secsize;
274c7996ddfSKirk McKusick 	LIST_FOREACH(gap, &upperpp->aliases, ga_next)
275c7996ddfSKirk McKusick 		g_provider_add_alias(newpp, "%s%s", gap->ga_alias,
276c7996ddfSKirk McKusick 		    G_UNION_SUFFIX);
277c7996ddfSKirk McKusick 	LIST_FOREACH(gap, &lowerpp->aliases, ga_next)
278c7996ddfSKirk McKusick 		g_provider_add_alias(newpp, "%s%s", gap->ga_alias,
279c7996ddfSKirk McKusick 		    G_UNION_SUFFIX);
280c7996ddfSKirk McKusick 	lowercp = g_new_consumer(gp);
281c7996ddfSKirk McKusick 	lowercp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
282c7996ddfSKirk McKusick 	if ((error = g_attach(lowercp, lowerpp)) != 0) {
283c7996ddfSKirk McKusick 		gctl_error(req, "Error %d: cannot attach to provider %s.",
284c7996ddfSKirk McKusick 		    error, lowerpp->name);
285c7996ddfSKirk McKusick 		goto fail1;
286c7996ddfSKirk McKusick 	}
287c7996ddfSKirk McKusick 	/* request read and exclusive access for lower */
288c7996ddfSKirk McKusick 	if ((error = g_access(lowercp, 1, 0, 1)) != 0) {
289c7996ddfSKirk McKusick 		gctl_error(req, "Error %d: cannot obtain exclusive access to "
290c7996ddfSKirk McKusick 		    "%s.\n\tMust be unmounted or mounted read-only.", error,
291c7996ddfSKirk McKusick 		    lowerpp->name);
292c7996ddfSKirk McKusick 		goto fail2;
293c7996ddfSKirk McKusick 	}
294c7996ddfSKirk McKusick 	uppercp = g_new_consumer(gp);
295c7996ddfSKirk McKusick 	uppercp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
296c7996ddfSKirk McKusick 	if ((error = g_attach(uppercp, upperpp)) != 0) {
297c7996ddfSKirk McKusick 		gctl_error(req, "Error %d: cannot attach to provider %s.",
298c7996ddfSKirk McKusick 		    error, upperpp->name);
299c7996ddfSKirk McKusick 		goto fail3;
300c7996ddfSKirk McKusick 	}
301c7996ddfSKirk McKusick 	/* request read, write, and exclusive access for upper */
302c7996ddfSKirk McKusick 	if ((error = g_access(uppercp, 1, 1, 1)) != 0) {
303c7996ddfSKirk McKusick 		gctl_error(req, "Error %d: cannot obtain write access to %s.",
304c7996ddfSKirk McKusick 		    error, upperpp->name);
305c7996ddfSKirk McKusick 		goto fail4;
306c7996ddfSKirk McKusick 	}
307c7996ddfSKirk McKusick 	sc->sc_uppercp = uppercp;
308c7996ddfSKirk McKusick 	sc->sc_lowercp = lowercp;
309c7996ddfSKirk McKusick 
310c7996ddfSKirk McKusick 	newpp->flags |= (upperpp->flags & G_PF_ACCEPT_UNMAPPED) &
311c7996ddfSKirk McKusick 	    (lowerpp->flags & G_PF_ACCEPT_UNMAPPED);
312c7996ddfSKirk McKusick 	g_error_provider(newpp, 0);
313c7996ddfSKirk McKusick 	/*
314c7996ddfSKirk McKusick 	 * Allocate the map that tracks the sectors that have been written
315c7996ddfSKirk McKusick 	 * to the top layer. We use a 2-level hierarchy as that lets us
316c7996ddfSKirk McKusick 	 * map up to 1 petabyte using allocations of less than 33 Mb
317c7996ddfSKirk McKusick 	 * when using 4K byte sectors (or 268 Mb with 512 byte sectors).
318c7996ddfSKirk McKusick 	 *
319c7996ddfSKirk McKusick 	 * We totally populate the leaf nodes rather than allocating them
320c7996ddfSKirk McKusick 	 * as they are first used because their usage occurs in the
321c7996ddfSKirk McKusick 	 * g_union_start() routine that may be running in the g_down
322c7996ddfSKirk McKusick 	 * thread which cannot sleep.
323c7996ddfSKirk McKusick 	 */
324c7996ddfSKirk McKusick 	sc->sc_map_size = roundup(size / secsize, BITS_PER_ENTRY);
325c7996ddfSKirk McKusick 	needed = sc->sc_map_size / BITS_PER_ENTRY;
326c7996ddfSKirk McKusick 	for (sc->sc_root_size = 1;
327c7996ddfSKirk McKusick 	     sc->sc_root_size * sc->sc_root_size < needed;
328c7996ddfSKirk McKusick 	     sc->sc_root_size++)
329c7996ddfSKirk McKusick 		continue;
330c7996ddfSKirk McKusick 	sc->sc_writemap_root = g_malloc(sc->sc_root_size * sizeof(uint64_t *),
331c7996ddfSKirk McKusick 	    M_WAITOK | M_ZERO);
332c7996ddfSKirk McKusick 	sc->sc_leaf_size = sc->sc_root_size;
333c7996ddfSKirk McKusick 	sc->sc_bits_per_leaf = sc->sc_leaf_size * BITS_PER_ENTRY;
334c7996ddfSKirk McKusick 	sc->sc_leafused = g_malloc(roundup(sc->sc_root_size, BITS_PER_ENTRY),
335c7996ddfSKirk McKusick 	    M_WAITOK | M_ZERO);
336c7996ddfSKirk McKusick 	for (i = 0; i < sc->sc_root_size; i++)
337c7996ddfSKirk McKusick 		sc->sc_writemap_root[i] =
338c7996ddfSKirk McKusick 		    g_malloc(sc->sc_leaf_size * sizeof(uint64_t),
339c7996ddfSKirk McKusick 		    M_WAITOK | M_ZERO);
340c7996ddfSKirk McKusick 	sc->sc_writemap_memory =
341c7996ddfSKirk McKusick 	    (sc->sc_root_size + sc->sc_root_size * sc->sc_leaf_size) *
342c7996ddfSKirk McKusick 	    sizeof(uint64_t) + roundup(sc->sc_root_size, BITS_PER_ENTRY);
343c7996ddfSKirk McKusick 	if (verbose)
34490e29718SKirk McKusick 		gctl_msg(req, 0, "Device %s created with memory map size %jd.",
3453cf2f812SKirk McKusick 		    gp->name, (intmax_t)sc->sc_writemap_memory);
34690e29718SKirk McKusick 	gctl_post_messages(req);
347c7996ddfSKirk McKusick 	G_UNION_DEBUG(1, "Device %s created with memory map size %jd.",
3483cf2f812SKirk McKusick 	    gp->name, (intmax_t)sc->sc_writemap_memory);
349c7996ddfSKirk McKusick 	return;
350c7996ddfSKirk McKusick 
351c7996ddfSKirk McKusick fail4:
352c7996ddfSKirk McKusick 	g_detach(uppercp);
353c7996ddfSKirk McKusick fail3:
354c7996ddfSKirk McKusick 	g_destroy_consumer(uppercp);
355c7996ddfSKirk McKusick 	g_access(lowercp, -1, 0, -1);
356c7996ddfSKirk McKusick fail2:
357c7996ddfSKirk McKusick 	g_detach(lowercp);
358c7996ddfSKirk McKusick fail1:
359c7996ddfSKirk McKusick 	g_destroy_consumer(lowercp);
360c7996ddfSKirk McKusick 	g_destroy_provider(newpp);
361c7996ddfSKirk McKusick 	g_destroy_geom(gp);
362c7996ddfSKirk McKusick }
363c7996ddfSKirk McKusick 
364c7996ddfSKirk McKusick /*
365c7996ddfSKirk McKusick  * Fetch named option and verify that it is positive.
366c7996ddfSKirk McKusick  */
367c7996ddfSKirk McKusick static intmax_t
g_union_fetcharg(struct gctl_req * req,const char * name)368c7996ddfSKirk McKusick g_union_fetcharg(struct gctl_req *req, const char *name)
369c7996ddfSKirk McKusick {
370c7996ddfSKirk McKusick 	intmax_t *val;
371c7996ddfSKirk McKusick 
372c7996ddfSKirk McKusick 	val = gctl_get_paraml_opt(req, name, sizeof(*val));
373c7996ddfSKirk McKusick 	if (val == NULL)
374c7996ddfSKirk McKusick 		return (0);
375c7996ddfSKirk McKusick 	if (*val >= 0)
376c7996ddfSKirk McKusick 		return (*val);
37790e29718SKirk McKusick 	gctl_msg(req, EINVAL, "Invalid '%s' (%jd): negative value, "
37890e29718SKirk McKusick 	    "using default.", name, *val);
379c7996ddfSKirk McKusick 	return (0);
380c7996ddfSKirk McKusick }
381c7996ddfSKirk McKusick 
382c7996ddfSKirk McKusick /*
383c7996ddfSKirk McKusick  * Verify that a name is alphanumeric.
384c7996ddfSKirk McKusick  */
385c7996ddfSKirk McKusick static bool
g_union_verify_nprefix(const char * name)386c7996ddfSKirk McKusick g_union_verify_nprefix(const char *name)
387c7996ddfSKirk McKusick {
388c7996ddfSKirk McKusick 	int i;
389c7996ddfSKirk McKusick 
390c7996ddfSKirk McKusick 	for (i = 0; i < strlen(name); i++) {
391c7996ddfSKirk McKusick 		if (isalpha(name[i]) == 0 && isdigit(name[i]) == 0) {
392c7996ddfSKirk McKusick 			return (false);
393c7996ddfSKirk McKusick 		}
394c7996ddfSKirk McKusick 	}
395c7996ddfSKirk McKusick 	return (true);
396c7996ddfSKirk McKusick }
397c7996ddfSKirk McKusick 
398c7996ddfSKirk McKusick /*
399c7996ddfSKirk McKusick  * Destroy a union device.
400c7996ddfSKirk McKusick  */
401c7996ddfSKirk McKusick static void
g_union_ctl_destroy(struct gctl_req * req,struct g_class * mp,bool verbose)402c7996ddfSKirk McKusick g_union_ctl_destroy(struct gctl_req *req, struct g_class *mp, bool verbose)
403c7996ddfSKirk McKusick {
404c7996ddfSKirk McKusick 	int *nargs, *force, error, i;
405c7996ddfSKirk McKusick 	struct g_geom *gp;
406c7996ddfSKirk McKusick 	const char *name;
407c7996ddfSKirk McKusick 	char param[16];
408c7996ddfSKirk McKusick 
409c7996ddfSKirk McKusick 	g_topology_assert();
410c7996ddfSKirk McKusick 
411c7996ddfSKirk McKusick 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
412c7996ddfSKirk McKusick 	if (nargs == NULL) {
413c7996ddfSKirk McKusick 		gctl_error(req, "No '%s' argument.", "nargs");
414c7996ddfSKirk McKusick 		return;
415c7996ddfSKirk McKusick 	}
416c7996ddfSKirk McKusick 	if (*nargs <= 0) {
417c7996ddfSKirk McKusick 		gctl_error(req, "Missing device(s).");
418c7996ddfSKirk McKusick 		return;
419c7996ddfSKirk McKusick 	}
420c7996ddfSKirk McKusick 	force = gctl_get_paraml(req, "force", sizeof(*force));
421c7996ddfSKirk McKusick 	if (force == NULL) {
422c7996ddfSKirk McKusick 		gctl_error(req, "No 'force' argument.");
423c7996ddfSKirk McKusick 		return;
424c7996ddfSKirk McKusick 	}
425c7996ddfSKirk McKusick 
426c7996ddfSKirk McKusick 	for (i = 0; i < *nargs; i++) {
427c7996ddfSKirk McKusick 		snprintf(param, sizeof(param), "arg%d", i);
428c7996ddfSKirk McKusick 		name = gctl_get_asciiparam(req, param);
429c7996ddfSKirk McKusick 		if (name == NULL) {
43090e29718SKirk McKusick 			gctl_msg(req, EINVAL, "No '%s' argument.", param);
431c7996ddfSKirk McKusick 			continue;
432c7996ddfSKirk McKusick 		}
433c7996ddfSKirk McKusick 		if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
434c7996ddfSKirk McKusick 			name += strlen(_PATH_DEV);
435c7996ddfSKirk McKusick 		gp = g_union_find_geom(mp, name);
436c7996ddfSKirk McKusick 		if (gp == NULL) {
43790e29718SKirk McKusick 			gctl_msg(req, EINVAL, "Device %s is invalid.", name);
438c7996ddfSKirk McKusick 			continue;
439c7996ddfSKirk McKusick 		}
440c7996ddfSKirk McKusick 		error = g_union_destroy(verbose ? req : NULL, gp, *force);
441c7996ddfSKirk McKusick 		if (error != 0)
44290e29718SKirk McKusick 			gctl_msg(req, error, "Error %d: "
44390e29718SKirk McKusick 			    "cannot destroy device %s.", error, gp->name);
444c7996ddfSKirk McKusick 	}
445c7996ddfSKirk McKusick 	gctl_post_messages(req);
446c7996ddfSKirk McKusick }
447c7996ddfSKirk McKusick 
448c7996ddfSKirk McKusick /*
449c7996ddfSKirk McKusick  * Find a union geom.
450c7996ddfSKirk McKusick  */
451c7996ddfSKirk McKusick static struct g_geom *
g_union_find_geom(struct g_class * mp,const char * name)452c7996ddfSKirk McKusick g_union_find_geom(struct g_class *mp, const char *name)
453c7996ddfSKirk McKusick {
454c7996ddfSKirk McKusick 	struct g_geom *gp;
455c7996ddfSKirk McKusick 
456c7996ddfSKirk McKusick 	LIST_FOREACH(gp, &mp->geom, geom) {
457c7996ddfSKirk McKusick 		if (strcmp(gp->name, name) == 0)
458c7996ddfSKirk McKusick 			return (gp);
459c7996ddfSKirk McKusick 	}
460c7996ddfSKirk McKusick 	return (NULL);
461c7996ddfSKirk McKusick }
462c7996ddfSKirk McKusick 
463c7996ddfSKirk McKusick /*
464c7996ddfSKirk McKusick  * Zero out all the statistics associated with a union device.
465c7996ddfSKirk McKusick  */
466c7996ddfSKirk McKusick static void
g_union_ctl_reset(struct gctl_req * req,struct g_class * mp,bool verbose)467c7996ddfSKirk McKusick g_union_ctl_reset(struct gctl_req *req, struct g_class *mp, bool verbose)
468c7996ddfSKirk McKusick {
469c7996ddfSKirk McKusick 	struct g_union_softc *sc;
470c7996ddfSKirk McKusick 	struct g_provider *pp;
471c7996ddfSKirk McKusick 	struct g_geom *gp;
472c7996ddfSKirk McKusick 	char param[16];
473c7996ddfSKirk McKusick 	int i, *nargs;
474c7996ddfSKirk McKusick 
475c7996ddfSKirk McKusick 	g_topology_assert();
476c7996ddfSKirk McKusick 
477c7996ddfSKirk McKusick 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
478c7996ddfSKirk McKusick 	if (nargs == NULL) {
479c7996ddfSKirk McKusick 		gctl_error(req, "No '%s' argument.", "nargs");
480c7996ddfSKirk McKusick 		return;
481c7996ddfSKirk McKusick 	}
482c7996ddfSKirk McKusick 	if (*nargs <= 0) {
483c7996ddfSKirk McKusick 		gctl_error(req, "Missing device(s).");
484c7996ddfSKirk McKusick 		return;
485c7996ddfSKirk McKusick 	}
486c7996ddfSKirk McKusick 
487c7996ddfSKirk McKusick 	for (i = 0; i < *nargs; i++) {
488c7996ddfSKirk McKusick 		snprintf(param, sizeof(param), "arg%d", i);
489c7996ddfSKirk McKusick 		pp = gctl_get_provider(req, param);
490c7996ddfSKirk McKusick 		if (pp == NULL) {
49190e29718SKirk McKusick 			gctl_msg(req, EINVAL, "No '%s' argument.", param);
492c7996ddfSKirk McKusick 			continue;
493c7996ddfSKirk McKusick 		}
494c7996ddfSKirk McKusick 		gp = pp->geom;
495c7996ddfSKirk McKusick 		if (gp->class != mp) {
49690e29718SKirk McKusick 			gctl_msg(req, EINVAL, "Provider %s is invalid.",
497c7996ddfSKirk McKusick 			    pp->name);
498c7996ddfSKirk McKusick 			continue;
499c7996ddfSKirk McKusick 		}
500c7996ddfSKirk McKusick 		sc = gp->softc;
501c7996ddfSKirk McKusick 		sc->sc_reads = 0;
502c7996ddfSKirk McKusick 		sc->sc_writes = 0;
503c7996ddfSKirk McKusick 		sc->sc_deletes = 0;
504c7996ddfSKirk McKusick 		sc->sc_getattrs = 0;
505c7996ddfSKirk McKusick 		sc->sc_flushes = 0;
506c7996ddfSKirk McKusick 		sc->sc_speedups = 0;
507c7996ddfSKirk McKusick 		sc->sc_cmd0s = 0;
508c7996ddfSKirk McKusick 		sc->sc_cmd1s = 0;
509c7996ddfSKirk McKusick 		sc->sc_cmd2s = 0;
510c7996ddfSKirk McKusick 		sc->sc_readbytes = 0;
511c7996ddfSKirk McKusick 		sc->sc_wrotebytes = 0;
512c7996ddfSKirk McKusick 		if (verbose)
51390e29718SKirk McKusick 			gctl_msg(req, 0, "Device %s has been reset.", pp->name);
514c7996ddfSKirk McKusick 		G_UNION_DEBUG(1, "Device %s has been reset.", pp->name);
515c7996ddfSKirk McKusick 	}
516c7996ddfSKirk McKusick 	gctl_post_messages(req);
517c7996ddfSKirk McKusick }
518c7996ddfSKirk McKusick 
519c7996ddfSKirk McKusick /*
520c7996ddfSKirk McKusick  * Revert all write requests made to the top layer of the union.
521c7996ddfSKirk McKusick  */
522c7996ddfSKirk McKusick static void
g_union_ctl_revert(struct gctl_req * req,struct g_class * mp,bool verbose)523c7996ddfSKirk McKusick g_union_ctl_revert(struct gctl_req *req, struct g_class *mp, bool verbose)
524c7996ddfSKirk McKusick {
525c7996ddfSKirk McKusick 	struct g_union_softc *sc;
526c7996ddfSKirk McKusick 	struct g_provider *pp;
527c7996ddfSKirk McKusick 	struct g_geom *gp;
528c7996ddfSKirk McKusick 	char param[16];
529c7996ddfSKirk McKusick 	int i, *nargs;
530c7996ddfSKirk McKusick 
531c7996ddfSKirk McKusick 	g_topology_assert();
532c7996ddfSKirk McKusick 
533c7996ddfSKirk McKusick 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
534c7996ddfSKirk McKusick 	if (nargs == NULL) {
535c7996ddfSKirk McKusick 		gctl_error(req, "No '%s' argument.", "nargs");
536c7996ddfSKirk McKusick 		return;
537c7996ddfSKirk McKusick 	}
538c7996ddfSKirk McKusick 	if (*nargs <= 0) {
539c7996ddfSKirk McKusick 		gctl_error(req, "Missing device(s).");
540c7996ddfSKirk McKusick 		return;
541c7996ddfSKirk McKusick 	}
542c7996ddfSKirk McKusick 
543c7996ddfSKirk McKusick 	for (i = 0; i < *nargs; i++) {
544c7996ddfSKirk McKusick 		snprintf(param, sizeof(param), "arg%d", i);
545c7996ddfSKirk McKusick 		pp = gctl_get_provider(req, param);
546c7996ddfSKirk McKusick 		if (pp == NULL) {
54790e29718SKirk McKusick 			gctl_msg(req, EINVAL, "No '%s' argument.", param);
548c7996ddfSKirk McKusick 			continue;
549c7996ddfSKirk McKusick 		}
550c7996ddfSKirk McKusick 		gp = pp->geom;
551c7996ddfSKirk McKusick 		if (gp->class != mp) {
55290e29718SKirk McKusick 			gctl_msg(req, EINVAL, "Provider %s is invalid.",
55390e29718SKirk McKusick 			    pp->name);
554c7996ddfSKirk McKusick 			continue;
555c7996ddfSKirk McKusick 		}
556c7996ddfSKirk McKusick 		sc = gp->softc;
557c7996ddfSKirk McKusick 		if (g_union_get_writelock(sc) != 0) {
55890e29718SKirk McKusick 			gctl_msg(req, EINVAL, "Revert already in progress for "
559c7996ddfSKirk McKusick 			    "provider %s.", pp->name);
560c7996ddfSKirk McKusick 			continue;
561c7996ddfSKirk McKusick 		}
562c7996ddfSKirk McKusick 		/*
563c7996ddfSKirk McKusick 		 * No mount or other use of union is allowed.
564c7996ddfSKirk McKusick 		 */
565c7996ddfSKirk McKusick 		if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
56690e29718SKirk McKusick 			gctl_msg(req, EPERM, "Unable to get exclusive access "
56790e29718SKirk McKusick 			    "for reverting of %s;\n\t%s cannot be mounted or "
568c7996ddfSKirk McKusick 			    "otherwise open during a revert.",
569c7996ddfSKirk McKusick 			     pp->name, pp->name);
570c7996ddfSKirk McKusick 			g_union_rel_writelock(sc);
571c7996ddfSKirk McKusick 			continue;
572c7996ddfSKirk McKusick 		}
573c7996ddfSKirk McKusick 		g_union_revert(sc);
574c7996ddfSKirk McKusick 		g_union_rel_writelock(sc);
575c7996ddfSKirk McKusick 		if (verbose)
57690e29718SKirk McKusick 			gctl_msg(req, 0, "Device %s has been reverted.",
57790e29718SKirk McKusick 			    pp->name);
578c7996ddfSKirk McKusick 		G_UNION_DEBUG(1, "Device %s has been reverted.", pp->name);
579c7996ddfSKirk McKusick 	}
580c7996ddfSKirk McKusick 	gctl_post_messages(req);
581c7996ddfSKirk McKusick }
582c7996ddfSKirk McKusick 
583c7996ddfSKirk McKusick /*
584c7996ddfSKirk McKusick  * Revert union writes by zero'ing out the writemap.
585c7996ddfSKirk McKusick  */
586c7996ddfSKirk McKusick static void
g_union_revert(struct g_union_softc * sc)587c7996ddfSKirk McKusick g_union_revert(struct g_union_softc *sc)
588c7996ddfSKirk McKusick {
589c7996ddfSKirk McKusick 	int i;
590c7996ddfSKirk McKusick 
591c7996ddfSKirk McKusick 	G_WLOCK(sc);
592c7996ddfSKirk McKusick 	for (i = 0; i < sc->sc_root_size; i++)
593c7996ddfSKirk McKusick 		memset(sc->sc_writemap_root[i], 0,
594c7996ddfSKirk McKusick 		    sc->sc_leaf_size * sizeof(uint64_t));
595c7996ddfSKirk McKusick 	memset(sc->sc_leafused, 0, roundup(sc->sc_root_size, BITS_PER_ENTRY));
596c7996ddfSKirk McKusick 	G_WUNLOCK(sc);
597c7996ddfSKirk McKusick }
598c7996ddfSKirk McKusick 
599c7996ddfSKirk McKusick /*
600c7996ddfSKirk McKusick  * Commit all the writes made in the top layer to the lower layer.
601c7996ddfSKirk McKusick  */
602c7996ddfSKirk McKusick static void
g_union_ctl_commit(struct gctl_req * req,struct g_class * mp,bool verbose)603c7996ddfSKirk McKusick g_union_ctl_commit(struct gctl_req *req, struct g_class *mp, bool verbose)
604c7996ddfSKirk McKusick {
605c7996ddfSKirk McKusick 	struct g_union_softc *sc;
606c7996ddfSKirk McKusick 	struct g_provider *pp, *lowerpp;
607c7996ddfSKirk McKusick 	struct g_consumer *lowercp;
608c7996ddfSKirk McKusick 	struct g_geom *gp;
609c7996ddfSKirk McKusick 	struct bio *bp;
610c7996ddfSKirk McKusick 	char param[16];
611c7996ddfSKirk McKusick 	off_t len2rd, len2wt, savelen;
612c7996ddfSKirk McKusick 	int i, error, error1, *nargs, *force, *reboot;
613c7996ddfSKirk McKusick 
614c7996ddfSKirk McKusick 	g_topology_assert();
615c7996ddfSKirk McKusick 
616c7996ddfSKirk McKusick 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
617c7996ddfSKirk McKusick 	if (nargs == NULL) {
618c7996ddfSKirk McKusick 		gctl_error(req, "No '%s' argument.", "nargs");
619c7996ddfSKirk McKusick 		return;
620c7996ddfSKirk McKusick 	}
621c7996ddfSKirk McKusick 	if (*nargs <= 0) {
622c7996ddfSKirk McKusick 		gctl_error(req, "Missing device(s).");
623c7996ddfSKirk McKusick 		return;
624c7996ddfSKirk McKusick 	}
625c7996ddfSKirk McKusick 	force = gctl_get_paraml(req, "force", sizeof(*force));
626c7996ddfSKirk McKusick 	if (force == NULL) {
627c7996ddfSKirk McKusick 		gctl_error(req, "No 'force' argument.");
628c7996ddfSKirk McKusick 		return;
629c7996ddfSKirk McKusick 	}
630c7996ddfSKirk McKusick 	reboot = gctl_get_paraml(req, "reboot", sizeof(*reboot));
631c7996ddfSKirk McKusick 	if (reboot == NULL) {
632c7996ddfSKirk McKusick 		gctl_error(req, "No 'reboot' argument.");
633c7996ddfSKirk McKusick 		return;
634c7996ddfSKirk McKusick 	}
635c7996ddfSKirk McKusick 
636c7996ddfSKirk McKusick 	/* Get a bio buffer to do our I/O */
637c7996ddfSKirk McKusick 	bp = g_alloc_bio();
638c7996ddfSKirk McKusick 	bp->bio_data = g_malloc(MAXBSIZE, M_WAITOK);
639c7996ddfSKirk McKusick 	bp->bio_done = biodone;
640c7996ddfSKirk McKusick 	for (i = 0; i < *nargs; i++) {
641c7996ddfSKirk McKusick 		snprintf(param, sizeof(param), "arg%d", i);
642c7996ddfSKirk McKusick 		pp = gctl_get_provider(req, param);
643c7996ddfSKirk McKusick 		if (pp == NULL) {
64490e29718SKirk McKusick 			gctl_msg(req, EINVAL, "No '%s' argument.", param);
645c7996ddfSKirk McKusick 			continue;
646c7996ddfSKirk McKusick 		}
647c7996ddfSKirk McKusick 		gp = pp->geom;
648c7996ddfSKirk McKusick 		if (gp->class != mp) {
64990e29718SKirk McKusick 			gctl_msg(req, EINVAL, "Provider %s is invalid.",
65090e29718SKirk McKusick 			    pp->name);
651c7996ddfSKirk McKusick 			continue;
652c7996ddfSKirk McKusick 		}
653c7996ddfSKirk McKusick 		sc = gp->softc;
654c7996ddfSKirk McKusick 		if (g_union_get_writelock(sc) != 0) {
65590e29718SKirk McKusick 			gctl_msg(req, EINVAL, "Commit already in progress for "
656c7996ddfSKirk McKusick 			    "provider %s.", pp->name);
657c7996ddfSKirk McKusick 			continue;
658c7996ddfSKirk McKusick 		}
659c7996ddfSKirk McKusick 
660c7996ddfSKirk McKusick 		/* upgrade to write access for lower */
661c7996ddfSKirk McKusick 		lowercp = sc->sc_lowercp;
662c7996ddfSKirk McKusick 		lowerpp = lowercp->provider;
663c7996ddfSKirk McKusick 		/*
664c7996ddfSKirk McKusick 		 * No mount or other use of union is allowed, unless the
665c7996ddfSKirk McKusick 		 * -f flag is given which allows read-only mount or usage.
666c7996ddfSKirk McKusick 		 */
667c7996ddfSKirk McKusick 		if ((*force == false && pp->acr > 0) || pp->acw > 0 ||
668c7996ddfSKirk McKusick 		     pp->ace > 0) {
66990e29718SKirk McKusick 			gctl_msg(req, EPERM, "Unable to get exclusive access "
67090e29718SKirk McKusick 			    "for writing of %s.\n\tNote that %s cannot be "
67190e29718SKirk McKusick 			    "mounted or otherwise\n\topen during a commit "
67290e29718SKirk McKusick 			    "unless the -f flag is used.", pp->name, pp->name);
673c7996ddfSKirk McKusick 			g_union_rel_writelock(sc);
674c7996ddfSKirk McKusick 			continue;
675c7996ddfSKirk McKusick 		}
676c7996ddfSKirk McKusick 		/*
677c7996ddfSKirk McKusick 		 * No mount or other use of lower media is allowed, unless the
678c7996ddfSKirk McKusick 		 * -f flag is given which allows read-only mount or usage.
679c7996ddfSKirk McKusick 		 */
680c7996ddfSKirk McKusick 		if ((*force == false && lowerpp->acr > lowercp->acr) ||
681c7996ddfSKirk McKusick 		     lowerpp->acw > lowercp->acw ||
682c7996ddfSKirk McKusick 		     lowerpp->ace > lowercp->ace) {
68390e29718SKirk McKusick 			gctl_msg(req, EPERM, "provider %s is unable to get "
684c7996ddfSKirk McKusick 			    "exclusive access to %s\n\tfor writing. Note that "
685c7996ddfSKirk McKusick 			    "%s cannot be mounted or otherwise open\n\tduring "
686c7996ddfSKirk McKusick 			    "a commit unless the -f flag is used.", pp->name,
687c7996ddfSKirk McKusick 			    lowerpp->name, lowerpp->name);
688c7996ddfSKirk McKusick 			g_union_rel_writelock(sc);
689c7996ddfSKirk McKusick 			continue;
690c7996ddfSKirk McKusick 		}
691c7996ddfSKirk McKusick 		if ((error = g_access(lowercp, 0, 1, 0)) != 0) {
69290e29718SKirk McKusick 			gctl_msg(req, error, "Error %d: provider %s is unable "
69390e29718SKirk McKusick 			    "to access %s for writing.", error, pp->name,
694c7996ddfSKirk McKusick 			    lowerpp->name);
695c7996ddfSKirk McKusick 			g_union_rel_writelock(sc);
696c7996ddfSKirk McKusick 			continue;
697c7996ddfSKirk McKusick 		}
698c7996ddfSKirk McKusick 		g_topology_unlock();
699c7996ddfSKirk McKusick 		/* Loop over write map copying across written blocks */
700c7996ddfSKirk McKusick 		bp->bio_offset = 0;
701c7996ddfSKirk McKusick 		bp->bio_length = sc->sc_map_size * sc->sc_sectorsize;
702c7996ddfSKirk McKusick 		G_RLOCK(sc);
703c7996ddfSKirk McKusick 		error = 0;
704c7996ddfSKirk McKusick 		while (bp->bio_length > 0) {
705c7996ddfSKirk McKusick 			if (!g_union_getmap(bp, sc, &len2rd)) {
706c7996ddfSKirk McKusick 				/* not written, so skip */
707c7996ddfSKirk McKusick 				bp->bio_offset += len2rd;
708c7996ddfSKirk McKusick 				bp->bio_length -= len2rd;
709c7996ddfSKirk McKusick 				continue;
710c7996ddfSKirk McKusick 			}
711c7996ddfSKirk McKusick 			G_RUNLOCK(sc);
712c7996ddfSKirk McKusick 			/* need to read then write len2rd sectors */
713c7996ddfSKirk McKusick 			for ( ; len2rd > 0; len2rd -= len2wt) {
714c7996ddfSKirk McKusick 				/* limit ourselves to MAXBSIZE size I/Os */
715c7996ddfSKirk McKusick 				len2wt = len2rd;
716c7996ddfSKirk McKusick 				if (len2wt > MAXBSIZE)
717c7996ddfSKirk McKusick 					len2wt = MAXBSIZE;
718c7996ddfSKirk McKusick 				savelen = bp->bio_length;
719c7996ddfSKirk McKusick 				bp->bio_length = len2wt;
720c7996ddfSKirk McKusick 				bp->bio_cmd = BIO_READ;
721c7996ddfSKirk McKusick 				g_io_request(bp, sc->sc_uppercp);
722c7996ddfSKirk McKusick 				if ((error = biowait(bp, "rdunion")) != 0) {
72390e29718SKirk McKusick 					gctl_msg(req, error, "Commit read "
72490e29718SKirk McKusick 					    "error %d in provider %s, commit "
72590e29718SKirk McKusick 					    "aborted.", error, pp->name);
726c7996ddfSKirk McKusick 					goto cleanup;
727c7996ddfSKirk McKusick 				}
728c7996ddfSKirk McKusick 				bp->bio_flags &= ~BIO_DONE;
729c7996ddfSKirk McKusick 				bp->bio_cmd = BIO_WRITE;
730c7996ddfSKirk McKusick 				g_io_request(bp, lowercp);
731c7996ddfSKirk McKusick 				if ((error = biowait(bp, "wtunion")) != 0) {
73290e29718SKirk McKusick 					gctl_msg(req, error, "Commit write "
73390e29718SKirk McKusick 					    "error %d in provider %s, commit "
73490e29718SKirk McKusick 					    "aborted.", error, pp->name);
735c7996ddfSKirk McKusick 					goto cleanup;
736c7996ddfSKirk McKusick 				}
737c7996ddfSKirk McKusick 				bp->bio_flags &= ~BIO_DONE;
738c7996ddfSKirk McKusick 				bp->bio_offset += len2wt;
739c7996ddfSKirk McKusick 				bp->bio_length = savelen - len2wt;
740c7996ddfSKirk McKusick 			}
741c7996ddfSKirk McKusick 			G_RLOCK(sc);
742c7996ddfSKirk McKusick 		}
743c7996ddfSKirk McKusick 		G_RUNLOCK(sc);
744c7996ddfSKirk McKusick 		/* clear the write map */
745c7996ddfSKirk McKusick 		g_union_revert(sc);
746c7996ddfSKirk McKusick cleanup:
747c7996ddfSKirk McKusick 		g_topology_lock();
748c7996ddfSKirk McKusick 		/* return lower to previous access */
749c7996ddfSKirk McKusick 		if ((error1 = g_access(lowercp, 0, -1, 0)) != 0) {
750c7996ddfSKirk McKusick 			G_UNION_DEBUG(2, "Error %d: device %s could not reset "
751c7996ddfSKirk McKusick 			    "access to %s (r=0 w=-1 e=0).", error1, pp->name,
752c7996ddfSKirk McKusick 			    lowerpp->name);
753c7996ddfSKirk McKusick 		}
754c7996ddfSKirk McKusick 		g_union_rel_writelock(sc);
755c7996ddfSKirk McKusick 		if (error == 0 && verbose)
75690e29718SKirk McKusick 			gctl_msg(req, 0, "Device %s has been committed.",
757c7996ddfSKirk McKusick 			    pp->name);
758c7996ddfSKirk McKusick 		G_UNION_DEBUG(1, "Device %s has been committed.", pp->name);
759c7996ddfSKirk McKusick 	}
760c7996ddfSKirk McKusick 	gctl_post_messages(req);
761c7996ddfSKirk McKusick 	g_free(bp->bio_data);
762c7996ddfSKirk McKusick 	g_destroy_bio(bp);
763c7996ddfSKirk McKusick 	if (*reboot)
764c7996ddfSKirk McKusick 		kern_reboot(RB_AUTOBOOT);
765c7996ddfSKirk McKusick }
766c7996ddfSKirk McKusick 
767c7996ddfSKirk McKusick /*
768c7996ddfSKirk McKusick  * Generally allow access unless a commit is in progress.
769c7996ddfSKirk McKusick  */
770c7996ddfSKirk McKusick static int
g_union_access(struct g_provider * pp,int r,int w,int e)771c7996ddfSKirk McKusick g_union_access(struct g_provider *pp, int r, int w, int e)
772c7996ddfSKirk McKusick {
773c7996ddfSKirk McKusick 	struct g_union_softc *sc;
774c7996ddfSKirk McKusick 
775c7996ddfSKirk McKusick 	sc = pp->geom->softc;
776c7996ddfSKirk McKusick 	if (sc == NULL) {
777c7996ddfSKirk McKusick 		if (r <= 0 && w <= 0 && e <= 0)
778c7996ddfSKirk McKusick 			return (0);
779c7996ddfSKirk McKusick 		return (ENXIO);
780c7996ddfSKirk McKusick 	}
781c7996ddfSKirk McKusick 	r += pp->acr;
782c7996ddfSKirk McKusick 	w += pp->acw;
783c7996ddfSKirk McKusick 	e += pp->ace;
784c7996ddfSKirk McKusick 	if (g_union_get_writelock(sc) != 0) {
785c7996ddfSKirk McKusick 		if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0)
786c7996ddfSKirk McKusick 			return (0);
787c7996ddfSKirk McKusick 		return (EBUSY);
788c7996ddfSKirk McKusick 	}
789c7996ddfSKirk McKusick 	g_union_rel_writelock(sc);
790c7996ddfSKirk McKusick 	return (0);
791c7996ddfSKirk McKusick }
792c7996ddfSKirk McKusick 
793c7996ddfSKirk McKusick /*
794c7996ddfSKirk McKusick  * Initiate an I/O operation on the union device.
795c7996ddfSKirk McKusick  */
796c7996ddfSKirk McKusick static void
g_union_start(struct bio * bp)797c7996ddfSKirk McKusick g_union_start(struct bio *bp)
798c7996ddfSKirk McKusick {
799c7996ddfSKirk McKusick 	struct g_union_softc *sc;
800c7996ddfSKirk McKusick 	struct g_union_wip *wip;
801c7996ddfSKirk McKusick 	struct bio *cbp;
802c7996ddfSKirk McKusick 
803c7996ddfSKirk McKusick 	sc = bp->bio_to->geom->softc;
804c7996ddfSKirk McKusick 	if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
805c7996ddfSKirk McKusick 		wip = g_malloc(sizeof(*wip), M_NOWAIT);
806c7996ddfSKirk McKusick 		if (wip == NULL) {
807c7996ddfSKirk McKusick 			g_io_deliver(bp, ENOMEM);
808c7996ddfSKirk McKusick 			return;
809c7996ddfSKirk McKusick 		}
810c7996ddfSKirk McKusick 		TAILQ_INIT(&wip->wip_waiting);
811c7996ddfSKirk McKusick 		wip->wip_bp = bp;
812c7996ddfSKirk McKusick 		wip->wip_sc = sc;
813c7996ddfSKirk McKusick 		wip->wip_start = bp->bio_offset + sc->sc_offset;
814c7996ddfSKirk McKusick 		wip->wip_end = wip->wip_start + bp->bio_length - 1;
815c7996ddfSKirk McKusick 		wip->wip_numios = 1;
816c7996ddfSKirk McKusick 		wip->wip_error = 0;
817c7996ddfSKirk McKusick 		g_union_doio(wip);
818c7996ddfSKirk McKusick 		return;
819c7996ddfSKirk McKusick 	}
820c7996ddfSKirk McKusick 
821c7996ddfSKirk McKusick 	/*
822c7996ddfSKirk McKusick 	 * All commands other than read and write are passed through to
823c7996ddfSKirk McKusick 	 * the upper-level device since it is writable and thus able to
824c7996ddfSKirk McKusick 	 * respond to delete, flush, and speedup requests.
825c7996ddfSKirk McKusick 	 */
826c7996ddfSKirk McKusick 	cbp = g_clone_bio(bp);
827c7996ddfSKirk McKusick 	if (cbp == NULL) {
828c7996ddfSKirk McKusick 		g_io_deliver(bp, ENOMEM);
829c7996ddfSKirk McKusick 		return;
830c7996ddfSKirk McKusick 	}
831c7996ddfSKirk McKusick 	cbp->bio_offset = bp->bio_offset + sc->sc_offset;
832c7996ddfSKirk McKusick 	cbp->bio_done = g_std_done;
833c7996ddfSKirk McKusick 
834c7996ddfSKirk McKusick 	switch (cbp->bio_cmd) {
835c7996ddfSKirk McKusick 	case BIO_DELETE:
836c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Delete request received.");
837c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_deletes, 1);
838c7996ddfSKirk McKusick 		break;
839c7996ddfSKirk McKusick 	case BIO_GETATTR:
840c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Getattr request received.");
841c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_getattrs, 1);
842c7996ddfSKirk McKusick 		if (strcmp(cbp->bio_attribute, "GEOM::kerneldump") != 0)
843c7996ddfSKirk McKusick 			/* forward the GETATTR to the lower-level device */
844c7996ddfSKirk McKusick 			break;
845c7996ddfSKirk McKusick 		g_union_kerneldump(bp, sc);
846c7996ddfSKirk McKusick 		return;
847c7996ddfSKirk McKusick 	case BIO_FLUSH:
848c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Flush request received.");
849c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_flushes, 1);
850c7996ddfSKirk McKusick 		break;
851c7996ddfSKirk McKusick 	case BIO_SPEEDUP:
852c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Speedup request received.");
853c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_speedups, 1);
854c7996ddfSKirk McKusick 		break;
855c7996ddfSKirk McKusick 	case BIO_CMD0:
856c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Cmd0 request received.");
857c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_cmd0s, 1);
858c7996ddfSKirk McKusick 		break;
859c7996ddfSKirk McKusick 	case BIO_CMD1:
860c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Cmd1 request received.");
861c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_cmd1s, 1);
862c7996ddfSKirk McKusick 		break;
863c7996ddfSKirk McKusick 	case BIO_CMD2:
864c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Cmd2 request received.");
865c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_cmd2s, 1);
866c7996ddfSKirk McKusick 		break;
867c7996ddfSKirk McKusick 	default:
868c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Unknown (%d) request received.",
869c7996ddfSKirk McKusick 		    cbp->bio_cmd);
870c7996ddfSKirk McKusick 		break;
871c7996ddfSKirk McKusick 	}
872c7996ddfSKirk McKusick 	g_io_request(cbp, sc->sc_uppercp);
873c7996ddfSKirk McKusick }
874c7996ddfSKirk McKusick 
875c7996ddfSKirk McKusick /*
876c7996ddfSKirk McKusick  * Initiate a read or write operation on the union device.
877c7996ddfSKirk McKusick  */
878c7996ddfSKirk McKusick static void
g_union_doio(struct g_union_wip * wip)879c7996ddfSKirk McKusick g_union_doio(struct g_union_wip *wip)
880c7996ddfSKirk McKusick {
881c7996ddfSKirk McKusick 	struct g_union_softc *sc;
882c7996ddfSKirk McKusick 	struct g_consumer *cp, *firstcp;
883c7996ddfSKirk McKusick 	struct g_union_wip *activewip;
884c7996ddfSKirk McKusick 	struct bio *cbp, *firstbp;
885c7996ddfSKirk McKusick 	off_t rdlen, len2rd, offset;
886c7996ddfSKirk McKusick 	int iocnt, needstoblock;
887c7996ddfSKirk McKusick 	char *level;
888c7996ddfSKirk McKusick 
889c7996ddfSKirk McKusick 	/*
890c7996ddfSKirk McKusick 	 * To maintain consistency, we cannot allow concurrent reads
891c7996ddfSKirk McKusick 	 * or writes to the same block.
892c7996ddfSKirk McKusick 	 *
893c7996ddfSKirk McKusick 	 * A work-in-progress (wip) structure is allocated for each
894c7996ddfSKirk McKusick 	 * read or write request. All active requests are kept on the
895c7996ddfSKirk McKusick 	 * softc sc_wiplist. As each request arrives, it is checked to
896c7996ddfSKirk McKusick 	 * see if it overlaps any of the active entries. If it does not
897c7996ddfSKirk McKusick 	 * overlap, then it is added to the active list and initiated.
898c7996ddfSKirk McKusick 	 * If it does overlap an active entry, it is added to the
899c7996ddfSKirk McKusick 	 * wip_waiting list for the active entry that it overlaps.
900c7996ddfSKirk McKusick 	 * When an active entry completes, it restarts all the requests
901c7996ddfSKirk McKusick 	 * on its wip_waiting list.
902c7996ddfSKirk McKusick 	 */
903c7996ddfSKirk McKusick 	sc = wip->wip_sc;
904c7996ddfSKirk McKusick 	G_WLOCK(sc);
905c7996ddfSKirk McKusick 	TAILQ_FOREACH(activewip, &sc->sc_wiplist, wip_next) {
906c7996ddfSKirk McKusick 		if (wip->wip_end < activewip->wip_start ||
907c7996ddfSKirk McKusick 		    wip->wip_start > activewip->wip_end)
908c7996ddfSKirk McKusick 			continue;
909c7996ddfSKirk McKusick 		needstoblock = 1;
910c7996ddfSKirk McKusick 		if (wip->wip_bp->bio_cmd == BIO_WRITE)
911c7996ddfSKirk McKusick 			if (activewip->wip_bp->bio_cmd == BIO_WRITE)
912c7996ddfSKirk McKusick 				sc->sc_writeblockwrite += 1;
913c7996ddfSKirk McKusick 			else
914c7996ddfSKirk McKusick 				sc->sc_readblockwrite += 1;
915c7996ddfSKirk McKusick 		else
916c7996ddfSKirk McKusick 			if (activewip->wip_bp->bio_cmd == BIO_WRITE)
917c7996ddfSKirk McKusick 				sc->sc_writeblockread += 1;
918c7996ddfSKirk McKusick 			else {
919c7996ddfSKirk McKusick 				sc->sc_readcurrentread += 1;
920c7996ddfSKirk McKusick 				needstoblock = 0;
921c7996ddfSKirk McKusick 			}
922c7996ddfSKirk McKusick 		/* Put request on a waiting list if necessary */
923c7996ddfSKirk McKusick 		if (needstoblock) {
924c7996ddfSKirk McKusick 			TAILQ_INSERT_TAIL(&activewip->wip_waiting, wip,
925c7996ddfSKirk McKusick 			    wip_next);
926c7996ddfSKirk McKusick 			G_WUNLOCK(sc);
927c7996ddfSKirk McKusick 			return;
928c7996ddfSKirk McKusick 		}
929c7996ddfSKirk McKusick 	}
930c7996ddfSKirk McKusick 	/* Put request on the active list */
931c7996ddfSKirk McKusick 	TAILQ_INSERT_TAIL(&sc->sc_wiplist, wip, wip_next);
932c7996ddfSKirk McKusick 
933c7996ddfSKirk McKusick 	/*
934c7996ddfSKirk McKusick 	 * Process I/O requests that have been cleared to go.
935c7996ddfSKirk McKusick 	 */
936c7996ddfSKirk McKusick 	cbp = g_clone_bio(wip->wip_bp);
937c7996ddfSKirk McKusick 	if (cbp == NULL) {
938c7996ddfSKirk McKusick 		TAILQ_REMOVE(&sc->sc_wiplist, wip, wip_next);
939c7996ddfSKirk McKusick 		G_WUNLOCK(sc);
940c7996ddfSKirk McKusick 		KASSERT(TAILQ_FIRST(&wip->wip_waiting) == NULL,
941c7996ddfSKirk McKusick 		    ("g_union_doio: non-empty work-in-progress waiting queue"));
942c7996ddfSKirk McKusick 		g_io_deliver(wip->wip_bp, ENOMEM);
943c7996ddfSKirk McKusick 		g_free(wip);
944c7996ddfSKirk McKusick 		return;
945c7996ddfSKirk McKusick 	}
946c7996ddfSKirk McKusick 	G_WUNLOCK(sc);
947c7996ddfSKirk McKusick 	cbp->bio_caller1 = wip;
948c7996ddfSKirk McKusick 	cbp->bio_done = g_union_done;
949c7996ddfSKirk McKusick 	cbp->bio_offset = wip->wip_start;
950c7996ddfSKirk McKusick 
951c7996ddfSKirk McKusick 	/*
952c7996ddfSKirk McKusick 	 * Writes are always done to the top level. The blocks that
953c7996ddfSKirk McKusick 	 * are written are recorded in the bitmap when the I/O completes.
954c7996ddfSKirk McKusick 	 */
955c7996ddfSKirk McKusick 	if (cbp->bio_cmd == BIO_WRITE) {
956c7996ddfSKirk McKusick 		G_UNION_LOGREQ(cbp, "Sending %jd byte write request to upper "
957c7996ddfSKirk McKusick 		    "level.", cbp->bio_length);
958c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_writes, 1);
959c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_wrotebytes, cbp->bio_length);
960c7996ddfSKirk McKusick 		g_io_request(cbp, sc->sc_uppercp);
961c7996ddfSKirk McKusick 		return;
962c7996ddfSKirk McKusick 	}
963c7996ddfSKirk McKusick 	/*
964c7996ddfSKirk McKusick 	 * The usual read case is that we either read the top layer
965c7996ddfSKirk McKusick 	 * if the block has been previously written or the bottom layer
966c7996ddfSKirk McKusick 	 * if it has not been written. However, it is possible that
967c7996ddfSKirk McKusick 	 * only part of the block has been written, For example we may
968c7996ddfSKirk McKusick 	 * have written a UFS/FFS file fragment comprising several
969c7996ddfSKirk McKusick 	 * sectors out of an 8-sector block.  Here, if the entire
970c7996ddfSKirk McKusick 	 * 8-sector block is read for example by a snapshot needing
971c7996ddfSKirk McKusick 	 * to copy the full block, then we need to read the written
972c7996ddfSKirk McKusick 	 * sectors from the upper level and the unwritten sectors from
973c7996ddfSKirk McKusick 	 * the lower level. We do this by alternately reading from the
974c7996ddfSKirk McKusick 	 * top and bottom layers until we complete the read. We
975c7996ddfSKirk McKusick 	 * simplify for the common case to just do the I/O and return.
976c7996ddfSKirk McKusick 	 */
977c7996ddfSKirk McKusick 	atomic_add_long(&sc->sc_reads, 1);
978c7996ddfSKirk McKusick 	atomic_add_long(&sc->sc_readbytes, cbp->bio_length);
979c7996ddfSKirk McKusick 	rdlen = cbp->bio_length;
980c7996ddfSKirk McKusick 	offset = 0;
981c7996ddfSKirk McKusick 	for (iocnt = 0; ; iocnt++) {
982c7996ddfSKirk McKusick 		if (g_union_getmap(cbp, sc, &len2rd)) {
983c7996ddfSKirk McKusick 			/* read top */
984c7996ddfSKirk McKusick 			cp = sc->sc_uppercp;
985c7996ddfSKirk McKusick 			level = "upper";
986c7996ddfSKirk McKusick 		} else {
987c7996ddfSKirk McKusick 			/* read bottom */
988c7996ddfSKirk McKusick 			cp = sc->sc_lowercp;
989c7996ddfSKirk McKusick 			level = "lower";
990c7996ddfSKirk McKusick 		}
991c7996ddfSKirk McKusick 		/* Check if only a single read is required */
992c7996ddfSKirk McKusick 		if (iocnt == 0 && rdlen == len2rd) {
993c7996ddfSKirk McKusick 			G_UNION_LOGREQLVL((cp == sc->sc_uppercp) ?
994c7996ddfSKirk McKusick 			    3 : 4, cbp, "Sending %jd byte read "
995c7996ddfSKirk McKusick 			    "request to %s level.", len2rd, level);
996c7996ddfSKirk McKusick 			g_io_request(cbp, cp);
997c7996ddfSKirk McKusick 			return;
998c7996ddfSKirk McKusick 		}
999c7996ddfSKirk McKusick 		cbp->bio_length = len2rd;
1000c7996ddfSKirk McKusick 		if ((cbp->bio_flags & BIO_UNMAPPED) != 0)
1001c7996ddfSKirk McKusick 			cbp->bio_ma_offset += offset;
1002c7996ddfSKirk McKusick 		else
1003c7996ddfSKirk McKusick 			cbp->bio_data += offset;
1004c7996ddfSKirk McKusick 		offset += len2rd;
1005c7996ddfSKirk McKusick 		rdlen -= len2rd;
1006c7996ddfSKirk McKusick 		G_UNION_LOGREQLVL(3, cbp, "Sending %jd byte read "
1007c7996ddfSKirk McKusick 		    "request to %s level.", len2rd, level);
1008c7996ddfSKirk McKusick 		/*
1009c7996ddfSKirk McKusick 		 * To avoid prematurely notifying our consumer
1010c7996ddfSKirk McKusick 		 * that their I/O has completed, we have to delay
1011c7996ddfSKirk McKusick 		 * issuing our first I/O request until we have
1012c7996ddfSKirk McKusick 		 * issued all the additional I/O requests.
1013c7996ddfSKirk McKusick 		 */
1014c7996ddfSKirk McKusick 		if (iocnt > 0) {
1015c7996ddfSKirk McKusick 			atomic_add_long(&wip->wip_numios, 1);
1016c7996ddfSKirk McKusick 			g_io_request(cbp, cp);
1017c7996ddfSKirk McKusick 		} else {
1018c7996ddfSKirk McKusick 			firstbp = cbp;
1019c7996ddfSKirk McKusick 			firstcp = cp;
1020c7996ddfSKirk McKusick 		}
1021c7996ddfSKirk McKusick 		if (rdlen == 0)
1022c7996ddfSKirk McKusick 			break;
1023c7996ddfSKirk McKusick 		/* set up for next read */
1024c7996ddfSKirk McKusick 		cbp = g_clone_bio(wip->wip_bp);
1025c7996ddfSKirk McKusick 		if (cbp == NULL) {
1026c7996ddfSKirk McKusick 			wip->wip_error = ENOMEM;
1027c7996ddfSKirk McKusick 			atomic_add_long(&wip->wip_numios, -1);
1028c7996ddfSKirk McKusick 			break;
1029c7996ddfSKirk McKusick 		}
1030c7996ddfSKirk McKusick 		cbp->bio_caller1 = wip;
1031c7996ddfSKirk McKusick 		cbp->bio_done = g_union_done;
1032c7996ddfSKirk McKusick 		cbp->bio_offset += offset;
1033c7996ddfSKirk McKusick 		cbp->bio_length = rdlen;
1034c7996ddfSKirk McKusick 		atomic_add_long(&sc->sc_reads, 1);
1035c7996ddfSKirk McKusick 	}
1036c7996ddfSKirk McKusick 	/* We have issued all our I/O, so start the first one */
1037c7996ddfSKirk McKusick 	g_io_request(firstbp, firstcp);
1038c7996ddfSKirk McKusick 	return;
1039c7996ddfSKirk McKusick }
1040c7996ddfSKirk McKusick 
1041c7996ddfSKirk McKusick /*
1042c7996ddfSKirk McKusick  * Used when completing a union I/O operation.
1043c7996ddfSKirk McKusick  */
1044c7996ddfSKirk McKusick static void
g_union_done(struct bio * bp)1045c7996ddfSKirk McKusick g_union_done(struct bio *bp)
1046c7996ddfSKirk McKusick {
1047c7996ddfSKirk McKusick 	struct g_union_wip *wip, *waitingwip;
1048c7996ddfSKirk McKusick 	struct g_union_softc *sc;
1049c7996ddfSKirk McKusick 
1050c7996ddfSKirk McKusick 	wip = bp->bio_caller1;
1051c7996ddfSKirk McKusick 	if (wip->wip_error != 0 && bp->bio_error == 0)
1052c7996ddfSKirk McKusick 		bp->bio_error = wip->wip_error;
1053c7996ddfSKirk McKusick 	wip->wip_error = 0;
1054c7996ddfSKirk McKusick 	if (atomic_fetchadd_long(&wip->wip_numios, -1) == 1) {
1055c7996ddfSKirk McKusick 		sc = wip->wip_sc;
1056c7996ddfSKirk McKusick 		G_WLOCK(sc);
1057c7996ddfSKirk McKusick 		if (bp->bio_cmd == BIO_WRITE)
1058c7996ddfSKirk McKusick 			g_union_setmap(bp, sc);
1059c7996ddfSKirk McKusick 		TAILQ_REMOVE(&sc->sc_wiplist, wip, wip_next);
1060c7996ddfSKirk McKusick 		G_WUNLOCK(sc);
1061c7996ddfSKirk McKusick 		while ((waitingwip = TAILQ_FIRST(&wip->wip_waiting)) != NULL) {
1062c7996ddfSKirk McKusick 			TAILQ_REMOVE(&wip->wip_waiting, waitingwip, wip_next);
1063c7996ddfSKirk McKusick 			g_union_doio(waitingwip);
1064c7996ddfSKirk McKusick 		}
1065c7996ddfSKirk McKusick 		g_free(wip);
1066c7996ddfSKirk McKusick 	}
1067c7996ddfSKirk McKusick 	g_std_done(bp);
1068c7996ddfSKirk McKusick }
1069c7996ddfSKirk McKusick 
1070c7996ddfSKirk McKusick /*
1071c7996ddfSKirk McKusick  * Record blocks that have been written in the map.
1072c7996ddfSKirk McKusick  */
1073c7996ddfSKirk McKusick static void
g_union_setmap(struct bio * bp,struct g_union_softc * sc)1074c7996ddfSKirk McKusick g_union_setmap(struct bio *bp, struct g_union_softc *sc)
1075c7996ddfSKirk McKusick {
1076c7996ddfSKirk McKusick 	size_t root_idx;
1077c7996ddfSKirk McKusick 	uint64_t **leaf;
1078c7996ddfSKirk McKusick 	uint64_t *wordp;
1079c7996ddfSKirk McKusick 	off_t start, numsec;
1080c7996ddfSKirk McKusick 
1081c7996ddfSKirk McKusick 	G_WLOCKOWNED(sc);
1082c7996ddfSKirk McKusick 	KASSERT(bp->bio_offset % sc->sc_sectorsize == 0,
1083c7996ddfSKirk McKusick 	    ("g_union_setmap: offset not on sector boundry"));
1084c7996ddfSKirk McKusick 	KASSERT(bp->bio_length % sc->sc_sectorsize == 0,
1085c7996ddfSKirk McKusick 	    ("g_union_setmap: length not a multiple of sectors"));
1086c7996ddfSKirk McKusick 	start = bp->bio_offset / sc->sc_sectorsize;
1087c7996ddfSKirk McKusick 	numsec = bp->bio_length / sc->sc_sectorsize;
1088c7996ddfSKirk McKusick 	KASSERT(start + numsec <= sc->sc_map_size,
1089c7996ddfSKirk McKusick 	    ("g_union_setmap: block %jd is out of range", start + numsec));
1090c7996ddfSKirk McKusick 	for ( ; numsec > 0; numsec--, start++) {
1091c7996ddfSKirk McKusick 		root_idx = start / sc->sc_bits_per_leaf;
1092c7996ddfSKirk McKusick 		leaf = &sc->sc_writemap_root[root_idx];
1093c7996ddfSKirk McKusick 		wordp = &(*leaf)
1094c7996ddfSKirk McKusick 		    [(start % sc->sc_bits_per_leaf) / BITS_PER_ENTRY];
1095c7996ddfSKirk McKusick 		*wordp |= 1ULL << (start % BITS_PER_ENTRY);
1096c7996ddfSKirk McKusick 		sc->sc_leafused[root_idx / BITS_PER_ENTRY] |=
1097c7996ddfSKirk McKusick 		    1ULL << (root_idx % BITS_PER_ENTRY);
1098c7996ddfSKirk McKusick 	}
1099c7996ddfSKirk McKusick }
1100c7996ddfSKirk McKusick 
1101c7996ddfSKirk McKusick /*
1102c7996ddfSKirk McKusick  * Check map to determine whether blocks have been written.
1103c7996ddfSKirk McKusick  *
1104c7996ddfSKirk McKusick  * Return true if they have been written so should be read from the top
1105c7996ddfSKirk McKusick  * layer. Return false if they have not been written so should be read
1106c7996ddfSKirk McKusick  * from the bottom layer. Return in len2read the bytes to be read. See
1107c7996ddfSKirk McKusick  * the comment above the BIO_READ implementation in g_union_start() for
1108c7996ddfSKirk McKusick  * an explantion of why len2read may be shorter than the buffer length.
1109c7996ddfSKirk McKusick  */
1110c7996ddfSKirk McKusick static bool
g_union_getmap(struct bio * bp,struct g_union_softc * sc,off_t * len2read)1111c7996ddfSKirk McKusick g_union_getmap(struct bio *bp, struct g_union_softc *sc, off_t *len2read)
1112c7996ddfSKirk McKusick {
1113c7996ddfSKirk McKusick 	off_t start, numsec, leafresid, bitloc;
1114c7996ddfSKirk McKusick 	bool first, maptype, retval;
1115c7996ddfSKirk McKusick 	uint64_t *leaf, word;
1116c7996ddfSKirk McKusick 	size_t root_idx;
1117c7996ddfSKirk McKusick 
1118c7996ddfSKirk McKusick 	KASSERT(bp->bio_offset % sc->sc_sectorsize == 0,
1119c7996ddfSKirk McKusick 	    ("g_union_getmap: offset not on sector boundry"));
1120c7996ddfSKirk McKusick 	KASSERT(bp->bio_length % sc->sc_sectorsize == 0,
1121c7996ddfSKirk McKusick 	    ("g_union_getmap: length not a multiple of sectors"));
1122c7996ddfSKirk McKusick 	start = bp->bio_offset / sc->sc_sectorsize;
1123c7996ddfSKirk McKusick 	numsec = bp->bio_length / sc->sc_sectorsize;
1124c7996ddfSKirk McKusick 	G_UNION_DEBUG(4, "g_union_getmap: check %jd sectors starting at %jd\n",
1125c7996ddfSKirk McKusick 	    numsec, start);
1126c7996ddfSKirk McKusick 	KASSERT(start + numsec <= sc->sc_map_size,
1127c7996ddfSKirk McKusick 	    ("g_union_getmap: block %jd is out of range", start + numsec));
1128c7996ddfSKirk McKusick 		root_idx = start / sc->sc_bits_per_leaf;
1129c7996ddfSKirk McKusick 	first = true;
1130c7996ddfSKirk McKusick 	maptype = false;
1131c7996ddfSKirk McKusick 	while (numsec > 0) {
1132c7996ddfSKirk McKusick 		/* Check first if the leaf records any written sectors */
1133c7996ddfSKirk McKusick 		root_idx = start / sc->sc_bits_per_leaf;
1134c7996ddfSKirk McKusick 		leafresid = sc->sc_bits_per_leaf -
1135c7996ddfSKirk McKusick 		    (start % sc->sc_bits_per_leaf);
1136c7996ddfSKirk McKusick 		if (((sc->sc_leafused[root_idx / BITS_PER_ENTRY]) &
1137c7996ddfSKirk McKusick 		    (1ULL << (root_idx % BITS_PER_ENTRY))) == 0) {
1138c7996ddfSKirk McKusick 			if (first) {
1139c7996ddfSKirk McKusick 				maptype = false;
1140c7996ddfSKirk McKusick 				first = false;
1141c7996ddfSKirk McKusick 			}
1142c7996ddfSKirk McKusick 			if (maptype)
1143c7996ddfSKirk McKusick 				break;
1144c7996ddfSKirk McKusick 			numsec -= leafresid;
1145c7996ddfSKirk McKusick 			start += leafresid;
1146c7996ddfSKirk McKusick 			continue;
1147c7996ddfSKirk McKusick 		}
1148c7996ddfSKirk McKusick 		/* Check up to a word boundry, then check word by word */
1149c7996ddfSKirk McKusick 		leaf = sc->sc_writemap_root[root_idx];
1150c7996ddfSKirk McKusick 		word = leaf[(start % sc->sc_bits_per_leaf) / BITS_PER_ENTRY];
1151c7996ddfSKirk McKusick 		bitloc = start % BITS_PER_ENTRY;
1152c7996ddfSKirk McKusick 		if (bitloc == 0 && (word == 0 || word == ~0)) {
1153c7996ddfSKirk McKusick 			if (first) {
1154c7996ddfSKirk McKusick 				if (word == 0)
1155c7996ddfSKirk McKusick 					maptype = false;
1156c7996ddfSKirk McKusick 				else
1157c7996ddfSKirk McKusick 					maptype = true;
1158c7996ddfSKirk McKusick 				first = false;
1159c7996ddfSKirk McKusick 			}
1160c7996ddfSKirk McKusick 			if ((word == 0 && maptype) ||
1161c7996ddfSKirk McKusick 			    (word == ~0 && !maptype))
1162c7996ddfSKirk McKusick 				break;
1163c7996ddfSKirk McKusick 			numsec -= BITS_PER_ENTRY;
1164c7996ddfSKirk McKusick 			start += BITS_PER_ENTRY;
1165c7996ddfSKirk McKusick 			continue;
1166c7996ddfSKirk McKusick 		}
1167c7996ddfSKirk McKusick 		for ( ; bitloc < BITS_PER_ENTRY; bitloc ++) {
1168c7996ddfSKirk McKusick 			retval = (word & (1ULL << bitloc)) != 0;
1169c7996ddfSKirk McKusick 			if (first) {
1170c7996ddfSKirk McKusick 				maptype = retval;
1171c7996ddfSKirk McKusick 				first = false;
1172c7996ddfSKirk McKusick 			}
1173c7996ddfSKirk McKusick 			if (maptype == retval) {
1174c7996ddfSKirk McKusick 				numsec--;
1175c7996ddfSKirk McKusick 				start++;
1176c7996ddfSKirk McKusick 				continue;
1177c7996ddfSKirk McKusick 			}
1178c7996ddfSKirk McKusick 			goto out;
1179c7996ddfSKirk McKusick 		}
1180c7996ddfSKirk McKusick 	}
1181c7996ddfSKirk McKusick out:
1182c7996ddfSKirk McKusick 	if (numsec < 0) {
1183c7996ddfSKirk McKusick 		start += numsec;
1184c7996ddfSKirk McKusick 		numsec = 0;
1185c7996ddfSKirk McKusick 	}
1186c7996ddfSKirk McKusick 	*len2read = bp->bio_length - (numsec * sc->sc_sectorsize);
1187c7996ddfSKirk McKusick 	G_UNION_DEBUG(maptype ? 3 : 4,
1188c7996ddfSKirk McKusick 	    "g_union_getmap: return maptype %swritten for %jd "
1189c7996ddfSKirk McKusick 	    "sectors ending at %jd\n", maptype ? "" : "NOT ",
1190c7996ddfSKirk McKusick 	    *len2read / sc->sc_sectorsize, start - 1);
1191c7996ddfSKirk McKusick 	return (maptype);
1192c7996ddfSKirk McKusick }
1193c7996ddfSKirk McKusick 
1194c7996ddfSKirk McKusick /*
1195c7996ddfSKirk McKusick  * Fill in details for a BIO_GETATTR request.
1196c7996ddfSKirk McKusick  */
1197c7996ddfSKirk McKusick static void
g_union_kerneldump(struct bio * bp,struct g_union_softc * sc)1198c7996ddfSKirk McKusick g_union_kerneldump(struct bio *bp, struct g_union_softc *sc)
1199c7996ddfSKirk McKusick {
1200c7996ddfSKirk McKusick 	struct g_kerneldump *gkd;
1201c7996ddfSKirk McKusick 	struct g_geom *gp;
1202c7996ddfSKirk McKusick 	struct g_provider *pp;
1203c7996ddfSKirk McKusick 
1204c7996ddfSKirk McKusick 	gkd = (struct g_kerneldump *)bp->bio_data;
1205c7996ddfSKirk McKusick 	gp = bp->bio_to->geom;
1206c7996ddfSKirk McKusick 	g_trace(G_T_TOPOLOGY, "%s(%s, %jd, %jd)", __func__, gp->name,
1207c7996ddfSKirk McKusick 	    (intmax_t)gkd->offset, (intmax_t)gkd->length);
1208c7996ddfSKirk McKusick 
1209c7996ddfSKirk McKusick 	pp = LIST_FIRST(&gp->provider);
1210c7996ddfSKirk McKusick 
1211c7996ddfSKirk McKusick 	gkd->di.dumper = g_union_dumper;
1212c7996ddfSKirk McKusick 	gkd->di.priv = sc;
1213c7996ddfSKirk McKusick 	gkd->di.blocksize = pp->sectorsize;
1214c7996ddfSKirk McKusick 	gkd->di.maxiosize = DFLTPHYS;
1215c7996ddfSKirk McKusick 	gkd->di.mediaoffset = sc->sc_offset + gkd->offset;
1216c7996ddfSKirk McKusick 	if (gkd->offset > sc->sc_size) {
1217c7996ddfSKirk McKusick 		g_io_deliver(bp, ENODEV);
1218c7996ddfSKirk McKusick 		return;
1219c7996ddfSKirk McKusick 	}
1220c7996ddfSKirk McKusick 	if (gkd->offset + gkd->length > sc->sc_size)
1221c7996ddfSKirk McKusick 		gkd->length = sc->sc_size - gkd->offset;
1222c7996ddfSKirk McKusick 	gkd->di.mediasize = gkd->length;
1223c7996ddfSKirk McKusick 	g_io_deliver(bp, 0);
1224c7996ddfSKirk McKusick }
1225c7996ddfSKirk McKusick 
1226c7996ddfSKirk McKusick /*
1227c7996ddfSKirk McKusick  * Handler for g_union_kerneldump().
1228c7996ddfSKirk McKusick  */
1229c7996ddfSKirk McKusick static int
g_union_dumper(void * priv,void * virtual,off_t offset,size_t length)1230489ba222SMitchell Horne g_union_dumper(void *priv, void *virtual, off_t offset, size_t length)
1231c7996ddfSKirk McKusick {
1232c7996ddfSKirk McKusick 
1233c7996ddfSKirk McKusick 	return (0);
1234c7996ddfSKirk McKusick }
1235c7996ddfSKirk McKusick 
1236c7996ddfSKirk McKusick /*
1237c7996ddfSKirk McKusick  * List union statistics.
1238c7996ddfSKirk McKusick  */
1239c7996ddfSKirk McKusick static void
g_union_dumpconf(struct sbuf * sb,const char * indent,struct g_geom * gp,struct g_consumer * cp,struct g_provider * pp)1240c7996ddfSKirk McKusick g_union_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1241c7996ddfSKirk McKusick     struct g_consumer *cp, struct g_provider *pp)
1242c7996ddfSKirk McKusick {
1243c7996ddfSKirk McKusick 	struct g_union_softc *sc;
1244c7996ddfSKirk McKusick 
1245c7996ddfSKirk McKusick 	if (pp != NULL || cp != NULL || gp->softc == NULL)
1246c7996ddfSKirk McKusick 		return;
1247c7996ddfSKirk McKusick 	sc = gp->softc;
12483cf2f812SKirk McKusick 	sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent,
12493cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_reads);
12503cf2f812SKirk McKusick 	sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent,
12513cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_writes);
12523cf2f812SKirk McKusick 	sbuf_printf(sb, "%s<Deletes>%ju</Deletes>\n", indent,
12533cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_deletes);
1254c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<Getattrs>%ju</Getattrs>\n", indent,
12553cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_getattrs);
12563cf2f812SKirk McKusick 	sbuf_printf(sb, "%s<Flushes>%ju</Flushes>\n", indent,
12573cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_flushes);
1258c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<Speedups>%ju</Speedups>\n", indent,
12593cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_speedups);
12603cf2f812SKirk McKusick 	sbuf_printf(sb, "%s<Cmd0s>%ju</Cmd0s>\n", indent,
12613cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_cmd0s);
12623cf2f812SKirk McKusick 	sbuf_printf(sb, "%s<Cmd1s>%ju</Cmd1s>\n", indent,
12633cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_cmd1s);
12643cf2f812SKirk McKusick 	sbuf_printf(sb, "%s<Cmd2s>%ju</Cmd2s>\n", indent,
12653cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_cmd2s);
1266c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<ReadCurrentRead>%ju</ReadCurrentRead>\n", indent,
12673cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_readcurrentread);
1268c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<ReadBlockWrite>%ju</ReadBlockWrite>\n", indent,
12693cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_readblockwrite);
1270c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<WriteBlockRead>%ju</WriteBlockRead>\n", indent,
12713cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_writeblockread);
1272c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<WriteBlockWrite>%ju</WriteBlockWrite>\n", indent,
12733cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_writeblockwrite);
1274c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
12753cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_readbytes);
1276c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
12773cf2f812SKirk McKusick 	    (uintmax_t)sc->sc_wrotebytes);
1278c7996ddfSKirk McKusick 	sbuf_printf(sb, "%s<Offset>%jd</Offset>\n", indent,
1279c7996ddfSKirk McKusick 	    (intmax_t)sc->sc_offset);
1280c7996ddfSKirk McKusick }
1281c7996ddfSKirk McKusick 
1282c7996ddfSKirk McKusick /*
1283c7996ddfSKirk McKusick  * Clean up an orphaned geom.
1284c7996ddfSKirk McKusick  */
1285c7996ddfSKirk McKusick static void
g_union_orphan(struct g_consumer * cp)1286c7996ddfSKirk McKusick g_union_orphan(struct g_consumer *cp)
1287c7996ddfSKirk McKusick {
1288c7996ddfSKirk McKusick 
1289c7996ddfSKirk McKusick 	g_topology_assert();
1290c7996ddfSKirk McKusick 	g_union_destroy(NULL, cp->geom, true);
1291c7996ddfSKirk McKusick }
1292c7996ddfSKirk McKusick 
1293c7996ddfSKirk McKusick /*
1294c7996ddfSKirk McKusick  * Clean up a union geom.
1295c7996ddfSKirk McKusick  */
1296c7996ddfSKirk McKusick static int
g_union_destroy_geom(struct gctl_req * req,struct g_class * mp,struct g_geom * gp)1297c7996ddfSKirk McKusick g_union_destroy_geom(struct gctl_req *req, struct g_class *mp,
1298c7996ddfSKirk McKusick     struct g_geom *gp)
1299c7996ddfSKirk McKusick {
1300c7996ddfSKirk McKusick 
1301c7996ddfSKirk McKusick 	return (g_union_destroy(NULL, gp, false));
1302c7996ddfSKirk McKusick }
1303c7996ddfSKirk McKusick 
1304c7996ddfSKirk McKusick /*
1305c7996ddfSKirk McKusick  * Clean up a union device.
1306c7996ddfSKirk McKusick  */
1307c7996ddfSKirk McKusick static int
g_union_destroy(struct gctl_req * req,struct g_geom * gp,bool force)1308c7996ddfSKirk McKusick g_union_destroy(struct gctl_req *req, struct g_geom *gp, bool force)
1309c7996ddfSKirk McKusick {
1310c7996ddfSKirk McKusick 	struct g_union_softc *sc;
1311c7996ddfSKirk McKusick 	struct g_provider *pp;
1312c7996ddfSKirk McKusick 	int error;
1313c7996ddfSKirk McKusick 
1314c7996ddfSKirk McKusick 	g_topology_assert();
1315c7996ddfSKirk McKusick 	sc = gp->softc;
1316c7996ddfSKirk McKusick 	if (sc == NULL)
1317c7996ddfSKirk McKusick 		return (ENXIO);
1318c7996ddfSKirk McKusick 	pp = LIST_FIRST(&gp->provider);
1319c7996ddfSKirk McKusick 	if ((sc->sc_flags & DOING_COMMIT) != 0 ||
1320c7996ddfSKirk McKusick 	    (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0))) {
1321c7996ddfSKirk McKusick 		if (force) {
1322c7996ddfSKirk McKusick 			if (req != NULL)
132390e29718SKirk McKusick 				gctl_msg(req, 0, "Device %s is still in use, "
13244710aa24SKirk McKusick 				    "so is being forcibly removed.", gp->name);
1325c7996ddfSKirk McKusick 			G_UNION_DEBUG(1, "Device %s is still in use, so "
13264710aa24SKirk McKusick 			    "is being forcibly removed.", gp->name);
1327c7996ddfSKirk McKusick 		} else {
1328c7996ddfSKirk McKusick 			if (req != NULL)
132990e29718SKirk McKusick 				gctl_msg(req, EBUSY, "Device %s is still open "
13304710aa24SKirk McKusick 				    "(r=%d w=%d e=%d).", gp->name, pp->acr,
1331c7996ddfSKirk McKusick 				    pp->acw, pp->ace);
1332c7996ddfSKirk McKusick 			G_UNION_DEBUG(1, "Device %s is still open "
13334710aa24SKirk McKusick 			    "(r=%d w=%d e=%d).", gp->name, pp->acr,
1334c7996ddfSKirk McKusick 			    pp->acw, pp->ace);
1335c7996ddfSKirk McKusick 			return (EBUSY);
1336c7996ddfSKirk McKusick 		}
1337c7996ddfSKirk McKusick 	} else {
1338c7996ddfSKirk McKusick 		if (req != NULL)
133990e29718SKirk McKusick 			gctl_msg(req, 0, "Device %s removed.", gp->name);
13404710aa24SKirk McKusick 		G_UNION_DEBUG(1, "Device %s removed.", gp->name);
1341c7996ddfSKirk McKusick 	}
1342c7996ddfSKirk McKusick 	/* Close consumers */
1343c7996ddfSKirk McKusick 	if ((error = g_access(sc->sc_lowercp, -1, 0, -1)) != 0)
1344c7996ddfSKirk McKusick 		G_UNION_DEBUG(2, "Error %d: device %s could not reset access "
13454710aa24SKirk McKusick 		    "to %s.", error, gp->name, sc->sc_lowercp->provider->name);
1346c7996ddfSKirk McKusick 	if ((error = g_access(sc->sc_uppercp, -1, -1, -1)) != 0)
1347c7996ddfSKirk McKusick 		G_UNION_DEBUG(2, "Error %d: device %s could not reset access "
13484710aa24SKirk McKusick 		    "to %s.", error, gp->name, sc->sc_uppercp->provider->name);
1349c7996ddfSKirk McKusick 
1350c7996ddfSKirk McKusick 	g_wither_geom(gp, ENXIO);
1351c7996ddfSKirk McKusick 
1352c7996ddfSKirk McKusick 	return (0);
1353c7996ddfSKirk McKusick }
1354c7996ddfSKirk McKusick 
1355c7996ddfSKirk McKusick /*
1356c7996ddfSKirk McKusick  * Clean up a union provider.
1357c7996ddfSKirk McKusick  */
1358c7996ddfSKirk McKusick static void
g_union_providergone(struct g_provider * pp)1359c7996ddfSKirk McKusick g_union_providergone(struct g_provider *pp)
1360c7996ddfSKirk McKusick {
1361c7996ddfSKirk McKusick 	struct g_geom *gp;
1362c7996ddfSKirk McKusick 	struct g_union_softc *sc;
1363c7996ddfSKirk McKusick 	size_t i;
1364c7996ddfSKirk McKusick 
1365c7996ddfSKirk McKusick 	gp = pp->geom;
1366c7996ddfSKirk McKusick 	sc = gp->softc;
1367c7996ddfSKirk McKusick 	gp->softc = NULL;
1368c7996ddfSKirk McKusick 	for (i = 0; i < sc->sc_root_size; i++)
1369c7996ddfSKirk McKusick 		g_free(sc->sc_writemap_root[i]);
1370c7996ddfSKirk McKusick 	g_free(sc->sc_writemap_root);
1371c7996ddfSKirk McKusick 	g_free(sc->sc_leafused);
1372c7996ddfSKirk McKusick 	rw_destroy(&sc->sc_rwlock);
1373c7996ddfSKirk McKusick 	g_free(sc);
1374c7996ddfSKirk McKusick }
1375c7996ddfSKirk McKusick 
1376c7996ddfSKirk McKusick /*
1377c7996ddfSKirk McKusick  * Respond to a resized provider.
1378c7996ddfSKirk McKusick  */
1379c7996ddfSKirk McKusick static void
g_union_resize(struct g_consumer * cp)1380c7996ddfSKirk McKusick g_union_resize(struct g_consumer *cp)
1381c7996ddfSKirk McKusick {
1382c7996ddfSKirk McKusick 	struct g_union_softc *sc;
1383c7996ddfSKirk McKusick 	struct g_geom *gp;
1384c7996ddfSKirk McKusick 
1385c7996ddfSKirk McKusick 	g_topology_assert();
1386c7996ddfSKirk McKusick 
1387c7996ddfSKirk McKusick 	gp = cp->geom;
1388c7996ddfSKirk McKusick 	sc = gp->softc;
1389c7996ddfSKirk McKusick 
1390c7996ddfSKirk McKusick 	/*
1391c7996ddfSKirk McKusick 	 * If size has gotten bigger, ignore it and just keep using
1392c7996ddfSKirk McKusick 	 * the space we already had. Otherwise we are done.
1393c7996ddfSKirk McKusick 	 */
1394c7996ddfSKirk McKusick 	if (sc->sc_size < cp->provider->mediasize - sc->sc_offset)
1395c7996ddfSKirk McKusick 		return;
1396c7996ddfSKirk McKusick 	g_union_destroy(NULL, gp, true);
1397c7996ddfSKirk McKusick }
1398c7996ddfSKirk McKusick 
1399c7996ddfSKirk McKusick DECLARE_GEOM_CLASS(g_union_class, g_union);
1400c7996ddfSKirk McKusick MODULE_VERSION(geom_union, 0);
1401