]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/geom/mountver/g_mountver.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sys / geom / mountver / g_mountver.c
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 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);