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