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