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