xref: /netbsd-src/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c (revision 75d2abaeb1baf9839167f74fdcc44faa66addc35)
1 /*	$NetBSD: simplehook_tester.c,v 1.2 2022/04/10 09:50:46 andvar Exp $	*/
2 /*
3  * Copyright (c) 2021 Internet Initiative Japan Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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 the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: simplehook_tester.c,v 1.2 2022/04/10 09:50:46 andvar Exp $");
30 
31 #include <sys/param.h>
32 
33 #include <sys/condvar.h>
34 #include <sys/hook.h>
35 #include <sys/module.h>
36 #include <sys/mutex.h>
37 #include <sys/sysctl.h>
38 #include <sys/workqueue.h>
39 
40 #ifdef SIMPLEHOOK_TESTER_DEBUG
41 #define HK_DPRINTF(a)	printf a
42 #else
43 #define HK_DPRINTF(a)	__nothing
44 #endif
45 
46 MODULE(MODULE_CLASS_MISC, simplehook_tester, NULL);
47 extern int simplehook_tester_init(void);
48 struct tester_context;
49 
50 struct tester_hook {
51 	struct tester_context	*th_ctx;
52 	khook_t			*th_hook;
53 	size_t			 th_idx;
54 	int			 th_count;
55 	bool			 th_stopping;
56 	bool			 th_stopped;
57 	bool			 th_disestablish;
58 };
59 
60 static struct tester_context {
61 	kmutex_t		 ctx_mutex;
62 	kcondvar_t		 ctx_cv;
63 	struct sysctllog	*ctx_sysctllog;
64 	struct workqueue	*ctx_wq;
65 	struct work		 ctx_wk;
66 	bool			 ctx_wk_enqueued;
67 	bool			 ctx_wk_waiting;
68 
69 	khook_list_t		*ctx_hooks;
70 	struct tester_hook	 ctx_hook[2];
71 
72 	khook_t			*ctx_nbhook;
73 } tester_ctx;
74 
75 static int
simplehook_tester_created(SYSCTLFN_ARGS)76 simplehook_tester_created(SYSCTLFN_ARGS)
77 {
78 	struct sysctlnode node;
79 	struct tester_context *ctx;
80 	int error, val;
81 	size_t i;
82 
83 	node = *rnode;
84 	ctx = node.sysctl_data;
85 
86 	mutex_enter(&ctx->ctx_mutex);
87 	val = ctx->ctx_hooks != NULL ? 1 : 0;
88 	mutex_exit(&ctx->ctx_mutex);
89 
90 	node.sysctl_data = &val;
91 	node.sysctl_size = sizeof(val);
92 
93 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
94 	if (error || newp == NULL)
95 		return error;
96 
97 	if (val != 0 && val != 1)
98 		return EINVAL;
99 
100 	error = 0;
101 	mutex_enter(&ctx->ctx_mutex);
102 	if (val == 1) {
103 		if (ctx->ctx_hooks != NULL) {
104 			error = EEXIST;
105 		} else {
106 			HK_DPRINTF(("[%s, %d]: create hook list\n",
107 			    __func__, __LINE__));
108 			ctx->ctx_hooks = simplehook_create(IPL_NONE,
109 			    "tester hooks");
110 			KASSERT(ctx->ctx_hooks != NULL);
111 		}
112 	} else {
113 		if (ctx->ctx_hooks == NULL) {
114 			error = ENXIO;
115 		} else if (ctx->ctx_wk_waiting) {
116 			error = EBUSY;
117 		} else {
118 			ctx->ctx_wk_waiting = true;
119 			mutex_exit(&ctx->ctx_mutex);
120 
121 			workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk);
122 
123 			mutex_enter(&ctx->ctx_mutex);
124 			ctx->ctx_wk_waiting = false;
125 
126 			HK_DPRINTF(("[%s, %d]: destroy hook list\n",
127 			    __func__, __LINE__));
128 			simplehook_destroy(ctx->ctx_hooks);
129 			ctx->ctx_hooks = NULL;
130 			ctx->ctx_nbhook = NULL;
131 			for (i = 0; i < __arraycount(ctx->ctx_hook); i++) {
132 				ctx->ctx_hook[i].th_hook = NULL;
133 			}
134 		}
135 	}
136 	mutex_exit(&ctx->ctx_mutex);
137 
138 	return error;
139 }
140 
141 static void
simplehook_tester_work(struct work * wk,void * xctx)142 simplehook_tester_work(struct work *wk, void *xctx)
143 {
144 	struct tester_context *ctx;
145 
146 	ctx = xctx;
147 
148 	mutex_enter(&ctx->ctx_mutex);
149 	ctx->ctx_wk_enqueued = false;
150 	mutex_exit(&ctx->ctx_mutex);
151 
152 	simplehook_dohooks(ctx->ctx_hooks);
153 }
154 
155 static int
simplehook_tester_dohooks(SYSCTLFN_ARGS)156 simplehook_tester_dohooks(SYSCTLFN_ARGS)
157 {
158 	struct sysctlnode node;
159 	struct tester_context *ctx;
160 	int error, val;
161 
162 	node = *rnode;
163 	ctx = node.sysctl_data;
164 
165 	mutex_enter(&ctx->ctx_mutex);
166 	val = ctx->ctx_wk_enqueued ? 1 : 0;
167 	mutex_exit(&ctx->ctx_mutex);
168 
169 	node.sysctl_data = &val;
170 	node.sysctl_size = sizeof(val);
171 
172 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
173 	if (error || newp == NULL)
174 		return error;
175 
176 	if (val != 0 && val != 1)
177 		return EINVAL;
178 
179 	mutex_enter(&ctx->ctx_mutex);
180 	if (val == 1) {
181 		if (ctx->ctx_wk_enqueued) {
182 			error = EEXIST;
183 		} else if (ctx->ctx_wk_waiting) {
184 			error = EBUSY;
185 		} else if (ctx->ctx_hooks == NULL) {
186 			error = ENXIO;
187 		} else {
188 			HK_DPRINTF(("[%s, %d]: dohook\n", __func__, __LINE__));
189 			ctx->ctx_wk_enqueued = true;
190 			workqueue_enqueue(ctx->ctx_wq,
191 			    &ctx->ctx_wk, NULL);
192 		}
193 	} else {
194 		if (ctx->ctx_wk_waiting) {
195 			error = EBUSY;
196 		} else {
197 			ctx->ctx_wk_waiting = true;
198 			mutex_exit(&ctx->ctx_mutex);
199 
200 			workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk);
201 
202 			mutex_enter(&ctx->ctx_mutex);
203 			ctx->ctx_wk_waiting = false;
204 		}
205 	}
206 	mutex_exit(&ctx->ctx_mutex);
207 
208 	return error;
209 }
210 
211 static void
simplehook_tester_hook(void * xth)212 simplehook_tester_hook(void *xth)
213 {
214 	struct tester_context *ctx;
215 	struct tester_hook *th;
216 
217 	th = xth;
218 	ctx = th->th_ctx;
219 	mutex_enter(&ctx->ctx_mutex);
220 
221 	HK_DPRINTF(("[%s, %d]: hook%zu called\n",
222 	    __func__, __LINE__, th->th_idx));
223 
224 	th->th_stopped = false;
225 
226 	while (th->th_stopping) {
227 		HK_DPRINTF(("[%s, %d]: hook%zu stopping\n",
228 		    __func__, __LINE__, th->th_idx));
229 		th->th_stopped = true;
230 		cv_wait(&ctx->ctx_cv, &ctx->ctx_mutex);
231 	}
232 
233 	if (th->th_stopped) {
234 		HK_DPRINTF(("[%s, %d]: hook%zu restart\n",
235 		    __func__, __LINE__, th->th_idx));
236 		th->th_stopped = false;
237 	}
238 
239 	th->th_count++;
240 
241 	if (th->th_disestablish && th->th_hook != NULL) {
242 		HK_DPRINTF(("[%s, %d]: disestablish running hook%zu\n",
243 		    __func__, __LINE__, th->th_idx));
244 		simplehook_disestablish(ctx->ctx_hooks,
245 		    th->th_hook, &ctx->ctx_mutex);
246 		th->th_hook = NULL;
247 	}
248 
249 	HK_DPRINTF(("[%s, %d]: hook%zu exit\n",
250 	    __func__, __LINE__, th->th_idx));
251 
252 	mutex_exit(&ctx->ctx_mutex);
253 }
254 
255 static void
simplehook_tester_hook_nb(void * xctx)256 simplehook_tester_hook_nb(void *xctx)
257 {
258 
259 	HK_DPRINTF(("[%s, %d]: non-block hook called\n",
260 	    __func__, __LINE__));
261 
262 	HK_DPRINTF(("[%s, %d]: sleep 1 sec\n",
263 	    __func__, __LINE__));
264 	kpause("smplhk_nb", true, 1 * hz, NULL);
265 
266 	HK_DPRINTF(("[%s, %d]: non-block hook exit\n",
267 	    __func__, __LINE__));
268 }
269 
270 static int
simplehook_tester_established(SYSCTLFN_ARGS)271 simplehook_tester_established(SYSCTLFN_ARGS)
272 {
273 	struct sysctlnode node;
274 	struct tester_context *ctx;
275 	struct tester_hook *th;
276 	int val, error;
277 
278 	node = *rnode;
279 	th = node.sysctl_data;
280 	ctx = th->th_ctx;
281 
282 	mutex_enter(&ctx->ctx_mutex);
283 	val = th->th_hook == NULL ? 0 : 1;
284 	mutex_exit(&ctx->ctx_mutex);
285 
286 	node.sysctl_data = &val;
287 	node.sysctl_size = sizeof(val);
288 
289 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
290 	if (error || newp == NULL)
291 		return error;
292 
293 	error = 0;
294 	mutex_enter(&ctx->ctx_mutex);
295 
296 	if (val == 1) {
297 		if (th->th_hook != NULL) {
298 			error = EEXIST;
299 		} else {
300 			th->th_hook = simplehook_establish(ctx->ctx_hooks,
301 			    simplehook_tester_hook, th);
302 			KASSERT(th->th_hook != NULL);
303 			HK_DPRINTF(("[%s, %d]: established hook%zu (%p)\n",
304 			    __func__, __LINE__, th->th_idx, th->th_hook));
305 		}
306 	} else {
307 		if (th->th_hook == NULL) {
308 			error = ENXIO;
309 		} else {
310 			bool stopped = false;
311 			if (th->th_stopping) {
312 				HK_DPRINTF(("[%s, %d]: stopping = false\n",
313 				    __func__, __LINE__));
314 				th->th_stopping = false;
315 				cv_broadcast(&ctx->ctx_cv);
316 				stopped = true;
317 			}
318 			HK_DPRINTF(("[%s, %d]: disestablish hook%zu (%p)\n",
319 			    __func__, __LINE__, th->th_idx, th->th_hook));
320 			simplehook_disestablish(ctx->ctx_hooks,
321 			    th->th_hook, &ctx->ctx_mutex);
322 			th->th_hook = NULL;
323 			if (stopped) {
324 				HK_DPRINTF(("[%s, %d]: disestablished hook%zu\n",
325 				    __func__, __LINE__, th->th_idx));
326 			}
327 		}
328 	}
329 
330 	mutex_exit(&ctx->ctx_mutex);
331 
332 	return error;
333 }
334 
335 static int
simplehook_tester_established_nb(SYSCTLFN_ARGS)336 simplehook_tester_established_nb(SYSCTLFN_ARGS)
337 {
338 	struct sysctlnode node;
339 	struct tester_context *ctx;
340 	int val, error;
341 
342 	node = *rnode;
343 	ctx = node.sysctl_data;
344 
345 	mutex_enter(&ctx->ctx_mutex);
346 	val = ctx->ctx_nbhook == NULL ? 0 : 1;
347 	mutex_exit(&ctx->ctx_mutex);
348 
349 	node.sysctl_data = &val;
350 	node.sysctl_size = sizeof(val);
351 
352 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
353 	if (error || newp == NULL)
354 		return error;
355 
356 	error = 0;
357 	mutex_enter(&ctx->ctx_mutex);
358 
359 	if (val == 1) {
360 		if (ctx->ctx_nbhook != NULL) {
361 			error = EEXIST;
362 		} else {
363 			ctx->ctx_nbhook = simplehook_establish(ctx->ctx_hooks,
364 			    simplehook_tester_hook_nb, ctx);
365 			KASSERT(ctx->ctx_nbhook != NULL);
366 			HK_DPRINTF(("[%s, %d]: established nbhook (%p)\n",
367 			    __func__, __LINE__, ctx->ctx_nbhook));
368 		}
369 	} else {
370 		if (ctx->ctx_nbhook == NULL) {
371 			error = ENXIO;
372 		} else {
373 			HK_DPRINTF(("[%s, %d]: disestablish nbhook (%p)\n",
374 			    __func__, __LINE__, ctx->ctx_nbhook));
375 			simplehook_disestablish(ctx->ctx_hooks,
376 			    ctx->ctx_nbhook, NULL);
377 			ctx->ctx_nbhook = NULL;
378 			HK_DPRINTF(("[%s, %d]: disestablished\n",
379 			    __func__, __LINE__));
380 		}
381 	}
382 
383 	mutex_exit(&ctx->ctx_mutex);
384 
385 	return error;
386 }
387 
388 static int
simplehook_tester_stopping(SYSCTLFN_ARGS)389 simplehook_tester_stopping(SYSCTLFN_ARGS)
390 {
391 	struct sysctlnode node;
392 	struct tester_context *ctx;
393 	struct tester_hook *th;
394 	int error;
395 	bool val;
396 
397 	node = *rnode;
398 	th = node.sysctl_data;
399 	ctx = th->th_ctx;
400 
401 	mutex_enter(&ctx->ctx_mutex);
402 	val = th->th_stopping;
403 	mutex_exit(&ctx->ctx_mutex);
404 
405 	node.sysctl_data = &val;
406 	node.sysctl_size = sizeof(val);
407 
408 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
409 	if (error || newp == NULL)
410 		return error;
411 
412 	error = 0;
413 	mutex_enter(&ctx->ctx_mutex);
414 	if (val == true && !th->th_stopping) {
415 		th->th_stopping = true;
416 	} else if (val == false && th->th_stopping) {
417 		th->th_stopping = false;
418 		cv_broadcast(&ctx->ctx_cv);
419 	}
420 	mutex_exit(&ctx->ctx_mutex);
421 
422 	return error;
423 }
424 
425 static int
simplehook_tester_stopped(SYSCTLFN_ARGS)426 simplehook_tester_stopped(SYSCTLFN_ARGS)
427 {
428 	struct sysctlnode node;
429 	struct tester_context *ctx;
430 	struct tester_hook *th;
431 	bool val;
432 
433 	node = *rnode;
434 	th = node.sysctl_data;
435 	ctx = th->th_ctx;
436 
437 	mutex_enter(&ctx->ctx_mutex);
438 	val = th->th_stopped;
439 	mutex_exit(&ctx->ctx_mutex);
440 
441 	node.sysctl_data = &val;
442 	node.sysctl_size = sizeof(val);
443 
444 	return sysctl_lookup(SYSCTLFN_CALL(&node));
445 }
446 
447 static int
simplehook_tester_disestablish(SYSCTLFN_ARGS)448 simplehook_tester_disestablish(SYSCTLFN_ARGS)
449 {
450 	struct sysctlnode node;
451 	struct tester_context *ctx;
452 	struct tester_hook *th;
453 	int error;
454 	bool val;
455 
456 	node = *rnode;
457 	th = node.sysctl_data;
458 	ctx = th->th_ctx;
459 
460 	mutex_enter(&ctx->ctx_mutex);
461 	val = th->th_disestablish;
462 	mutex_exit(&ctx->ctx_mutex);
463 
464 	node.sysctl_data = &val;
465 	node.sysctl_size = sizeof(val);
466 
467 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
468 	if (error || newp == NULL)
469 		return error;
470 
471 	if (val != 0 && val != 1)
472 		return EINVAL;
473 
474 	error = 0;
475 	mutex_enter(&ctx->ctx_mutex);
476 	if (val == true && !th->th_disestablish) {
477 		th->th_disestablish = true;
478 	} else if (val == false && th->th_disestablish) {
479 		th->th_disestablish = false;
480 	}
481 	mutex_exit(&ctx->ctx_mutex);
482 
483 	return 0;
484 }
485 
486 static int
simplehook_tester_count(SYSCTLFN_ARGS)487 simplehook_tester_count(SYSCTLFN_ARGS)
488 {
489 	struct sysctlnode node;
490 	struct tester_context *ctx;
491 	struct tester_hook *th;
492 	int error, val;
493 
494 	node = *rnode;
495 	th = node.sysctl_data;
496 	ctx = th->th_ctx;
497 
498 	mutex_enter(&ctx->ctx_mutex);
499 	val = th->th_count;
500 	mutex_exit(&ctx->ctx_mutex);
501 
502 	node.sysctl_data = &val;
503 	node.sysctl_size = sizeof(val);
504 
505 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
506 	if (error || newp == NULL)
507 		return error;
508 
509 	mutex_enter(&ctx->ctx_mutex);
510 	th->th_count = val;
511 	mutex_exit(&ctx->ctx_mutex);
512 
513 	return 0;
514 }
515 
516 static int
simplehook_tester_create_sysctl(struct tester_context * ctx)517 simplehook_tester_create_sysctl(struct tester_context *ctx)
518 {
519 	struct sysctllog **log;
520 	const struct sysctlnode *rnode, *cnode;
521 	void *ptr;
522 	char buf[32];
523 	int error;
524 	size_t i;
525 
526 	log = &ctx->ctx_sysctllog;
527 	ptr = (void *)ctx;
528 
529 	error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT,
530 	    CTLTYPE_NODE, "simplehook_tester",
531 	    SYSCTL_DESCR("simplehook testing interface"),
532 	    NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL);
533 	if (error != 0)
534 		goto bad;
535 
536 	error = sysctl_createv(log, 0, &rnode, &cnode,
537 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hook_list",
538 	    SYSCTL_DESCR("hook list"), NULL, 0, NULL, 0,
539 	    CTL_CREATE, CTL_EOL);
540 
541 	error = sysctl_createv(log, 0, &cnode, NULL,
542 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
543 	    "created", SYSCTL_DESCR("create and destroy hook list"),
544 	    simplehook_tester_created, 0, ptr, 0,
545 	    CTL_CREATE, CTL_EOL);
546 	if (error != 0)
547 		goto bad;
548 
549 	error = sysctl_createv(log, 0, &cnode, NULL,
550 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
551 	    "dohooks", SYSCTL_DESCR("do hooks"),
552 	    simplehook_tester_dohooks, 0, ptr, 0,
553 	    CTL_CREATE, CTL_EOL);
554 	if (error != 0)
555 		goto bad;
556 
557 	error = sysctl_createv(log, 0, &rnode, &cnode,
558 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "nbhook",
559 	    SYSCTL_DESCR("non-blocking hook"), NULL, 0, NULL, 0,
560 	    CTL_CREATE, CTL_EOL);
561 	if (error != 0)
562 		goto bad;
563 
564 	error = sysctl_createv(log, 0, &cnode, NULL,
565 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
566 	    CTLTYPE_INT, "established",
567 	    SYSCTL_DESCR("establish and disestablish hook"),
568 	    simplehook_tester_established_nb,
569 	    0, ptr, 0, CTL_CREATE, CTL_EOL);
570 	if (error != 0)
571 		goto bad;
572 
573 	for (i = 0; i < __arraycount(ctx->ctx_hook); i++) {
574 		snprintf(buf, sizeof(buf), "hook%zu", i);
575 		ptr = (void *)&ctx->ctx_hook[i];
576 
577 		error = sysctl_createv(log, 0, &rnode, &cnode,
578 		    CTLFLAG_PERMANENT, CTLTYPE_NODE, buf,
579 		    SYSCTL_DESCR("hook information"), NULL, 0, NULL, 0,
580 		    CTL_CREATE, CTL_EOL);
581 		if (error != 0)
582 			goto bad;
583 
584 		error = sysctl_createv(log, 0, &cnode, NULL,
585 		    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
586 		    CTLTYPE_INT, "established",
587 		    SYSCTL_DESCR("establish and disestablish hook"),
588 		    simplehook_tester_established,
589 		    0, ptr, 0, CTL_CREATE, CTL_EOL);
590 		if (error != 0)
591 			goto bad;
592 
593 		error = sysctl_createv(log, 0, &cnode, NULL,
594 		    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
595 		    CTLTYPE_BOOL, "stopping",
596 		    SYSCTL_DESCR("stopping at beginning of the hook"),
597 		    simplehook_tester_stopping, 0, ptr, 0,
598 		    CTL_CREATE, CTL_EOL);
599 		if (error != 0)
600 			goto bad;
601 
602 		error = sysctl_createv(log, 0, &cnode, NULL,
603 		    CTLFLAG_PERMANENT|CTLFLAG_READONLY,
604 		    CTLTYPE_BOOL, "stopped",
605 		    SYSCTL_DESCR("the hook is stopped"),
606 		    simplehook_tester_stopped, 0, ptr, 0,
607 		    CTL_CREATE, CTL_EOL);
608 		if (error != 0)
609 			goto bad;
610 
611 		error = sysctl_createv(log, 0, &cnode, NULL,
612 		    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_BOOL,
613 		    "disestablish_in_hook",
614 		    SYSCTL_DESCR("disestablish this hook in it"),
615 		    simplehook_tester_disestablish, 0, ptr, 0,
616 		    CTL_CREATE, CTL_EOL);
617 		if (error != 0)
618 			goto bad;
619 
620 		error = sysctl_createv(log, 0, &cnode, NULL,
621 		    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
622 		    CTLTYPE_INT, "count",
623 		    SYSCTL_DESCR("the number of calls of the hook"),
624 		    simplehook_tester_count, 0, ptr, 0,
625 		    CTL_CREATE, CTL_EOL);
626 		if (error != 0)
627 			goto bad;
628 	}
629 
630 	HK_DPRINTF(("[%s, %d]: created sysctls\n", __func__, __LINE__));
631 	return 0;
632 
633 bad:
634 	sysctl_teardown(log);
635 	return error;
636 }
637 
638 static void
simplehook_tester_init_ctx(struct tester_context * ctx)639 simplehook_tester_init_ctx(struct tester_context *ctx)
640 {
641 	size_t i;
642 
643 	memset(ctx, 0, sizeof(*ctx));
644 	mutex_init(&ctx->ctx_mutex, MUTEX_DEFAULT, IPL_NONE);
645 	workqueue_create(&ctx->ctx_wq, "shook_tester_wq",
646 	    simplehook_tester_work, ctx, PRI_NONE, IPL_NONE, WQ_MPSAFE);
647 	cv_init(&ctx->ctx_cv, "simplehook_tester_cv");
648 
649 	for (i = 0; i < __arraycount(ctx->ctx_hook); i++) {
650 		ctx->ctx_hook[i].th_ctx = ctx;
651 		ctx->ctx_hook[i].th_idx = i;
652 	}
653 }
654 
655 
656 int
simplehook_tester_init(void)657 simplehook_tester_init(void)
658 {
659 	int error;
660 
661 	simplehook_tester_init_ctx(&tester_ctx);
662 	error = simplehook_tester_create_sysctl(&tester_ctx);
663 
664 	return error;
665 }
666 
667 static int
simplehook_tester_fini(void)668 simplehook_tester_fini(void)
669 {
670 	struct tester_context *ctx;
671 	struct tester_hook *th;
672 	khook_list_t *hooks;
673 	size_t i;
674 
675 	ctx = &tester_ctx;
676 
677 	sysctl_teardown(&ctx->ctx_sysctllog);
678 
679 	mutex_enter(&ctx->ctx_mutex);
680 
681 	hooks = ctx->ctx_hooks;
682 	ctx->ctx_hooks = NULL;
683 	ctx->ctx_wk_waiting = true;
684 
685 	for (i = 0; i < __arraycount(ctx->ctx_hook); i++) {
686 		th = &ctx->ctx_hook[i];
687 		th->th_stopping = false;
688 	}
689 	cv_broadcast(&ctx->ctx_cv);
690 
691 	mutex_exit(&ctx->ctx_mutex);
692 
693 	workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk);
694 
695 	workqueue_destroy(ctx->ctx_wq);
696 	if (hooks != NULL)
697 		simplehook_destroy(hooks);
698 	cv_destroy(&ctx->ctx_cv);
699 	mutex_destroy(&ctx->ctx_mutex);
700 
701 	return 0;
702 }
703 
704 static int
simplehook_tester_modcmd(modcmd_t cmd,void * arg __unused)705 simplehook_tester_modcmd(modcmd_t cmd, void *arg __unused)
706 {
707 	int error;
708 
709 	switch (cmd) {
710 	case MODULE_CMD_INIT:
711 		error = simplehook_tester_init();
712 		break;
713 
714 	case MODULE_CMD_FINI:
715 		error = simplehook_tester_fini();
716 		break;
717 
718 	case MODULE_CMD_STAT:
719 	default:
720 		error = ENOTTY;
721 	}
722 
723 	return error;
724 }
725