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