xref: /dragonfly/sys/dev/misc/tbridge/tbridge.c (revision e96fb831)
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 <sys/ioccom.h>
42 #include <machine/varargs.h>
43 #include <sys/module.h>
44 #include <libprop/proplib.h>
45 #include <sys/tbridge.h>
46 
47 static cdev_t		tbridge_dev;
48 static d_open_t		tbridge_dev_open;
49 static d_close_t	tbridge_dev_close;
50 static d_ioctl_t	tbridge_dev_ioctl;
51 
52 
53 static struct dev_ops tbridge_dev_ops = {
54 	{ "tbridge", 0, 0 },
55 	.d_open = tbridge_dev_open,
56 	.d_close = tbridge_dev_close,
57 	.d_ioctl = tbridge_dev_ioctl
58 };
59 
60 static struct tbridge_testcase *tbridge_curtest = NULL;
61 static prop_dictionary_t tbridge_testcase = NULL;
62 static int tbridge_result_ready = 0;
63 static char tbridge_msgbuf[131072]; /* 128 kB message buffer */
64 static char *tbridge_msgbuf_ptr;
65 size_t tbridge_msgbuf_remsz;
66 
67 static prop_dictionary_t
68 testcase_get_result_dict(prop_dictionary_t testcase)
69 {
70 	prop_dictionary_t result_dict;
71 	int r;
72 
73 	result_dict = prop_dictionary_get(testcase, "result");
74 	if (result_dict == NULL) {
75 		result_dict = prop_dictionary_create();
76 		if (result_dict == NULL) {
77 			kprintf("could not allocate new result dict");
78 			return NULL;
79 		}
80 
81 		r = prop_dictionary_set(testcase, "result", result_dict);
82 		if (r == 0) {
83 			kprintf("prop_dictionary operation failed");
84 			return NULL;
85 		}
86 	}
87 
88 	return result_dict;
89 }
90 
91 static int
92 testcase_get_timeout(prop_dictionary_t testcase)
93 {
94 	int32_t val;
95 	int r;
96 
97 	r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
98 	    "timeout_in_secs", &val);
99 	if (r == 0)
100 		val = 600/* default timeout = 10min */;
101 
102 	return val;
103 }
104 
105 static int
106 testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
107 {
108 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
109 
110 	return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
111 }
112 
113 static int
114 testcase_set_result(prop_dictionary_t testcase, int result)
115 {
116 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
117 
118 	return !prop_dictionary_set_int32(dict, "result", result);
119 }
120 
121 static const char *
122 testcase_get_name(prop_dictionary_t testcase)
123 {
124 	const char *str;
125 	int r;
126 
127 	r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str);
128 	if (r == 0)
129 		str = "???";
130 
131 	return str;
132 }
133 
134 int
135 tbridge_printf(const char *fmt, ...)
136 {
137 	__va_list args;
138 	int i;
139 
140 	__va_start(args,fmt);
141 	i = kvsnprintf(tbridge_msgbuf_ptr, tbridge_msgbuf_remsz, fmt, args);
142 	__va_end(args);
143 
144 	tbridge_msgbuf_ptr += i;
145 	tbridge_msgbuf_remsz -= i;
146 
147 	return i;
148 }
149 
150 static int
151 tbridge_register(struct tbridge_testcase *test)
152 {
153 	if (tbridge_curtest != NULL)
154 		return EBUSY;
155 
156 	tbridge_curtest = test;
157 
158 	return 0;
159 }
160 
161 static int
162 tbridge_unregister(void)
163 {
164 	tbridge_curtest = NULL;
165 
166 	return 0;
167 }
168 
169 int
170 tbridge_testcase_modhandler(module_t mod, int type, void *arg)
171 {
172 	struct tbridge_testcase *tcase = (struct tbridge_testcase *)arg;
173 	int error = 0;
174 
175 	switch (type) {
176 	case MOD_LOAD:
177 		error = tbridge_register(tcase);
178 		break;
179 
180 	case MOD_UNLOAD:
181 		if (tcase->tb_abort != NULL)
182 			error = tcase->tb_abort();
183 
184 		if (!error)
185 			tbridge_unregister();
186 		break;
187 
188 	default:
189 		break;
190 	}
191 
192 	return error;
193 }
194 
195 void
196 tbridge_test_done(int result)
197 {
198 	KKASSERT(tbridge_testcase != NULL);
199 
200 	kprintf("testcase '%s' called test_done with result=%d\n",
201 	    testcase_get_name(tbridge_testcase), result);
202 
203 	testcase_set_result(tbridge_testcase, result);
204 	testcase_set_stdout_buf(tbridge_testcase, tbridge_msgbuf);
205 
206 	tbridge_result_ready = 1;
207 	wakeup(&tbridge_result_ready);
208 }
209 
210 static void
211 tbridge_reset(void)
212 {
213 	tbridge_msgbuf_ptr = tbridge_msgbuf;
214 	tbridge_msgbuf_remsz = sizeof(tbridge_msgbuf);
215 
216 	if (tbridge_testcase != NULL) {
217 		prop_object_release(tbridge_testcase);
218 		tbridge_testcase = NULL;
219 	}
220 
221 	safemem_reset_error_count();
222 	tbridge_result_ready = 0;
223 }
224 
225 
226 /*
227  * dev stuff
228  */
229 static int
230 tbridge_dev_open(struct dev_open_args *ap)
231 {
232 	return 0;
233 }
234 
235 static int
236 tbridge_dev_close(struct dev_close_args *ap)
237 {
238 	return 0;
239 }
240 
241 static int
242 tbridge_dev_ioctl(struct dev_ioctl_args *ap)
243 {
244 	struct plistref *pref;
245 	struct thread *td;
246 	int error, r;
247 
248 	error = 0;
249 
250 	/* Use proplib(3) for userspace/kernel communication */
251 	pref = (struct plistref *)ap->a_data;
252 
253 	switch(ap->a_cmd) {
254 	case TBRIDGE_LOADTEST:
255 		tbridge_reset();
256 
257 		if (tbridge_curtest == NULL)
258 			return EINVAL;
259 
260 		error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd,
261 		    &tbridge_testcase);
262 		if (error)
263 			return error;
264 
265 		break;
266 
267 	case TBRIDGE_GETRESULT:
268 		error = kthread_create(tbridge_curtest->tb_run, NULL, &td,
269 		    "testrunner");
270 		if (error)
271 			tbridge_test_done(RESULT_PREFAIL);
272 
273 		/* The following won't be called if the thread wasn't created */
274 		if (!tbridge_result_ready) {
275 			r = tsleep(&tbridge_result_ready, 0, "tbridgeres",
276 			    hz * testcase_get_timeout(tbridge_testcase));
277 			if (r != 0) {
278 				if (tbridge_curtest->tb_abort != NULL)
279 					tbridge_curtest->tb_abort();
280 
281 				tbridge_test_done(RESULT_TIMEOUT);
282 			}
283 		}
284 
285 		if (safemem_get_error_count() != 0) {
286 			/*
287 			 * If there were any double-frees or buffer over- or
288 			 * underflows, we mark the result as 'signalled', i.e.
289 			 * the equivalent of a SIGSEGV/SIGBUS in userland.
290 			 */
291 			testcase_set_result(tbridge_testcase, RESULT_SIGNALLED);
292 		}
293 
294 		error = prop_dictionary_copyout_ioctl(pref, ap->a_cmd,
295 		    tbridge_testcase);
296 		if (error)
297 			return error;
298 
299 		break;
300 	default:
301 		error = ENOTTY; /* Inappropriate ioctl for device */
302 		break;
303 	}
304 
305 	return(error);
306 }
307 
308 
309 static int
310 testbridge_modevent(module_t mod, int type, void *unused)
311 {
312 	switch (type) {
313 	case MOD_LOAD:
314 		tbridge_dev = make_dev(&tbridge_dev_ops,
315 		    0,
316 		    UID_ROOT,
317 		    GID_WHEEL,
318 		    0600,
319 		    "tbridge");
320 
321 		tbridge_testcase = NULL;
322 		tbridge_reset();
323 
324 		kprintf("dfregress kernel test bridge ready!\n");
325 		return 0;
326 
327 	case MOD_UNLOAD:
328 		destroy_dev(tbridge_dev);
329 		kprintf("dfregress kernel test bridge unloaded.\n");
330 		return 0;
331 	}
332 
333 	return EINVAL;
334 }
335 
336 static moduledata_t testbridge_mod = {
337 	"testbridge",
338 	testbridge_modevent,
339 	0
340 };
341 
342 DECLARE_MODULE(testbridge, testbridge_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
343 MODULE_VERSION(testbridge, 1);
344