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