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