]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/geom/nop/g_nop.c
MFC r362623:
[FreeBSD/stable/8.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         pp = LIST_FIRST(&gp->provider);
115         KASSERT(pp != NULL, ("NULL pp"));
116         cbp->bio_to = pp;
117         G_NOP_LOGREQ(cbp, "Sending request.");
118         g_io_request(cbp, LIST_FIRST(&gp->consumer));
119 }
120
121 static int
122 g_nop_access(struct g_provider *pp, int dr, int dw, int de)
123 {
124         struct g_geom *gp;
125         struct g_consumer *cp;
126         int error;
127
128         gp = pp->geom;
129         cp = LIST_FIRST(&gp->consumer);
130         error = g_access(cp, dr, dw, de);
131
132         return (error);
133 }
134
135 static int
136 g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp,
137     int ioerror, u_int rfailprob, u_int wfailprob, off_t offset, off_t size,
138     u_int secsize)
139 {
140         struct g_nop_softc *sc;
141         struct g_geom *gp;
142         struct g_provider *newpp;
143         struct g_consumer *cp;
144         char name[64];
145         int error;
146
147         g_topology_assert();
148
149         gp = NULL;
150         newpp = NULL;
151         cp = NULL;
152
153         if ((offset % pp->sectorsize) != 0) {
154                 gctl_error(req, "Invalid offset for provider %s.", pp->name);
155                 return (EINVAL);
156         }
157         if ((size % pp->sectorsize) != 0) {
158                 gctl_error(req, "Invalid size for provider %s.", pp->name);
159                 return (EINVAL);
160         }
161         if (offset >= pp->mediasize) {
162                 gctl_error(req, "Invalid offset for provider %s.", pp->name);
163                 return (EINVAL);
164         }
165         if (size == 0)
166                 size = pp->mediasize - offset;
167         if (offset + size > pp->mediasize) {
168                 gctl_error(req, "Invalid size for provider %s.", pp->name);
169                 return (EINVAL);
170         }
171         if (secsize == 0)
172                 secsize = pp->sectorsize;
173         else if ((secsize % pp->sectorsize) != 0) {
174                 gctl_error(req, "Invalid secsize for provider %s.", pp->name);
175                 return (EINVAL);
176         }
177         if (secsize > MAXPHYS) {
178                 gctl_error(req, "secsize is too big.");
179                 return (EINVAL);
180         }
181         size -= size % secsize;
182         snprintf(name, sizeof(name), "%s%s", pp->name, G_NOP_SUFFIX);
183         LIST_FOREACH(gp, &mp->geom, geom) {
184                 if (strcmp(gp->name, name) == 0) {
185                         gctl_error(req, "Provider %s already exists.", name);
186                         return (EEXIST);
187                 }
188         }
189         gp = g_new_geomf(mp, name);
190         sc = g_malloc(sizeof(*sc), M_WAITOK);
191         sc->sc_offset = offset;
192         sc->sc_error = ioerror;
193         sc->sc_rfailprob = rfailprob;
194         sc->sc_wfailprob = wfailprob;
195         sc->sc_reads = 0;
196         sc->sc_writes = 0;
197         sc->sc_readbytes = 0;
198         sc->sc_wrotebytes = 0;
199         gp->softc = sc;
200         gp->start = g_nop_start;
201         gp->orphan = g_nop_orphan;
202         gp->access = g_nop_access;
203         gp->dumpconf = g_nop_dumpconf;
204
205         newpp = g_new_providerf(gp, gp->name);
206         newpp->mediasize = size;
207         newpp->sectorsize = secsize;
208
209         cp = g_new_consumer(gp);
210         error = g_attach(cp, pp);
211         if (error != 0) {
212                 gctl_error(req, "Cannot attach to provider %s.", pp->name);
213                 goto fail;
214         }
215
216         g_error_provider(newpp, 0);
217         G_NOP_DEBUG(0, "Device %s created.", gp->name);
218         return (0);
219 fail:
220         if (cp->provider != NULL)
221                 g_detach(cp);
222         g_destroy_consumer(cp);
223         g_destroy_provider(newpp);
224         g_free(gp->softc);
225         g_destroy_geom(gp);
226         return (error);
227 }
228
229 static int
230 g_nop_destroy(struct g_geom *gp, boolean_t force)
231 {
232         struct g_provider *pp;
233
234         g_topology_assert();
235         if (gp->softc == NULL)
236                 return (ENXIO);
237         pp = LIST_FIRST(&gp->provider);
238         if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
239                 if (force) {
240                         G_NOP_DEBUG(0, "Device %s is still open, so it "
241                             "can't be definitely removed.", pp->name);
242                 } else {
243                         G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).",
244                             pp->name, pp->acr, pp->acw, pp->ace);
245                         return (EBUSY);
246                 }
247         } else {
248                 G_NOP_DEBUG(0, "Device %s removed.", gp->name);
249         }
250         g_free(gp->softc);
251         gp->softc = NULL;
252         g_wither_geom(gp, ENXIO);
253
254         return (0);
255 }
256
257 static int
258 g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
259 {
260
261         return (g_nop_destroy(gp, 0));
262 }
263
264 static void
265 g_nop_ctl_create(struct gctl_req *req, struct g_class *mp)
266 {
267         struct g_provider *pp;
268         intmax_t *error, *rfailprob, *wfailprob, *offset, *secsize, *size;
269         const char *name;
270         char param[16];
271         int i, *nargs;
272
273         g_topology_assert();
274
275         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
276         if (nargs == NULL) {
277                 gctl_error(req, "No '%s' argument", "nargs");
278                 return;
279         }
280         if (*nargs <= 0) {
281                 gctl_error(req, "Missing device(s).");
282                 return;
283         }
284         error = gctl_get_paraml(req, "error", sizeof(*error));
285         if (error == NULL) {
286                 gctl_error(req, "No '%s' argument", "error");
287                 return;
288         }
289         rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
290         if (rfailprob == NULL) {
291                 gctl_error(req, "No '%s' argument", "rfailprob");
292                 return;
293         }
294         if (*rfailprob < -1 || *rfailprob > 100) {
295                 gctl_error(req, "Invalid '%s' argument", "rfailprob");
296                 return;
297         }
298         wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
299         if (wfailprob == NULL) {
300                 gctl_error(req, "No '%s' argument", "wfailprob");
301                 return;
302         }
303         if (*wfailprob < -1 || *wfailprob > 100) {
304                 gctl_error(req, "Invalid '%s' argument", "wfailprob");
305                 return;
306         }
307         offset = gctl_get_paraml(req, "offset", sizeof(*offset));
308         if (offset == NULL) {
309                 gctl_error(req, "No '%s' argument", "offset");
310                 return;
311         }
312         if (*offset < 0) {
313                 gctl_error(req, "Invalid '%s' argument", "offset");
314                 return;
315         }
316         size = gctl_get_paraml(req, "size", sizeof(*size));
317         if (size == NULL) {
318                 gctl_error(req, "No '%s' argument", "size");
319                 return;
320         }
321         if (*size < 0) {
322                 gctl_error(req, "Invalid '%s' argument", "size");
323                 return;
324         }
325         secsize = gctl_get_paraml(req, "secsize", sizeof(*secsize));
326         if (secsize == NULL) {
327                 gctl_error(req, "No '%s' argument", "secsize");
328                 return;
329         }
330         if (*secsize < 0) {
331                 gctl_error(req, "Invalid '%s' argument", "secsize");
332                 return;
333         }
334
335         for (i = 0; i < *nargs; i++) {
336                 snprintf(param, sizeof(param), "arg%d", i);
337                 name = gctl_get_asciiparam(req, param);
338                 if (name == NULL) {
339                         gctl_error(req, "No 'arg%d' argument", i);
340                         return;
341                 }
342                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
343                         name += strlen("/dev/");
344                 pp = g_provider_by_name(name);
345                 if (pp == NULL) {
346                         G_NOP_DEBUG(1, "Provider %s is invalid.", name);
347                         gctl_error(req, "Provider %s is invalid.", name);
348                         return;
349                 }
350                 if (g_nop_create(req, mp, pp,
351                     *error == -1 ? EIO : (int)*error,
352                     *rfailprob == -1 ? 0 : (u_int)*rfailprob,
353                     *wfailprob == -1 ? 0 : (u_int)*wfailprob,
354                     (off_t)*offset, (off_t)*size, (u_int)*secsize) != 0) {
355                         return;
356                 }
357         }
358 }
359
360 static void
361 g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp)
362 {
363         struct g_nop_softc *sc;
364         struct g_provider *pp;
365         intmax_t *error, *rfailprob, *wfailprob;
366         const char *name;
367         char param[16];
368         int i, *nargs;
369
370         g_topology_assert();
371
372         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
373         if (nargs == NULL) {
374                 gctl_error(req, "No '%s' argument", "nargs");
375                 return;
376         }
377         if (*nargs <= 0) {
378                 gctl_error(req, "Missing device(s).");
379                 return;
380         }
381         error = gctl_get_paraml(req, "error", sizeof(*error));
382         if (error == NULL) {
383                 gctl_error(req, "No '%s' argument", "error");
384                 return;
385         }
386         rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
387         if (rfailprob == NULL) {
388                 gctl_error(req, "No '%s' argument", "rfailprob");
389                 return;
390         }
391         if (*rfailprob < -1 || *rfailprob > 100) {
392                 gctl_error(req, "Invalid '%s' argument", "rfailprob");
393                 return;
394         }
395         wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
396         if (wfailprob == NULL) {
397                 gctl_error(req, "No '%s' argument", "wfailprob");
398                 return;
399         }
400         if (*wfailprob < -1 || *wfailprob > 100) {
401                 gctl_error(req, "Invalid '%s' argument", "wfailprob");
402                 return;
403         }
404
405         for (i = 0; i < *nargs; i++) {
406                 snprintf(param, sizeof(param), "arg%d", i);
407                 name = gctl_get_asciiparam(req, param);
408                 if (name == NULL) {
409                         gctl_error(req, "No 'arg%d' argument", i);
410                         return;
411                 }
412                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
413                         name += strlen("/dev/");
414                 pp = g_provider_by_name(name);
415                 if (pp == NULL || pp->geom->class != mp) {
416                         G_NOP_DEBUG(1, "Provider %s is invalid.", name);
417                         gctl_error(req, "Provider %s is invalid.", name);
418                         return;
419                 }
420                 sc = pp->geom->softc;
421                 if (*error != -1)
422                         sc->sc_error = (int)*error;
423                 if (*rfailprob != -1)
424                         sc->sc_rfailprob = (u_int)*rfailprob;
425                 if (*wfailprob != -1)
426                         sc->sc_wfailprob = (u_int)*wfailprob;
427         }
428 }
429
430 static struct g_geom *
431 g_nop_find_geom(struct g_class *mp, const char *name)
432 {
433         struct g_geom *gp;
434
435         LIST_FOREACH(gp, &mp->geom, geom) {
436                 if (strcmp(gp->name, name) == 0)
437                         return (gp);
438         }
439         return (NULL);
440 }
441
442 static void
443 g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp)
444 {
445         int *nargs, *force, error, i;
446         struct g_geom *gp;
447         const char *name;
448         char param[16];
449
450         g_topology_assert();
451
452         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
453         if (nargs == NULL) {
454                 gctl_error(req, "No '%s' argument", "nargs");
455                 return;
456         }
457         if (*nargs <= 0) {
458                 gctl_error(req, "Missing device(s).");
459                 return;
460         }
461         force = gctl_get_paraml(req, "force", sizeof(*force));
462         if (force == NULL) {
463                 gctl_error(req, "No 'force' argument");
464                 return;
465         }
466
467         for (i = 0; i < *nargs; i++) {
468                 snprintf(param, sizeof(param), "arg%d", i);
469                 name = gctl_get_asciiparam(req, param);
470                 if (name == NULL) {
471                         gctl_error(req, "No 'arg%d' argument", i);
472                         return;
473                 }
474                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
475                         name += strlen("/dev/");
476                 gp = g_nop_find_geom(mp, name);
477                 if (gp == NULL) {
478                         G_NOP_DEBUG(1, "Device %s is invalid.", name);
479                         gctl_error(req, "Device %s is invalid.", name);
480                         return;
481                 }
482                 error = g_nop_destroy(gp, *force);
483                 if (error != 0) {
484                         gctl_error(req, "Cannot destroy device %s (error=%d).",
485                             gp->name, error);
486                         return;
487                 }
488         }
489 }
490
491 static void
492 g_nop_ctl_reset(struct gctl_req *req, struct g_class *mp)
493 {
494         struct g_nop_softc *sc;
495         struct g_provider *pp;
496         const char *name;
497         char param[16];
498         int i, *nargs;
499
500         g_topology_assert();
501
502         nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
503         if (nargs == NULL) {
504                 gctl_error(req, "No '%s' argument", "nargs");
505                 return;
506         }
507         if (*nargs <= 0) {
508                 gctl_error(req, "Missing device(s).");
509                 return;
510         }
511
512         for (i = 0; i < *nargs; i++) {
513                 snprintf(param, sizeof(param), "arg%d", i);
514                 name = gctl_get_asciiparam(req, param);
515                 if (name == NULL) {
516                         gctl_error(req, "No 'arg%d' argument", i);
517                         return;
518                 }
519                 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
520                         name += strlen("/dev/");
521                 pp = g_provider_by_name(name);
522                 if (pp == NULL || pp->geom->class != mp) {
523                         G_NOP_DEBUG(1, "Provider %s is invalid.", name);
524                         gctl_error(req, "Provider %s is invalid.", name);
525                         return;
526                 }
527                 sc = pp->geom->softc;
528                 sc->sc_reads = 0;
529                 sc->sc_writes = 0;
530                 sc->sc_readbytes = 0;
531                 sc->sc_wrotebytes = 0;
532         }
533 }
534
535 static void
536 g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb)
537 {
538         uint32_t *version;
539
540         g_topology_assert();
541
542         version = gctl_get_paraml(req, "version", sizeof(*version));
543         if (version == NULL) {
544                 gctl_error(req, "No '%s' argument.", "version");
545                 return;
546         }
547         if (*version != G_NOP_VERSION) {
548                 gctl_error(req, "Userland and kernel parts are out of sync.");
549                 return;
550         }
551
552         if (strcmp(verb, "create") == 0) {
553                 g_nop_ctl_create(req, mp);
554                 return;
555         } else if (strcmp(verb, "configure") == 0) {
556                 g_nop_ctl_configure(req, mp);
557                 return;
558         } else if (strcmp(verb, "destroy") == 0) {
559                 g_nop_ctl_destroy(req, mp);
560                 return;
561         } else if (strcmp(verb, "reset") == 0) {
562                 g_nop_ctl_reset(req, mp);
563                 return;
564         }
565
566         gctl_error(req, "Unknown verb.");
567 }
568
569 static void
570 g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
571     struct g_consumer *cp, struct g_provider *pp)
572 {
573         struct g_nop_softc *sc;
574
575         if (pp != NULL || cp != NULL)
576                 return;
577         sc = gp->softc;
578         sbuf_printf(sb, "%s<Offset>%jd</Offset>\n", indent,
579             (intmax_t)sc->sc_offset);
580         sbuf_printf(sb, "%s<ReadFailProb>%u</ReadFailProb>\n", indent,
581             sc->sc_rfailprob);
582         sbuf_printf(sb, "%s<WriteFailProb>%u</WriteFailProb>\n", indent,
583             sc->sc_wfailprob);
584         sbuf_printf(sb, "%s<Error>%d</Error>\n", indent, sc->sc_error);
585         sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads);
586         sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes);
587         sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
588             sc->sc_readbytes);
589         sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
590             sc->sc_wrotebytes);
591 }
592
593 DECLARE_GEOM_CLASS(g_nop_class, g_nop);