xref: /freebsd/sys/geom/nop/g_nop.c (revision a5ada504b340e3e85bf907cbdbb7109ee16edb70)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <sys/bio.h>
39 #include <sys/sbuf.h>
40 #include <sys/sysctl.h>
41 #include <sys/malloc.h>
42 #include <geom/geom.h>
43 #include <geom/nop/g_nop.h>
44 
45 
46 SYSCTL_DECL(_kern_geom);
47 static SYSCTL_NODE(_kern_geom, OID_AUTO, nop, CTLFLAG_RW, 0, "GEOM_NOP stuff");
48 static u_int g_nop_debug = 0;
49 SYSCTL_UINT(_kern_geom_nop, OID_AUTO, debug, CTLFLAG_RW, &g_nop_debug, 0,
50     "Debug level");
51 
52 static int g_nop_destroy(struct g_geom *gp, boolean_t force);
53 static int g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp,
54     struct g_geom *gp);
55 static void g_nop_config(struct gctl_req *req, struct g_class *mp,
56     const char *verb);
57 static g_access_t g_nop_access;
58 static g_dumpconf_t g_nop_dumpconf;
59 static g_orphan_t g_nop_orphan;
60 static g_provgone_t g_nop_providergone;
61 static g_resize_t g_nop_resize;
62 static g_start_t g_nop_start;
63 
64 struct g_class g_nop_class = {
65 	.name = G_NOP_CLASS_NAME,
66 	.version = G_VERSION,
67 	.ctlreq = g_nop_config,
68 	.destroy_geom = g_nop_destroy_geom,
69 	.access = g_nop_access,
70 	.dumpconf = g_nop_dumpconf,
71 	.orphan = g_nop_orphan,
72 	.providergone = g_nop_providergone,
73 	.resize = g_nop_resize,
74 	.start = g_nop_start,
75 };
76 
77 struct g_nop_delay {
78 	struct callout			 dl_cal;
79 	struct bio			*dl_bio;
80 	TAILQ_ENTRY(g_nop_delay)	 dl_next;
81 };
82 
83 static void
84 g_nop_orphan(struct g_consumer *cp)
85 {
86 
87 	g_topology_assert();
88 	g_nop_destroy(cp->geom, 1);
89 }
90 
91 static void
92 g_nop_resize(struct g_consumer *cp)
93 {
94 	struct g_nop_softc *sc;
95 	struct g_geom *gp;
96 	struct g_provider *pp;
97 	off_t size;
98 
99 	g_topology_assert();
100 
101 	gp = cp->geom;
102 	sc = gp->softc;
103 
104 	if (sc->sc_explicitsize != 0)
105 		return;
106 	if (cp->provider->mediasize < sc->sc_offset) {
107 		g_nop_destroy(gp, 1);
108 		return;
109 	}
110 	size = cp->provider->mediasize - sc->sc_offset;
111 	LIST_FOREACH(pp, &gp->provider, provider)
112 		g_resize_provider(pp, size);
113 }
114 
115 static int
116 g_nop_dumper(void *priv, void *virtual, vm_offset_t physical, off_t offset,
117     size_t length)
118 {
119 
120 	return (0);
121 }
122 
123 static void
124 g_nop_kerneldump(struct bio *bp, struct g_nop_softc *sc)
125 {
126 	struct g_kerneldump *gkd;
127 	struct g_geom *gp;
128 	struct g_provider *pp;
129 
130 	gkd = (struct g_kerneldump *)bp->bio_data;
131 	gp = bp->bio_to->geom;
132 	g_trace(G_T_TOPOLOGY, "%s(%s, %jd, %jd)", __func__, gp->name,
133 	    (intmax_t)gkd->offset, (intmax_t)gkd->length);
134 
135 	pp = LIST_FIRST(&gp->provider);
136 
137 	gkd->di.dumper = g_nop_dumper;
138 	gkd->di.priv = sc;
139 	gkd->di.blocksize = pp->sectorsize;
140 	gkd->di.maxiosize = DFLTPHYS;
141 	gkd->di.mediaoffset = sc->sc_offset + gkd->offset;
142 	if (gkd->offset > sc->sc_explicitsize) {
143 		g_io_deliver(bp, ENODEV);
144 		return;
145 	}
146 	if (gkd->offset + gkd->length > sc->sc_explicitsize)
147 		gkd->length = sc->sc_explicitsize - gkd->offset;
148 	gkd->di.mediasize = gkd->length;
149 	g_io_deliver(bp, 0);
150 }
151 
152 static void
153 g_nop_pass(struct bio *cbp, struct g_geom *gp)
154 {
155 
156 	G_NOP_LOGREQ(cbp, "Sending request.");
157 	g_io_request(cbp, LIST_FIRST(&gp->consumer));
158 }
159 
160 static void
161 g_nop_pass_timeout(void *data)
162 {
163 	struct g_nop_softc *sc;
164 	struct g_geom *gp;
165 	struct g_nop_delay *gndelay;
166 
167 	gndelay = (struct g_nop_delay *)data;
168 
169 	gp = gndelay->dl_bio->bio_to->geom;
170 	sc = gp->softc;
171 
172 	mtx_lock(&sc->sc_lock);
173 	TAILQ_REMOVE(&sc->sc_head_delay, gndelay, dl_next);
174 	mtx_unlock(&sc->sc_lock);
175 
176 	g_nop_pass(gndelay->dl_bio, gp);
177 
178 	g_free(data);
179 }
180 
181 static void
182 g_nop_start(struct bio *bp)
183 {
184 	struct g_nop_softc *sc;
185 	struct g_geom *gp;
186 	struct g_provider *pp;
187 	struct bio *cbp;
188 	u_int failprob, delayprob, delaytime;
189 
190 	failprob = delayprob = 0;
191 
192 	gp = bp->bio_to->geom;
193 	sc = gp->softc;
194 
195 	G_NOP_LOGREQ(bp, "Request received.");
196 	mtx_lock(&sc->sc_lock);
197 	switch (bp->bio_cmd) {
198 	case BIO_READ:
199 		sc->sc_reads++;
200 		sc->sc_readbytes += bp->bio_length;
201 		failprob = sc->sc_rfailprob;
202 		delayprob = sc->sc_rdelayprob;
203 		delaytime = sc->sc_delaymsec;
204 		break;
205 	case BIO_WRITE:
206 		sc->sc_writes++;
207 		sc->sc_wrotebytes += bp->bio_length;
208 		failprob = sc->sc_wfailprob;
209 		delayprob = sc->sc_wdelayprob;
210 		delaytime = sc->sc_delaymsec;
211 		break;
212 	case BIO_DELETE:
213 		sc->sc_deletes++;
214 		break;
215 	case BIO_GETATTR:
216 		sc->sc_getattrs++;
217 		if (sc->sc_physpath &&
218 		    g_handleattr_str(bp, "GEOM::physpath", sc->sc_physpath))
219 			;
220 		else if (strcmp(bp->bio_attribute, "GEOM::kerneldump") == 0)
221 			g_nop_kerneldump(bp, sc);
222 		else
223 			/*
224 			 * Fallthrough to forwarding the GETATTR down to the
225 			 * lower level device.
226 			 */
227 			break;
228 		mtx_unlock(&sc->sc_lock);
229 		return;
230 	case BIO_FLUSH:
231 		sc->sc_flushes++;
232 		break;
233 	case BIO_CMD0:
234 		sc->sc_cmd0s++;
235 		break;
236 	case BIO_CMD1:
237 		sc->sc_cmd1s++;
238 		break;
239 	case BIO_CMD2:
240 		sc->sc_cmd2s++;
241 		break;
242 	}
243 	mtx_unlock(&sc->sc_lock);
244 	if (failprob > 0) {
245 		u_int rval;
246 
247 		rval = arc4random() % 100;
248 		if (rval < failprob) {
249 			G_NOP_LOGREQLVL(1, bp, "Returning error=%d.", sc->sc_error);
250 			g_io_deliver(bp, sc->sc_error);
251 			return;
252 		}
253 	}
254 
255 	cbp = g_clone_bio(bp);
256 	if (cbp == NULL) {
257 		g_io_deliver(bp, ENOMEM);
258 		return;
259 	}
260 	cbp->bio_done = g_std_done;
261 	cbp->bio_offset = bp->bio_offset + sc->sc_offset;
262 	pp = LIST_FIRST(&gp->provider);
263 	KASSERT(pp != NULL, ("NULL pp"));
264 	cbp->bio_to = pp;
265 
266 	if (delayprob > 0) {
267 		struct g_nop_delay *gndelay;
268 		u_int rval;
269 
270 		rval = arc4random() % 100;
271 		if (rval < delayprob) {
272 			gndelay = g_malloc(sizeof(*gndelay), M_NOWAIT | M_ZERO);
273 			if (gndelay != NULL) {
274 				callout_init(&gndelay->dl_cal, 1);
275 
276 				gndelay->dl_bio = cbp;
277 
278 				mtx_lock(&sc->sc_lock);
279 				TAILQ_INSERT_TAIL(&sc->sc_head_delay, gndelay,
280 				    dl_next);
281 				mtx_unlock(&sc->sc_lock);
282 
283 				callout_reset(&gndelay->dl_cal,
284 				    MSEC_2_TICKS(delaytime), g_nop_pass_timeout,
285 				    gndelay);
286 				return;
287 			}
288 		}
289 	}
290 
291 	g_nop_pass(cbp, gp);
292 }
293 
294 static int
295 g_nop_access(struct g_provider *pp, int dr, int dw, int de)
296 {
297 	struct g_geom *gp;
298 	struct g_consumer *cp;
299 	int error;
300 
301 	gp = pp->geom;
302 	cp = LIST_FIRST(&gp->consumer);
303 	error = g_access(cp, dr, dw, de);
304 
305 	return (error);
306 }
307 
308 static int
309 g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp,
310     int ioerror, u_int rfailprob, u_int wfailprob, u_int delaymsec, u_int rdelayprob,
311     u_int wdelayprob, off_t offset, off_t size, u_int secsize, off_t stripesize,
312     off_t stripeoffset, const char *physpath)
313 {
314 	struct g_nop_softc *sc;
315 	struct g_geom *gp;
316 	struct g_provider *newpp;
317 	struct g_consumer *cp;
318 	char name[64];
319 	int error;
320 	off_t explicitsize;
321 
322 	g_topology_assert();
323 
324 	gp = NULL;
325 	newpp = NULL;
326 	cp = NULL;
327 
328 	if ((offset % pp->sectorsize) != 0) {
329 		gctl_error(req, "Invalid offset for provider %s.", pp->name);
330 		return (EINVAL);
331 	}
332 	if ((size % pp->sectorsize) != 0) {
333 		gctl_error(req, "Invalid size for provider %s.", pp->name);
334 		return (EINVAL);
335 	}
336 	if (offset >= pp->mediasize) {
337 		gctl_error(req, "Invalid offset for provider %s.", pp->name);
338 		return (EINVAL);
339 	}
340 	explicitsize = size;
341 	if (size == 0)
342 		size = pp->mediasize - offset;
343 	if (offset + size > pp->mediasize) {
344 		gctl_error(req, "Invalid size for provider %s.", pp->name);
345 		return (EINVAL);
346 	}
347 	if (secsize == 0)
348 		secsize = pp->sectorsize;
349 	else if ((secsize % pp->sectorsize) != 0) {
350 		gctl_error(req, "Invalid secsize for provider %s.", pp->name);
351 		return (EINVAL);
352 	}
353 	if (secsize > MAXPHYS) {
354 		gctl_error(req, "secsize is too big.");
355 		return (EINVAL);
356 	}
357 	size -= size % secsize;
358 	if ((stripesize % pp->sectorsize) != 0) {
359 		gctl_error(req, "Invalid stripesize for provider %s.", pp->name);
360 		return (EINVAL);
361 	}
362 	if ((stripeoffset % pp->sectorsize) != 0) {
363 		gctl_error(req, "Invalid stripeoffset for provider %s.", pp->name);
364 		return (EINVAL);
365 	}
366 	if (stripesize != 0 && stripeoffset >= stripesize) {
367 		gctl_error(req, "stripeoffset is too big.");
368 		return (EINVAL);
369 	}
370 	snprintf(name, sizeof(name), "%s%s", pp->name, G_NOP_SUFFIX);
371 	LIST_FOREACH(gp, &mp->geom, geom) {
372 		if (strcmp(gp->name, name) == 0) {
373 			gctl_error(req, "Provider %s already exists.", name);
374 			return (EEXIST);
375 		}
376 	}
377 	gp = g_new_geomf(mp, "%s", name);
378 	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
379 	sc->sc_offset = offset;
380 	sc->sc_explicitsize = explicitsize;
381 	sc->sc_stripesize = stripesize;
382 	sc->sc_stripeoffset = stripeoffset;
383 	if (physpath && strcmp(physpath, G_NOP_PHYSPATH_PASSTHROUGH)) {
384 		sc->sc_physpath = strndup(physpath, MAXPATHLEN, M_GEOM);
385 	} else
386 		sc->sc_physpath = NULL;
387 	sc->sc_error = ioerror;
388 	sc->sc_rfailprob = rfailprob;
389 	sc->sc_wfailprob = wfailprob;
390 	sc->sc_delaymsec = delaymsec;
391 	sc->sc_rdelayprob = rdelayprob;
392 	sc->sc_wdelayprob = wdelayprob;
393 	sc->sc_reads = 0;
394 	sc->sc_writes = 0;
395 	sc->sc_deletes = 0;
396 	sc->sc_getattrs = 0;
397 	sc->sc_flushes = 0;
398 	sc->sc_cmd0s = 0;
399 	sc->sc_cmd1s = 0;
400 	sc->sc_cmd2s = 0;
401 	sc->sc_readbytes = 0;
402 	sc->sc_wrotebytes = 0;
403 	TAILQ_INIT(&sc->sc_head_delay);
404 	mtx_init(&sc->sc_lock, "gnop lock", NULL, MTX_DEF);
405 	gp->softc = sc;
406 
407 	newpp = g_new_providerf(gp, "%s", gp->name);
408 	newpp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
409 	newpp->mediasize = size;
410 	newpp->sectorsize = secsize;
411 	newpp->stripesize = stripesize;
412 	newpp->stripeoffset = stripeoffset;
413 
414 	cp = g_new_consumer(gp);
415 	cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
416 	error = g_attach(cp, pp);
417 	if (error != 0) {
418 		gctl_error(req, "Cannot attach to provider %s.", pp->name);
419 		goto fail;
420 	}
421 
422 	newpp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
423 	g_error_provider(newpp, 0);
424 	G_NOP_DEBUG(0, "Device %s created.", gp->name);
425 	return (0);
426 fail:
427 	if (cp->provider != NULL)
428 		g_detach(cp);
429 	g_destroy_consumer(cp);
430 	g_destroy_provider(newpp);
431 	mtx_destroy(&sc->sc_lock);
432 	free(sc->sc_physpath, M_GEOM);
433 	g_free(gp->softc);
434 	g_destroy_geom(gp);
435 	return (error);
436 }
437 
438 static void
439 g_nop_providergone(struct g_provider *pp)
440 {
441 	struct g_geom *gp = pp->geom;
442 	struct g_nop_softc *sc = gp->softc;
443 
444 	KASSERT(TAILQ_EMPTY(&sc->sc_head_delay),
445 	    ("delayed request list is not empty"));
446 
447 	gp->softc = NULL;
448 	free(sc->sc_physpath, M_GEOM);
449 	mtx_destroy(&sc->sc_lock);
450 	g_free(sc);
451 }
452 
453 static int
454 g_nop_destroy(struct g_geom *gp, boolean_t force)
455 {
456 	struct g_nop_softc *sc;
457 	struct g_provider *pp;
458 
459 	g_topology_assert();
460 	sc = gp->softc;
461 	if (sc == NULL)
462 		return (ENXIO);
463 	pp = LIST_FIRST(&gp->provider);
464 	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
465 		if (force) {
466 			G_NOP_DEBUG(0, "Device %s is still open, so it "
467 			    "can't be definitely removed.", pp->name);
468 		} else {
469 			G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).",
470 			    pp->name, pp->acr, pp->acw, pp->ace);
471 			return (EBUSY);
472 		}
473 	} else {
474 		G_NOP_DEBUG(0, "Device %s removed.", gp->name);
475 	}
476 
477 	g_wither_geom(gp, ENXIO);
478 
479 	return (0);
480 }
481 
482 static int
483 g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
484 {
485 
486 	return (g_nop_destroy(gp, 0));
487 }
488 
489 static void
490 g_nop_ctl_create(struct gctl_req *req, struct g_class *mp)
491 {
492 	struct g_provider *pp;
493 	intmax_t *error, *rfailprob, *wfailprob, *offset, *secsize, *size,
494 	    *stripesize, *stripeoffset, *delaymsec, *rdelayprob, *wdelayprob;
495 	const char *name, *physpath;
496 	char param[16];
497 	int i, *nargs;
498 
499 	g_topology_assert();
500 
501 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
502 	if (nargs == NULL) {
503 		gctl_error(req, "No '%s' argument", "nargs");
504 		return;
505 	}
506 	if (*nargs <= 0) {
507 		gctl_error(req, "Missing device(s).");
508 		return;
509 	}
510 	error = gctl_get_paraml(req, "error", sizeof(*error));
511 	if (error == NULL) {
512 		gctl_error(req, "No '%s' argument", "error");
513 		return;
514 	}
515 	rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
516 	if (rfailprob == NULL) {
517 		gctl_error(req, "No '%s' argument", "rfailprob");
518 		return;
519 	}
520 	if (*rfailprob < -1 || *rfailprob > 100) {
521 		gctl_error(req, "Invalid '%s' argument", "rfailprob");
522 		return;
523 	}
524 	wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
525 	if (wfailprob == NULL) {
526 		gctl_error(req, "No '%s' argument", "wfailprob");
527 		return;
528 	}
529 	if (*wfailprob < -1 || *wfailprob > 100) {
530 		gctl_error(req, "Invalid '%s' argument", "wfailprob");
531 		return;
532 	}
533 	delaymsec = gctl_get_paraml(req, "delaymsec", sizeof(*delaymsec));
534 	if (delaymsec == NULL) {
535 		gctl_error(req, "No '%s' argument", "delaymsec");
536 		return;
537 	}
538 	if (*delaymsec < 1 && *delaymsec != -1) {
539 		gctl_error(req, "Invalid '%s' argument", "delaymsec");
540 		return;
541 	}
542 	rdelayprob = gctl_get_paraml(req, "rdelayprob", sizeof(*rdelayprob));
543 	if (rdelayprob == NULL) {
544 		gctl_error(req, "No '%s' argument", "rdelayprob");
545 		return;
546 	}
547 	if (*rdelayprob < -1 || *rdelayprob > 100) {
548 		gctl_error(req, "Invalid '%s' argument", "rdelayprob");
549 		return;
550 	}
551 	wdelayprob = gctl_get_paraml(req, "wdelayprob", sizeof(*wdelayprob));
552 	if (wdelayprob == NULL) {
553 		gctl_error(req, "No '%s' argument", "wdelayprob");
554 		return;
555 	}
556 	if (*wdelayprob < -1 || *wdelayprob > 100) {
557 		gctl_error(req, "Invalid '%s' argument", "wdelayprob");
558 		return;
559 	}
560 	offset = gctl_get_paraml(req, "offset", sizeof(*offset));
561 	if (offset == NULL) {
562 		gctl_error(req, "No '%s' argument", "offset");
563 		return;
564 	}
565 	if (*offset < 0) {
566 		gctl_error(req, "Invalid '%s' argument", "offset");
567 		return;
568 	}
569 	size = gctl_get_paraml(req, "size", sizeof(*size));
570 	if (size == NULL) {
571 		gctl_error(req, "No '%s' argument", "size");
572 		return;
573 	}
574 	if (*size < 0) {
575 		gctl_error(req, "Invalid '%s' argument", "size");
576 		return;
577 	}
578 	secsize = gctl_get_paraml(req, "secsize", sizeof(*secsize));
579 	if (secsize == NULL) {
580 		gctl_error(req, "No '%s' argument", "secsize");
581 		return;
582 	}
583 	if (*secsize < 0) {
584 		gctl_error(req, "Invalid '%s' argument", "secsize");
585 		return;
586 	}
587 	stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize));
588 	if (stripesize == NULL) {
589 		gctl_error(req, "No '%s' argument", "stripesize");
590 		return;
591 	}
592 	if (*stripesize < 0) {
593 		gctl_error(req, "Invalid '%s' argument", "stripesize");
594 		return;
595 	}
596 	stripeoffset = gctl_get_paraml(req, "stripeoffset", sizeof(*stripeoffset));
597 	if (stripeoffset == NULL) {
598 		gctl_error(req, "No '%s' argument", "stripeoffset");
599 		return;
600 	}
601 	if (*stripeoffset < 0) {
602 		gctl_error(req, "Invalid '%s' argument", "stripeoffset");
603 		return;
604 	}
605 	physpath = gctl_get_asciiparam(req, "physpath");
606 
607 	for (i = 0; i < *nargs; i++) {
608 		snprintf(param, sizeof(param), "arg%d", i);
609 		name = gctl_get_asciiparam(req, param);
610 		if (name == NULL) {
611 			gctl_error(req, "No 'arg%d' argument", i);
612 			return;
613 		}
614 		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
615 			name += strlen("/dev/");
616 		pp = g_provider_by_name(name);
617 		if (pp == NULL) {
618 			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
619 			gctl_error(req, "Provider %s is invalid.", name);
620 			return;
621 		}
622 		if (g_nop_create(req, mp, pp,
623 		    *error == -1 ? EIO : (int)*error,
624 		    *rfailprob == -1 ? 0 : (u_int)*rfailprob,
625 		    *wfailprob == -1 ? 0 : (u_int)*wfailprob,
626 		    *delaymsec == -1 ? 1 : (u_int)*delaymsec,
627 		    *rdelayprob == -1 ? 0 : (u_int)*rdelayprob,
628 		    *wdelayprob == -1 ? 0 : (u_int)*wdelayprob,
629 		    (off_t)*offset, (off_t)*size, (u_int)*secsize,
630 		    (off_t)*stripesize, (off_t)*stripeoffset,
631 		    physpath) != 0) {
632 			return;
633 		}
634 	}
635 }
636 
637 static void
638 g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp)
639 {
640 	struct g_nop_softc *sc;
641 	struct g_provider *pp;
642 	intmax_t *delaymsec, *error, *rdelayprob, *rfailprob, *wdelayprob, *wfailprob;
643 	const char *name;
644 	char param[16];
645 	int i, *nargs;
646 
647 	g_topology_assert();
648 
649 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
650 	if (nargs == NULL) {
651 		gctl_error(req, "No '%s' argument", "nargs");
652 		return;
653 	}
654 	if (*nargs <= 0) {
655 		gctl_error(req, "Missing device(s).");
656 		return;
657 	}
658 	error = gctl_get_paraml(req, "error", sizeof(*error));
659 	if (error == NULL) {
660 		gctl_error(req, "No '%s' argument", "error");
661 		return;
662 	}
663 	rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
664 	if (rfailprob == NULL) {
665 		gctl_error(req, "No '%s' argument", "rfailprob");
666 		return;
667 	}
668 	if (*rfailprob < -1 || *rfailprob > 100) {
669 		gctl_error(req, "Invalid '%s' argument", "rfailprob");
670 		return;
671 	}
672 	wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
673 	if (wfailprob == NULL) {
674 		gctl_error(req, "No '%s' argument", "wfailprob");
675 		return;
676 	}
677 	if (*wfailprob < -1 || *wfailprob > 100) {
678 		gctl_error(req, "Invalid '%s' argument", "wfailprob");
679 		return;
680 	}
681 
682 	delaymsec = gctl_get_paraml(req, "delaymsec", sizeof(*delaymsec));
683 	if (delaymsec == NULL) {
684 		gctl_error(req, "No '%s' argument", "delaymsec");
685 		return;
686 	}
687 	if (*delaymsec < 1 && *delaymsec != -1) {
688 		gctl_error(req, "Invalid '%s' argument", "delaymsec");
689 		return;
690 	}
691 	rdelayprob = gctl_get_paraml(req, "rdelayprob", sizeof(*rdelayprob));
692 	if (rdelayprob == NULL) {
693 		gctl_error(req, "No '%s' argument", "rdelayprob");
694 		return;
695 	}
696 	if (*rdelayprob < -1 || *rdelayprob > 100) {
697 		gctl_error(req, "Invalid '%s' argument", "rdelayprob");
698 		return;
699 	}
700 	wdelayprob = gctl_get_paraml(req, "wdelayprob", sizeof(*wdelayprob));
701 	if (wdelayprob == NULL) {
702 		gctl_error(req, "No '%s' argument", "wdelayprob");
703 		return;
704 	}
705 	if (*wdelayprob < -1 || *wdelayprob > 100) {
706 		gctl_error(req, "Invalid '%s' argument", "wdelayprob");
707 		return;
708 	}
709 
710 	for (i = 0; i < *nargs; i++) {
711 		snprintf(param, sizeof(param), "arg%d", i);
712 		name = gctl_get_asciiparam(req, param);
713 		if (name == NULL) {
714 			gctl_error(req, "No 'arg%d' argument", i);
715 			return;
716 		}
717 		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
718 			name += strlen("/dev/");
719 		pp = g_provider_by_name(name);
720 		if (pp == NULL || pp->geom->class != mp) {
721 			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
722 			gctl_error(req, "Provider %s is invalid.", name);
723 			return;
724 		}
725 		sc = pp->geom->softc;
726 		if (*error != -1)
727 			sc->sc_error = (int)*error;
728 		if (*rfailprob != -1)
729 			sc->sc_rfailprob = (u_int)*rfailprob;
730 		if (*wfailprob != -1)
731 			sc->sc_wfailprob = (u_int)*wfailprob;
732 		if (*rdelayprob != -1)
733 			sc->sc_rdelayprob = (u_int)*rdelayprob;
734 		if (*wdelayprob != -1)
735 			sc->sc_wdelayprob = (u_int)*wdelayprob;
736 		if (*delaymsec != -1)
737 			sc->sc_delaymsec = (u_int)*delaymsec;
738 	}
739 }
740 
741 static struct g_geom *
742 g_nop_find_geom(struct g_class *mp, const char *name)
743 {
744 	struct g_geom *gp;
745 
746 	LIST_FOREACH(gp, &mp->geom, geom) {
747 		if (strcmp(gp->name, name) == 0)
748 			return (gp);
749 	}
750 	return (NULL);
751 }
752 
753 static void
754 g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp)
755 {
756 	int *nargs, *force, error, i;
757 	struct g_geom *gp;
758 	const char *name;
759 	char param[16];
760 
761 	g_topology_assert();
762 
763 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
764 	if (nargs == NULL) {
765 		gctl_error(req, "No '%s' argument", "nargs");
766 		return;
767 	}
768 	if (*nargs <= 0) {
769 		gctl_error(req, "Missing device(s).");
770 		return;
771 	}
772 	force = gctl_get_paraml(req, "force", sizeof(*force));
773 	if (force == NULL) {
774 		gctl_error(req, "No 'force' argument");
775 		return;
776 	}
777 
778 	for (i = 0; i < *nargs; i++) {
779 		snprintf(param, sizeof(param), "arg%d", i);
780 		name = gctl_get_asciiparam(req, param);
781 		if (name == NULL) {
782 			gctl_error(req, "No 'arg%d' argument", i);
783 			return;
784 		}
785 		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
786 			name += strlen("/dev/");
787 		gp = g_nop_find_geom(mp, name);
788 		if (gp == NULL) {
789 			G_NOP_DEBUG(1, "Device %s is invalid.", name);
790 			gctl_error(req, "Device %s is invalid.", name);
791 			return;
792 		}
793 		error = g_nop_destroy(gp, *force);
794 		if (error != 0) {
795 			gctl_error(req, "Cannot destroy device %s (error=%d).",
796 			    gp->name, error);
797 			return;
798 		}
799 	}
800 }
801 
802 static void
803 g_nop_ctl_reset(struct gctl_req *req, struct g_class *mp)
804 {
805 	struct g_nop_softc *sc;
806 	struct g_provider *pp;
807 	const char *name;
808 	char param[16];
809 	int i, *nargs;
810 
811 	g_topology_assert();
812 
813 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
814 	if (nargs == NULL) {
815 		gctl_error(req, "No '%s' argument", "nargs");
816 		return;
817 	}
818 	if (*nargs <= 0) {
819 		gctl_error(req, "Missing device(s).");
820 		return;
821 	}
822 
823 	for (i = 0; i < *nargs; i++) {
824 		snprintf(param, sizeof(param), "arg%d", i);
825 		name = gctl_get_asciiparam(req, param);
826 		if (name == NULL) {
827 			gctl_error(req, "No 'arg%d' argument", i);
828 			return;
829 		}
830 		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
831 			name += strlen("/dev/");
832 		pp = g_provider_by_name(name);
833 		if (pp == NULL || pp->geom->class != mp) {
834 			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
835 			gctl_error(req, "Provider %s is invalid.", name);
836 			return;
837 		}
838 		sc = pp->geom->softc;
839 		sc->sc_reads = 0;
840 		sc->sc_writes = 0;
841 		sc->sc_deletes = 0;
842 		sc->sc_getattrs = 0;
843 		sc->sc_flushes = 0;
844 		sc->sc_cmd0s = 0;
845 		sc->sc_cmd1s = 0;
846 		sc->sc_cmd2s = 0;
847 		sc->sc_readbytes = 0;
848 		sc->sc_wrotebytes = 0;
849 	}
850 }
851 
852 static void
853 g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb)
854 {
855 	uint32_t *version;
856 
857 	g_topology_assert();
858 
859 	version = gctl_get_paraml(req, "version", sizeof(*version));
860 	if (version == NULL) {
861 		gctl_error(req, "No '%s' argument.", "version");
862 		return;
863 	}
864 	if (*version != G_NOP_VERSION) {
865 		gctl_error(req, "Userland and kernel parts are out of sync.");
866 		return;
867 	}
868 
869 	if (strcmp(verb, "create") == 0) {
870 		g_nop_ctl_create(req, mp);
871 		return;
872 	} else if (strcmp(verb, "configure") == 0) {
873 		g_nop_ctl_configure(req, mp);
874 		return;
875 	} else if (strcmp(verb, "destroy") == 0) {
876 		g_nop_ctl_destroy(req, mp);
877 		return;
878 	} else if (strcmp(verb, "reset") == 0) {
879 		g_nop_ctl_reset(req, mp);
880 		return;
881 	}
882 
883 	gctl_error(req, "Unknown verb.");
884 }
885 
886 static void
887 g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
888     struct g_consumer *cp, struct g_provider *pp)
889 {
890 	struct g_nop_softc *sc;
891 
892 	if (pp != NULL || cp != NULL)
893 		return;
894 	sc = gp->softc;
895 	sbuf_printf(sb, "%s<Offset>%jd</Offset>\n", indent,
896 	    (intmax_t)sc->sc_offset);
897 	sbuf_printf(sb, "%s<ReadFailProb>%u</ReadFailProb>\n", indent,
898 	    sc->sc_rfailprob);
899 	sbuf_printf(sb, "%s<WriteFailProb>%u</WriteFailProb>\n", indent,
900 	    sc->sc_wfailprob);
901 	sbuf_printf(sb, "%s<ReadDelayedProb>%u</ReadDelayedProb>\n", indent,
902 	    sc->sc_rdelayprob);
903 	sbuf_printf(sb, "%s<WriteDelayedProb>%u</WriteDelayedProb>\n", indent,
904 	    sc->sc_wdelayprob);
905 	sbuf_printf(sb, "%s<Delay>%d</Delay>\n", indent, sc->sc_delaymsec);
906 	sbuf_printf(sb, "%s<Error>%d</Error>\n", indent, sc->sc_error);
907 	sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads);
908 	sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes);
909 	sbuf_printf(sb, "%s<Deletes>%ju</Deletes>\n", indent, sc->sc_deletes);
910 	sbuf_printf(sb, "%s<Getattrs>%ju</Getattrs>\n", indent, sc->sc_getattrs);
911 	sbuf_printf(sb, "%s<Flushes>%ju</Flushes>\n", indent, sc->sc_flushes);
912 	sbuf_printf(sb, "%s<Cmd0s>%ju</Cmd0s>\n", indent, sc->sc_cmd0s);
913 	sbuf_printf(sb, "%s<Cmd1s>%ju</Cmd1s>\n", indent, sc->sc_cmd1s);
914 	sbuf_printf(sb, "%s<Cmd2s>%ju</Cmd2s>\n", indent, sc->sc_cmd2s);
915 	sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
916 	    sc->sc_readbytes);
917 	sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
918 	    sc->sc_wrotebytes);
919 }
920 
921 DECLARE_GEOM_CLASS(g_nop_class, g_nop);
922 MODULE_VERSION(geom_nop, 0);
923