#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <machine/md_var.h>
#include <sys/ctype.h>
#include <sys/kthread.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/module.h>
#include <libprop/proplib.h>
#include <sys/tbridge.h>
static cdev_t tbridge_dev;
static d_open_t tbridge_dev_open;
static d_close_t tbridge_dev_close;
static d_ioctl_t tbridge_dev_ioctl;
static struct dev_ops tbridge_dev_ops = {
{ "tbridge", 0, 0 },
.d_open = tbridge_dev_open,
.d_close = tbridge_dev_close,
.d_ioctl = tbridge_dev_ioctl
};
static struct tbridge_testcase *tbridge_curtest = NULL;
static prop_dictionary_t tbridge_testcase = NULL;
static int tbridge_result_ready = 0;
static char tbridge_msgbuf[131072];
static char *tbridge_msgbuf_ptr;
size_t tbridge_msgbuf_remsz;
static prop_dictionary_t
testcase_get_result_dict(prop_dictionary_t testcase)
{
prop_dictionary_t result_dict;
int r;
result_dict = prop_dictionary_get(testcase, "result");
if (result_dict == NULL) {
result_dict = prop_dictionary_create();
if (result_dict == NULL) {
kprintf("could not allocate new result dict");
return NULL;
}
r = prop_dictionary_set(testcase, "result", result_dict);
if (r == 0) {
kprintf("prop_dictionary operation failed");
return NULL;
}
}
return result_dict;
}
static int
testcase_get_timeout(prop_dictionary_t testcase)
{
int32_t val;
int r;
r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
"timeout_in_secs", &val);
if (r == 0)
val = 600;
return val;
}
static int
testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
}
static int
testcase_set_result(prop_dictionary_t testcase, int result)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_int32(dict, "result", result);
}
static const char *
testcase_get_name(prop_dictionary_t testcase)
{
const char *str;
int r;
r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str);
if (r == 0)
str = "???";
return str;
}
int
tbridge_printf(const char *fmt, ...)
{
__va_list args;
int i;
__va_start(args,fmt);
i = kvsnprintf(tbridge_msgbuf_ptr, tbridge_msgbuf_remsz, fmt, args);
__va_end(args);
tbridge_msgbuf_ptr += i;
tbridge_msgbuf_remsz -= i;
return i;
}
static int
tbridge_register(struct tbridge_testcase *test)
{
if (tbridge_curtest != NULL)
return EBUSY;
tbridge_curtest = test;
return 0;
}
static int
tbridge_unregister(void)
{
tbridge_curtest = NULL;
return 0;
}
int
tbridge_testcase_modhandler(module_t mod, int type, void *arg)
{
struct tbridge_testcase *tcase = (struct tbridge_testcase *)arg;
int error = 0;
switch (type) {
case MOD_LOAD:
error = tbridge_register(tcase);
break;
case MOD_UNLOAD:
if (tcase->tb_abort != NULL)
error = tcase->tb_abort();
if (!error)
tbridge_unregister();
break;
default:
break;
}
return error;
}
void
tbridge_test_done(int result)
{
KKASSERT(tbridge_testcase != NULL);
kprintf("testcase '%s' called test_done with result=%d\n",
testcase_get_name(tbridge_testcase), result);
testcase_set_result(tbridge_testcase, result);
testcase_set_stdout_buf(tbridge_testcase, tbridge_msgbuf);
tbridge_result_ready = 1;
wakeup(&tbridge_result_ready);
}
static void
tbridge_reset(void)
{
tbridge_msgbuf_ptr = tbridge_msgbuf;
tbridge_msgbuf_remsz = sizeof(tbridge_msgbuf);
if (tbridge_testcase != NULL) {
prop_object_release(tbridge_testcase);
tbridge_testcase = NULL;
}
safemem_reset_error_count();
tbridge_result_ready = 0;
}
static int
tbridge_dev_open(struct dev_open_args *ap)
{
return 0;
}
static int
tbridge_dev_close(struct dev_close_args *ap)
{
return 0;
}
static int
tbridge_dev_ioctl(struct dev_ioctl_args *ap)
{
struct plistref *pref;
struct thread *td;
int error, r;
error = 0;
pref = (struct plistref *)ap->a_data;
switch(ap->a_cmd) {
case TBRIDGE_LOADTEST:
tbridge_reset();
if (tbridge_curtest == NULL)
return EINVAL;
error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd,
&tbridge_testcase);
if (error)
return error;
break;
case TBRIDGE_GETRESULT:
error = kthread_create(tbridge_curtest->tb_run, NULL, &td,
"testrunner");
if (error)
tbridge_test_done(RESULT_PREFAIL);
if (!tbridge_result_ready) {
r = tsleep(&tbridge_result_ready, 0, "tbridgeres",
hz * testcase_get_timeout(tbridge_testcase));
if (r != 0) {
if (tbridge_curtest->tb_abort != NULL)
tbridge_curtest->tb_abort();
tbridge_test_done(RESULT_TIMEOUT);
}
}
if (safemem_get_error_count() != 0) {
testcase_set_result(tbridge_testcase, RESULT_SIGNALLED);
}
error = prop_dictionary_copyout_ioctl(pref, ap->a_cmd,
tbridge_testcase);
if (error)
return error;
break;
default:
error = ENOTTY;
break;
}
return(error);
}
static int
testbridge_modevent(module_t mod, int type, void *unused)
{
switch (type) {
case MOD_LOAD:
tbridge_dev = make_dev(&tbridge_dev_ops,
0,
UID_ROOT,
GID_WHEEL,
0600,
"tbridge");
tbridge_testcase = NULL;
tbridge_reset();
kprintf("dfregress kernel test bridge ready!\n");
return 0;
case MOD_UNLOAD:
destroy_dev(tbridge_dev);
kprintf("dfregress kernel test bridge unloaded.\n");
return 0;
}
return EINVAL;
}
static moduledata_t testbridge_mod = {
"testbridge",
testbridge_modevent,
0
};
DECLARE_MODULE(testbridge, testbridge_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_VERSION(testbridge, 1);