Module Name: src Committed By: yamaguchi Date: Thu Sep 30 02:00:20 UTC 2021
Modified Files: src/distrib/sets/lists/base: shl.mi src/distrib/sets/lists/comp: mi src/distrib/sets/lists/debug: mi shl.mi src/distrib/sets/lists/tests: mi src/sys/rump/kern: Makefile.rumpkerncomp src/tests/kernel: Makefile Added Files: src/sys/rump/kern/lib/libsimplehook_tester: Makefile simplehook_tester.c src/tests/kernel: t_simplehook.sh Log Message: Added tests for the linear hook APIs To generate a diff of this commit: cvs rdiff -u -r1.927 -r1.928 src/distrib/sets/lists/base/shl.mi cvs rdiff -u -r1.2394 -r1.2395 src/distrib/sets/lists/comp/mi cvs rdiff -u -r1.361 -r1.362 src/distrib/sets/lists/debug/mi cvs rdiff -u -r1.284 -r1.285 src/distrib/sets/lists/debug/shl.mi cvs rdiff -u -r1.1130 -r1.1131 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.13 -r1.14 src/sys/rump/kern/Makefile.rumpkerncomp cvs rdiff -u -r0 -r1.1 src/sys/rump/kern/lib/libsimplehook_tester/Makefile \ src/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c cvs rdiff -u -r1.68 -r1.69 src/tests/kernel/Makefile cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_simplehook.sh Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/base/shl.mi diff -u src/distrib/sets/lists/base/shl.mi:1.927 src/distrib/sets/lists/base/shl.mi:1.928 --- src/distrib/sets/lists/base/shl.mi:1.927 Fri Sep 24 13:13:07 2021 +++ src/distrib/sets/lists/base/shl.mi Thu Sep 30 02:00:19 2021 @@ -1,4 +1,4 @@ -# $NetBSD: shl.mi,v 1.927 2021/09/24 13:13:07 christos Exp $ +# $NetBSD: shl.mi,v 1.928 2021/09/30 02:00:19 yamaguchi Exp $ # # Note: Don't delete entries from here - mark them as "obsolete" instead, # unless otherwise stated below. @@ -741,6 +741,9 @@ ./usr/lib/librumpkern_nv.so base-rump-shlib rump ./usr/lib/librumpkern_nv.so.0 base-rump-shlib rump ./usr/lib/librumpkern_nv.so.0.0 base-rump-shlib rump +./usr/lib/librumpkern_simplehook_tester.so base-rump-shlib rump +./usr/lib/librumpkern_simplehook_tester.so.0 base-rump-shlib rump +./usr/lib/librumpkern_simplehook_tester.so.0.0 base-rump-shlib rump ./usr/lib/librumpkern_sljit.so base-rump-shlib rump,sljit ./usr/lib/librumpkern_sljit.so.0 base-rump-shlib rump,sljit ./usr/lib/librumpkern_sljit.so.0.0 base-rump-shlib rump,sljit Index: src/distrib/sets/lists/comp/mi diff -u src/distrib/sets/lists/comp/mi:1.2394 src/distrib/sets/lists/comp/mi:1.2395 --- src/distrib/sets/lists/comp/mi:1.2394 Thu Sep 30 01:26:07 2021 +++ src/distrib/sets/lists/comp/mi Thu Sep 30 02:00:19 2021 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.2394 2021/09/30 01:26:07 yamaguchi Exp $ +# $NetBSD: mi,v 1.2395 2021/09/30 02:00:19 yamaguchi Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. ./etc/mtree/set.comp comp-sys-root @@ -4062,6 +4062,8 @@ ./usr/lib/librumpkern_ksem_p.a comp-obsolete obsolete ./usr/lib/librumpkern_nv.a comp-c-lib rump ./usr/lib/librumpkern_nv_p.a comp-c-proflib rump,profile +./usr/lib/librumpkern_simplehook_tester.a comp-c-lib rump +./usr/lib/librumpkern_simplehook_tester_p.a comp-c-proflib rump,profile ./usr/lib/librumpkern_sljit.a comp-c-lib rump,sljit ./usr/lib/librumpkern_sljit_p.a comp-c-proflib rump,sljit,profile ./usr/lib/librumpkern_solaris.a comp-c-lib rump,zfs Index: src/distrib/sets/lists/debug/mi diff -u src/distrib/sets/lists/debug/mi:1.361 src/distrib/sets/lists/debug/mi:1.362 --- src/distrib/sets/lists/debug/mi:1.361 Sun Sep 19 15:51:28 2021 +++ src/distrib/sets/lists/debug/mi Thu Sep 30 02:00:19 2021 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.361 2021/09/19 15:51:28 thorpej Exp $ +# $NetBSD: mi,v 1.362 2021/09/30 02:00:19 yamaguchi Exp $ ./etc/mtree/set.debug comp-sys-root ./usr/lib comp-sys-usr compatdir ./usr/lib/i18n/libBIG5_g.a comp-c-debuglib debuglib,compatfile @@ -208,6 +208,7 @@ ./usr/lib/librumpkern_crypto_g.a comp-c-debuglib debuglib,rump ./usr/lib/librumpkern_ksem_g.a comp-obsolete obsolete,compatfile ./usr/lib/librumpkern_nv_g.a comp-c-debuglib debuglib,rump +./usr/lib/librumpkern_simplehook_tester_g.a comp-c-debuglib debuglib,rump ./usr/lib/librumpkern_sljit_g.a comp-c-debuglib debuglib,rump,sljit ./usr/lib/librumpkern_solaris_g.a comp-c-debuglib debuglib,rump,zfs ./usr/lib/librumpkern_sysproxy_g.a comp-c-debuglib debuglib,rump Index: src/distrib/sets/lists/debug/shl.mi diff -u src/distrib/sets/lists/debug/shl.mi:1.284 src/distrib/sets/lists/debug/shl.mi:1.285 --- src/distrib/sets/lists/debug/shl.mi:1.284 Fri Sep 24 13:13:07 2021 +++ src/distrib/sets/lists/debug/shl.mi Thu Sep 30 02:00:19 2021 @@ -1,4 +1,4 @@ -# $NetBSD: shl.mi,v 1.284 2021/09/24 13:13:07 christos Exp $ +# $NetBSD: shl.mi,v 1.285 2021/09/30 02:00:19 yamaguchi Exp $ ./usr/lib/libbfd_g.a comp-c-debuglib debuglib,compatfile,binutils ./usr/libdata/debug/lib base-sys-usr debug,dynamicroot,compatdir ./usr/libdata/debug/lib/libavl.so.0.0.debug comp-zfs-debug debug,dynamicroot,zfs @@ -261,6 +261,7 @@ ./usr/libdata/debug/usr/lib/librumpkern_crypto.so.0.0.debug comp-rump-debug debug,rump ./usr/libdata/debug/usr/lib/librumpkern_ksem.so.0.0.debug comp-obsolete obsolete,compatfile ./usr/libdata/debug/usr/lib/librumpkern_nv.so.0.0.debug comp-rump-debug debug,rump +./usr/libdata/debug/usr/lib/librumpkern_simplehook_tester.so.0.0.debug comp-rump-debug debug,rump ./usr/libdata/debug/usr/lib/librumpkern_sljit.so.0.0.debug comp-rump-debug debug,rump,sljit ./usr/libdata/debug/usr/lib/librumpkern_solaris.so.0.0.debug comp-rump-debug debug,rump,zfs ./usr/libdata/debug/usr/lib/librumpkern_sysproxy.so.0.0.debug comp-rump-debug debug,rump Index: src/distrib/sets/lists/tests/mi diff -u src/distrib/sets/lists/tests/mi:1.1130 src/distrib/sets/lists/tests/mi:1.1131 --- src/distrib/sets/lists/tests/mi:1.1130 Sun Sep 26 03:17:59 2021 +++ src/distrib/sets/lists/tests/mi Thu Sep 30 02:00:19 2021 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1130 2021/09/26 03:17:59 rillig Exp $ +# $NetBSD: mi,v 1.1131 2021/09/30 02:00:19 yamaguchi Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -2237,6 +2237,7 @@ ./usr/tests/kernel/t_pty tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_rnd tests-kernel-tests atf,rump ./usr/tests/kernel/t_sigaction tests-obsolete obsolete +./usr/tests/kernel/t_simplehook tests-kernel-tests atf,rump ./usr/tests/kernel/t_subr_prf tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_sysctl tests-kernel-tests compattestfile,atf ./usr/tests/kernel/t_sysv tests-kernel-tests compattestfile,atf Index: src/sys/rump/kern/Makefile.rumpkerncomp diff -u src/sys/rump/kern/Makefile.rumpkerncomp:1.13 src/sys/rump/kern/Makefile.rumpkerncomp:1.14 --- src/sys/rump/kern/Makefile.rumpkerncomp:1.13 Sat Sep 22 12:54:34 2018 +++ src/sys/rump/kern/Makefile.rumpkerncomp Thu Sep 30 02:00:20 2021 @@ -1,9 +1,9 @@ -# $NetBSD: Makefile.rumpkerncomp,v 1.13 2018/09/22 12:54:34 rmind Exp $ +# $NetBSD: Makefile.rumpkerncomp,v 1.14 2021/09/30 02:00:20 yamaguchi Exp $ # .include <bsd.own.mk> -RUMPKERNCOMPS= crypto nv sysproxy tty z +RUMPKERNCOMPS= crypto nv sysproxy tty z simplehook_tester RUMPSYSEMUS= sys_cygwin sys_linux sys_sunos .if make(rumpdescribe) Index: src/tests/kernel/Makefile diff -u src/tests/kernel/Makefile:1.68 src/tests/kernel/Makefile:1.69 --- src/tests/kernel/Makefile:1.68 Wed Jul 1 13:49:26 2020 +++ src/tests/kernel/Makefile Thu Sep 30 02:00:20 2021 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.68 2020/07/01 13:49:26 jruoho Exp $ +# $NetBSD: Makefile,v 1.69 2021/09/30 02:00:20 yamaguchi Exp $ NOMAN= # defined @@ -32,6 +32,7 @@ TESTS_SH+= t_origin TESTS_SH+= t_procpath TESTS_SH+= t_fexecve TESTS_SH+= t_fpufork +TESTS_SH+= t_simplehook BINDIR= ${TESTSDIR} PROGS= h_fexecve Added files: Index: src/sys/rump/kern/lib/libsimplehook_tester/Makefile diff -u /dev/null src/sys/rump/kern/lib/libsimplehook_tester/Makefile:1.1 --- /dev/null Thu Sep 30 02:00:20 2021 +++ src/sys/rump/kern/lib/libsimplehook_tester/Makefile Thu Sep 30 02:00:20 2021 @@ -0,0 +1,13 @@ + + +LIB= rumpkern_simplehook_tester +COMMENT=Tester for simplehook + +SRCS= simplehook_tester.c + +.ifdef RUMP_DEBUG +CPPFLAGS.simplehook_tester.c+= -DSIMPLEHOOK_TESTER_DEBUG +.endif + +.include <bsd.lib.mk> +.include <bsd.klinks.mk> Index: src/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c diff -u /dev/null src/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c:1.1 --- /dev/null Thu Sep 30 02:00:20 2021 +++ src/sys/rump/kern/lib/libsimplehook_tester/simplehook_tester.c Thu Sep 30 02:00:20 2021 @@ -0,0 +1,724 @@ +/* $NetBSD: simplehook_tester.c,v 1.1 2021/09/30 02:00:20 yamaguchi Exp $ */ +/* + * Copyright (c) 2021 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: simplehook_tester.c,v 1.1 2021/09/30 02:00:20 yamaguchi Exp $"); + +#include <sys/param.h> + +#include <sys/condvar.h> +#include <sys/hook.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/sysctl.h> +#include <sys/workqueue.h> + +#ifdef SIMPLEHOOK_TESTER_DEBUG +#define HK_DPRINTF(a) printf a +#else +#define HK_DPRINTF(a) __nothing +#endif + +MODULE(MODULE_CLASS_MISC, simplehook_tester, NULL); +extern int simplehook_tester_init(void); +struct tester_context; + +struct tester_hook { + struct tester_context *th_ctx; + khook_t *th_hook; + size_t th_idx; + int th_count; + bool th_stopping; + bool th_stopped; + bool th_disestablish; +}; + +static struct tester_context { + kmutex_t ctx_mutex; + kcondvar_t ctx_cv; + struct sysctllog *ctx_sysctllog; + struct workqueue *ctx_wq; + struct work ctx_wk; + bool ctx_wk_enqueued; + bool ctx_wk_waiting; + + khook_list_t *ctx_hooks; + struct tester_hook ctx_hook[2]; + + khook_t *ctx_nbhook; +} tester_ctx; + +static int +simplehook_tester_created(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + int error, val; + size_t i; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_hooks != NULL ? 1 : 0; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (val != 0 && val != 1) + return EINVAL; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + if (val == 1) { + if (ctx->ctx_hooks != NULL) { + error = EEXIST; + } else { + HK_DPRINTF(("[%s, %d]: create hook list\n", + __func__, __LINE__)); + ctx->ctx_hooks = simplehook_create(IPL_NONE, + "tester hooks"); + KASSERT(ctx->ctx_hooks != NULL); + } + } else { + if (ctx->ctx_hooks == NULL) { + error = ENXIO; + } else if (ctx->ctx_wk_waiting) { + error = EBUSY; + } else { + ctx->ctx_wk_waiting = true; + mutex_exit(&ctx->ctx_mutex); + + workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk); + + mutex_enter(&ctx->ctx_mutex); + ctx->ctx_wk_waiting = false; + + HK_DPRINTF(("[%s, %d]: destroy hook list\n", + __func__, __LINE__)); + simplehook_destroy(ctx->ctx_hooks); + ctx->ctx_hooks = NULL; + ctx->ctx_nbhook = NULL; + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + ctx->ctx_hook[i].th_hook = NULL; + } + } + } + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static void +simplehook_tester_work(struct work *wk, void *xctx) +{ + struct tester_context *ctx; + + ctx = xctx; + + mutex_enter(&ctx->ctx_mutex); + ctx->ctx_wk_enqueued = false; + mutex_exit(&ctx->ctx_mutex); + + simplehook_dohooks(ctx->ctx_hooks); +} + +static int +simplehook_tester_dohooks(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_wk_enqueued ? 1 : 0; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (val != 0 && val != 1) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + if (val == 1) { + if (ctx->ctx_wk_enqueued) { + error = EEXIST; + } else if (ctx->ctx_wk_waiting) { + error = EBUSY; + } else if (ctx->ctx_hooks == NULL) { + error = ENXIO; + } else { + HK_DPRINTF(("[%s, %d]: dohook\n", __func__, __LINE__)); + ctx->ctx_wk_enqueued = true; + workqueue_enqueue(ctx->ctx_wq, + &ctx->ctx_wk, NULL); + } + } else { + if (ctx->ctx_wk_waiting) { + error = EBUSY; + } else { + ctx->ctx_wk_waiting = true; + mutex_exit(&ctx->ctx_mutex); + + workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk); + + mutex_enter(&ctx->ctx_mutex); + ctx->ctx_wk_waiting = false; + } + } + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static void +simplehook_tester_hook(void *xth) +{ + struct tester_context *ctx; + struct tester_hook *th; + + th = xth; + ctx = th->th_ctx; + mutex_enter(&ctx->ctx_mutex); + + HK_DPRINTF(("[%s, %d]: hook%zu called\n", + __func__, __LINE__, th->th_idx)); + + th->th_stopped = false; + + while (th->th_stopping) { + HK_DPRINTF(("[%s, %d]: hook%zu stopping\n", + __func__, __LINE__, th->th_idx)); + th->th_stopped = true; + cv_wait(&ctx->ctx_cv, &ctx->ctx_mutex); + } + + if (th->th_stopped) { + HK_DPRINTF(("[%s, %d]: hook%zu restart\n", + __func__, __LINE__, th->th_idx)); + th->th_stopped = false; + } + + th->th_count++; + + if (th->th_disestablish && th->th_hook != NULL) { + HK_DPRINTF(("[%s, %d]: disestablish runing hook%zu\n", + __func__, __LINE__, th->th_idx)); + simplehook_disestablish(ctx->ctx_hooks, + th->th_hook, &ctx->ctx_mutex); + th->th_hook = NULL; + } + + HK_DPRINTF(("[%s, %d]: hook%zu exit\n", + __func__, __LINE__, th->th_idx)); + + mutex_exit(&ctx->ctx_mutex); +} + +static void +simplehook_tester_hook_nb(void *xctx) +{ + + HK_DPRINTF(("[%s, %d]: non-block hook called\n", + __func__, __LINE__)); + + HK_DPRINTF(("[%s, %d]: sleep 1 sec\n", + __func__, __LINE__)); + kpause("smplhk_nb", true, 1 * hz, NULL); + + HK_DPRINTF(("[%s, %d]: non-block hook exit\n", + __func__, __LINE__)); +} + +static int +simplehook_tester_established(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int val, error; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_hook == NULL ? 0 : 1; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + + if (val == 1) { + if (th->th_hook != NULL) { + error = EEXIST; + } else { + th->th_hook = simplehook_establish(ctx->ctx_hooks, + simplehook_tester_hook, th); + KASSERT(th->th_hook != NULL); + HK_DPRINTF(("[%s, %d]: established hook%zu (%p)\n", + __func__, __LINE__, th->th_idx, th->th_hook)); + } + } else { + if (th->th_hook == NULL) { + error = ENXIO; + } else { + bool stopped = false; + if (th->th_stopping) { + HK_DPRINTF(("[%s, %d]: stopping = false\n", + __func__, __LINE__)); + th->th_stopping = false; + cv_broadcast(&ctx->ctx_cv); + stopped = true; + } + HK_DPRINTF(("[%s, %d]: disestablish hook%zu (%p)\n", + __func__, __LINE__, th->th_idx, th->th_hook)); + simplehook_disestablish(ctx->ctx_hooks, + th->th_hook, &ctx->ctx_mutex); + th->th_hook = NULL; + if (stopped) { + HK_DPRINTF(("[%s, %d]: disestablished hook%zu\n", + __func__, __LINE__, th->th_idx)); + } + } + } + + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static int +simplehook_tester_established_nb(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + int val, error; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_nbhook == NULL ? 0 : 1; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + + if (val == 1) { + if (ctx->ctx_nbhook != NULL) { + error = EEXIST; + } else { + ctx->ctx_nbhook = simplehook_establish(ctx->ctx_hooks, + simplehook_tester_hook_nb, ctx); + KASSERT(ctx->ctx_nbhook != NULL); + HK_DPRINTF(("[%s, %d]: established nbhook (%p)\n", + __func__, __LINE__, ctx->ctx_nbhook)); + } + } else { + if (ctx->ctx_nbhook == NULL) { + error = ENXIO; + } else { + HK_DPRINTF(("[%s, %d]: disestablish nbhook (%p)\n", + __func__, __LINE__, ctx->ctx_nbhook)); + simplehook_disestablish(ctx->ctx_hooks, + ctx->ctx_nbhook, NULL); + ctx->ctx_nbhook = NULL; + HK_DPRINTF(("[%s, %d]: disestablished\n", + __func__, __LINE__)); + } + } + + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static int +simplehook_tester_stopping(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int error; + bool val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_stopping; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + if (val == true && !th->th_stopping) { + th->th_stopping = true; + } else if (val == false && th->th_stopping) { + th->th_stopping = false; + cv_broadcast(&ctx->ctx_cv); + } + mutex_exit(&ctx->ctx_mutex); + + return error; +} + +static int +simplehook_tester_stopped(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + bool val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_stopped; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + return sysctl_lookup(SYSCTLFN_CALL(&node)); +} + +static int +simplehook_tester_disestablish(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int error; + bool val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_disestablish; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (val != 0 && val != 1) + return EINVAL; + + error = 0; + mutex_enter(&ctx->ctx_mutex); + if (val == true && !th->th_disestablish) { + th->th_disestablish = true; + } else if (val == false && th->th_disestablish) { + th->th_disestablish = false; + } + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +simplehook_tester_count(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct tester_context *ctx; + struct tester_hook *th; + int error, val; + + node = *rnode; + th = node.sysctl_data; + ctx = th->th_ctx; + + mutex_enter(&ctx->ctx_mutex); + val = th->th_count; + mutex_exit(&ctx->ctx_mutex); + + node.sysctl_data = &val; + node.sysctl_size = sizeof(val); + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + mutex_enter(&ctx->ctx_mutex); + th->th_count = val; + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +simplehook_tester_create_sysctl(struct tester_context *ctx) +{ + struct sysctllog **log; + const struct sysctlnode *rnode, *cnode; + void *ptr; + char buf[32]; + int error; + size_t i; + + log = &ctx->ctx_sysctllog; + ptr = (void *)ctx; + + error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, + CTLTYPE_NODE, "simplehook_tester", + SYSCTL_DESCR("simplehook testing interface"), + NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "hook_list", + SYSCTL_DESCR("hook list"), NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "created", SYSCTL_DESCR("create and destroy hook list"), + simplehook_tester_created, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "dohooks", SYSCTL_DESCR("do hooks"), + simplehook_tester_dohooks, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "nbhook", + SYSCTL_DESCR("non-blocking hook"), NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "established", + SYSCTL_DESCR("establish and disestablish hook"), + simplehook_tester_established_nb, + 0, ptr, 0, CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + snprintf(buf, sizeof(buf), "hook%zu", i); + ptr = (void *)&ctx->ctx_hook[i]; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, buf, + SYSCTL_DESCR("hook information"), NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "established", + SYSCTL_DESCR("establish and disestablish hook"), + simplehook_tester_established, + 0, ptr, 0, CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_BOOL, "stopping", + SYSCTL_DESCR("stopping at beginning of the hook"), + simplehook_tester_stopping, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READONLY, + CTLTYPE_BOOL, "stopped", + SYSCTL_DESCR("the hook is stopped"), + simplehook_tester_stopped, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_BOOL, + "disestablish_in_hook", + SYSCTL_DESCR("disestablish this hook in it"), + simplehook_tester_disestablish, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + + error = sysctl_createv(log, 0, &cnode, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "count", + SYSCTL_DESCR("the number of calls of the hook"), + simplehook_tester_count, 0, ptr, 0, + CTL_CREATE, CTL_EOL); + if (error != 0) + goto bad; + } + + HK_DPRINTF(("[%s, %d]: created sysctls\n", __func__, __LINE__)); + return 0; + +bad: + sysctl_teardown(log); + return error; +} + +static void +simplehook_tester_init_ctx(struct tester_context *ctx) +{ + size_t i; + + memset(ctx, 0, sizeof(*ctx)); + mutex_init(&ctx->ctx_mutex, MUTEX_DEFAULT, IPL_NONE); + workqueue_create(&ctx->ctx_wq, "shook_tester_wq", + simplehook_tester_work, ctx, PRI_NONE, IPL_NONE, WQ_MPSAFE); + cv_init(&ctx->ctx_cv, "simplehook_tester_cv"); + + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + ctx->ctx_hook[i].th_ctx = ctx; + ctx->ctx_hook[i].th_idx = i; + } +} + + +int +simplehook_tester_init(void) +{ + int error; + + simplehook_tester_init_ctx(&tester_ctx); + error = simplehook_tester_create_sysctl(&tester_ctx); + + return error; +} + +static int +simplehook_tester_fini(void) +{ + struct tester_context *ctx; + struct tester_hook *th; + khook_list_t *hooks; + size_t i; + + ctx = &tester_ctx; + + sysctl_teardown(&ctx->ctx_sysctllog); + + mutex_enter(&ctx->ctx_mutex); + + hooks = ctx->ctx_hooks; + ctx->ctx_hooks = NULL; + ctx->ctx_wk_waiting = true; + + for (i = 0; i < __arraycount(ctx->ctx_hook); i++) { + th = &ctx->ctx_hook[i]; + th->th_stopping = false; + } + cv_broadcast(&ctx->ctx_cv); + + mutex_exit(&ctx->ctx_mutex); + + workqueue_wait(ctx->ctx_wq, &ctx->ctx_wk); + + workqueue_destroy(ctx->ctx_wq); + if (hooks != NULL) + simplehook_destroy(hooks); + cv_destroy(&ctx->ctx_cv); + mutex_destroy(&ctx->ctx_mutex); + + return 0; +} + +static int +simplehook_tester_modcmd(modcmd_t cmd, void *arg __unused) +{ + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = simplehook_tester_init(); + break; + + case MODULE_CMD_FINI: + error = simplehook_tester_fini(); + break; + + case MODULE_CMD_STAT: + default: + error = ENOTTY; + } + + return error; +} Index: src/tests/kernel/t_simplehook.sh diff -u /dev/null src/tests/kernel/t_simplehook.sh:1.1 --- /dev/null Thu Sep 30 02:00:20 2021 +++ src/tests/kernel/t_simplehook.sh Thu Sep 30 02:00:20 2021 @@ -0,0 +1,294 @@ +# $NetBSD: t_simplehook.sh,v 1.1 2021/09/30 02:00:20 yamaguchi Exp $ +# +# Copyright (c) 2021 Internet Initiative Japan Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +DEBUG=${DEBUG:-false} +SOCK=unix://commsock +HIJACKING="env LD_PRELOAD=/usr/lib/librumphijack.so \ + RUMPHIJACK=path=/rump,socket=all:nolocal,sysctl=yes" + +atf_sysctl_rd() +{ + local sysctl_key=$1 + local expected=$2 + + atf_check -s exit:0 -o match:"$expected" \ + rump.sysctl -n $sysctl_key +} + +atf_sysctl_wr() +{ + local sysctl_key=$1 + local value=$2 + + atf_check -s exit:0 -o ignore rump.sysctl -w $sysctl_key=$value +} + +atf_sysctl_wait() +{ + local sysctl_key=$1 + local expected=$2 + local n=10 + local i + local v + + for i in $(seq $n); do + v=$(rump.sysctl -n $sysctl_key) + if [ x"$v" = x"$expected" ]; then + return + fi + sleep 0.5 + done + + atf_fail "Couldn't get the value for $n seconds." +} + +atf_test_case simplehook_basic cleanup +simplehook_basic_head() +{ + + atf_set "descr" "tests for basically functions of simplehook" + atf_set "require.progs" "rump_server" +} + + +simplehook_basic_body() +{ + local key_hklist="kern.simplehook_tester.hook_list" + local key_hk0="kern.simplehook_tester.hook0" + local key_hk1="kern.simplehook_tester.hook1" + + rump_server -lrumpkern_simplehook_tester $SOCK + + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester + + # create and destroy + atf_sysctl_rd ${key_hklist}.created '0' + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hklist}.created '0' + $DEBUG && rump.sysctl -e kern.simplehook_tester + + # establish one hook + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hklist}.dohooks '0' + atf_sysctl_rd ${key_hk0}.count '1' + atf_sysctl_wr ${key_hk0}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' + + # establish two hooks + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk1}.established '1' + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hk1}.count '0' + + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hklist}.dohooks '0' + atf_sysctl_rd ${key_hk0}.count '1' + atf_sysctl_rd ${key_hk1}.count '1' + + atf_sysctl_wr ${key_hk0}.established '0' + atf_sysctl_wr ${key_hk1}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' +} + +simplehook_basic_cleanup() +{ + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + $DEBUG && $HIJACKING dmesg + rump.halt +} + +atf_test_case simplehook_disestablish cleanup +simplehook_disestablish_head() +{ + + atf_set "descr" "tests for disestablish of simplehook" + atf_set "require.progs" "rump_server" +} + +simplehook_disestablish_body() +{ + local key_hklist="kern.simplehook_tester.hook_list" + local key_hk0="kern.simplehook_tester.hook0" + local key_hk1="kern.simplehook_tester.hook1" + + rump_server -lrumpkern_simplehook_tester $SOCK + + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester + + # + # disestablish on the running hook + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk0}.disestablish_in_hook '1' + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + # already disestablished + atf_sysctl_rd ${key_hk0}.established '0' + atf_sysctl_wr ${key_hk0}.disestablish_in_hook '0' + + atf_sysctl_wr ${key_hklist}.created '0' + + # + # disestablish called hook while doing other hook + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk1}.established '1' + + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hk1}.count '0' + atf_sysctl_wr ${key_hk0}.stopping '1' + + # calls hook1 -> hook0 + atf_sysctl_wr ${key_hklist}.dohooks '1' + + # stop in hook0 + atf_sysctl_wait ${key_hk0}.stopped '1' + + atf_sysctl_rd ${key_hk1}.count '1' + atf_sysctl_wr ${key_hk1}.established '0' + + # wakeup hook0 + atf_sysctl_wr ${key_hk0}.stopping '0' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + atf_sysctl_wr ${key_hk0}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' + + # + # disestablish a hook in running hook list + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk1}.established '1' + + atf_sysctl_wr ${key_hk0}.count '0' + atf_sysctl_wr ${key_hk1}.count '0' + atf_sysctl_wr ${key_hk1}.stopping '1' + + # calls hook1 -> hook0 + atf_sysctl_wr ${key_hklist}.dohooks '1' + + # stop in hook1 + atf_sysctl_wait ${key_hk1}.stopped '1' + + atf_sysctl_wr ${key_hk0}.established '0' + + # wakeup hook1 + atf_sysctl_wr ${key_hk1}.stopping '0' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + # hook0 is not called + atf_sysctl_rd ${key_hk0}.count '0' + + atf_sysctl_wr ${key_hk1}.established '0' + atf_sysctl_wr ${key_hklist}.created '0' + + # + # disestablish the running hook + # + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk0}.established '1' + atf_sysctl_wr ${key_hk0}.stopping '1' + + atf_sysctl_wr ${key_hklist}.dohooks '1' + + atf_sysctl_wait ${key_hk0}.stopped '1' + atf_sysctl_wr ${key_hk0}.established '0' + + atf_sysctl_wr ${key_hklist}.dohooks '0' + atf_sysctl_wr ${key_hklist}.created '0' +} + +simplehook_disestablish_cleanup() +{ + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + $DEBUG && $HIJACKING dmesg + rump.halt +} + +atf_test_case simplehook_nolock cleanup +simplehook_nolock_head() +{ + + atf_set "descr" "tests for hook that does not use lock in it" + atf_set "require.progs" "rump_server" +} + +simplehook_nolock_body() +{ + local key_hklist="kern.simplehook_tester.hook_list" + local key_hk="kern.simplehook_tester.nbhook" + + rump_server -lrumpkern_simplehook_tester $SOCK + + export RUMP_SERVER=$SOCK + $DEBUG && rump.sysctl -e kern.simplehook_tester + atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester + + atf_sysctl_wr ${key_hklist}.created '1' + atf_sysctl_wr ${key_hk}.established '1' + + atf_sysctl_wr ${key_hklist}.dohooks '1' + atf_sysctl_wr ${key_hk}.established '0' + atf_sysctl_wr ${key_hklist}.dohooks '0' + + atf_sysctl_wr ${key_hklist}.created '0' +} + +simplehook_nolock_cleanup() +{ + export RUMP_SERVER=$SOCK + + $DEBUG && rump.sysctl -e kern.simplehook_tester + $DEBUG && $HIJACKING dmesg + rump.halt +} + +atf_init_test_cases() +{ + + atf_add_test_case simplehook_basic + atf_add_test_case simplehook_disestablish + atf_add_test_case simplehook_nolock +}