Home | History | Annotate | Line # | Download | only in libsimplehook_tester
      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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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