Index: share/mk/src.opts.mk =================================================================== --- share/mk/src.opts.mk +++ share/mk/src.opts.mk @@ -153,6 +153,7 @@ TCSH \ TELNET \ TESTS \ + TESTKERN \ TEXTPROC \ UNBOUND \ USB \ Index: sys/amd64/conf/GENERIC =================================================================== --- sys/amd64/conf/GENERIC +++ sys/amd64/conf/GENERIC @@ -73,7 +73,8 @@ options KDTRACE_HOOKS # Kernel DTrace hooks options DDB_CTF # Kernel ELF linker loads CTF data options INCLUDE_CONFIG_FILE # Include this file in kernel - +# Add in the kernel test framework +options KERN_TEST_FRWK # Debugging support. Always need this: options KDB # Enable kernel debugger support. options KDB_TRACE # Print a stack trace for a panic. Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3003,6 +3003,7 @@ kern/kern_syscalls.c standard kern/kern_sysctl.c standard kern/kern_tc.c standard +kern/kern_testfrwk.c standard kern/kern_thr.c standard kern/kern_thread.c standard kern/kern_time.c standard Index: sys/conf/options =================================================================== --- sys/conf/options +++ sys/conf/options @@ -156,6 +156,7 @@ MAC_STATIC opt_mac.h MAC_STUB opt_dontuse.h MAC_TEST opt_dontuse.h +KERN_TEST_FRWK opt_testfw.h MD_ROOT opt_md.h MD_ROOT_FSTYPE opt_md.h MD_ROOT_SIZE opt_md.h Index: sys/kern/kern_testfrwk.c =================================================================== --- sys/kern/kern_testfrwk.c +++ sys/kern/kern_testfrwk.c @@ -0,0 +1,298 @@ +/*- + * Copyright (c) 2015 + * Netflix Incorporated, 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 REGENTS 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 REGENTS 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 +__FBSDID("$FreeBSD$"); + +#include "opt_testfw.h" +#include +#include +#include "opt_testfw.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SMP +#include +#endif + +#ifdef KERN_TEST_FRWK +struct kern_test_list { + TAILQ_ENTRY(kern_test_list) next; + char name[TEST_NAME_LEN]; + kerntfunc func; +}; + +TAILQ_HEAD(ktestlist, kern_test_list); + +struct kern_test_entry { + TAILQ_ENTRY(kern_test_entry) next; + struct kern_test_list *kt_e; + struct kern_test kt_data; +}; +TAILQ_HEAD(ktestqueue, kern_test_entry); + +MALLOC_DEFINE(M_KTFRWK, "kern_tfrwk", "Kernel Test Framework"); +struct kern_totfrwk { + struct taskqueue *kfrwk_tq; + struct task kfrwk_que; + struct ktestlist kfrwk_testlist; + struct ktestqueue kfrwk_testq; + struct mtx kfrwk_mtx; + int kfrwk_waiting; +}; + +struct kern_totfrwk kfrwk; + +#define KTFRWK_MUTEX_INIT() mtx_init(&kfrwk.kfrwk_mtx, "kern_test_frwk", "tfrwk", MTX_DEF) + +#define KTFRWK_DESTROY() mtx_destroy(&kfrwk.kfrwk_mtx) + +#define KTFRWK_LOCK() mtx_lock(&kfrwk.kfrwk_mtx) + +#define KTFRWK_UNLOCK() mtx_unlock(&kfrwk.kfrwk_mtx) + +static void +kfrwk_task(void *context, int pending) +{ + struct kern_totfrwk *tf; + struct kern_test_entry *wk; + int free_mem=0; + struct kern_test kt_data; + kerntfunc ktf; + + memset(&kt_data, 0, sizeof(kt_data)); + ktf = NULL; + tf = (struct kern_totfrwk *)context; + KTFRWK_LOCK(); + wk = TAILQ_FIRST(&tf->kfrwk_testq); + if (wk) { + wk->kt_data.num_threads--; + tf->kfrwk_waiting--; + if (wk->kt_data.num_threads == 0) { + TAILQ_REMOVE(&tf->kfrwk_testq, wk, next); + free_mem = 1; + } else { + /* Wake one of my colleages up to help too */ + taskqueue_enqueue(tf->kfrwk_tq, &tf->kfrwk_que); + } + memcpy(&kt_data, &wk->kt_data, sizeof(kt_data)); + if (wk->kt_e) { + ktf = wk->kt_e->func; + } + } + KTFRWK_UNLOCK(); + if (wk && free_mem) { + free(wk, M_KTFRWK); + } + /* Execute the test */ + if (ktf){ + (*ktf)(&kt_data); + } + /* We are done */ + atomic_add_int(&tf->kfrwk_waiting, 1); +} + +static void +kerntest_frwk_init(void *st) +{ + u_int ncpus = mp_ncpus ? mp_ncpus : MAXCPU; + + KTFRWK_MUTEX_INIT(); + TAILQ_INIT(&kfrwk.kfrwk_testq); + TAILQ_INIT(&kfrwk.kfrwk_testlist); + /* Now lets start up a number of tasks to do the work */ + TASK_INIT(&kfrwk.kfrwk_que, 0, kfrwk_task, &kfrwk); + kfrwk.kfrwk_tq = taskqueue_create_fast("sbtls_task", M_NOWAIT, + taskqueue_thread_enqueue, &kfrwk.kfrwk_tq); + if (kfrwk.kfrwk_tq == NULL) { + printf("Can't start taskqueue for Kernel Test Framework\n"); + panic("Taskqueue init fails for kfrwk"); + } + taskqueue_start_threads(&kfrwk.kfrwk_tq, ncpus, PI_NET, "[kt_frwk task]"); + kfrwk.kfrwk_waiting = ncpus; +} + +SYSINIT(kerntest_frwk, SI_SUB_KPROF, SI_ORDER_ANY, kerntest_frwk_init, NULL); + +#endif + +static int kerntest_execute(SYSCTL_HANDLER_ARGS); + +SYSCTL_DECL(_kern); +SYSCTL_NODE(_kern, OID_AUTO, testfrwk, CTLFLAG_RW, 0, "Kernel Test Framework"); + + +SYSCTL_PROC(_kern_testfrwk, OID_AUTO, runtest, (CTLTYPE_STRUCT | CTLFLAG_RW), + 0, 0, kerntest_execute, "IU", "Execute a kernel test"); + +int +kerntest_execute(SYSCTL_HANDLER_ARGS) +{ +#ifndef KERN_TEST_FRWK + return (ENOTSUP); +#else + struct kern_test kt; + struct kern_test_list *li, *te=NULL; + struct kern_test_entry *kte=NULL; + int error = 0; + /* Find the entry if possible */ + error = SYSCTL_IN(req, &kt, sizeof(struct kern_test)); + if (error) { + return(error); + } + if (kt.num_threads <= 0) { + return (EINVAL); + } + KTFRWK_LOCK(); + TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) { + if (strcmp(li->name, kt.name) == 0) { + te = li; + break; + } + } + if (te == NULL) { + error = ENOENT; + goto out; + } + /* Grab some memory */ + kte = malloc(sizeof(struct kern_test_entry), M_KTFRWK, M_WAITOK); + if (kte == NULL) { + error = ENOMEM; + goto out; + } + /* Ok we have a test item to run, can we? */ + if (!TAILQ_EMPTY(&kfrwk.kfrwk_testq)) { + /* We don't know if there is enough threads */ + error = EAGAIN; + free(kte, M_KTFRWK); + goto out; + } + if (kfrwk.kfrwk_waiting < kt.num_threads) { + error = E2BIG; + free(kte, M_KTFRWK); + goto out; + } + /* Ok it looks like we can do it, lets get an entry */ + kte->kt_e = li; + memcpy(&kte->kt_data, &kt, sizeof(kt)); + TAILQ_INSERT_TAIL(&kfrwk.kfrwk_testq, kte, next); + taskqueue_enqueue(kfrwk.kfrwk_tq, &kfrwk.kfrwk_que); +out: + KTFRWK_UNLOCK(); + return(error); +#endif +} + +int +kern_testframework_register(const char *name, kerntfunc func) +{ +#ifndef KERN_TEST_FRWK + return (ENOTSUP); +#else + int error = 0; + struct kern_test_list *li, *te=NULL; + int len; + + len = strlen(name); + if (len >= TEST_NAME_LEN) { + return (E2BIG); + } + KTFRWK_LOCK(); + /* First does it already exist? */ + TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) { + if (strcmp(li->name, name) == 0) { + error = EALREADY; + goto out; + } + } + /* Ok we can do it, lets add it to the list */ + te = malloc(sizeof(struct kern_test_list), M_KTFRWK, M_WAITOK); + if (te == NULL) { + error = ENOMEM; + goto out; + } + te->func = func; + strcpy(te->name, name); + TAILQ_INSERT_TAIL(&kfrwk.kfrwk_testlist, te, next); +out: + KTFRWK_UNLOCK(); + return(error); +#endif +} + +int +kern_testframework_deregister(const char *name) +{ +#ifndef KERN_TEST_FRWK + return (ENOTSUP); +#else + struct kern_test_list *li, *te=NULL; + u_int ncpus = mp_ncpus ? mp_ncpus : MAXCPU; + int error = 0; + + KTFRWK_LOCK(); + /* First does it already exist? */ + TAILQ_FOREACH (li, &kfrwk.kfrwk_testlist, next) { + if (strcmp(li->name, name) == 0) { + te = li; + break; + } + } + if (te == NULL) { + /* It is not registered so no problem */ + goto out; + } + if (ncpus != kfrwk.kfrwk_waiting) { + /* We are busy executing something -- can't unload */ + error = EBUSY; + goto out; + } + if (!TAILQ_EMPTY(&kfrwk.kfrwk_testq)) { + /* Something still to execute */ + error = EBUSY; + goto out; + } + /* Ok we can remove the dude safely */ + TAILQ_REMOVE(&kfrwk.kfrwk_testlist, te, next); + memset(te, 0, sizeof(struct kern_test_list)); + free(te, M_KTFRWK); +out: + KTFRWK_UNLOCK(); + return(error); +#endif +} Index: sys/modules/Makefile =================================================================== --- sys/modules/Makefile +++ sys/modules/Makefile @@ -62,6 +62,7 @@ cam \ ${_canbepm} \ ${_canbus} \ + ${_callout_test} \ ${_cardbus} \ ${_carp} \ cas \ @@ -386,6 +387,10 @@ _autofs= autofs .endif +.if ${MK_TESTS} != "no" || defined(ALL_MODULES) +_callout_test= tests/callout_test +.endif + .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) .if exists(${.CURDIR}/../opencrypto) _crypto= crypto Index: sys/modules/tests/callout_test/Makefile =================================================================== --- sys/modules/tests/callout_test/Makefile +++ sys/modules/tests/callout_test/Makefile @@ -0,0 +1,15 @@ +# +# $FreeBSD$ +# + +.PATH: ${.CURDIR} + +KMOD= callout_test +SRCS= callout_test.c + +# +# Enable full debugging +# +#CFLAGS += -g + +.include Index: sys/modules/tests/callout_test/callout_test.c =================================================================== --- sys/modules/tests/callout_test/callout_test.c +++ sys/modules/tests/callout_test/callout_test.c @@ -0,0 +1,272 @@ +/*- + * Copyright (c) 2015 Hans Petter Selasky. All rights reserved. + * 2015 Netflix 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 AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SMP +#include +#else +#define cpu_spinwait() +#endif + + +static volatile int callouttest_test; +static volatile int callouttest_timo_for_counter; +static struct callout test_co; + +static struct mtx ca_mtx_test; +static struct mtx ca_print_mtx_test; +static uint64_t callout_counting_fun=0; +static uint64_t callout_counting_fun_mig=0; +static uint64_t callout_counting_fun_ab=0; +static uint64_t callout_total_enters=0; +static uint64_t callout_sees_freed_mem=0; +static sbintime_t callout_x_time; +static sbintime_t callout_f_time; +static int mem_freed=0; + +static void +test_callout(void *arg) +{ + struct kern_test *kt; + struct callout_test *u; + + + kt = (struct kern_test *)arg; + u = (struct callout_test *)&kt->test_options; + callout_x_time = sbinuptime(); + callout_total_enters++; + callouttest_timo_for_counter = u->sim_intr_delay; + callouttest_test = 1; + /* simulate fast interrupt affecting timing */ + while (callouttest_test != 2) { + cpu_spinwait(); + callouttest_timo_for_counter--; + if (callouttest_timo_for_counter <= 0) { + break; + } + } + mtx_lock(&ca_mtx_test); + if (callout_pending(&test_co) || + !callout_active(&test_co)) { + callout_counting_fun_ab++; + if (callout_migrating(&test_co)) { + callout_counting_fun_mig++; + } + callout_f_time = sbinuptime(); + callouttest_test = 3; + mtx_unlock(&ca_mtx_test); + return; + } + callout_deactivate(&test_co); + callout_counting_fun++; + if (mem_freed) { + callout_sees_freed_mem++; + } + callout_f_time = sbinuptime(); + callouttest_test = 4; + mtx_unlock(&ca_mtx_test); +} + + + +static void +run_callout_test(struct kern_test *test) +{ + struct callout_test *u; + int test_number; + unsigned int y, rv; + int test_cnt=0; + uint64_t rst_cnt, stop_cnt, drain_cnt; + int dst_cpu; + rst_cnt = stop_cnt = drain_cnt = 0; + /* + * Set this to different things if you want, we + * it the same for predicatbility between runs. + */ + u = (struct callout_test *)&test->test_options; + test_number = test->test_number; + if (test_number < 1) { + outofit: + printf("Test %d unrecognized\n", test_number); + return; + } + if (test_number > 4) { + goto outofit; + } +do_it_again: + mtx_lock(&ca_mtx_test); + callouttest_test = 0; + mem_freed = 0; + callout_reset(&test_co, 1, &test_callout, test); + mtx_unlock(&ca_mtx_test); + while (callouttest_test == 0) { + cpu_spinwait(); + } + callouttest_test = 2; + for (y = 0; y != u->num_loop_iterations; y++) { + if ((test_number == 1) || (test_number == 2)) { + mtx_lock(&ca_mtx_test); + if (test_number == 1) { + dst_cpu = 0; + } else { + /* test 2 */ + dst_cpu = y % mp_ncpus; + } + mem_freed = 0; + callout_reset_on(&test_co, 1, &test_callout, test, dst_cpu); + rst_cnt++; + mtx_unlock(&ca_mtx_test); + } else if ((test_number == 3) || (test_number == 4)) { + rv = random(); + mtx_lock(&ca_mtx_test); + if (test_number == 3) { + dst_cpu = 0; + } else { + /* Test 4 */ + dst_cpu = y % mp_ncpus; + } + if ((rv % 4) == 0) { + mem_freed = 0; + callout_reset_on(&test_co, 1, &test_callout, test, dst_cpu); + rst_cnt++; + } else if ((rv % 4) == 1) { + callout_stop(&test_co); + stop_cnt++; + mem_freed = 1; + } else if ((rv % 4) == 2) { + mtx_unlock(&ca_mtx_test); + callout_drain(&test_co); + mem_freed = 1; + mtx_lock(&ca_mtx_test); + drain_cnt++; + } else if ((rv % 4) == 3) { + mem_freed = 0; + callout_reset_on(&test_co, 1, &test_callout, test, dst_cpu); + rst_cnt++; + callout_reset_on(&test_co, 1, &test_callout, test, ((dst_cpu+1)%mp_ncpus)); + rst_cnt++; + } + mtx_unlock(&ca_mtx_test); + } + } + callout_drain(&test_co); + mem_freed = 1; + drain_cnt++; + mtx_lock(&ca_print_mtx_test); + test_cnt++; + if (test_cnt < u->num_of_times) { + printf("Looping around again for test number:%d\n", + test_cnt); + mtx_unlock(&ca_print_mtx_test); + goto do_it_again; + } + printf("Callout testing complete\ntotentry:%zu trigok:%zu trigab:%zu (mig:%zu sfm:%zu)\n", + callout_total_enters, + callout_counting_fun, + callout_counting_fun_ab, + callout_counting_fun_mig, + callout_sees_freed_mem); + printf("resets:%zu stops:%zu drains:%zu\n", + rst_cnt, + stop_cnt, + drain_cnt); + mtx_unlock(&ca_print_mtx_test); +} + +int callout_test_is_loaded=0; + +static int +callout_test_modevent(module_t mod, int type, void *data) +{ + int err=0; + u_long random_seed; + + switch (type) { + case MOD_LOAD: + err = kern_testframework_register("callout_test", + run_callout_test); + if (err) { + printf("Can't load callout_test err:%d returned\n", + err); + } else { + callout_init(&test_co, CALLOUT_MPSAFE); + mtx_init(&ca_mtx_test, "ca_test_mtx", "ctm", MTX_DEF); + mtx_init(&ca_print_mtx_test, "ca__print_mtx", "cptm", MTX_DEF); + callout_test_is_loaded = 1; + random_seed = 1; + srandom(random_seed); + } + break; + case MOD_QUIESCE: + err = kern_testframework_deregister("callout_test"); + if (err == 0) { + callout_test_is_loaded = 0; + mtx_destroy(&ca_mtx_test); + mtx_destroy(&ca_print_mtx_test); + } + break; + case MOD_UNLOAD: + if (callout_test_is_loaded) { + err = kern_testframework_deregister("callout_test"); + if (err == 0) { + callout_test_is_loaded = 0; + mtx_destroy(&ca_mtx_test); + mtx_destroy(&ca_print_mtx_test); + } + } + break; + default: + return (EOPNOTSUPP); + } + return (err); +} + +static moduledata_t callout_test_mod = { + .name = "callout_test", + .evhand = callout_test_modevent, + .priv = 0 +}; +DECLARE_MODULE(callout_test, callout_test_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); Index: sys/sys/callout_test.h =================================================================== --- sys/sys/callout_test.h +++ sys/sys/callout_test.h @@ -0,0 +1,16 @@ +#ifndef __callout_test_h__ +#define __callout_test_h__ + +/* + * The default test RRS was using was: + * sim_intr_delay = 10,000,000 + * num_loop_iterations = 10,000,000- + * num_of_times = 1,000 + */ +struct callout_test { + uint32_t sim_intr_delay; /* Max Number of spin cycles to simulate int delay */ + uint32_t num_loop_iterations; /* Number of loops to do the callout in */ + uint32_t num_of_times; /* Number of times to run entire test */ +}; + +#endif Index: sys/sys/kern_testfrwk.h =================================================================== --- sys/sys/kern_testfrwk.h +++ sys/sys/kern_testfrwk.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2015 + * Netflix Incorporated, 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 REGENTS 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 REGENTS 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. + * + */ + +#ifndef _SYS_KERN_TESTFRWKT_H_ +#define _SYS_KERN_TESTFRWKT_H_ + +#define TEST_NAME_LEN 32 +#define TEST_OPTION_SPACE 256 +/*- + * Copyright (c) 2015 + * Netflix Incorporated, 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 REGENTS 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 REGENTS 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. + *__FBSDID("$FreeBSD$"); + * + */ + +struct kern_test { + char name[TEST_NAME_LEN]; + int test_number; + int num_threads; + uint8_t test_options[TEST_OPTION_SPACE]; +}; + +typedef void (*kerntfunc)(struct kern_test *); + +#ifdef _KERNEL +int kern_testframework_register(const char *name, kerntfunc); + +int kern_testframework_deregister(const char *name); +#endif +#endif