g_mountver.c revision 230643
1/*-
2 * Copyright (c) 2010 Edward Tomasz Napierala <trasz@FreeBSD.org>
3 * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/geom/mountver/g_mountver.c 230643 2012-01-28 14:00:21Z attilio $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/module.h>
35#include <sys/lock.h>
36#include <sys/mutex.h>
37#include <sys/bio.h>
38#include <sys/disk.h>
39#include <sys/proc.h>
40#include <sys/sbuf.h>
41#include <sys/sysctl.h>
42#include <sys/malloc.h>
43#include <sys/eventhandler.h>
44#include <geom/geom.h>
45#include <geom/mountver/g_mountver.h>
46
47
48SYSCTL_DECL(_kern_geom);
49static SYSCTL_NODE(_kern_geom, OID_AUTO, mountver, CTLFLAG_RW,
50    0, "GEOM_MOUNTVER stuff");
51static u_int g_mountver_debug = 0;
52static u_int g_mountver_check_ident = 1;
53SYSCTL_UINT(_kern_geom_mountver, OID_AUTO, debug, CTLFLAG_RW,
54    &g_mountver_debug, 0, "Debug level");
55SYSCTL_UINT(_kern_geom_mountver, OID_AUTO, check_ident, CTLFLAG_RW,
56    &g_mountver_check_ident, 0, "Check disk ident when reattaching");
57
58static eventhandler_tag g_mountver_pre_sync = NULL;
59
60static void g_mountver_queue(struct bio *bp);
61static void g_mountver_orphan(struct g_consumer *cp);
62static int g_mountver_destroy(struct g_geom *gp, boolean_t force);
63static g_taste_t g_mountver_taste;
64static int g_mountver_destroy_geom(struct gctl_req *req, struct g_class *mp,
65    struct g_geom *gp);
66static void g_mountver_config(struct gctl_req *req, struct g_class *mp,
67    const char *verb);
68static void g_mountver_dumpconf(struct sbuf *sb, const char *indent,
69    struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp);
70static void g_mountver_init(struct g_class *mp);
71static void g_mountver_fini(struct g_class *mp);
72
73struct g_class g_mountver_class = {
74	.name = G_MOUNTVER_CLASS_NAME,
75	.version = G_VERSION,
76	.ctlreq = g_mountver_config,
77	.taste = g_mountver_taste,
78	.destroy_geom = g_mountver_destroy_geom,
79	.init = g_mountver_init,
80	.fini = g_mountver_fini
81};
82
83static void
84g_mountver_done(struct bio *bp)
85{
86	struct g_geom *gp;
87	struct bio *pbp;
88
89	if (bp->bio_error != ENXIO) {
90		g_std_done(bp);
91		return;
92	}
93
94	/*
95	 * When the device goes away, it's possible that few requests
96	 * will be completed with ENXIO before g_mountver_orphan()
97	 * gets called.  To work around that, we have to queue requests
98	 * that failed with ENXIO, in order to send them later.
99	 */
100	gp = bp->bio_from->geom;
101
102	pbp = bp->bio_parent;
103	KASSERT(pbp->bio_to == LIST_FIRST(&gp->provider),
104	    ("parent request was for someone else"));
105	g_destroy_bio(bp);
106	pbp->bio_inbed++;
107	g_mountver_queue(pbp);
108}
109
110static void
111g_mountver_send(struct bio *bp)
112{
113	struct g_geom *gp;
114	struct bio *cbp;
115
116	gp = bp->bio_to->geom;
117
118	cbp = g_clone_bio(bp);
119	if (cbp == NULL) {
120		g_io_deliver(bp, ENOMEM);
121		return;
122	}
123
124	cbp->bio_done = g_mountver_done;
125	g_io_request(cbp, LIST_FIRST(&gp->consumer));
126}
127
128static void
129g_mountver_queue(struct bio *bp)
130{
131	struct g_mountver_softc *sc;
132	struct g_geom *gp;
133
134	gp = bp->bio_to->geom;
135	sc = gp->softc;
136
137	mtx_lock(&sc->sc_mtx);
138	TAILQ_INSERT_TAIL(&sc->sc_queue, bp, bio_queue);
139	mtx_unlock(&sc->sc_mtx);
140}
141
142static void
143g_mountver_send_queued(struct g_geom *gp)
144{
145	struct g_mountver_softc *sc;
146	struct bio *bp;
147
148	sc = gp->softc;
149
150	mtx_lock(&sc->sc_mtx);
151	while ((bp = TAILQ_FIRST(&sc->sc_queue)) != NULL) {
152		TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue);
153		G_MOUNTVER_LOGREQ(bp, "Sending queued request.");
154		g_mountver_send(bp);
155	}
156	mtx_unlock(&sc->sc_mtx);
157}
158
159static void
160g_mountver_discard_queued(struct g_geom *gp)
161{
162	struct g_mountver_softc *sc;
163	struct bio *bp;
164
165	sc = gp->softc;
166
167	mtx_lock(&sc->sc_mtx);
168	while ((bp = TAILQ_FIRST(&sc->sc_queue)) != NULL) {
169		TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue);
170		G_MOUNTVER_LOGREQ(bp, "Discarding queued request.");
171		g_io_deliver(bp, ENXIO);
172	}
173	mtx_unlock(&sc->sc_mtx);
174}
175
176static void
177g_mountver_start(struct bio *bp)
178{
179	struct g_mountver_softc *sc;
180	struct g_geom *gp;
181
182	gp = bp->bio_to->geom;
183	sc = gp->softc;
184	G_MOUNTVER_LOGREQ(bp, "Request received.");
185
186	/*
187	 * It is possible that some bios were returned with ENXIO, even though
188	 * orphaning didn't happen yet.  In that case, queue all subsequent
189	 * requests in order to maintain ordering.
190	 */
191	if (sc->sc_orphaned || !TAILQ_EMPTY(&sc->sc_queue)) {
192		G_MOUNTVER_LOGREQ(bp, "Queueing request.");
193		g_mountver_queue(bp);
194		if (!sc->sc_orphaned)
195			g_mountver_send_queued(gp);
196	} else {
197		G_MOUNTVER_LOGREQ(bp, "Sending request.");
198		g_mountver_send(bp);
199	}
200}
201
202static int
203g_mountver_access(struct g_provider *pp, int dr, int dw, int de)
204{
205	struct g_mountver_softc *sc;
206	struct g_geom *gp;
207	struct g_consumer *cp;
208
209	g_topology_assert();
210
211	gp = pp->geom;
212	cp = LIST_FIRST(&gp->consumer);
213	sc = gp->softc;
214	if (sc == NULL && dr <= 0 && dw <= 0 && de <= 0)
215		return (0);
216	KASSERT(sc != NULL, ("Trying to access withered provider \"%s\".", pp->name));
217
218	sc->sc_access_r += dr;
219	sc->sc_access_w += dw;
220	sc->sc_access_e += de;
221
222	if (sc->sc_orphaned)
223		return (0);
224
225	return (g_access(cp, dr, dw, de));
226}
227
228static int
229g_mountver_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp)
230{
231	struct g_mountver_softc *sc;
232	struct g_geom *gp;
233	struct g_provider *newpp;
234	struct g_consumer *cp;
235	char name[64];
236	int error;
237	int identsize = DISK_IDENT_SIZE;
238
239	g_topology_assert();
240
241	gp = NULL;
242	newpp = NULL;
243	cp = NULL;
244
245	snprintf(name, sizeof(name), "%s%s", pp->name, G_MOUNTVER_SUFFIX);
246	LIST_FOREACH(gp, &mp->geom, geom) {
247		if (strcmp(gp->name, name) == 0) {
248			gctl_error(req, "Provider %s already exists.", name);
249			return (EEXIST);
250		}
251	}
252	gp = g_new_geomf(mp, name);
253	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
254	mtx_init(&sc->sc_mtx, "gmountver", NULL, MTX_DEF);
255	TAILQ_INIT(&sc->sc_queue);
256	sc->sc_provider_name = strdup(pp->name, M_GEOM);
257	gp->softc = sc;
258	gp->start = g_mountver_start;
259	gp->orphan = g_mountver_orphan;
260	gp->access = g_mountver_access;
261	gp->dumpconf = g_mountver_dumpconf;
262
263	newpp = g_new_providerf(gp, gp->name);
264	newpp->mediasize = pp->mediasize;
265	newpp->sectorsize = pp->sectorsize;
266
267	cp = g_new_consumer(gp);
268	error = g_attach(cp, pp);
269	if (error != 0) {
270		gctl_error(req, "Cannot attach to provider %s.", pp->name);
271		goto fail;
272	}
273	error = g_access(cp, 1, 0, 0);
274	if (error != 0) {
275		gctl_error(req, "Cannot access provider %s.", pp->name);
276		goto fail;
277	}
278	error = g_io_getattr("GEOM::ident", cp, &identsize, sc->sc_ident);
279	g_access(cp, -1, 0, 0);
280	if (error != 0) {
281		if (g_mountver_check_ident) {
282			gctl_error(req, "Cannot get disk ident from %s; error = %d.", pp->name, error);
283			goto fail;
284		}
285
286		G_MOUNTVER_DEBUG(0, "Cannot get disk ident from %s; error = %d.", pp->name, error);
287		sc->sc_ident[0] = '\0';
288	}
289
290	g_error_provider(newpp, 0);
291	G_MOUNTVER_DEBUG(0, "Device %s created.", gp->name);
292	return (0);
293fail:
294	g_free(sc->sc_provider_name);
295	if (cp->provider != NULL)
296		g_detach(cp);
297	g_destroy_consumer(cp);
298	g_destroy_provider(newpp);
299	g_free(gp->softc);
300	g_destroy_geom(gp);
301	return (error);
302}
303
304static int
305g_mountver_destroy(struct g_geom *gp, boolean_t force)
306{
307	struct g_mountver_softc *sc;
308	struct g_provider *pp;
309
310	g_topology_assert();
311	if (gp->softc == NULL)
312		return (ENXIO);
313	sc = gp->softc;
314	pp = LIST_FIRST(&gp->provider);
315	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
316		if (force) {
317			G_MOUNTVER_DEBUG(0, "Device %s is still open, so it "
318			    "can't be definitely removed.", pp->name);
319		} else {
320			G_MOUNTVER_DEBUG(1, "Device %s is still open (r%dw%de%d).",
321			    pp->name, pp->acr, pp->acw, pp->ace);
322			return (EBUSY);
323		}
324	} else {
325		G_MOUNTVER_DEBUG(0, "Device %s removed.", gp->name);
326	}
327	if (pp != NULL)
328		g_orphan_provider(pp, ENXIO);
329	g_mountver_discard_queued(gp);
330	g_free(sc->sc_provider_name);
331	g_free(gp->softc);
332	gp->softc = NULL;
333	g_wither_geom(gp, ENXIO);
334
335	return (0);
336}
337
338static int
339g_mountver_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
340{
341
342	return (g_mountver_destroy(gp, 0));
343}
344
345static void
346g_mountver_ctl_create(struct gctl_req *req, struct g_class *mp)
347{
348	struct g_provider *pp;
349	const char *name;
350	char param[16];
351	int i, *nargs;
352
353	g_topology_assert();
354
355	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
356	if (nargs == NULL) {
357		gctl_error(req, "No '%s' argument", "nargs");
358		return;
359	}
360	if (*nargs <= 0) {
361		gctl_error(req, "Missing device(s).");
362		return;
363	}
364	for (i = 0; i < *nargs; i++) {
365		snprintf(param, sizeof(param), "arg%d", i);
366		name = gctl_get_asciiparam(req, param);
367		if (name == NULL) {
368			gctl_error(req, "No 'arg%d' argument", i);
369			return;
370		}
371		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
372			name += strlen("/dev/");
373		pp = g_provider_by_name(name);
374		if (pp == NULL) {
375			G_MOUNTVER_DEBUG(1, "Provider %s is invalid.", name);
376			gctl_error(req, "Provider %s is invalid.", name);
377			return;
378		}
379		if (g_mountver_create(req, mp, pp) != 0)
380			return;
381	}
382}
383
384static struct g_geom *
385g_mountver_find_geom(struct g_class *mp, const char *name)
386{
387	struct g_geom *gp;
388
389	LIST_FOREACH(gp, &mp->geom, geom) {
390		if (strcmp(gp->name, name) == 0)
391			return (gp);
392	}
393	return (NULL);
394}
395
396static void
397g_mountver_ctl_destroy(struct gctl_req *req, struct g_class *mp)
398{
399	int *nargs, *force, error, i;
400	struct g_geom *gp;
401	const char *name;
402	char param[16];
403
404	g_topology_assert();
405
406	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
407	if (nargs == NULL) {
408		gctl_error(req, "No '%s' argument", "nargs");
409		return;
410	}
411	if (*nargs <= 0) {
412		gctl_error(req, "Missing device(s).");
413		return;
414	}
415	force = gctl_get_paraml(req, "force", sizeof(*force));
416	if (force == NULL) {
417		gctl_error(req, "No 'force' argument");
418		return;
419	}
420
421	for (i = 0; i < *nargs; i++) {
422		snprintf(param, sizeof(param), "arg%d", i);
423		name = gctl_get_asciiparam(req, param);
424		if (name == NULL) {
425			gctl_error(req, "No 'arg%d' argument", i);
426			return;
427		}
428		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
429			name += strlen("/dev/");
430		gp = g_mountver_find_geom(mp, name);
431		if (gp == NULL) {
432			G_MOUNTVER_DEBUG(1, "Device %s is invalid.", name);
433			gctl_error(req, "Device %s is invalid.", name);
434			return;
435		}
436		error = g_mountver_destroy(gp, *force);
437		if (error != 0) {
438			gctl_error(req, "Cannot destroy device %s (error=%d).",
439			    gp->name, error);
440			return;
441		}
442	}
443}
444
445static void
446g_mountver_orphan(struct g_consumer *cp)
447{
448	struct g_mountver_softc *sc;
449
450	g_topology_assert();
451
452	sc = cp->geom->softc;
453	sc->sc_orphaned = 1;
454	if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
455		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
456	g_detach(cp);
457	G_MOUNTVER_DEBUG(0, "%s is offline.  Mount verification in progress.", sc->sc_provider_name);
458}
459
460static int
461g_mountver_ident_matches(struct g_geom *gp)
462{
463	struct g_consumer *cp;
464	struct g_mountver_softc *sc;
465	char ident[DISK_IDENT_SIZE];
466	int error, identsize = DISK_IDENT_SIZE;
467
468	sc = gp->softc;
469	cp = LIST_FIRST(&gp->consumer);
470
471	if (g_mountver_check_ident == 0)
472		return (0);
473
474	error = g_access(cp, 1, 0, 0);
475	if (error != 0) {
476		G_MOUNTVER_DEBUG(0, "Cannot access %s; "
477		    "not attaching; error = %d.", gp->name, error);
478		return (1);
479	}
480	error = g_io_getattr("GEOM::ident", cp, &identsize, ident);
481	g_access(cp, -1, 0, 0);
482	if (error != 0) {
483		G_MOUNTVER_DEBUG(0, "Cannot get disk ident for %s; "
484		    "not attaching; error = %d.", gp->name, error);
485		return (1);
486	}
487	if (strcmp(ident, sc->sc_ident) != 0) {
488		G_MOUNTVER_DEBUG(1, "Disk ident for %s (\"%s\") is different "
489		    "from expected \"%s\", not attaching.", gp->name, ident,
490		    sc->sc_ident);
491		return (1);
492	}
493
494	return (0);
495}
496
497static struct g_geom *
498g_mountver_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
499{
500	struct g_mountver_softc *sc;
501	struct g_consumer *cp;
502	struct g_geom *gp;
503	int error;
504
505	g_topology_assert();
506	g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
507	G_MOUNTVER_DEBUG(2, "Tasting %s.", pp->name);
508
509	/*
510	 * Let's check if device already exists.
511	 */
512	LIST_FOREACH(gp, &mp->geom, geom) {
513		sc = gp->softc;
514		if (sc == NULL)
515			continue;
516
517		/* Already attached? */
518		if (pp == LIST_FIRST(&gp->provider))
519			return (NULL);
520
521		if (sc->sc_orphaned && strcmp(pp->name, sc->sc_provider_name) == 0)
522			break;
523	}
524	if (gp == NULL)
525		return (NULL);
526
527	cp = LIST_FIRST(&gp->consumer);
528	g_attach(cp, pp);
529	error = g_mountver_ident_matches(gp);
530	if (error != 0) {
531		g_detach(cp);
532		return (NULL);
533	}
534	if (sc->sc_access_r > 0 || sc->sc_access_w > 0 || sc->sc_access_e > 0) {
535		error = g_access(cp, sc->sc_access_r, sc->sc_access_w, sc->sc_access_e);
536		if (error != 0) {
537			G_MOUNTVER_DEBUG(0, "Cannot access %s; error = %d.", pp->name, error);
538			g_detach(cp);
539			return (NULL);
540		}
541	}
542	g_mountver_send_queued(gp);
543	sc->sc_orphaned = 0;
544	G_MOUNTVER_DEBUG(0, "%s has completed mount verification.", sc->sc_provider_name);
545
546	return (gp);
547}
548
549static void
550g_mountver_config(struct gctl_req *req, struct g_class *mp, const char *verb)
551{
552	uint32_t *version;
553
554	g_topology_assert();
555
556	version = gctl_get_paraml(req, "version", sizeof(*version));
557	if (version == NULL) {
558		gctl_error(req, "No '%s' argument.", "version");
559		return;
560	}
561	if (*version != G_MOUNTVER_VERSION) {
562		gctl_error(req, "Userland and kernel parts are out of sync.");
563		return;
564	}
565
566	if (strcmp(verb, "create") == 0) {
567		g_mountver_ctl_create(req, mp);
568		return;
569	} else if (strcmp(verb, "destroy") == 0) {
570		g_mountver_ctl_destroy(req, mp);
571		return;
572	}
573
574	gctl_error(req, "Unknown verb.");
575}
576
577static void
578g_mountver_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
579    struct g_consumer *cp, struct g_provider *pp)
580{
581	struct g_mountver_softc *sc;
582
583	if (pp != NULL || cp != NULL)
584		return;
585
586	sc = gp->softc;
587	sbuf_printf(sb, "%s<State>%s</State>\n", indent,
588	    sc->sc_orphaned ? "OFFLINE" : "ONLINE");
589	sbuf_printf(sb, "%s<Provider-Name>%s</Provider-Name>\n", indent, sc->sc_provider_name);
590	sbuf_printf(sb, "%s<Disk-Ident>%s</Disk-Ident>\n", indent, sc->sc_ident);
591}
592
593static void
594g_mountver_shutdown_pre_sync(void *arg, int howto)
595{
596	struct g_class *mp;
597	struct g_geom *gp, *gp2;
598
599	mp = arg;
600	DROP_GIANT();
601	g_topology_lock();
602	LIST_FOREACH_SAFE(gp, &mp->geom, geom, gp2)
603		g_mountver_destroy(gp, 1);
604	g_topology_unlock();
605	PICKUP_GIANT();
606}
607
608static void
609g_mountver_init(struct g_class *mp)
610{
611
612	g_mountver_pre_sync = EVENTHANDLER_REGISTER(shutdown_pre_sync,
613	    g_mountver_shutdown_pre_sync, mp, SHUTDOWN_PRI_FIRST);
614	if (g_mountver_pre_sync == NULL)
615		G_MOUNTVER_DEBUG(0, "Warning! Cannot register shutdown event.");
616}
617
618static void
619g_mountver_fini(struct g_class *mp)
620{
621
622	if (g_mountver_pre_sync != NULL)
623		EVENTHANDLER_DEREGISTER(shutdown_pre_sync, g_mountver_pre_sync);
624}
625
626DECLARE_GEOM_CLASS(g_mountver_class, g_mountver);
627