]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/geom/nop/g_nop.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / geom / nop / g_nop.c
1 /*-
2  * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/bio.h>
37 #include <sys/sysctl.h>
38 #include <sys/malloc.h>
39 #include <geom/geom.h>
40 #include <geom/nop/g_nop.h>
41
42
43 SYSCTL_DECL(_kern_geom);
44 SYSCTL_NODE(_kern_geom, OID_AUTO, nop, CTLFLAG_RW, 0, "GEOM_NOP stuff");
45 static u_int g_nop_debug = 0;
46 SYSCTL_UINT(_kern_geom_nop, OID_AUTO, debug, CTLFLAG_RW, &g_nop_debug, 0,
47     "Debug level");
48
49 static int g_nop_destroy(struct g_geom *gp, boolean_t force);
50 static int g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp,
51     struct g_geom *gp);
52 static void g_nop_config(struct gctl_req *req, struct g_class *mp,
53     const char *verb);
54 static void g_nop_dumpconf(struct sbuf *sb, const char *indent,
55     struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp);
56
57 struct g_class g_nop_class = {
58         .name = G_NOP_CLASS_NAME,
59         .version = G_VERSION,
60         .ctlreq = g_nop_config,
61         .destroy_geom = g_nop_destroy_geom
62 };
63
64
65 static void
66 g_nop_orphan(struct g_consumer *cp)
67 {
68
69         g_topology_assert();
70         g_nop_destroy(cp->geom, 1);
71 }
72
73 static void
74 g_nop_start(struct bio *bp)
75 {
76         struct g_nop_softc *sc;
77         struct g_geom *gp;
78         struct g_provider *pp;
79         struct bio *cbp;
80         u_int failprob = 0;
81
82         gp = bp->bio_to->geom;
83         sc = gp->softc;
84         G_NOP_LOGREQ(bp, "Request received.");
85         switch (bp->bio_cmd) {
86         case BIO_READ:
87                 sc->sc_reads++;
88                 sc->sc_readbytes += bp->bio_length;
89                 failprob = sc->sc_rfailprob;
90                 break;
91         case BIO_WRITE:
92                 sc->sc_writes++;
93                 sc->sc_wrotebytes += bp->bio_length;
94                 failprob = sc->sc_wfailprob;
95                 break;
96         }
97         if (failprob > 0) {
98                 u_int rval;
99
100                 rval = arc4random() % 100;
101                 if (rval < failprob) {
102                         G_NOP_LOGREQ(bp, "Returning error=%d.", sc->sc_error);
103                         g_io_deliver(bp, sc->sc_error);
104                         return;
105                 }
106         }
107         cbp = g_clone_bio(bp);
108         if (cbp == NULL) {
109                 g_io_deliver(bp, ENOMEM);
110                 return;
111         }
112         cbp->bio_done = g_std_done;
113         cbp->bio_offset = bp->bio_offset + sc->sc_offset;
114         cbp->bio_data = bp->bio_data;
115         cbp->bio_length = bp->bio_length;
116         pp = LIST_FIRST(&gp->provider);
117         KASSERT(pp != NULL, ("NULL pp"));
118         cbp->bio_to = pp;
119         G_NOP_LOGREQ(cbp, "Sending request.");
120         g_io_request(cbp, LIST_FIRST(&gp->consumer));
121 }
122
123 static int
124 g_nop_access(struct g_provider *pp, int dr, int dw, int de)
125 {
126         struct g_geom *gp;
127         struct g_consumer *cp;
128         int error;
129
130         gp = pp->geom;
131         cp = LIST_FIRST(&gp->consumer);
132         error = g_access(cp, dr, dw, de);
133
134         return (error);
135 }
136
137 static int
138 g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp,
139     int ioerror, u_int rfailprob, u_int wfailprob, off_t offset, off_t size,
140     u_int secsize)
141 {
142         struct g_nop_softc *sc;
143         struct g_geom *gp;
144         struct g_provider *newpp;
145         struct g_consumer *cp;
146         char name[64];
147         int error;
148
149         g_topology_assert();
150
151         gp = NULL;
152         newpp = NULL;
153         cp = NULL;
154
155         if ((offset % pp->sectorsize) != 0) {
156                 gctl_error(req, "Invalid offset for provider %s.", pp->name);
157                 return (EINVAL);
158         }
159         if ((size % pp->sectorsize) != 0) {
160                 gctl_error(req, "Invalid size for provider %s.", pp->name);
161                 return (EINVAL);
162         }
163         if (offset >= pp->mediasize) {
164                 gctl_error(req, "Invalid offset for provider %s.", pp->name);
165                 return (EINVAL);
166         }
167         if (size == 0)
168                 size = pp->mediasize - offset;
169         if (offset + size > pp->mediasize) {
170                 gctl_error(req, "Invalid size for provider %s.", pp->name);
171                 return (EINVAL);
172         }
173         if (secsize == 0)
174                 secsize = pp->sectorsize;
175         else if ((secsize % pp->sectorsize) != 0) {
176                 gctl_error(req, "Invalid secsize for provider %s.", pp->name);
177                 return (EINVAL);
178         }
179         snprintf(name, sizeof(name), "%s%s", pp->name, G_NOP_SUFFIX);
180         LIST_FOREACH(gp, &mp->geom, geom) {
181                 if (strcmp(gp->name, name) == 0) {
182                         gctl_error(req, "Provider %s already exists.", name);
183                         return (EEXIST);
184                 }
185         }
186         gp = g_new_geomf(mp, name);
187         if (gp == NULL) {
188                 gctl_error(req, "Cannot create geom %s.", name);
189                 return (ENOMEM);
190         }
191         sc = g_malloc(sizeof(*sc), M_WAITOK);
192         sc->sc_offset = offset;
193         sc->sc_error = ioerror;
194         sc->sc_rfailprob = rfailprob;
195         sc->sc_wfailprob = wfailprob;
196         sc->sc_reads = 0;
197         sc->sc_writes = 0;
198         sc->sc_readbytes = 0;
199         sc->sc_wrotebytes = 0;
200         gp->softc = sc;
201         gp->start = g_nop_start;
202         gp->orphan = g_nop_orphan;
203         gp->access = g_nop_access;
204         gp->dumpconf = g_nop_dumpconf;
205
206         newpp = g_new_providerf(gp, gp->name);
207         if (newpp == NULL) {
208                 gctl_error(req, "Cannot create provider %s.", name);
209                 error = ENOMEM;
210                 goto fail;
211         }
212         newpp->mediasize = size;
213         newpp->sectorsize = secsize;
214
215         cp = g_new_consumer(gp);
216         if (cp == NULL) {
217                 gctl_error(req, "Cannot create consumer for %s.", gp->name);
218                 error = ENOMEM;
219                 goto fail;
220         }
221         error = g_attach(cp, pp);
222         if (error != 0) {
223                 gctl_error(req, "Cannot attach to provider %s.", pp->name);
224                 goto fail;
225         }
226
227         g_error_provider(newpp, 0);
228         G_NOP_DEBUG(0, "Device %s created.", gp->name);
229         return (0);
230 fail:
231         if (cp != NULL) {
232                 if (cp->provider != NULL)
233                         g_detach(cp);
234                 g_destroy_consumer(cp);
235         }
236         if (newpp != NULL)
237                 g_destroy_provider(newpp);
238         if (gp != NULL) {
239                 if (gp->softc != NULL)
240                         g_free(gp->softc);
241                 g_destroy_geom(gp);
242         }
243         return (error);
244 }
245
246 static int
247 g_nop_destroy(struct g_geom *gp, boolean_t force)
248 {
249         struct g_provider *pp;
250
251         g_topology_assert();
252         if (gp->softc == NULL)
253                 return (ENXIO);
254         pp = LIST_FIRST(&gp->provider);
255         if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
256                 if (force) {
257                         G_NOP_DEBUG(0, "Device %s is still open, so it "
258                             "can't be definitely removed.", pp->name);
259                 } else {
260                         G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).",
261                             pp->name, pp->acr, pp->acw, pp->ace);
262                         return (EBUSY);
263                 }
264         } else {
265                 G_NOP_DEBUG(0, "Device %s removed.", gp->name);
266         }
267         g_free(gp->softc);
268         gp->softc = NULL;
269         g_wither_geom(gp, ENXIO);
270
271         return (0);
272 }
273
274 static int
275 g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
276 {
277
278         return (g_nop_destroy(gp, 0));
279 }
280
281 static void
282 g_nop_ctl_create(struct gctl_req *req, struct g_class *mp)
283 {
284         struct g_provider *pp;
285         intmax_t *error, *rfailprob, *wfailprob, *offset, *secsize, *size;
286         const char *name;
287         char param[16];
288         int i, *nargs;
289
290         g_topology_assert();
291
292         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
293         if (nargs == NULL) {
294                 gctl_error(req, "No '%s' argument", "nargs");
295                 return;
296         }
297         if (*nargs <= 0) {
298                 gctl_error(req, "Missing device(s).");
299                 return;
300         }
301         error = gctl_get_paraml(req, "error", sizeof(*error));
302         if (error == NULL) {
303                 gctl_error(req, "No '%s' argument", "error");
304                 return;
305         }
306         rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
307         if (rfailprob == NULL) {
308                 gctl_error(req, "No '%s' argument", "rfailprob");
309                 return;
310         }
311         if (*rfailprob < -1 || *rfailprob > 100) {
312                 gctl_error(req, "Invalid '%s' argument", "rfailprob");
313                 return;
314         }
315         wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
316         if (wfailprob == NULL) {
317                 gctl_error(req, "No '%s' argument", "wfailprob");
318                 return;
319         }
320         if (*wfailprob < -1 || *wfailprob > 100) {
321                 gctl_error(req, "Invalid '%s' argument", "wfailprob");
322                 return;
323         }
324         offset = gctl_get_paraml(req, "offset", sizeof(*offset));
325         if (offset == NULL) {
326                 gctl_error(req, "No '%s' argument", "offset");
327                 return;
328         }
329         if (*offset < 0) {
330                 gctl_error(req, "Invalid '%s' argument", "offset");
331                 return;
332         }
333         size = gctl_get_paraml(req, "size", sizeof(*size));
334         if (size == NULL) {
335                 gctl_error(req, "No '%s' argument", "size");
336                 return;
337         }
338         if (*size < 0) {
339                 gctl_error(req, "Invalid '%s' argument", "size");
340                 return;
341         }
342         secsize = gctl_get_paraml(req, "secsize", sizeof(*secsize));
343         if (secsize == NULL) {
344                 gctl_error(req, "No '%s' argument", "secsize");
345                 return;
346         }
347         if (*secsize < 0) {
348                 gctl_error(req, "Invalid '%s' argument", "secsize");
349                 return;
350         }
351
352         for (i = 0; i < *nargs; i++) {
353                 snprintf(param, sizeof(param), "arg%d", i);
354                 name = gctl_get_asciiparam(req, param);
355                 if (name == NULL) {
356                         gctl_error(req, "No 'arg%d' argument", i);
357                         return;
358                 }
359                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
360                         name += strlen("/dev/");
361                 pp = g_provider_by_name(name);
362                 if (pp == NULL) {
363                         G_NOP_DEBUG(1, "Provider %s is invalid.", name);
364                         gctl_error(req, "Provider %s is invalid.", name);
365                         return;
366                 }
367                 if (g_nop_create(req, mp, pp,
368                     *error == -1 ? EIO : (int)*error,
369                     *rfailprob == -1 ? 0 : (u_int)*rfailprob,
370                     *wfailprob == -1 ? 0 : (u_int)*wfailprob,
371                     (off_t)*offset, (off_t)*size, (u_int)*secsize) != 0) {
372                         return;
373                 }
374         }
375 }
376
377 static void
378 g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp)
379 {
380         struct g_nop_softc *sc;
381         struct g_provider *pp;
382         intmax_t *error, *rfailprob, *wfailprob;
383         const char *name;
384         char param[16];
385         int i, *nargs;
386
387         g_topology_assert();
388
389         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
390         if (nargs == NULL) {
391                 gctl_error(req, "No '%s' argument", "nargs");
392                 return;
393         }
394         if (*nargs <= 0) {
395                 gctl_error(req, "Missing device(s).");
396                 return;
397         }
398         error = gctl_get_paraml(req, "error", sizeof(*error));
399         if (error == NULL) {
400                 gctl_error(req, "No '%s' argument", "error");
401                 return;
402         }
403         rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
404         if (rfailprob == NULL) {
405                 gctl_error(req, "No '%s' argument", "rfailprob");
406                 return;
407         }
408         if (*rfailprob < -1 || *rfailprob > 100) {
409                 gctl_error(req, "Invalid '%s' argument", "rfailprob");
410                 return;
411         }
412         wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
413         if (wfailprob == NULL) {
414                 gctl_error(req, "No '%s' argument", "wfailprob");
415                 return;
416         }
417         if (*wfailprob < -1 || *wfailprob > 100) {
418                 gctl_error(req, "Invalid '%s' argument", "wfailprob");
419                 return;
420         }
421
422         for (i = 0; i < *nargs; i++) {
423                 snprintf(param, sizeof(param), "arg%d", i);
424                 name = gctl_get_asciiparam(req, param);
425                 if (name == NULL) {
426                         gctl_error(req, "No 'arg%d' argument", i);
427                         return;
428                 }
429                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
430                         name += strlen("/dev/");
431                 pp = g_provider_by_name(name);
432                 if (pp == NULL || pp->geom->class != mp) {
433                         G_NOP_DEBUG(1, "Provider %s is invalid.", name);
434                         gctl_error(req, "Provider %s is invalid.", name);
435                         return;
436                 }
437                 sc = pp->geom->softc;
438                 if (*error != -1)
439                         sc->sc_error = (int)*error;
440                 if (*rfailprob != -1)
441                         sc->sc_rfailprob = (u_int)*rfailprob;
442                 if (*wfailprob != -1)
443                         sc->sc_wfailprob = (u_int)*wfailprob;
444         }
445 }
446
447 static struct g_geom *
448 g_nop_find_geom(struct g_class *mp, const char *name)
449 {
450         struct g_geom *gp;
451
452         LIST_FOREACH(gp, &mp->geom, geom) {
453                 if (strcmp(gp->name, name) == 0)
454                         return (gp);
455         }
456         return (NULL);
457 }
458
459 static void
460 g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp)
461 {
462         int *nargs, *force, error, i;
463         struct g_geom *gp;
464         const char *name;
465         char param[16];
466
467         g_topology_assert();
468
469         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
470         if (nargs == NULL) {
471                 gctl_error(req, "No '%s' argument", "nargs");
472                 return;
473         }
474         if (*nargs <= 0) {
475                 gctl_error(req, "Missing device(s).");
476                 return;
477         }
478         force = gctl_get_paraml(req, "force", sizeof(*force));
479         if (force == NULL) {
480                 gctl_error(req, "No 'force' argument");
481                 return;
482         }
483
484         for (i = 0; i < *nargs; i++) {
485                 snprintf(param, sizeof(param), "arg%d", i);
486                 name = gctl_get_asciiparam(req, param);
487                 if (name == NULL) {
488                         gctl_error(req, "No 'arg%d' argument", i);
489                         return;
490                 }
491                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
492                         name += strlen("/dev/");
493                 gp = g_nop_find_geom(mp, name);
494                 if (gp == NULL) {
495                         G_NOP_DEBUG(1, "Device %s is invalid.", name);
496                         gctl_error(req, "Device %s is invalid.", name);
497                         return;
498                 }
499                 error = g_nop_destroy(gp, *force);
500                 if (error != 0) {
501                         gctl_error(req, "Cannot destroy device %s (error=%d).",
502                             gp->name, error);
503                         return;
504                 }
505         }
506 }
507
508 static void
509 g_nop_ctl_reset(struct gctl_req *req, struct g_class *mp)
510 {
511         struct g_nop_softc *sc;
512         struct g_provider *pp;
513         const char *name;
514         char param[16];
515         int i, *nargs;
516
517         g_topology_assert();
518
519         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
520         if (nargs == NULL) {
521                 gctl_error(req, "No '%s' argument", "nargs");
522                 return;
523         }
524         if (*nargs <= 0) {
525                 gctl_error(req, "Missing device(s).");
526                 return;
527         }
528
529         for (i = 0; i < *nargs; i++) {
530                 snprintf(param, sizeof(param), "arg%d", i);
531                 name = gctl_get_asciiparam(req, param);
532                 if (name == NULL) {
533                         gctl_error(req, "No 'arg%d' argument", i);
534                         return;
535                 }
536                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
537                         name += strlen("/dev/");
538                 pp = g_provider_by_name(name);
539                 if (pp == NULL || pp->geom->class != mp) {
540                         G_NOP_DEBUG(1, "Provider %s is invalid.", name);
541                         gctl_error(req, "Provider %s is invalid.", name);
542                         return;
543                 }
544                 sc = pp->geom->softc;
545                 sc->sc_reads = 0;
546                 sc->sc_writes = 0;
547                 sc->sc_readbytes = 0;
548                 sc->sc_wrotebytes = 0;
549         }
550 }
551
552 static void
553 g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb)
554 {
555         uint32_t *version;
556
557         g_topology_assert();
558
559         version = gctl_get_paraml(req, "version", sizeof(*version));
560         if (version == NULL) {
561                 gctl_error(req, "No '%s' argument.", "version");
562                 return;
563         }
564         if (*version != G_NOP_VERSION) {
565                 gctl_error(req, "Userland and kernel parts are out of sync.");
566                 return;
567         }
568
569         if (strcmp(verb, "create") == 0) {
570                 g_nop_ctl_create(req, mp);
571                 return;
572         } else if (strcmp(verb, "configure") == 0) {
573                 g_nop_ctl_configure(req, mp);
574                 return;
575         } else if (strcmp(verb, "destroy") == 0) {
576                 g_nop_ctl_destroy(req, mp);
577                 return;
578         } else if (strcmp(verb, "reset") == 0) {
579                 g_nop_ctl_reset(req, mp);
580                 return;
581         }
582
583         gctl_error(req, "Unknown verb.");
584 }
585
586 static void
587 g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
588     struct g_consumer *cp, struct g_provider *pp)
589 {
590         struct g_nop_softc *sc;
591
592         if (pp != NULL || cp != NULL)
593                 return;
594         sc = gp->softc;
595         sbuf_printf(sb, "%s<Offset>%jd</Offset>\n", indent,
596             (intmax_t)sc->sc_offset);
597         sbuf_printf(sb, "%s<ReadFailProb>%u</ReadFailProb>\n", indent,
598             sc->sc_rfailprob);
599         sbuf_printf(sb, "%s<WriteFailProb>%u</WriteFailProb>\n", indent,
600             sc->sc_wfailprob);
601         sbuf_printf(sb, "%s<Error>%d</Error>\n", indent, sc->sc_error);
602         sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads);
603         sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes);
604         sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
605             sc->sc_readbytes);
606         sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
607             sc->sc_wrotebytes);
608 }
609
610 DECLARE_GEOM_CLASS(g_nop_class, g_nop);