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