g_nop.c revision 260385
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 260385 2014-01-07 01:32:23Z scottl $");
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)
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	snprintf(name, sizeof(name), "%s%s", pp->name, G_NOP_SUFFIX);
212	LIST_FOREACH(gp, &mp->geom, geom) {
213		if (strcmp(gp->name, name) == 0) {
214			gctl_error(req, "Provider %s already exists.", name);
215			return (EEXIST);
216		}
217	}
218	gp = g_new_geomf(mp, "%s", name);
219	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
220	sc->sc_offset = offset;
221	sc->sc_explicitsize = explicitsize;
222	sc->sc_error = ioerror;
223	sc->sc_rfailprob = rfailprob;
224	sc->sc_wfailprob = wfailprob;
225	sc->sc_reads = 0;
226	sc->sc_writes = 0;
227	sc->sc_readbytes = 0;
228	sc->sc_wrotebytes = 0;
229	mtx_init(&sc->sc_lock, "gnop lock", NULL, MTX_DEF);
230	gp->softc = sc;
231	gp->start = g_nop_start;
232	gp->orphan = g_nop_orphan;
233	gp->resize = g_nop_resize;
234	gp->access = g_nop_access;
235	gp->dumpconf = g_nop_dumpconf;
236
237	newpp = g_new_providerf(gp, "%s", gp->name);
238	newpp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
239	newpp->mediasize = size;
240	newpp->sectorsize = secsize;
241
242	cp = g_new_consumer(gp);
243	cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
244	error = g_attach(cp, pp);
245	if (error != 0) {
246		gctl_error(req, "Cannot attach to provider %s.", pp->name);
247		goto fail;
248	}
249
250	newpp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
251	g_error_provider(newpp, 0);
252	G_NOP_DEBUG(0, "Device %s created.", gp->name);
253	return (0);
254fail:
255	if (cp->provider != NULL)
256		g_detach(cp);
257	g_destroy_consumer(cp);
258	g_destroy_provider(newpp);
259	mtx_destroy(&sc->sc_lock);
260	g_free(gp->softc);
261	g_destroy_geom(gp);
262	return (error);
263}
264
265static int
266g_nop_destroy(struct g_geom *gp, boolean_t force)
267{
268	struct g_nop_softc *sc;
269	struct g_provider *pp;
270
271	g_topology_assert();
272	sc = gp->softc;
273	if (sc == NULL)
274		return (ENXIO);
275	pp = LIST_FIRST(&gp->provider);
276	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
277		if (force) {
278			G_NOP_DEBUG(0, "Device %s is still open, so it "
279			    "can't be definitely removed.", pp->name);
280		} else {
281			G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).",
282			    pp->name, pp->acr, pp->acw, pp->ace);
283			return (EBUSY);
284		}
285	} else {
286		G_NOP_DEBUG(0, "Device %s removed.", gp->name);
287	}
288	gp->softc = NULL;
289	mtx_destroy(&sc->sc_lock);
290	g_free(sc);
291	g_wither_geom(gp, ENXIO);
292
293	return (0);
294}
295
296static int
297g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
298{
299
300	return (g_nop_destroy(gp, 0));
301}
302
303static void
304g_nop_ctl_create(struct gctl_req *req, struct g_class *mp)
305{
306	struct g_provider *pp;
307	intmax_t *error, *rfailprob, *wfailprob, *offset, *secsize, *size;
308	const char *name;
309	char param[16];
310	int i, *nargs;
311
312	g_topology_assert();
313
314	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
315	if (nargs == NULL) {
316		gctl_error(req, "No '%s' argument", "nargs");
317		return;
318	}
319	if (*nargs <= 0) {
320		gctl_error(req, "Missing device(s).");
321		return;
322	}
323	error = gctl_get_paraml(req, "error", sizeof(*error));
324	if (error == NULL) {
325		gctl_error(req, "No '%s' argument", "error");
326		return;
327	}
328	rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
329	if (rfailprob == NULL) {
330		gctl_error(req, "No '%s' argument", "rfailprob");
331		return;
332	}
333	if (*rfailprob < -1 || *rfailprob > 100) {
334		gctl_error(req, "Invalid '%s' argument", "rfailprob");
335		return;
336	}
337	wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
338	if (wfailprob == NULL) {
339		gctl_error(req, "No '%s' argument", "wfailprob");
340		return;
341	}
342	if (*wfailprob < -1 || *wfailprob > 100) {
343		gctl_error(req, "Invalid '%s' argument", "wfailprob");
344		return;
345	}
346	offset = gctl_get_paraml(req, "offset", sizeof(*offset));
347	if (offset == NULL) {
348		gctl_error(req, "No '%s' argument", "offset");
349		return;
350	}
351	if (*offset < 0) {
352		gctl_error(req, "Invalid '%s' argument", "offset");
353		return;
354	}
355	size = gctl_get_paraml(req, "size", sizeof(*size));
356	if (size == NULL) {
357		gctl_error(req, "No '%s' argument", "size");
358		return;
359	}
360	if (*size < 0) {
361		gctl_error(req, "Invalid '%s' argument", "size");
362		return;
363	}
364	secsize = gctl_get_paraml(req, "secsize", sizeof(*secsize));
365	if (secsize == NULL) {
366		gctl_error(req, "No '%s' argument", "secsize");
367		return;
368	}
369	if (*secsize < 0) {
370		gctl_error(req, "Invalid '%s' argument", "secsize");
371		return;
372	}
373
374	for (i = 0; i < *nargs; i++) {
375		snprintf(param, sizeof(param), "arg%d", i);
376		name = gctl_get_asciiparam(req, param);
377		if (name == NULL) {
378			gctl_error(req, "No 'arg%d' argument", i);
379			return;
380		}
381		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
382			name += strlen("/dev/");
383		pp = g_provider_by_name(name);
384		if (pp == NULL) {
385			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
386			gctl_error(req, "Provider %s is invalid.", name);
387			return;
388		}
389		if (g_nop_create(req, mp, pp,
390		    *error == -1 ? EIO : (int)*error,
391		    *rfailprob == -1 ? 0 : (u_int)*rfailprob,
392		    *wfailprob == -1 ? 0 : (u_int)*wfailprob,
393		    (off_t)*offset, (off_t)*size, (u_int)*secsize) != 0) {
394			return;
395		}
396	}
397}
398
399static void
400g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp)
401{
402	struct g_nop_softc *sc;
403	struct g_provider *pp;
404	intmax_t *error, *rfailprob, *wfailprob;
405	const char *name;
406	char param[16];
407	int i, *nargs;
408
409	g_topology_assert();
410
411	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
412	if (nargs == NULL) {
413		gctl_error(req, "No '%s' argument", "nargs");
414		return;
415	}
416	if (*nargs <= 0) {
417		gctl_error(req, "Missing device(s).");
418		return;
419	}
420	error = gctl_get_paraml(req, "error", sizeof(*error));
421	if (error == NULL) {
422		gctl_error(req, "No '%s' argument", "error");
423		return;
424	}
425	rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob));
426	if (rfailprob == NULL) {
427		gctl_error(req, "No '%s' argument", "rfailprob");
428		return;
429	}
430	if (*rfailprob < -1 || *rfailprob > 100) {
431		gctl_error(req, "Invalid '%s' argument", "rfailprob");
432		return;
433	}
434	wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob));
435	if (wfailprob == NULL) {
436		gctl_error(req, "No '%s' argument", "wfailprob");
437		return;
438	}
439	if (*wfailprob < -1 || *wfailprob > 100) {
440		gctl_error(req, "Invalid '%s' argument", "wfailprob");
441		return;
442	}
443
444	for (i = 0; i < *nargs; i++) {
445		snprintf(param, sizeof(param), "arg%d", i);
446		name = gctl_get_asciiparam(req, param);
447		if (name == NULL) {
448			gctl_error(req, "No 'arg%d' argument", i);
449			return;
450		}
451		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
452			name += strlen("/dev/");
453		pp = g_provider_by_name(name);
454		if (pp == NULL || pp->geom->class != mp) {
455			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
456			gctl_error(req, "Provider %s is invalid.", name);
457			return;
458		}
459		sc = pp->geom->softc;
460		if (*error != -1)
461			sc->sc_error = (int)*error;
462		if (*rfailprob != -1)
463			sc->sc_rfailprob = (u_int)*rfailprob;
464		if (*wfailprob != -1)
465			sc->sc_wfailprob = (u_int)*wfailprob;
466	}
467}
468
469static struct g_geom *
470g_nop_find_geom(struct g_class *mp, const char *name)
471{
472	struct g_geom *gp;
473
474	LIST_FOREACH(gp, &mp->geom, geom) {
475		if (strcmp(gp->name, name) == 0)
476			return (gp);
477	}
478	return (NULL);
479}
480
481static void
482g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp)
483{
484	int *nargs, *force, error, i;
485	struct g_geom *gp;
486	const char *name;
487	char param[16];
488
489	g_topology_assert();
490
491	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
492	if (nargs == NULL) {
493		gctl_error(req, "No '%s' argument", "nargs");
494		return;
495	}
496	if (*nargs <= 0) {
497		gctl_error(req, "Missing device(s).");
498		return;
499	}
500	force = gctl_get_paraml(req, "force", sizeof(*force));
501	if (force == NULL) {
502		gctl_error(req, "No 'force' argument");
503		return;
504	}
505
506	for (i = 0; i < *nargs; i++) {
507		snprintf(param, sizeof(param), "arg%d", i);
508		name = gctl_get_asciiparam(req, param);
509		if (name == NULL) {
510			gctl_error(req, "No 'arg%d' argument", i);
511			return;
512		}
513		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
514			name += strlen("/dev/");
515		gp = g_nop_find_geom(mp, name);
516		if (gp == NULL) {
517			G_NOP_DEBUG(1, "Device %s is invalid.", name);
518			gctl_error(req, "Device %s is invalid.", name);
519			return;
520		}
521		error = g_nop_destroy(gp, *force);
522		if (error != 0) {
523			gctl_error(req, "Cannot destroy device %s (error=%d).",
524			    gp->name, error);
525			return;
526		}
527	}
528}
529
530static void
531g_nop_ctl_reset(struct gctl_req *req, struct g_class *mp)
532{
533	struct g_nop_softc *sc;
534	struct g_provider *pp;
535	const char *name;
536	char param[16];
537	int i, *nargs;
538
539	g_topology_assert();
540
541	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
542	if (nargs == NULL) {
543		gctl_error(req, "No '%s' argument", "nargs");
544		return;
545	}
546	if (*nargs <= 0) {
547		gctl_error(req, "Missing device(s).");
548		return;
549	}
550
551	for (i = 0; i < *nargs; i++) {
552		snprintf(param, sizeof(param), "arg%d", i);
553		name = gctl_get_asciiparam(req, param);
554		if (name == NULL) {
555			gctl_error(req, "No 'arg%d' argument", i);
556			return;
557		}
558		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
559			name += strlen("/dev/");
560		pp = g_provider_by_name(name);
561		if (pp == NULL || pp->geom->class != mp) {
562			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
563			gctl_error(req, "Provider %s is invalid.", name);
564			return;
565		}
566		sc = pp->geom->softc;
567		sc->sc_reads = 0;
568		sc->sc_writes = 0;
569		sc->sc_readbytes = 0;
570		sc->sc_wrotebytes = 0;
571	}
572}
573
574static void
575g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb)
576{
577	uint32_t *version;
578
579	g_topology_assert();
580
581	version = gctl_get_paraml(req, "version", sizeof(*version));
582	if (version == NULL) {
583		gctl_error(req, "No '%s' argument.", "version");
584		return;
585	}
586	if (*version != G_NOP_VERSION) {
587		gctl_error(req, "Userland and kernel parts are out of sync.");
588		return;
589	}
590
591	if (strcmp(verb, "create") == 0) {
592		g_nop_ctl_create(req, mp);
593		return;
594	} else if (strcmp(verb, "configure") == 0) {
595		g_nop_ctl_configure(req, mp);
596		return;
597	} else if (strcmp(verb, "destroy") == 0) {
598		g_nop_ctl_destroy(req, mp);
599		return;
600	} else if (strcmp(verb, "reset") == 0) {
601		g_nop_ctl_reset(req, mp);
602		return;
603	}
604
605	gctl_error(req, "Unknown verb.");
606}
607
608static void
609g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
610    struct g_consumer *cp, struct g_provider *pp)
611{
612	struct g_nop_softc *sc;
613
614	if (pp != NULL || cp != NULL)
615		return;
616	sc = gp->softc;
617	sbuf_printf(sb, "%s<Offset>%jd</Offset>\n", indent,
618	    (intmax_t)sc->sc_offset);
619	sbuf_printf(sb, "%s<ReadFailProb>%u</ReadFailProb>\n", indent,
620	    sc->sc_rfailprob);
621	sbuf_printf(sb, "%s<WriteFailProb>%u</WriteFailProb>\n", indent,
622	    sc->sc_wfailprob);
623	sbuf_printf(sb, "%s<Error>%d</Error>\n", indent, sc->sc_error);
624	sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads);
625	sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes);
626	sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
627	    sc->sc_readbytes);
628	sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
629	    sc->sc_wrotebytes);
630}
631
632DECLARE_GEOM_CLASS(g_nop_class, g_nop);
633