Path: blob/main/sys/tests/framework/kern_testfrwk.c
39476 views
/*-1* Copyright (c) 2015 Netflix, Inc.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions and the following disclaimer.8* 2. Redistributions in binary form must reproduce the above copyright9* notice, this list of conditions and the following disclaimer in the10* documentation and/or other materials provided with the distribution.11*12* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND13* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE14* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE15* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE16* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL17* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS18* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)19* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT20* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY21* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF22* SUCH DAMAGE.23*24*/2526#include <sys/param.h>27#include <sys/systm.h>28#include <sys/bus.h>29#include <sys/callout.h>30#include <sys/kernel.h>31#include <sys/ktr.h>32#include <sys/lock.h>33#include <sys/malloc.h>34#include <sys/module.h>35#include <sys/mutex.h>36#include <sys/sdt.h>37#include <sys/smp.h>38#include <sys/sysctl.h>39#include <sys/taskqueue.h>40#include <sys/queue.h>41#include <tests/kern_testfrwk.h>42#ifdef SMP43#include <machine/cpu.h>44#endif4546struct kern_test_list {47TAILQ_ENTRY(kern_test_list) next;48char name[TEST_NAME_LEN];49kerntfunc func;50};5152TAILQ_HEAD(ktestlist, kern_test_list);5354struct kern_test_entry {55TAILQ_ENTRY(kern_test_entry) next;56struct kern_test_list *kt_e;57struct kern_test kt_data;58};5960TAILQ_HEAD(ktestqueue, kern_test_entry);6162MALLOC_DEFINE(M_KTFRWK, "kern_tfrwk", "Kernel Test Framework");63struct kern_totfrwk {64struct taskqueue *kfrwk_tq;65struct task kfrwk_que;66struct ktestlist kfrwk_testlist;67struct ktestqueue kfrwk_testq;68struct mtx kfrwk_mtx;69int kfrwk_waiting;70};7172struct kern_totfrwk kfrwk;73static int ktest_frwk_inited = 0;7475#define KTFRWK_MUTEX_INIT() mtx_init(&kfrwk.kfrwk_mtx, "kern_test_frwk", "tfrwk", MTX_DEF)7677#define KTFRWK_DESTROY() mtx_destroy(&kfrwk.kfrwk_mtx)7879#define KTFRWK_LOCK() mtx_lock(&kfrwk.kfrwk_mtx)8081#define KTFRWK_UNLOCK() mtx_unlock(&kfrwk.kfrwk_mtx)8283static void84kfrwk_task(void *context, int pending)85{86struct kern_totfrwk *tf;87struct kern_test_entry *wk;88int free_mem = 0;89struct kern_test kt_data;90kerntfunc ktf;9192memset(&kt_data, 0, sizeof(kt_data));93ktf = NULL;94tf = (struct kern_totfrwk *)context;95KTFRWK_LOCK();96wk = TAILQ_FIRST(&tf->kfrwk_testq);97if (wk) {98wk->kt_data.tot_threads_running--;99tf->kfrwk_waiting--;100memcpy(&kt_data, &wk->kt_data, sizeof(kt_data));101if (wk->kt_data.tot_threads_running == 0) {102TAILQ_REMOVE(&tf->kfrwk_testq, wk, next);103free_mem = 1;104} else {105/* Wake one of my colleages up to help too */106taskqueue_enqueue(tf->kfrwk_tq, &tf->kfrwk_que);107}108if (wk->kt_e) {109ktf = wk->kt_e->func;110}111}112KTFRWK_UNLOCK();113if (wk && free_mem) {114free(wk, M_KTFRWK);115}116/* Execute the test */117if (ktf) {118(*ktf) (&kt_data);119}120/* We are done */121atomic_add_int(&tf->kfrwk_waiting, 1);122}123124static int125kerntest_frwk_init(void)126{127u_int ncpus = mp_ncpus ? mp_ncpus : MAXCPU;128129KTFRWK_MUTEX_INIT();130TAILQ_INIT(&kfrwk.kfrwk_testq);131TAILQ_INIT(&kfrwk.kfrwk_testlist);132/* Now lets start up a number of tasks to do the work */133TASK_INIT(&kfrwk.kfrwk_que, 0, kfrwk_task, &kfrwk);134kfrwk.kfrwk_tq = taskqueue_create_fast("sbtls_task", M_NOWAIT,135taskqueue_thread_enqueue, &kfrwk.kfrwk_tq);136if (kfrwk.kfrwk_tq == NULL) {137printf("Can't start taskqueue for Kernel Test Framework\n");138panic("Taskqueue init fails for kfrwk");139}140taskqueue_start_threads(&kfrwk.kfrwk_tq, ncpus, PI_NET, "[kt_frwk task]");141kfrwk.kfrwk_waiting = ncpus;142ktest_frwk_inited = 1;143return (0);144}145146static int147kerntest_frwk_fini(void)148{149KTFRWK_LOCK();150if (!TAILQ_EMPTY(&kfrwk.kfrwk_testlist)) {151/* Still modules registered */152KTFRWK_UNLOCK();153return (EBUSY);154}155ktest_frwk_inited = 0;156KTFRWK_UNLOCK();157taskqueue_free(kfrwk.kfrwk_tq);158/* Ok lets destroy the mutex on the way outs */159KTFRWK_DESTROY();160return (0);161}162163164static int kerntest_execute(SYSCTL_HANDLER_ARGS);165166SYSCTL_NODE(_kern, OID_AUTO, testfrwk, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,167"Kernel Test Framework");168SYSCTL_PROC(_kern_testfrwk, OID_AUTO, runtest,169CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,1700, 0, kerntest_execute, "IU",171"Execute a kernel test");172173int174kerntest_execute(SYSCTL_HANDLER_ARGS)175{176struct kern_test kt;177struct kern_test_list *li, *te = NULL;178struct kern_test_entry *kte = NULL;179int error = 0;180181if (ktest_frwk_inited == 0) {182return (ENOENT);183}184/* Find the entry if possible */185error = SYSCTL_IN(req, &kt, sizeof(struct kern_test));186if (error) {187return (error);188}189if (kt.num_threads <= 0) {190return (EINVAL);191}192/* Grab some memory */193kte = malloc(sizeof(struct kern_test_entry), M_KTFRWK, M_WAITOK);194KTFRWK_LOCK();195TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) {196if (strcmp(li->name, kt.name) == 0) {197te = li;198break;199}200}201if (te == NULL) {202printf("Can't find the test %s\n", kt.name);203error = ENOENT;204free(kte, M_KTFRWK);205goto out;206}207/* Ok we have a test item to run, can we? */208if (!TAILQ_EMPTY(&kfrwk.kfrwk_testq)) {209/* We don't know if there is enough threads */210error = EAGAIN;211free(kte, M_KTFRWK);212goto out;213}214if (kfrwk.kfrwk_waiting < kt.num_threads) {215error = E2BIG;216free(kte, M_KTFRWK);217goto out;218}219kt.tot_threads_running = kt.num_threads;220/* Ok it looks like we can do it, lets get an entry */221kte->kt_e = li;222memcpy(&kte->kt_data, &kt, sizeof(kt));223TAILQ_INSERT_TAIL(&kfrwk.kfrwk_testq, kte, next);224taskqueue_enqueue(kfrwk.kfrwk_tq, &kfrwk.kfrwk_que);225out:226KTFRWK_UNLOCK();227return (error);228}229230int231kern_testframework_register(const char *name, kerntfunc func)232{233int error = 0;234struct kern_test_list *li, *te = NULL;235int len;236237len = strlen(name);238if (len >= TEST_NAME_LEN) {239return (E2BIG);240}241te = malloc(sizeof(struct kern_test_list), M_KTFRWK, M_WAITOK);242KTFRWK_LOCK();243/* First does it already exist? */244TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) {245if (strcmp(li->name, name) == 0) {246error = EALREADY;247free(te, M_KTFRWK);248goto out;249}250}251/* Ok we can do it, lets add it to the list */252te->func = func;253strcpy(te->name, name);254TAILQ_INSERT_TAIL(&kfrwk.kfrwk_testlist, te, next);255out:256KTFRWK_UNLOCK();257return (error);258}259260int261kern_testframework_deregister(const char *name)262{263struct kern_test_list *li, *te = NULL;264u_int ncpus = mp_ncpus ? mp_ncpus : MAXCPU;265int error = 0;266267KTFRWK_LOCK();268/* First does it already exist? */269TAILQ_FOREACH(li, &kfrwk.kfrwk_testlist, next) {270if (strcmp(li->name, name) == 0) {271te = li;272break;273}274}275if (te == NULL) {276/* It is not registered so no problem */277goto out;278}279if (ncpus != kfrwk.kfrwk_waiting) {280/* We are busy executing something -- can't unload */281error = EBUSY;282goto out;283}284if (!TAILQ_EMPTY(&kfrwk.kfrwk_testq)) {285/* Something still to execute */286error = EBUSY;287goto out;288}289/* Ok we can remove the dude safely */290TAILQ_REMOVE(&kfrwk.kfrwk_testlist, te, next);291memset(te, 0, sizeof(struct kern_test_list));292free(te, M_KTFRWK);293out:294KTFRWK_UNLOCK();295return (error);296}297298static int299kerntest_mod_init(module_t mod, int type, void *data)300{301int err;302303switch (type) {304case MOD_LOAD:305err = kerntest_frwk_init();306break;307case MOD_QUIESCE:308KTFRWK_LOCK();309if (TAILQ_EMPTY(&kfrwk.kfrwk_testlist)) {310err = 0;311} else {312err = EBUSY;313}314KTFRWK_UNLOCK();315break;316case MOD_UNLOAD:317err = kerntest_frwk_fini();318break;319default:320return (EOPNOTSUPP);321}322return (err);323}324325static moduledata_t kern_test_framework = {326.name = "kernel_testfrwk",327.evhand = kerntest_mod_init,328.priv = 0329};330331MODULE_VERSION(kern_testframework, 1);332DECLARE_MODULE(kern_testframework, kern_test_framework, SI_SUB_PSEUDO, SI_ORDER_ANY);333334335