g_nop.c revision 332640
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: stable/11/sys/geom/nop/g_nop.c 332640 2018-04-17 02:18:04Z kevans $");
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
44SYSCTL_DECL(_kern_geom);
45static SYSCTL_NODE(_kern_geom, OID_AUTO, nop, CTLFLAG_RW, 0, "GEOM_NOP stuff");
46static u_int g_nop_debug = 0;
47SYSCTL_UINT(_kern_geom_nop, OID_AUTO, debug, CTLFLAG_RW, &g_nop_debug, 0,
48    "Debug level");
49
50static int g_nop_destroy(struct g_geom *gp, boolean_t force);
51static int g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp,
52    struct g_geom *gp);
53static void g_nop_config(struct gctl_req *req, struct g_class *mp,
54    const char *verb);
55static void g_nop_dumpconf(struct sbuf *sb, const char *indent,
56    struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp);
57
58struct 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
66static void
67g_nop_orphan(struct g_consumer *cp)
68{
69
70	g_topology_assert();
71	g_nop_destroy(cp->geom, 1);
72}
73
74static void
75g_nop_resize(struct g_consumer *cp)
76{
77	struct g_nop_softc *sc;
78	struct g_geom *gp;
79	struct g_provider *pp;
80	off_t size;
81
82	g_topology_assert();
83
84	gp = cp->geom;
85	sc = gp->softc;
86
87	if (sc->sc_explicitsize != 0)
88		return;
89	if (cp->provider->mediasize < sc->sc_offset) {
90		g_nop_destroy(gp, 1);
91		return;
92	}
93	size = cp->provider->mediasize - sc->sc_offset;
94	LIST_FOREACH(pp, &gp->provider, provider)
95		g_resize_provider(pp, size);
96}
97
98static void
99g_nop_start(struct bio *bp)
100{
101	struct g_nop_softc *sc;
102	struct g_geom *gp;
103	struct g_provider *pp;
104	struct bio *cbp;
105	u_int failprob = 0;
106
107	gp = bp->bio_to->geom;
108	sc = gp->softc;
109	G_NOP_LOGREQ(bp, "Request received.");
110	mtx_lock(&sc->sc_lock);
111	switch (bp->bio_cmd) {
112	case BIO_READ:
113		sc->sc_reads++;
114		sc->sc_readbytes += bp->bio_length;
115		failprob = sc->sc_rfailprob;
116		break;
117	case BIO_WRITE:
118		sc->sc_writes++;
119		sc->sc_wrotebytes += bp->bio_length;
120		failprob = sc->sc_wfailprob;
121		break;
122	case BIO_DELETE:
123		sc->sc_deletes++;
124		break;
125	case BIO_GETATTR:
126		sc->sc_getattrs++;
127		if (sc->sc_physpath &&
128		    g_handleattr_str(bp, "GEOM::physpath", sc->sc_physpath)) {
129			mtx_unlock(&sc->sc_lock);
130			return;
131		}
132		break;
133	case BIO_FLUSH:
134		sc->sc_flushes++;
135		break;
136	case BIO_CMD0:
137		sc->sc_cmd0s++;
138		break;
139	case BIO_CMD1:
140		sc->sc_cmd1s++;
141		break;
142	case BIO_CMD2:
143		sc->sc_cmd2s++;
144		break;
145	}
146	mtx_unlock(&sc->sc_lock);
147	if (failprob > 0) {
148		u_int rval;
149
150		rval = arc4random() % 100;
151		if (rval < failprob) {
152			G_NOP_LOGREQLVL(1, bp, "Returning error=%d.", sc->sc_error);
153			g_io_deliver(bp, sc->sc_error);
154			return;
155		}
156	}
157	cbp = g_clone_bio(bp);
158	if (cbp == NULL) {
159		g_io_deliver(bp, ENOMEM);
160		return;
161	}
162	cbp->bio_done = g_std_done;
163	cbp->bio_offset = bp->bio_offset + sc->sc_offset;
164	pp = LIST_FIRST(&gp->provider);
165	KASSERT(pp != NULL, ("NULL pp"));
166	cbp->bio_to = pp;
167	G_NOP_LOGREQ(cbp, "Sending request.");
168	g_io_request(cbp, LIST_FIRST(&gp->consumer));
169}
170
171static int
172g_nop_access(struct g_provider *pp, int dr, int dw, int de)
173{
174	struct g_geom *gp;
175	struct g_consumer *cp;
176	int error;
177
178	gp = pp->geom;
179	cp = LIST_FIRST(&gp->consumer);
180	error = g_access(cp, dr, dw, de);
181
182	return (error);
183}
184
185static int
186g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp,
187    int ioerror, u_int rfailprob, u_int wfailprob, off_t offset, off_t size,
188    u_int secsize, u_int stripesize, u_int stripeoffset, const char *physpath)
189{
190	struct g_nop_softc *sc;
191	struct g_geom *gp;
192	struct g_provider *newpp;
193	struct g_consumer *cp;
194	char name[64];
195	int error;
196	off_t explicitsize;
197
198	g_topology_assert();
199
200	gp = NULL;
201	newpp = NULL;
202	cp = NULL;
203
204	if ((offset % pp->sectorsize) != 0) {
205		gctl_error(req, "Invalid offset for provider %s.", pp->name);
206		return (EINVAL);
207	}
208	if ((size % pp->sectorsize) != 0) {
209		gctl_error(req, "Invalid size for provider %s.", pp->name);
210		return (EINVAL);
211	}
212	if (offset >= pp->mediasize) {
213		gctl_error(req, "Invalid offset for provider %s.", pp->name);
214		return (EINVAL);
215	}
216	explicitsize = size;
217	if (size == 0)
218		size = pp->mediasize - offset;
219	if (offset + size > pp->mediasize) {
220		gctl_error(req, "Invalid size for provider %s.", pp->name);
221		return (EINVAL);
222	}
223	if (secsize == 0)
224		secsize = pp->sectorsize;
225	else if ((secsize % pp->sectorsize) != 0) {
226		gctl_error(req, "Invalid secsize for provider %s.", pp->name);
227		return (EINVAL);
228	}
229	if (secsize > MAXPHYS) {
230		gctl_error(req, "secsize is too big.");
231		return (EINVAL);
232	}
233	size -= size % secsize;
234	if ((stripesize % pp->sectorsize) != 0) {
235		gctl_error(req, "Invalid stripesize for provider %s.", pp->name);
236		return (EINVAL);
237	}
238	if ((stripeoffset % pp->sectorsize) != 0) {
239		gctl_error(req, "Invalid stripeoffset for provider %s.", pp->name);
240		return (EINVAL);
241	}
242	if (stripesize != 0 && stripeoffset >= stripesize) {
243		gctl_error(req, "stripeoffset is too big.");
244		return (EINVAL);
245	}
246	snprintf(name, sizeof(name), "%s%s", pp->name, G_NOP_SUFFIX);
247	LIST_FOREACH(gp, &mp->geom, geom) {
248		if (strcmp(gp->name, name) == 0) {
249			gctl_error(req, "Provider %s already exists.", name);
250			return (EEXIST);
251		}
252	}
253	gp = g_new_geomf(mp, "%s", name);
254	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
255	sc->sc_offset = offset;
256	sc->sc_explicitsize = explicitsize;
257	sc->sc_stripesize = stripesize;
258	sc->sc_stripeoffset = stripeoffset;
259	if (physpath && strcmp(physpath, G_NOP_PHYSPATH_PASSTHROUGH)) {
260		sc->sc_physpath = strndup(physpath, MAXPATHLEN, M_GEOM);
261	} else
262		sc->sc_physpath = NULL;
263	sc->sc_error = ioerror;
264	sc->sc_rfailprob = rfailprob;
265	sc->sc_wfailprob = wfailprob;
266	sc->sc_reads = 0;
267	sc->sc_writes = 0;
268	sc->sc_deletes = 0;
269	sc->sc_getattrs = 0;
270	sc->sc_flushes = 0;
271	sc->sc_cmd0s = 0;
272	sc->sc_cmd1s = 0;
273	sc->sc_cmd2s = 0;
274	sc->sc_readbytes = 0;
275	sc->sc_wrotebytes = 0;
276	mtx_init(&sc->sc_lock, "gnop lock", NULL, MTX_DEF);
277	gp->softc = sc;
278	gp->start = g_nop_start;
279	gp->orphan = g_nop_orphan;
280	gp->resize = g_nop_resize;
281	gp->access = g_nop_access;
282	gp->dumpconf = g_nop_dumpconf;
283
284	newpp = g_new_providerf(gp, "%s", gp->name);
285	newpp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
286	newpp->mediasize = size;
287	newpp->sectorsize = secsize;
288	newpp->stripesize = stripesize;
289	newpp->stripeoffset = stripeoffset;
290
291	cp = g_new_consumer(gp);
292	cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
293	error = g_attach(cp, pp);
294	if (error != 0) {
295		gctl_error(req, "Cannot attach to provider %s.", pp->name);
296		goto fail;
297	}
298
299	newpp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
300	g_error_provider(newpp, 0);
301	G_NOP_DEBUG(0, "Device %s created.", gp->name);
302	return (0);
303fail:
304	if (cp->provider != NULL)
305		g_detach(cp);
306	g_destroy_consumer(cp);
307	g_destroy_provider(newpp);
308	mtx_destroy(&sc->sc_lock);
309	free(sc->sc_physpath, M_GEOM);
310	g_free(gp->softc);
311	g_destroy_geom(gp);
312	return (error);
313}
314
315static int
316g_nop_destroy(struct g_geom *gp, boolean_t force)
317{
318	struct g_nop_softc *sc;
319	struct g_provider *pp;
320
321	g_topology_assert();
322	sc = gp->softc;
323	if (sc == NULL)
324		return (ENXIO);
325	free(sc->sc_physpath, M_GEOM);
326	pp = LIST_FIRST(&gp->provider);
327	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
328		if (force) {
329			G_NOP_DEBUG(0, "Device %s is still open, so it "
330			    "can't be definitely removed.", pp->name);
331		} else {
332			G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).",
333			    pp->name, pp->acr, pp->acw, pp->ace);
334			return (EBUSY);
335		}
336	} else {
337		G_NOP_DEBUG(0, "Device %s removed.", gp->name);
338	}
339	gp->softc = NULL;
340	mtx_destroy(&sc->sc_lock);
341	g_free(sc);
342	g_wither_geom(gp, ENXIO);
343
344	return (0);
345}
346
347static int
348g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
349{
350
351	return (g_nop_destroy(gp, 0));
352}
353
354static void
355g_nop_ctl_create(struct gctl_req *req, struct g_class *mp)
356{
357	struct g_provider *pp;
358	intmax_t *error, *rfailprob, *wfailprob, *offset, *secsize, *size,
359	    *stripesize, *stripeoffset;
360	const char *name, *physpath;
361	char param[16];
362	int i, *nargs;
363
364	g_topology_assert();
365
366	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
367	if (nargs == NULL) {
368		gctl_error(req, "No '%s' argument", "nargs");
369		return;
370	}
371	if (*nargs <= 0) {
372		gctl_error(req, "Missing device(s).");
373		return;
374	}
375	error = gctl_get_paraml(req, "error", sizeof(*error));
376	if (error == NULL) {
377		gctl_error(req, "No '%s' argument", "error");
378		return;
379	}
380	rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
381	if (rfailprob == NULL) {
382		gctl_error(req, "No '%s' argument", "rfailprob");
383		return;
384	}
385	if (*rfailprob < -1 || *rfailprob > 100) {
386		gctl_error(req, "Invalid '%s' argument", "rfailprob");
387		return;
388	}
389	wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
390	if (wfailprob == NULL) {
391		gctl_error(req, "No '%s' argument", "wfailprob");
392		return;
393	}
394	if (*wfailprob < -1 || *wfailprob > 100) {
395		gctl_error(req, "Invalid '%s' argument", "wfailprob");
396		return;
397	}
398	offset = gctl_get_paraml(req, "offset", sizeof(*offset));
399	if (offset == NULL) {
400		gctl_error(req, "No '%s' argument", "offset");
401		return;
402	}
403	if (*offset < 0) {
404		gctl_error(req, "Invalid '%s' argument", "offset");
405		return;
406	}
407	size = gctl_get_paraml(req, "size", sizeof(*size));
408	if (size == NULL) {
409		gctl_error(req, "No '%s' argument", "size");
410		return;
411	}
412	if (*size < 0) {
413		gctl_error(req, "Invalid '%s' argument", "size");
414		return;
415	}
416	secsize = gctl_get_paraml(req, "secsize", sizeof(*secsize));
417	if (secsize == NULL) {
418		gctl_error(req, "No '%s' argument", "secsize");
419		return;
420	}
421	if (*secsize < 0) {
422		gctl_error(req, "Invalid '%s' argument", "secsize");
423		return;
424	}
425	stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize));
426	if (stripesize == NULL) {
427		gctl_error(req, "No '%s' argument", "stripesize");
428		return;
429	}
430	if (*stripesize < 0) {
431		gctl_error(req, "Invalid '%s' argument", "stripesize");
432		return;
433	}
434	stripeoffset = gctl_get_paraml(req, "stripeoffset", sizeof(*stripeoffset));
435	if (stripeoffset == NULL) {
436		gctl_error(req, "No '%s' argument", "stripeoffset");
437		return;
438	}
439	if (*stripeoffset < 0) {
440		gctl_error(req, "Invalid '%s' argument", "stripeoffset");
441		return;
442	}
443	physpath = gctl_get_asciiparam(req, "physpath");
444
445	for (i = 0; i < *nargs; i++) {
446		snprintf(param, sizeof(param), "arg%d", i);
447		name = gctl_get_asciiparam(req, param);
448		if (name == NULL) {
449			gctl_error(req, "No 'arg%d' argument", i);
450			return;
451		}
452		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
453			name += strlen("/dev/");
454		pp = g_provider_by_name(name);
455		if (pp == NULL) {
456			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
457			gctl_error(req, "Provider %s is invalid.", name);
458			return;
459		}
460		if (g_nop_create(req, mp, pp,
461		    *error == -1 ? EIO : (int)*error,
462		    *rfailprob == -1 ? 0 : (u_int)*rfailprob,
463		    *wfailprob == -1 ? 0 : (u_int)*wfailprob,
464		    (off_t)*offset, (off_t)*size, (u_int)*secsize,
465		    (u_int)*stripesize, (u_int)*stripeoffset,
466		    physpath) != 0) {
467			return;
468		}
469	}
470}
471
472static void
473g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp)
474{
475	struct g_nop_softc *sc;
476	struct g_provider *pp;
477	intmax_t *error, *rfailprob, *wfailprob;
478	const char *name;
479	char param[16];
480	int i, *nargs;
481
482	g_topology_assert();
483
484	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
485	if (nargs == NULL) {
486		gctl_error(req, "No '%s' argument", "nargs");
487		return;
488	}
489	if (*nargs <= 0) {
490		gctl_error(req, "Missing device(s).");
491		return;
492	}
493	error = gctl_get_paraml(req, "error", sizeof(*error));
494	if (error == NULL) {
495		gctl_error(req, "No '%s' argument", "error");
496		return;
497	}
498	rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
499	if (rfailprob == NULL) {
500		gctl_error(req, "No '%s' argument", "rfailprob");
501		return;
502	}
503	if (*rfailprob < -1 || *rfailprob > 100) {
504		gctl_error(req, "Invalid '%s' argument", "rfailprob");
505		return;
506	}
507	wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
508	if (wfailprob == NULL) {
509		gctl_error(req, "No '%s' argument", "wfailprob");
510		return;
511	}
512	if (*wfailprob < -1 || *wfailprob > 100) {
513		gctl_error(req, "Invalid '%s' argument", "wfailprob");
514		return;
515	}
516
517	for (i = 0; i < *nargs; i++) {
518		snprintf(param, sizeof(param), "arg%d", i);
519		name = gctl_get_asciiparam(req, param);
520		if (name == NULL) {
521			gctl_error(req, "No 'arg%d' argument", i);
522			return;
523		}
524		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
525			name += strlen("/dev/");
526		pp = g_provider_by_name(name);
527		if (pp == NULL || pp->geom->class != mp) {
528			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
529			gctl_error(req, "Provider %s is invalid.", name);
530			return;
531		}
532		sc = pp->geom->softc;
533		if (*error != -1)
534			sc->sc_error = (int)*error;
535		if (*rfailprob != -1)
536			sc->sc_rfailprob = (u_int)*rfailprob;
537		if (*wfailprob != -1)
538			sc->sc_wfailprob = (u_int)*wfailprob;
539	}
540}
541
542static struct g_geom *
543g_nop_find_geom(struct g_class *mp, const char *name)
544{
545	struct g_geom *gp;
546
547	LIST_FOREACH(gp, &mp->geom, geom) {
548		if (strcmp(gp->name, name) == 0)
549			return (gp);
550	}
551	return (NULL);
552}
553
554static void
555g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp)
556{
557	int *nargs, *force, error, i;
558	struct g_geom *gp;
559	const char *name;
560	char param[16];
561
562	g_topology_assert();
563
564	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
565	if (nargs == NULL) {
566		gctl_error(req, "No '%s' argument", "nargs");
567		return;
568	}
569	if (*nargs <= 0) {
570		gctl_error(req, "Missing device(s).");
571		return;
572	}
573	force = gctl_get_paraml(req, "force", sizeof(*force));
574	if (force == NULL) {
575		gctl_error(req, "No 'force' argument");
576		return;
577	}
578
579	for (i = 0; i < *nargs; i++) {
580		snprintf(param, sizeof(param), "arg%d", i);
581		name = gctl_get_asciiparam(req, param);
582		if (name == NULL) {
583			gctl_error(req, "No 'arg%d' argument", i);
584			return;
585		}
586		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
587			name += strlen("/dev/");
588		gp = g_nop_find_geom(mp, name);
589		if (gp == NULL) {
590			G_NOP_DEBUG(1, "Device %s is invalid.", name);
591			gctl_error(req, "Device %s is invalid.", name);
592			return;
593		}
594		error = g_nop_destroy(gp, *force);
595		if (error != 0) {
596			gctl_error(req, "Cannot destroy device %s (error=%d).",
597			    gp->name, error);
598			return;
599		}
600	}
601}
602
603static void
604g_nop_ctl_reset(struct gctl_req *req, struct g_class *mp)
605{
606	struct g_nop_softc *sc;
607	struct g_provider *pp;
608	const char *name;
609	char param[16];
610	int i, *nargs;
611
612	g_topology_assert();
613
614	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
615	if (nargs == NULL) {
616		gctl_error(req, "No '%s' argument", "nargs");
617		return;
618	}
619	if (*nargs <= 0) {
620		gctl_error(req, "Missing device(s).");
621		return;
622	}
623
624	for (i = 0; i < *nargs; i++) {
625		snprintf(param, sizeof(param), "arg%d", i);
626		name = gctl_get_asciiparam(req, param);
627		if (name == NULL) {
628			gctl_error(req, "No 'arg%d' argument", i);
629			return;
630		}
631		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
632			name += strlen("/dev/");
633		pp = g_provider_by_name(name);
634		if (pp == NULL || pp->geom->class != mp) {
635			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
636			gctl_error(req, "Provider %s is invalid.", name);
637			return;
638		}
639		sc = pp->geom->softc;
640		sc->sc_reads = 0;
641		sc->sc_writes = 0;
642		sc->sc_deletes = 0;
643		sc->sc_getattrs = 0;
644		sc->sc_flushes = 0;
645		sc->sc_cmd0s = 0;
646		sc->sc_cmd1s = 0;
647		sc->sc_cmd2s = 0;
648		sc->sc_readbytes = 0;
649		sc->sc_wrotebytes = 0;
650	}
651}
652
653static void
654g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb)
655{
656	uint32_t *version;
657
658	g_topology_assert();
659
660	version = gctl_get_paraml(req, "version", sizeof(*version));
661	if (version == NULL) {
662		gctl_error(req, "No '%s' argument.", "version");
663		return;
664	}
665	if (*version != G_NOP_VERSION) {
666		gctl_error(req, "Userland and kernel parts are out of sync.");
667		return;
668	}
669
670	if (strcmp(verb, "create") == 0) {
671		g_nop_ctl_create(req, mp);
672		return;
673	} else if (strcmp(verb, "configure") == 0) {
674		g_nop_ctl_configure(req, mp);
675		return;
676	} else if (strcmp(verb, "destroy") == 0) {
677		g_nop_ctl_destroy(req, mp);
678		return;
679	} else if (strcmp(verb, "reset") == 0) {
680		g_nop_ctl_reset(req, mp);
681		return;
682	}
683
684	gctl_error(req, "Unknown verb.");
685}
686
687static void
688g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
689    struct g_consumer *cp, struct g_provider *pp)
690{
691	struct g_nop_softc *sc;
692
693	if (pp != NULL || cp != NULL)
694		return;
695	sc = gp->softc;
696	sbuf_printf(sb, "%s<Offset>%jd</Offset>\n", indent,
697	    (intmax_t)sc->sc_offset);
698	sbuf_printf(sb, "%s<ReadFailProb>%u</ReadFailProb>\n", indent,
699	    sc->sc_rfailprob);
700	sbuf_printf(sb, "%s<WriteFailProb>%u</WriteFailProb>\n", indent,
701	    sc->sc_wfailprob);
702	sbuf_printf(sb, "%s<Error>%d</Error>\n", indent, sc->sc_error);
703	sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads);
704	sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes);
705	sbuf_printf(sb, "%s<Deletes>%ju</Deletes>\n", indent, sc->sc_deletes);
706	sbuf_printf(sb, "%s<Getattrs>%ju</Getattrs>\n", indent, sc->sc_getattrs);
707	sbuf_printf(sb, "%s<Flushes>%ju</Flushes>\n", indent, sc->sc_flushes);
708	sbuf_printf(sb, "%s<Cmd0s>%ju</Cmd0s>\n", indent, sc->sc_cmd0s);
709	sbuf_printf(sb, "%s<Cmd1s>%ju</Cmd1s>\n", indent, sc->sc_cmd1s);
710	sbuf_printf(sb, "%s<Cmd2s>%ju</Cmd2s>\n", indent, sc->sc_cmd2s);
711	sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
712	    sc->sc_readbytes);
713	sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
714	    sc->sc_wrotebytes);
715}
716
717DECLARE_GEOM_CLASS(g_nop_class, g_nop);
718MODULE_VERSION(geom_nop, 0);
719