xref: /freebsd/sys/geom/mountver/g_mountver.c (revision 7aa65846327fe5bc7e5961c2f7fd0c61f2ec0b01)
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/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 
48 SYSCTL_DECL(_kern_geom);
49 static SYSCTL_NODE(_kern_geom, OID_AUTO, mountver, CTLFLAG_RW,
50     0, "GEOM_MOUNTVER stuff");
51 static u_int g_mountver_debug = 0;
52 static u_int g_mountver_check_ident = 1;
53 SYSCTL_UINT(_kern_geom_mountver, OID_AUTO, debug, CTLFLAG_RW,
54     &g_mountver_debug, 0, "Debug level");
55 SYSCTL_UINT(_kern_geom_mountver, OID_AUTO, check_ident, CTLFLAG_RW,
56     &g_mountver_check_ident, 0, "Check disk ident when reattaching");
57 
58 static eventhandler_tag g_mountver_pre_sync = NULL;
59 
60 static void g_mountver_queue(struct bio *bp);
61 static void g_mountver_orphan(struct g_consumer *cp);
62 static int g_mountver_destroy(struct g_geom *gp, boolean_t force);
63 static g_taste_t g_mountver_taste;
64 static int g_mountver_destroy_geom(struct gctl_req *req, struct g_class *mp,
65     struct g_geom *gp);
66 static void g_mountver_config(struct gctl_req *req, struct g_class *mp,
67     const char *verb);
68 static void g_mountver_dumpconf(struct sbuf *sb, const char *indent,
69     struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp);
70 static void g_mountver_init(struct g_class *mp);
71 static void g_mountver_fini(struct g_class *mp);
72 
73 struct 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 
83 static void
84 g_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 
110 static void
111 g_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 
128 static void
129 g_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 
142 static void
143 g_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 
159 static void
160 g_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 
176 static void
177 g_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 
202 static int
203 g_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 
228 static int
229 g_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);
293 fail:
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 
304 static int
305 g_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 
338 static int
339 g_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 
345 static void
346 g_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 
384 static struct g_geom *
385 g_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 
396 static void
397 g_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 
445 static void
446 g_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 
460 static int
461 g_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 
497 static struct g_geom *
498 g_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 
549 static void
550 g_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 
577 static void
578 g_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 
593 static void
594 g_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 
608 static void
609 g_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 
618 static void
619 g_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 
626 DECLARE_GEOM_CLASS(g_mountver_class, g_mountver);
627