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