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