xref: /freebsd/sys/tests/framework/kern_testfrwk.c (revision 1c05a6ea6b849ff95e539c31adea887c644a6a01)
1 /*-
2  * Copyright (c) 2015
3  *	Netflix Incorporated, 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 REGENTS 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 REGENTS 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/bus.h>
33 #include <sys/callout.h>
34 #include <sys/kernel.h>
35 #include <sys/ktr.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/sdt.h>
41 #include <sys/smp.h>
42 #include <sys/sysctl.h>
43 #include <sys/taskqueue.h>
44 #include <sys/queue.h>
45 #include <tests/kern_testfrwk.h>
46 #ifdef SMP
47 #include <machine/cpu.h>
48 #endif
49 
50 struct kern_test_list {
51 	TAILQ_ENTRY(kern_test_list) next;
52 	char name[TEST_NAME_LEN];
53 	kerntfunc func;
54 };
55 
56 TAILQ_HEAD(ktestlist, kern_test_list);
57 
58 struct kern_test_entry {
59 	TAILQ_ENTRY(kern_test_entry) next;
60 	struct kern_test_list *kt_e;
61 	struct kern_test kt_data;
62 };
63 
64 TAILQ_HEAD(ktestqueue, kern_test_entry);
65 
66 MALLOC_DEFINE(M_KTFRWK, "kern_tfrwk", "Kernel Test Framework");
67 struct kern_totfrwk {
68 	struct taskqueue *kfrwk_tq;
69 	struct task kfrwk_que;
70 	struct ktestlist kfrwk_testlist;
71 	struct ktestqueue kfrwk_testq;
72 	struct mtx kfrwk_mtx;
73 	int kfrwk_waiting;
74 };
75 
76 struct kern_totfrwk kfrwk;
77 static int ktest_frwk_inited = 0;
78 
79 #define KTFRWK_MUTEX_INIT() mtx_init(&kfrwk.kfrwk_mtx, "kern_test_frwk", "tfrwk", MTX_DEF)
80 
81 #define KTFRWK_DESTROY() mtx_destroy(&kfrwk.kfrwk_mtx)
82 
83 #define KTFRWK_LOCK() mtx_lock(&kfrwk.kfrwk_mtx)
84 
85 #define KTFRWK_UNLOCK()	mtx_unlock(&kfrwk.kfrwk_mtx)
86 
87 static void
88 kfrwk_task(void *context, int pending)
89 {
90 	struct kern_totfrwk *tf;
91 	struct kern_test_entry *wk;
92 	int free_mem = 0;
93 	struct kern_test kt_data;
94 	kerntfunc ktf;
95 
96 	memset(&kt_data, 0, sizeof(kt_data));
97 	ktf = NULL;
98 	tf = (struct kern_totfrwk *)context;
99 	KTFRWK_LOCK();
100 	wk = TAILQ_FIRST(&tf->kfrwk_testq);
101 	if (wk) {
102 		wk->kt_data.tot_threads_running--;
103 		tf->kfrwk_waiting--;
104 		memcpy(&kt_data, &wk->kt_data, sizeof(kt_data));
105 		if (wk->kt_data.tot_threads_running == 0) {
106 			TAILQ_REMOVE(&tf->kfrwk_testq, wk, next);
107 			free_mem = 1;
108 		} else {
109 			/* Wake one of my colleages up to help too */
110 			taskqueue_enqueue(tf->kfrwk_tq, &tf->kfrwk_que);
111 		}
112 		if (wk->kt_e) {
113 			ktf = wk->kt_e->func;
114 		}
115 	}
116 	KTFRWK_UNLOCK();
117 	if (wk && free_mem) {
118 		free(wk, M_KTFRWK);
119 	}
120 	/* Execute the test */
121 	if (ktf) {
122 		(*ktf) (&kt_data);
123 	}
124 	/* We are done */
125 	atomic_add_int(&tf->kfrwk_waiting, 1);
126 }
127 
128 static int
129 kerntest_frwk_init(void)
130 {
131 	u_int ncpus = mp_ncpus ? mp_ncpus : MAXCPU;
132 
133 	KTFRWK_MUTEX_INIT();
134 	TAILQ_INIT(&kfrwk.kfrwk_testq);
135 	TAILQ_INIT(&kfrwk.kfrwk_testlist);
136 	/* Now lets start up a number of tasks to do the work */
137 	TASK_INIT(&kfrwk.kfrwk_que, 0, kfrwk_task, &kfrwk);
138 	kfrwk.kfrwk_tq = taskqueue_create_fast("sbtls_task", M_NOWAIT,
139 	    taskqueue_thread_enqueue, &kfrwk.kfrwk_tq);
140 	if (kfrwk.kfrwk_tq == NULL) {
141 		printf("Can't start taskqueue for Kernel Test Framework\n");
142 		panic("Taskqueue init fails for kfrwk");
143 	}
144 	taskqueue_start_threads(&kfrwk.kfrwk_tq, ncpus, PI_NET, "[kt_frwk task]");
145 	kfrwk.kfrwk_waiting = ncpus;
146 	ktest_frwk_inited = 1;
147 	return (0);
148 }
149 
150 static int
151 kerntest_frwk_fini(void)
152 {
153 	KTFRWK_LOCK();
154 	if (!TAILQ_EMPTY(&kfrwk.kfrwk_testlist)) {
155 		/* Still modules registered */
156 		KTFRWK_UNLOCK();
157 		return (EBUSY);
158 	}
159 	ktest_frwk_inited = 0;
160 	KTFRWK_UNLOCK();
161 	taskqueue_free(kfrwk.kfrwk_tq);
162 	/* Ok lets destroy the mutex on the way outs */
163 	KTFRWK_DESTROY();
164 	return (0);
165 }
166 
167 
168 static int kerntest_execute(SYSCTL_HANDLER_ARGS);
169 
170 SYSCTL_NODE(_kern, OID_AUTO, testfrwk, CTLFLAG_RW, 0, "Kernel Test Framework");
171 SYSCTL_PROC(_kern_testfrwk, OID_AUTO, runtest, (CTLTYPE_STRUCT | CTLFLAG_RW),
172     0, 0, kerntest_execute, "IU", "Execute a kernel test");
173 
174 int
175 kerntest_execute(SYSCTL_HANDLER_ARGS)
176 {
177 	struct kern_test kt;
178 	struct kern_test_list *li, *te = NULL;
179 	struct kern_test_entry *kte = NULL;
180 	int error = 0;
181 
182 	if (ktest_frwk_inited == 0) {
183 		return (ENOENT);
184 	}
185 	/* Find the entry if possible */
186 	error = SYSCTL_IN(req, &kt, sizeof(struct kern_test));
187 	if (error) {
188 		return (error);
189 	}
190 	if (kt.num_threads <= 0) {
191 		return (EINVAL);
192 	}
193 	/* Grab some memory */
194 	kte = malloc(sizeof(struct kern_test_entry), M_KTFRWK, M_WAITOK);
195 	if (kte == NULL) {
196 		error = ENOMEM;
197 		goto out;
198 	}
199 	KTFRWK_LOCK();
200 	TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) {
201 		if (strcmp(li->name, kt.name) == 0) {
202 			te = li;
203 			break;
204 		}
205 	}
206 	if (te == NULL) {
207 		printf("Can't find the test %s\n", kt.name);
208 		error = ENOENT;
209 		free(kte, M_KTFRWK);
210 		goto out;
211 	}
212 	/* Ok we have a test item to run, can we? */
213 	if (!TAILQ_EMPTY(&kfrwk.kfrwk_testq)) {
214 		/* We don't know if there is enough threads */
215 		error = EAGAIN;
216 		free(kte, M_KTFRWK);
217 		goto out;
218 	}
219 	if (kfrwk.kfrwk_waiting < kt.num_threads) {
220 		error = E2BIG;
221 		free(kte, M_KTFRWK);
222 		goto out;
223 	}
224 	kt.tot_threads_running = kt.num_threads;
225 	/* Ok it looks like we can do it, lets get an entry */
226 	kte->kt_e = li;
227 	memcpy(&kte->kt_data, &kt, sizeof(kt));
228 	TAILQ_INSERT_TAIL(&kfrwk.kfrwk_testq, kte, next);
229 	taskqueue_enqueue(kfrwk.kfrwk_tq, &kfrwk.kfrwk_que);
230 out:
231 	KTFRWK_UNLOCK();
232 	return (error);
233 }
234 
235 int
236 kern_testframework_register(const char *name, kerntfunc func)
237 {
238 	int error = 0;
239 	struct kern_test_list *li, *te = NULL;
240 	int len;
241 
242 	len = strlen(name);
243 	if (len >= TEST_NAME_LEN) {
244 		return (E2BIG);
245 	}
246 	te = malloc(sizeof(struct kern_test_list), M_KTFRWK, M_WAITOK);
247 	if (te == NULL) {
248 		error = ENOMEM;
249 		goto out;
250 	}
251 	KTFRWK_LOCK();
252 	/* First does it already exist? */
253 	TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) {
254 		if (strcmp(li->name, name) == 0) {
255 			error = EALREADY;
256 			free(te, M_KTFRWK);
257 			goto out;
258 		}
259 	}
260 	/* Ok we can do it, lets add it to the list */
261 	te->func = func;
262 	strcpy(te->name, name);
263 	TAILQ_INSERT_TAIL(&kfrwk.kfrwk_testlist, te, next);
264 out:
265 	KTFRWK_UNLOCK();
266 	return (error);
267 }
268 
269 int
270 kern_testframework_deregister(const char *name)
271 {
272 	struct kern_test_list *li, *te = NULL;
273 	u_int ncpus = mp_ncpus ? mp_ncpus : MAXCPU;
274 	int error = 0;
275 
276 	KTFRWK_LOCK();
277 	/* First does it already exist? */
278 	TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) {
279 		if (strcmp(li->name, name) == 0) {
280 			te = li;
281 			break;
282 		}
283 	}
284 	if (te == NULL) {
285 		/* It is not registered so no problem */
286 		goto out;
287 	}
288 	if (ncpus != kfrwk.kfrwk_waiting) {
289 		/* We are busy executing something -- can't unload */
290 		error = EBUSY;
291 		goto out;
292 	}
293 	if (!TAILQ_EMPTY(&kfrwk.kfrwk_testq)) {
294 		/* Something still to execute */
295 		error = EBUSY;
296 		goto out;
297 	}
298 	/* Ok we can remove the dude safely */
299 	TAILQ_REMOVE(&kfrwk.kfrwk_testlist, te, next);
300 	memset(te, 0, sizeof(struct kern_test_list));
301 	free(te, M_KTFRWK);
302 out:
303 	KTFRWK_UNLOCK();
304 	return (error);
305 }
306 
307 static int
308 kerntest_mod_init(module_t mod, int type, void *data)
309 {
310 	int err;
311 
312 	switch (type) {
313 	case MOD_LOAD:
314 		err = kerntest_frwk_init();
315 		break;
316 	case MOD_QUIESCE:
317 		KTFRWK_LOCK();
318 		if (TAILQ_EMPTY(&kfrwk.kfrwk_testlist)) {
319 			err = 0;
320 		} else {
321 			err = EBUSY;
322 		}
323 		KTFRWK_UNLOCK();
324 		break;
325 	case MOD_UNLOAD:
326 		err = kerntest_frwk_fini();
327 		break;
328 	default:
329 		return (EOPNOTSUPP);
330 	}
331 	return (err);
332 }
333 
334 static moduledata_t kern_test_framework = {
335 	.name = "kernel_testfrwk",
336 	.evhand = kerntest_mod_init,
337 	.priv = 0
338 };
339 
340 MODULE_VERSION(kern_testframework, 1);
341 DECLARE_MODULE(kern_testframework, kern_test_framework, SI_SUB_PSEUDO, SI_ORDER_ANY);
342