xref: /freebsd/sys/geom/nop/g_nop.c (revision 63dab8eed99114670445270e26cf7193fe55e0fa)
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 static 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);
597