xref: /dragonfly/sys/dev/misc/tbridge/tbridge.c (revision e0b1d537)
1 /*
2  * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
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  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/proc.h>
34 #include <sys/conf.h>
35 #include <sys/malloc.h>
36 #include <machine/md_var.h>
37 #include <sys/ctype.h>
38 #include <sys/kthread.h>
39 #include <sys/device.h>
40 #include <sys/fcntl.h>
41 #include <machine/varargs.h>
42 #include <sys/module.h>
43 #include <libprop/proplib.h>
44 #include <sys/tbridge.h>
45 
46 static cdev_t		tbridge_dev;
47 static d_open_t		tbridge_dev_open;
48 static d_close_t	tbridge_dev_close;
49 static d_ioctl_t	tbridge_dev_ioctl;
50 
51 
52 static struct dev_ops tbridge_dev_ops = {
53 	{ "tbridge", 0, 0 },
54 	.d_open = tbridge_dev_open,
55 	.d_close = tbridge_dev_close,
56 	.d_ioctl = tbridge_dev_ioctl
57 };
58 
59 static struct tbridge_testcase *tbridge_curtest = NULL;
60 static prop_dictionary_t tbridge_testcase = NULL;
61 static int tbridge_result_ready = 0;
62 static char tbridge_msgbuf[131072]; /* 128 kB message buffer */
63 static char *tbridge_msgbuf_ptr;
64 size_t tbridge_msgbuf_remsz;
65 
66 static prop_dictionary_t
67 testcase_get_result_dict(prop_dictionary_t testcase)
68 {
69 	prop_dictionary_t result_dict;
70 	int r;
71 
72 	result_dict = prop_dictionary_get(testcase, "result");
73 	if (result_dict == NULL) {
74 		result_dict = prop_dictionary_create();
75 		if (result_dict == NULL) {
76 			kprintf("could not allocate new result dict");
77 			return NULL;
78 		}
79 
80 		r = prop_dictionary_set(testcase, "result", result_dict);
81 		if (r == 0) {
82 			kprintf("prop_dictionary operation failed");
83 			return NULL;
84 		}
85 	}
86 
87 	return result_dict;
88 }
89 
90 static int
91 testcase_get_timeout(prop_dictionary_t testcase)
92 {
93 	int32_t val;
94 	int r;
95 
96 	r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
97 	    "timeout_in_secs", &val);
98 	if (r == 0)
99 		val = 600/* default timeout = 10min */;
100 
101 	return val;
102 }
103 
104 static int
105 testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
106 {
107 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
108 
109 	return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
110 }
111 
112 static int
113 testcase_set_result(prop_dictionary_t testcase, int result)
114 {
115 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
116 
117 	return !prop_dictionary_set_int32(dict, "result", result);
118 }
119 
120 static const char *
121 testcase_get_name(prop_dictionary_t testcase)
122 {
123 	const char *str;
124 	int r;
125 
126 	r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str);
127 	if (r == 0)
128 		str = "???";
129 
130 	return str;
131 }
132 
133 int
134 tbridge_printf(const char *fmt, ...)
135 {
136 	__va_list args;
137 	int i;
138 
139 	__va_start(args,fmt);
140 	i = kvsnprintf(tbridge_msgbuf_ptr, tbridge_msgbuf_remsz, fmt, args);
141 	__va_end(args);
142 
143 	tbridge_msgbuf_ptr += i;
144 	tbridge_msgbuf_remsz -= i;
145 
146 	return i;
147 }
148 
149 static int
150 tbridge_register(struct tbridge_testcase *test)
151 {
152 	if (tbridge_curtest != NULL)
153 		return EBUSY;
154 
155 	tbridge_curtest = test;
156 
157 	return 0;
158 }
159 
160 static int
161 tbridge_unregister(void)
162 {
163 	tbridge_curtest = NULL;
164 
165 	return 0;
166 }
167 
168 int
169 tbridge_testcase_modhandler(module_t mod, int type, void *arg)
170 {
171 	struct tbridge_testcase *tcase = (struct tbridge_testcase *)arg;
172 	int error = 0;
173 
174 	switch (type) {
175 	case MOD_LOAD:
176 		error = tbridge_register(tcase);
177 		break;
178 
179 	case MOD_UNLOAD:
180 		if (tcase->tb_abort != NULL)
181 			error = tcase->tb_abort();
182 
183 		if (!error)
184 			tbridge_unregister();
185 		break;
186 
187 	default:
188 		break;
189 	}
190 
191 	return error;
192 }
193 
194 void
195 tbridge_test_done(int result)
196 {
197 	KKASSERT(tbridge_testcase != NULL);
198 
199 	kprintf("testcase '%s' called test_done with result=%d\n",
200 	    testcase_get_name(tbridge_testcase), result);
201 
202 	testcase_set_result(tbridge_testcase, result);
203 	testcase_set_stdout_buf(tbridge_testcase, tbridge_msgbuf);
204 
205 	tbridge_result_ready = 1;
206 	wakeup(&tbridge_result_ready);
207 }
208 
209 static void
210 tbridge_reset(void)
211 {
212 	tbridge_msgbuf_ptr = tbridge_msgbuf;
213 	tbridge_msgbuf_remsz = sizeof(tbridge_msgbuf);
214 
215 	if (tbridge_testcase != NULL) {
216 		prop_object_release(tbridge_testcase);
217 		tbridge_testcase = NULL;
218 	}
219 
220 	safemem_reset_error_count();
221 	tbridge_result_ready = 0;
222 }
223 
224 
225 /*
226  * dev stuff
227  */
228 static int
229 tbridge_dev_open(struct dev_open_args *ap)
230 {
231 	return 0;
232 }
233 
234 static int
235 tbridge_dev_close(struct dev_close_args *ap)
236 {
237 	return 0;
238 }
239 
240 static int
241 tbridge_dev_ioctl(struct dev_ioctl_args *ap)
242 {
243 	struct plistref *pref;
244 	struct thread *td;
245 	int error, r;
246 
247 	error = 0;
248 
249 	/* Use proplib(3) for userspace/kernel communication */
250 	pref = (struct plistref *)ap->a_data;
251 
252 	switch(ap->a_cmd) {
253 	case TBRIDGE_LOADTEST:
254 		tbridge_reset();
255 
256 		if (tbridge_curtest == NULL)
257 			return EINVAL;
258 
259 		error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd,
260 		    &tbridge_testcase);
261 		if (error)
262 			return error;
263 
264 		break;
265 
266 	case TBRIDGE_GETRESULT:
267 		error = kthread_create(tbridge_curtest->tb_run, NULL, &td,
268 		    "testrunner");
269 		if (error)
270 			tbridge_test_done(RESULT_PREFAIL);
271 
272 		/* The following won't be called if the thread wasn't created */
273 		if (!tbridge_result_ready) {
274 			r = tsleep(&tbridge_result_ready, 0, "tbridgeres",
275 			    hz * testcase_get_timeout(tbridge_testcase));
276 			if (r != 0) {
277 				if (tbridge_curtest->tb_abort != NULL)
278 					tbridge_curtest->tb_abort();
279 
280 				tbridge_test_done(RESULT_TIMEOUT);
281 			}
282 		}
283 
284 		if (safemem_get_error_count() != 0) {
285 			/*
286 			 * If there were any double-frees or buffer over- or
287 			 * underflows, we mark the result as 'signalled', i.e.
288 			 * the equivalent of a SIGSEGV/SIGBUS in userland.
289 			 */
290 			testcase_set_result(tbridge_testcase, RESULT_SIGNALLED);
291 		}
292 
293 		error = prop_dictionary_copyout_ioctl(pref, ap->a_cmd,
294 		    tbridge_testcase);
295 		if (error)
296 			return error;
297 
298 		break;
299 	default:
300 		error = ENOTTY; /* Inappropriate ioctl for device */
301 		break;
302 	}
303 
304 	return(error);
305 }
306 
307 
308 static int
309 testbridge_modevent(module_t mod, int type, void *unused)
310 {
311 	switch (type) {
312 	case MOD_LOAD:
313 		tbridge_dev = make_dev(&tbridge_dev_ops,
314 		    0,
315 		    UID_ROOT,
316 		    GID_WHEEL,
317 		    0600,
318 		    "tbridge");
319 
320 		tbridge_testcase = NULL;
321 		tbridge_reset();
322 
323 		kprintf("dfregress kernel test bridge ready!\n");
324 		return 0;
325 
326 	case MOD_UNLOAD:
327 		destroy_dev(tbridge_dev);
328 		kprintf("dfregress kernel test bridge unloaded.\n");
329 		return 0;
330 	}
331 
332 	return EINVAL;
333 }
334 
335 static moduledata_t testbridge_mod = {
336 	"testbridge",
337 	testbridge_modevent,
338 	0
339 };
340 
341 DECLARE_MODULE(testbridge, testbridge_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
342 MODULE_VERSION(testbridge, 1);
343