xref: /freebsd/sys/geom/nop/g_nop.c (revision dba6dd177bdee890cf445fbe21a5dccefd5de18e)
1 /*-
2  * Copyright (c) 2004 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/sysctl.h>
38 #include <sys/malloc.h>
39 #include <geom/geom.h>
40 #include <geom/nop/g_nop.h>
41 
42 
43 SYSCTL_DECL(_kern_geom);
44 SYSCTL_NODE(_kern_geom, OID_AUTO, nop, CTLFLAG_RW, 0, "GEOM_NOP stuff");
45 static u_int g_nop_debug = 0;
46 SYSCTL_UINT(_kern_geom_nop, OID_AUTO, debug, CTLFLAG_RW, &g_nop_debug, 0,
47     "Debug level");
48 
49 static int g_nop_destroy(struct g_geom *gp, boolean_t force);
50 static int g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp,
51     struct g_geom *gp);
52 static void g_nop_config(struct gctl_req *req, struct g_class *mp,
53     const char *verb);
54 static void g_nop_dumpconf(struct sbuf *sb, const char *indent,
55     struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp);
56 
57 struct g_class g_nop_class = {
58 	.name = G_NOP_CLASS_NAME,
59 	.ctlreq = g_nop_config,
60 	.destroy_geom = g_nop_destroy_geom
61 };
62 
63 
64 static void
65 g_nop_orphan(struct g_consumer *cp)
66 {
67 
68 	g_topology_assert();
69 	g_nop_destroy(cp->geom, 1);
70 }
71 
72 static void
73 g_nop_start(struct bio *bp)
74 {
75 	struct g_geom *gp;
76 	struct g_provider *pp;
77 	struct bio *cbp;
78 
79 	gp = bp->bio_to->geom;
80 	G_NOP_LOGREQ(bp, "Request received.");
81 	cbp = g_clone_bio(bp);
82 	if (cbp == NULL) {
83 		g_io_deliver(bp, ENOMEM);
84 		return;
85 	}
86 	pp = LIST_FIRST(&gp->provider);
87 	KASSERT(pp != NULL, ("NULL pp"));
88 	if (pp->index > 0) {
89 		u_int rval;
90 
91 		rval = arc4random() % 100;
92 		if (rval < pp->index) {
93 			g_io_deliver(bp, EIO);
94 			return;
95 		}
96 	}
97 	cbp->bio_done = g_std_done;
98 	cbp->bio_offset = bp->bio_offset;
99 	cbp->bio_data = bp->bio_data;
100 	cbp->bio_length = bp->bio_length;
101 	cbp->bio_to = LIST_FIRST(&gp->provider);
102 	G_NOP_LOGREQ(cbp, "Sending request.");
103 	g_io_request(cbp, LIST_FIRST(&gp->consumer));
104 }
105 
106 static int
107 g_nop_access(struct g_provider *pp, int dr, int dw, int de)
108 {
109 	struct g_geom *gp;
110 	struct g_consumer *cp;
111 	int error;
112 
113 	gp = pp->geom;
114 	cp = LIST_FIRST(&gp->consumer);
115 	error = g_access(cp, dr, dw, de);
116 
117 	return (error);
118 }
119 
120 static int
121 g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp,
122     u_int failprob)
123 {
124 	struct g_geom *gp;
125 	struct g_provider *newpp;
126 	struct g_consumer *cp;
127 	int error;
128 
129 	g_topology_assert();
130 
131 	gp = NULL;
132 	newpp = NULL;
133 	cp = NULL;
134 
135 	gp = g_new_geomf(mp, "%s%s", pp->name, G_NOP_SUFFIX);
136 	if (gp == NULL) {
137 		gctl_error(req, "Cannot create geom %s%s.", pp->name,
138 		    G_NOP_SUFFIX);
139 		return (ENOMEM);
140 	}
141 	gp->softc = NULL;
142 	gp->start = g_nop_start;
143 	gp->spoiled = g_nop_orphan;
144 	gp->orphan = g_nop_orphan;
145 	gp->access = g_nop_access;
146 	gp->dumpconf = g_nop_dumpconf;
147 
148 	newpp = g_new_providerf(gp, gp->name);
149 	if (newpp == NULL) {
150 		gctl_error(req, "Cannot create provider %s%s.", pp->name,
151 		    G_NOP_SUFFIX);
152 		error = ENOMEM;
153 		goto fail;
154 	}
155 	newpp->mediasize = pp->mediasize;
156 	newpp->sectorsize = pp->sectorsize;
157 	newpp->index = failprob;
158 
159 	cp = g_new_consumer(gp);
160 	if (cp == NULL) {
161 		gctl_error(req, "Cannot create consumer for %s.", gp->name);
162 		error = ENOMEM;
163 		goto fail;
164 	}
165 	error = g_attach(cp, pp);
166 	if (error != 0) {
167 		gctl_error(req, "Cannot attach to provider %s.", pp->name);
168 		goto fail;
169 	}
170 
171 	g_error_provider(newpp, 0);
172 	G_NOP_DEBUG(0, "Device %s created.", gp->name);
173 	return (0);
174 fail:
175 	if (cp != NULL) {
176 		if (cp->provider != NULL)
177 			g_detach(cp);
178 		g_destroy_consumer(cp);
179 	}
180 	if (newpp != NULL)
181 		g_destroy_provider(pp);
182 	if (gp != NULL)
183 		g_destroy_geom(gp);
184 	return (error);
185 }
186 
187 static int
188 g_nop_destroy(struct g_geom *gp, boolean_t force)
189 {
190 	struct g_provider *pp;
191 
192 	g_topology_assert();
193 	pp = LIST_FIRST(&gp->provider);
194 	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
195 		if (force) {
196 			G_NOP_DEBUG(0, "Device %s is still open, so it "
197 			    "can't be definitely removed.", pp->name);
198 		} else {
199 			G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).",
200 			    pp->name, pp->acr, pp->acw, pp->ace);
201 			return (EBUSY);
202 		}
203 	} else {
204 		G_NOP_DEBUG(0, "Device %s removed.", gp->name);
205 	}
206 	g_wither_geom(gp, ENXIO);
207 
208 	return (0);
209 }
210 
211 static int
212 g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
213 {
214 
215 	return (g_nop_destroy(gp, 0));
216 }
217 
218 static void
219 g_nop_ctl_create(struct gctl_req *req, struct g_class *mp)
220 {
221 	struct g_provider *pp;
222 	intmax_t *failprob;
223 	const char *name;
224 	char param[16];
225 	int i, *nargs;
226 
227 	g_topology_assert();
228 
229 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
230 	if (nargs == NULL) {
231 		gctl_error(req, "No '%s' argument", "nargs");
232 		return;
233 	}
234 	if (*nargs <= 0) {
235 		gctl_error(req, "Missing device(s).");
236 		return;
237 	}
238 	failprob = gctl_get_paraml(req, "failprob", sizeof(*failprob));
239 	if (failprob == NULL) {
240 		gctl_error(req, "No '%s' argument", "failprob");
241 		return;
242 	}
243 	if (*failprob < 0 || *failprob > 100) {
244 		gctl_error(req, "Invalid '%s' argument", "failprob");
245 		return;
246 	}
247 
248 	for (i = 0; i < *nargs; i++) {
249 		snprintf(param, sizeof(param), "arg%d", i);
250 		name = gctl_get_asciiparam(req, param);
251 		if (name == NULL) {
252 			gctl_error(req, "No 'arg%d' argument", i);
253 			return;
254 		}
255 		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
256 			name += strlen("/dev/");
257 		pp = g_provider_by_name(name);
258 		if (pp == NULL) {
259 			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
260 			gctl_error(req, "Provider %s is invalid.", name);
261 			return;
262 		}
263 		if (g_nop_create(req, mp, pp, (u_int)*failprob) != 0)
264 			return;
265 	}
266 }
267 
268 static void
269 g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp)
270 {
271 	struct g_provider *pp;
272 	intmax_t *failprob;
273 	const char *name;
274 	char param[16];
275 	int i, *nargs;
276 
277 	g_topology_assert();
278 
279 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
280 	if (nargs == NULL) {
281 		gctl_error(req, "No '%s' argument", "nargs");
282 		return;
283 	}
284 	if (*nargs <= 0) {
285 		gctl_error(req, "Missing device(s).");
286 		return;
287 	}
288 	failprob = gctl_get_paraml(req, "failprob", sizeof(*failprob));
289 	if (failprob == NULL) {
290 		gctl_error(req, "No '%s' argument", "failprob");
291 		return;
292 	}
293 	if (*failprob < 0 || *failprob > 100) {
294 		gctl_error(req, "Invalid '%s' argument", "failprob");
295 		return;
296 	}
297 
298 	for (i = 0; i < *nargs; i++) {
299 		snprintf(param, sizeof(param), "arg%d", i);
300 		name = gctl_get_asciiparam(req, param);
301 		if (name == NULL) {
302 			gctl_error(req, "No 'arg%d' argument", i);
303 			return;
304 		}
305 		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
306 			name += strlen("/dev/");
307 		pp = g_provider_by_name(name);
308 		if (pp == NULL || pp->geom->class != mp) {
309 			G_NOP_DEBUG(1, "Provider %s is invalid.", name);
310 			gctl_error(req, "Provider %s is invalid.", name);
311 			return;
312 		}
313 		pp->index = (u_int)*failprob;
314 	}
315 }
316 
317 static struct g_geom *
318 g_nop_find_geom(struct g_class *mp, const char *name)
319 {
320 	struct g_geom *gp;
321 
322 	LIST_FOREACH(gp, &mp->geom, geom) {
323 		if (strcmp(gp->name, name) == 0)
324 			return (gp);
325 	}
326 	return (NULL);
327 }
328 
329 static void
330 g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp)
331 {
332 	int *nargs, *force, error, i;
333 	struct g_geom *gp;
334 	const char *name;
335 	char param[16];
336 
337 	g_topology_assert();
338 
339 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
340 	if (nargs == NULL) {
341 		gctl_error(req, "No '%s' argument", "nargs");
342 		return;
343 	}
344 	if (*nargs <= 0) {
345 		gctl_error(req, "Missing device(s).");
346 		return;
347 	}
348 	force = gctl_get_paraml(req, "force", sizeof(*force));
349 	if (force == NULL) {
350 		gctl_error(req, "No 'force' argument");
351 		return;
352 	}
353 
354 	for (i = 0; i < *nargs; i++) {
355 		snprintf(param, sizeof(param), "arg%d", i);
356 		name = gctl_get_asciiparam(req, param);
357 		if (name == NULL) {
358 			gctl_error(req, "No 'arg%d' argument", i);
359 			return;
360 		}
361 		if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
362 			name += strlen("/dev/");
363 		gp = g_nop_find_geom(mp, name);
364 		if (gp == NULL) {
365 			G_NOP_DEBUG(1, "Device %s is invalid.", name);
366 			gctl_error(req, "Device %s is invalid.", name);
367 			return;
368 		}
369 		error = g_nop_destroy(gp, *force);
370 		if (error != 0) {
371 			gctl_error(req, "Cannot destroy device %s (error=%d).",
372 			    gp->name, error);
373 			return;
374 		}
375 	}
376 }
377 
378 static void
379 g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb)
380 {
381 	uint32_t *version;
382 
383 	g_topology_assert();
384 
385 	version = gctl_get_paraml(req, "version", sizeof(*version));
386 	if (version == NULL) {
387 		gctl_error(req, "No '%s' argument.", "version");
388 		return;
389 	}
390 	if (*version != G_NOP_VERSION) {
391 		gctl_error(req, "Userland and kernel parts are out of sync.");
392 		return;
393 	}
394 
395 	if (strcmp(verb, "create") == 0) {
396 		g_nop_ctl_create(req, mp);
397 		return;
398 	} else if (strcmp(verb, "configure") == 0) {
399 		g_nop_ctl_configure(req, mp);
400 		return;
401 	} else if (strcmp(verb, "destroy") == 0) {
402 		g_nop_ctl_destroy(req, mp);
403 		return;
404 	}
405 
406 	gctl_error(req, "Unknown verb.");
407 }
408 
409 static void
410 g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
411     struct g_consumer *cp, struct g_provider *pp)
412 {
413 
414 	if (pp != NULL) {
415 		sbuf_printf(sb, "%s<failprob>%u</failprob>\n", indent,
416 		    pp->index);
417 	}
418 }
419 
420 DECLARE_GEOM_CLASS(g_nop_class, g_nop);
421