xref: /freebsd/sys/tests/ktest.c (revision ee507b70f150855cfff0ef1080c38a3c70c1e7cd)
13e5d0784SAlexander V. Chernikov /*-
23e5d0784SAlexander V. Chernikov  * SPDX-License-Identifier: BSD-2-Clause
33e5d0784SAlexander V. Chernikov  *
43e5d0784SAlexander V. Chernikov  * Copyright (c) 2023 Alexander V. Chernikov
53e5d0784SAlexander V. Chernikov  *
63e5d0784SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
73e5d0784SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
83e5d0784SAlexander V. Chernikov  * are met:
93e5d0784SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
103e5d0784SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
113e5d0784SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
123e5d0784SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
133e5d0784SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
143e5d0784SAlexander V. Chernikov  *
153e5d0784SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
163e5d0784SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
173e5d0784SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
183e5d0784SAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
193e5d0784SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
203e5d0784SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
213e5d0784SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
223e5d0784SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
233e5d0784SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
243e5d0784SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
253e5d0784SAlexander V. Chernikov  * SUCH DAMAGE.
263e5d0784SAlexander V. Chernikov  */
273e5d0784SAlexander V. Chernikov 
283e5d0784SAlexander V. Chernikov #include <sys/param.h>
293e5d0784SAlexander V. Chernikov #include <sys/refcount.h>
303e5d0784SAlexander V. Chernikov #include <sys/types.h>
313e5d0784SAlexander V. Chernikov #include <sys/kernel.h>
323e5d0784SAlexander V. Chernikov #include <sys/lock.h>
33f656a960SAlexander V. Chernikov #include <sys/mutex.h>
343e5d0784SAlexander V. Chernikov #include <sys/malloc.h>
353e5d0784SAlexander V. Chernikov #include <sys/module.h>
363e5d0784SAlexander V. Chernikov #include <sys/socket.h>
373e5d0784SAlexander V. Chernikov #include <sys/priv.h>
383e5d0784SAlexander V. Chernikov 
393e5d0784SAlexander V. Chernikov #include <netlink/netlink.h>
403e5d0784SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
413e5d0784SAlexander V. Chernikov #include <netlink/netlink_generic.h>
423e5d0784SAlexander V. Chernikov #include <netlink/netlink_message_parser.h>
433e5d0784SAlexander V. Chernikov 
443e5d0784SAlexander V. Chernikov #include <machine/stdarg.h>
453e5d0784SAlexander V. Chernikov #include <tests/ktest.h>
463e5d0784SAlexander V. Chernikov 
473e5d0784SAlexander V. Chernikov struct mtx ktest_mtx;
483e5d0784SAlexander V. Chernikov #define	KTEST_LOCK()		mtx_lock(&ktest_mtx)
493e5d0784SAlexander V. Chernikov #define	KTEST_UNLOCK()		mtx_unlock(&ktest_mtx)
503e5d0784SAlexander V. Chernikov #define	KTEST_LOCK_ASSERT()	mtx_assert(&ktest_mtx, MA_OWNED)
513e5d0784SAlexander V. Chernikov 
523e5d0784SAlexander V. Chernikov MTX_SYSINIT(ktest_mtx, &ktest_mtx, "ktest mutex", MTX_DEF);
533e5d0784SAlexander V. Chernikov 
543e5d0784SAlexander V. Chernikov struct ktest_module {
553e5d0784SAlexander V. Chernikov 	struct ktest_module_info	*info;
563e5d0784SAlexander V. Chernikov 	volatile u_int			refcount;
573e5d0784SAlexander V. Chernikov 	TAILQ_ENTRY(ktest_module)	entries;
583e5d0784SAlexander V. Chernikov };
593e5d0784SAlexander V. Chernikov static TAILQ_HEAD(, ktest_module) module_list = TAILQ_HEAD_INITIALIZER(module_list);
603e5d0784SAlexander V. Chernikov 
613e5d0784SAlexander V. Chernikov struct nl_ktest_parsed {
623e5d0784SAlexander V. Chernikov 	char		*mod_name;
633e5d0784SAlexander V. Chernikov 	char		*test_name;
643e5d0784SAlexander V. Chernikov 	struct nlattr	*test_meta;
653e5d0784SAlexander V. Chernikov };
663e5d0784SAlexander V. Chernikov 
673e5d0784SAlexander V. Chernikov #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
683e5d0784SAlexander V. Chernikov #define	_OUT(_field)	offsetof(struct nl_ktest_parsed, _field)
693e5d0784SAlexander V. Chernikov 
703e5d0784SAlexander V. Chernikov static const struct nlattr_parser nla_p_get[] = {
713e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_MOD_NAME, .off = _OUT(mod_name), .cb = nlattr_get_string },
723e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_TEST_NAME, .off = _OUT(test_name), .cb = nlattr_get_string },
733e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_TEST_META, .off = _OUT(test_meta), .cb = nlattr_get_nla },
743e5d0784SAlexander V. Chernikov };
753e5d0784SAlexander V. Chernikov static const struct nlfield_parser nlf_p_get[] = {
763e5d0784SAlexander V. Chernikov };
773e5d0784SAlexander V. Chernikov NL_DECLARE_PARSER(ktest_parser, struct genlmsghdr, nlf_p_get, nla_p_get);
783e5d0784SAlexander V. Chernikov #undef _IN
793e5d0784SAlexander V. Chernikov #undef _OUT
803e5d0784SAlexander V. Chernikov 
813e5d0784SAlexander V. Chernikov static bool
create_reply(struct nl_writer * nw,struct nlmsghdr * hdr,int cmd)823e5d0784SAlexander V. Chernikov create_reply(struct nl_writer *nw, struct nlmsghdr *hdr, int cmd)
833e5d0784SAlexander V. Chernikov {
843e5d0784SAlexander V. Chernikov 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
853e5d0784SAlexander V. Chernikov 		return (false);
863e5d0784SAlexander V. Chernikov 
873e5d0784SAlexander V. Chernikov 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
883e5d0784SAlexander V. Chernikov 	ghdr_new->cmd = cmd;
893e5d0784SAlexander V. Chernikov 	ghdr_new->version = 0;
903e5d0784SAlexander V. Chernikov 	ghdr_new->reserved = 0;
913e5d0784SAlexander V. Chernikov 
923e5d0784SAlexander V. Chernikov 	return (true);
933e5d0784SAlexander V. Chernikov }
943e5d0784SAlexander V. Chernikov 
953e5d0784SAlexander V. Chernikov static int
dump_mod_test(struct nlmsghdr * hdr,struct nl_pstate * npt,struct ktest_module * mod,const struct ktest_test_info * test_info)963e5d0784SAlexander V. Chernikov dump_mod_test(struct nlmsghdr *hdr, struct nl_pstate *npt,
973e5d0784SAlexander V. Chernikov     struct ktest_module *mod, const struct ktest_test_info *test_info)
983e5d0784SAlexander V. Chernikov {
993e5d0784SAlexander V. Chernikov 	struct nl_writer *nw = npt->nw;
1003e5d0784SAlexander V. Chernikov 
1013e5d0784SAlexander V. Chernikov 	if (!create_reply(nw, hdr, KTEST_CMD_NEWTEST))
1023e5d0784SAlexander V. Chernikov 		goto enomem;
1033e5d0784SAlexander V. Chernikov 
1043e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_MOD_NAME, mod->info->name);
1053e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_TEST_NAME, test_info->name);
1063e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_TEST_DESCR, test_info->desc);
1073e5d0784SAlexander V. Chernikov 
1083e5d0784SAlexander V. Chernikov 	if (nlmsg_end(nw))
1093e5d0784SAlexander V. Chernikov 		return (0);
1103e5d0784SAlexander V. Chernikov enomem:
1113e5d0784SAlexander V. Chernikov 	nlmsg_abort(nw);
1123e5d0784SAlexander V. Chernikov 	return (ENOMEM);
1133e5d0784SAlexander V. Chernikov }
1143e5d0784SAlexander V. Chernikov 
1153e5d0784SAlexander V. Chernikov static int
dump_mod_tests(struct nlmsghdr * hdr,struct nl_pstate * npt,struct ktest_module * mod,struct nl_ktest_parsed * attrs)1163e5d0784SAlexander V. Chernikov dump_mod_tests(struct nlmsghdr *hdr, struct nl_pstate *npt,
1173e5d0784SAlexander V. Chernikov     struct ktest_module *mod, struct nl_ktest_parsed *attrs)
1183e5d0784SAlexander V. Chernikov {
1193e5d0784SAlexander V. Chernikov 	for (int i = 0; i < mod->info->num_tests; i++) {
1203e5d0784SAlexander V. Chernikov 		const struct ktest_test_info *test_info = &mod->info->tests[i];
1213e5d0784SAlexander V. Chernikov 		if (attrs->test_name != NULL && strcmp(attrs->test_name, test_info->name))
1223e5d0784SAlexander V. Chernikov 			continue;
1233e5d0784SAlexander V. Chernikov 		int error = dump_mod_test(hdr, npt, mod, test_info);
1243e5d0784SAlexander V. Chernikov 		if (error != 0)
1253e5d0784SAlexander V. Chernikov 			return (error);
1263e5d0784SAlexander V. Chernikov 	}
1273e5d0784SAlexander V. Chernikov 
1283e5d0784SAlexander V. Chernikov 	return (0);
1293e5d0784SAlexander V. Chernikov }
1303e5d0784SAlexander V. Chernikov 
1313e5d0784SAlexander V. Chernikov static int
dump_tests(struct nlmsghdr * hdr,struct nl_pstate * npt)1323e5d0784SAlexander V. Chernikov dump_tests(struct nlmsghdr *hdr, struct nl_pstate *npt)
1333e5d0784SAlexander V. Chernikov {
1343e5d0784SAlexander V. Chernikov 	struct nl_ktest_parsed attrs = { };
1353e5d0784SAlexander V. Chernikov 	struct ktest_module *mod;
1363e5d0784SAlexander V. Chernikov 	int error;
1373e5d0784SAlexander V. Chernikov 
1383e5d0784SAlexander V. Chernikov 	error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
1393e5d0784SAlexander V. Chernikov 	if (error != 0)
1403e5d0784SAlexander V. Chernikov 		return (error);
1413e5d0784SAlexander V. Chernikov 
1423e5d0784SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_MULTI;
1433e5d0784SAlexander V. Chernikov 
1443e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
1453e5d0784SAlexander V. Chernikov 	TAILQ_FOREACH(mod, &module_list, entries) {
1463e5d0784SAlexander V. Chernikov 		if (attrs.mod_name && strcmp(attrs.mod_name, mod->info->name))
1473e5d0784SAlexander V. Chernikov 			continue;
1483e5d0784SAlexander V. Chernikov 		error = dump_mod_tests(hdr, npt, mod, &attrs);
1493e5d0784SAlexander V. Chernikov 		if (error != 0)
1503e5d0784SAlexander V. Chernikov 			break;
1513e5d0784SAlexander V. Chernikov 	}
1523e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
1533e5d0784SAlexander V. Chernikov 
1543e5d0784SAlexander V. Chernikov 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
1553e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
1563e5d0784SAlexander V. Chernikov 		return (ENOMEM);
1573e5d0784SAlexander V. Chernikov 	}
1583e5d0784SAlexander V. Chernikov 
1593e5d0784SAlexander V. Chernikov 	return (error);
1603e5d0784SAlexander V. Chernikov }
1613e5d0784SAlexander V. Chernikov 
1623e5d0784SAlexander V. Chernikov static int
run_test(struct nlmsghdr * hdr,struct nl_pstate * npt)1633e5d0784SAlexander V. Chernikov run_test(struct nlmsghdr *hdr, struct nl_pstate *npt)
1643e5d0784SAlexander V. Chernikov {
1653e5d0784SAlexander V. Chernikov 	struct nl_ktest_parsed attrs = { };
1663e5d0784SAlexander V. Chernikov 	struct ktest_module *mod;
1673e5d0784SAlexander V. Chernikov 	int error;
1683e5d0784SAlexander V. Chernikov 
1693e5d0784SAlexander V. Chernikov 	error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
1703e5d0784SAlexander V. Chernikov 	if (error != 0)
1713e5d0784SAlexander V. Chernikov 		return (error);
1723e5d0784SAlexander V. Chernikov 
1733e5d0784SAlexander V. Chernikov 	if (attrs.mod_name == NULL) {
1743e5d0784SAlexander V. Chernikov 		nlmsg_report_err_msg(npt, "KTEST_ATTR_MOD_NAME not set");
1753e5d0784SAlexander V. Chernikov 		return (EINVAL);
1763e5d0784SAlexander V. Chernikov 	}
1773e5d0784SAlexander V. Chernikov 
1783e5d0784SAlexander V. Chernikov 	if (attrs.test_name == NULL) {
1793e5d0784SAlexander V. Chernikov 		nlmsg_report_err_msg(npt, "KTEST_ATTR_TEST_NAME not set");
1803e5d0784SAlexander V. Chernikov 		return (EINVAL);
1813e5d0784SAlexander V. Chernikov 	}
1823e5d0784SAlexander V. Chernikov 
1833e5d0784SAlexander V. Chernikov 	const struct ktest_test_info *test = NULL;
1843e5d0784SAlexander V. Chernikov 
1853e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
1863e5d0784SAlexander V. Chernikov 	TAILQ_FOREACH(mod, &module_list, entries) {
1873e5d0784SAlexander V. Chernikov 		if (strcmp(attrs.mod_name, mod->info->name))
1883e5d0784SAlexander V. Chernikov 			continue;
1893e5d0784SAlexander V. Chernikov 
1903e5d0784SAlexander V. Chernikov 		const struct ktest_module_info *info = mod->info;
1913e5d0784SAlexander V. Chernikov 
1923e5d0784SAlexander V. Chernikov 		for (int i = 0; i < info->num_tests; i++) {
1933e5d0784SAlexander V. Chernikov 			const struct ktest_test_info *test_info = &info->tests[i];
1943e5d0784SAlexander V. Chernikov 
1953e5d0784SAlexander V. Chernikov 			if (!strcmp(attrs.test_name, test_info->name)) {
1963e5d0784SAlexander V. Chernikov 				test = test_info;
1973e5d0784SAlexander V. Chernikov 				break;
1983e5d0784SAlexander V. Chernikov 			}
1993e5d0784SAlexander V. Chernikov 		}
2003e5d0784SAlexander V. Chernikov 		break;
2013e5d0784SAlexander V. Chernikov 	}
2023e5d0784SAlexander V. Chernikov 	if (test != NULL)
2033e5d0784SAlexander V. Chernikov 		refcount_acquire(&mod->refcount);
2043e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
2053e5d0784SAlexander V. Chernikov 
2063e5d0784SAlexander V. Chernikov 	if (test == NULL)
2073e5d0784SAlexander V. Chernikov 		return (ESRCH);
2083e5d0784SAlexander V. Chernikov 
2093e5d0784SAlexander V. Chernikov 	/* Run the test */
2103e5d0784SAlexander V. Chernikov 	struct ktest_test_context ctx = {
2113e5d0784SAlexander V. Chernikov 		.npt = npt,
2123e5d0784SAlexander V. Chernikov 		.hdr = hdr,
2133e5d0784SAlexander V. Chernikov 		.buf = npt_alloc(npt, KTEST_MAX_BUF),
2143e5d0784SAlexander V. Chernikov 		.bufsize = KTEST_MAX_BUF,
2153e5d0784SAlexander V. Chernikov 	};
2163e5d0784SAlexander V. Chernikov 
2173e5d0784SAlexander V. Chernikov 	if (ctx.buf == NULL) {
2183e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "unable to allocate temporary buffer");
2193e5d0784SAlexander V. Chernikov 		return (ENOMEM);
2203e5d0784SAlexander V. Chernikov 	}
2213e5d0784SAlexander V. Chernikov 
2223e5d0784SAlexander V. Chernikov 	if (test->parse != NULL && attrs.test_meta != NULL) {
2233e5d0784SAlexander V. Chernikov 		error = test->parse(&ctx, attrs.test_meta);
2243e5d0784SAlexander V. Chernikov 		if (error != 0)
2253e5d0784SAlexander V. Chernikov 			return (error);
2263e5d0784SAlexander V. Chernikov 	}
2273e5d0784SAlexander V. Chernikov 
2283e5d0784SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_MULTI;
2293e5d0784SAlexander V. Chernikov 
2303e5d0784SAlexander V. Chernikov 	KTEST_LOG_LEVEL(&ctx, LOG_INFO, "start running %s", test->name);
2313e5d0784SAlexander V. Chernikov 	error = test->func(&ctx);
2323e5d0784SAlexander V. Chernikov 	KTEST_LOG_LEVEL(&ctx, LOG_INFO, "end running %s", test->name);
2333e5d0784SAlexander V. Chernikov 
2343e5d0784SAlexander V. Chernikov 	refcount_release(&mod->refcount);
2353e5d0784SAlexander V. Chernikov 
2363e5d0784SAlexander V. Chernikov 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
2373e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
2383e5d0784SAlexander V. Chernikov 		return (ENOMEM);
2393e5d0784SAlexander V. Chernikov 	}
2403e5d0784SAlexander V. Chernikov 
2413e5d0784SAlexander V. Chernikov 	return (error);
2423e5d0784SAlexander V. Chernikov }
2433e5d0784SAlexander V. Chernikov 
2443e5d0784SAlexander V. Chernikov 
2453e5d0784SAlexander V. Chernikov /* USER API */
2463e5d0784SAlexander V. Chernikov static void
register_test_module(struct ktest_module_info * info)2473e5d0784SAlexander V. Chernikov register_test_module(struct ktest_module_info *info)
2483e5d0784SAlexander V. Chernikov {
2493e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = malloc(sizeof(*mod), M_TEMP, M_WAITOK | M_ZERO);
2503e5d0784SAlexander V. Chernikov 
2513e5d0784SAlexander V. Chernikov 	mod->info = info;
2523e5d0784SAlexander V. Chernikov 	info->module_ptr = mod;
2533e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
2543e5d0784SAlexander V. Chernikov 	TAILQ_INSERT_TAIL(&module_list, mod, entries);
2553e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
2563e5d0784SAlexander V. Chernikov }
2573e5d0784SAlexander V. Chernikov 
2583e5d0784SAlexander V. Chernikov static void
unregister_test_module(struct ktest_module_info * info)2593e5d0784SAlexander V. Chernikov unregister_test_module(struct ktest_module_info *info)
2603e5d0784SAlexander V. Chernikov {
2613e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = info->module_ptr;
2623e5d0784SAlexander V. Chernikov 
2633e5d0784SAlexander V. Chernikov 	info->module_ptr = NULL;
2643e5d0784SAlexander V. Chernikov 
2653e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
2663e5d0784SAlexander V. Chernikov 	TAILQ_REMOVE(&module_list, mod, entries);
2673e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
2683e5d0784SAlexander V. Chernikov 
2693e5d0784SAlexander V. Chernikov 	free(mod, M_TEMP);
2703e5d0784SAlexander V. Chernikov }
2713e5d0784SAlexander V. Chernikov 
2723e5d0784SAlexander V. Chernikov static bool
can_unregister(struct ktest_module_info * info)2733e5d0784SAlexander V. Chernikov can_unregister(struct ktest_module_info *info)
2743e5d0784SAlexander V. Chernikov {
2753e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = info->module_ptr;
2763e5d0784SAlexander V. Chernikov 
2773e5d0784SAlexander V. Chernikov 	return (refcount_load(&mod->refcount) == 0);
2783e5d0784SAlexander V. Chernikov }
2793e5d0784SAlexander V. Chernikov 
2803e5d0784SAlexander V. Chernikov int
ktest_default_modevent(module_t mod,int type,void * arg)2813e5d0784SAlexander V. Chernikov ktest_default_modevent(module_t mod, int type, void *arg)
2823e5d0784SAlexander V. Chernikov {
2833e5d0784SAlexander V. Chernikov 	struct ktest_module_info *info = (struct ktest_module_info *)arg;
2843e5d0784SAlexander V. Chernikov 	int error = 0;
2853e5d0784SAlexander V. Chernikov 
2863e5d0784SAlexander V. Chernikov 	switch (type) {
2873e5d0784SAlexander V. Chernikov 	case MOD_LOAD:
2883e5d0784SAlexander V. Chernikov 		register_test_module(info);
2893e5d0784SAlexander V. Chernikov 		break;
2903e5d0784SAlexander V. Chernikov 	case MOD_UNLOAD:
2913e5d0784SAlexander V. Chernikov 		if (!can_unregister(info))
2923e5d0784SAlexander V. Chernikov 			return (EBUSY);
2933e5d0784SAlexander V. Chernikov 		unregister_test_module(info);
2943e5d0784SAlexander V. Chernikov 		break;
2953e5d0784SAlexander V. Chernikov 	default:
2963e5d0784SAlexander V. Chernikov 		error = EOPNOTSUPP;
2973e5d0784SAlexander V. Chernikov 		break;
2983e5d0784SAlexander V. Chernikov 	}
2993e5d0784SAlexander V. Chernikov 	return (error);
3003e5d0784SAlexander V. Chernikov }
3013e5d0784SAlexander V. Chernikov 
3023e5d0784SAlexander V. Chernikov bool
ktest_start_msg(struct ktest_test_context * ctx)3033e5d0784SAlexander V. Chernikov ktest_start_msg(struct ktest_test_context *ctx)
3043e5d0784SAlexander V. Chernikov {
3053e5d0784SAlexander V. Chernikov 	return (create_reply(ctx->npt->nw, ctx->hdr, KTEST_CMD_NEWMESSAGE));
3063e5d0784SAlexander V. Chernikov }
3073e5d0784SAlexander V. Chernikov 
3083e5d0784SAlexander V. Chernikov void
ktest_add_msg_meta(struct ktest_test_context * ctx,const char * func,const char * fname,int line)3093e5d0784SAlexander V. Chernikov ktest_add_msg_meta(struct ktest_test_context *ctx, const char *func,
3103e5d0784SAlexander V. Chernikov     const char *fname, int line)
3113e5d0784SAlexander V. Chernikov {
3123e5d0784SAlexander V. Chernikov 	struct nl_writer *nw = ctx->npt->nw;
3133e5d0784SAlexander V. Chernikov 	struct timespec ts;
3143e5d0784SAlexander V. Chernikov 
3153e5d0784SAlexander V. Chernikov 	nanouptime(&ts);
3163e5d0784SAlexander V. Chernikov 	nlattr_add(nw, KTEST_MSG_ATTR_TS, sizeof(ts), &ts);
3173e5d0784SAlexander V. Chernikov 
3183e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_MSG_ATTR_FUNC, func);
3193e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_MSG_ATTR_FILE, fname);
3203e5d0784SAlexander V. Chernikov 	nlattr_add_u32(nw, KTEST_MSG_ATTR_LINE, line);
3213e5d0784SAlexander V. Chernikov }
3223e5d0784SAlexander V. Chernikov 
3233e5d0784SAlexander V. Chernikov void
ktest_add_msg_text(struct ktest_test_context * ctx,int msg_level,const char * fmt,...)3243e5d0784SAlexander V. Chernikov ktest_add_msg_text(struct ktest_test_context *ctx, int msg_level,
3253e5d0784SAlexander V. Chernikov     const char *fmt, ...)
3263e5d0784SAlexander V. Chernikov {
3273e5d0784SAlexander V. Chernikov 	va_list ap;
3283e5d0784SAlexander V. Chernikov 
3293e5d0784SAlexander V. Chernikov 	va_start(ap, fmt);
3303e5d0784SAlexander V. Chernikov 	vsnprintf(ctx->buf, ctx->bufsize, fmt, ap);
3313e5d0784SAlexander V. Chernikov 	va_end(ap);
3323e5d0784SAlexander V. Chernikov 
3333e5d0784SAlexander V. Chernikov 	nlattr_add_u8(ctx->npt->nw, KTEST_MSG_ATTR_LEVEL, msg_level);
3343e5d0784SAlexander V. Chernikov 	nlattr_add_string(ctx->npt->nw, KTEST_MSG_ATTR_TEXT, ctx->buf);
3353e5d0784SAlexander V. Chernikov }
3363e5d0784SAlexander V. Chernikov 
3373e5d0784SAlexander V. Chernikov void
ktest_end_msg(struct ktest_test_context * ctx)3383e5d0784SAlexander V. Chernikov ktest_end_msg(struct ktest_test_context *ctx)
3393e5d0784SAlexander V. Chernikov {
3403e5d0784SAlexander V. Chernikov 	nlmsg_end(ctx->npt->nw);
3413e5d0784SAlexander V. Chernikov }
3423e5d0784SAlexander V. Chernikov 
3433e5d0784SAlexander V. Chernikov /* Module glue */
3443e5d0784SAlexander V. Chernikov 
3453e5d0784SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &ktest_parser };
3463e5d0784SAlexander V. Chernikov 
3473e5d0784SAlexander V. Chernikov static const struct genl_cmd ktest_cmds[] = {
3483e5d0784SAlexander V. Chernikov 	{
3493e5d0784SAlexander V. Chernikov 		.cmd_num = KTEST_CMD_LIST,
3503e5d0784SAlexander V. Chernikov 		.cmd_name = "KTEST_CMD_LIST",
3513e5d0784SAlexander V. Chernikov 		.cmd_cb = dump_tests,
3523e5d0784SAlexander V. Chernikov 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
3533e5d0784SAlexander V. Chernikov 	},
3543e5d0784SAlexander V. Chernikov 	{
3553e5d0784SAlexander V. Chernikov 		.cmd_num = KTEST_CMD_RUN,
3563e5d0784SAlexander V. Chernikov 		.cmd_name = "KTEST_CMD_RUN",
3573e5d0784SAlexander V. Chernikov 		.cmd_cb = run_test,
3583e5d0784SAlexander V. Chernikov 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
3593e5d0784SAlexander V. Chernikov 		.cmd_priv = PRIV_KLD_LOAD,
3603e5d0784SAlexander V. Chernikov 	},
3613e5d0784SAlexander V. Chernikov };
3623e5d0784SAlexander V. Chernikov 
363*ee507b70SGleb Smirnoff static int family_id;
3643e5d0784SAlexander V. Chernikov static void
ktest_nl_register(void)3653e5d0784SAlexander V. Chernikov ktest_nl_register(void)
3663e5d0784SAlexander V. Chernikov {
3673e5d0784SAlexander V. Chernikov 	bool ret __diagused;
3683e5d0784SAlexander V. Chernikov 
3693e5d0784SAlexander V. Chernikov 	NL_VERIFY_PARSERS(all_parsers);
3703e5d0784SAlexander V. Chernikov 	family_id = genl_register_family(KTEST_FAMILY_NAME, 0, 1, KTEST_CMD_MAX);
3713e5d0784SAlexander V. Chernikov 	MPASS(family_id != 0);
3723e5d0784SAlexander V. Chernikov 
373*ee507b70SGleb Smirnoff 	ret = genl_register_cmds(family_id, ktest_cmds, nitems(ktest_cmds));
3743e5d0784SAlexander V. Chernikov 	MPASS(ret);
3753e5d0784SAlexander V. Chernikov }
3763e5d0784SAlexander V. Chernikov 
3773e5d0784SAlexander V. Chernikov static void
ktest_nl_unregister(void)3783e5d0784SAlexander V. Chernikov ktest_nl_unregister(void)
3793e5d0784SAlexander V. Chernikov {
3803e5d0784SAlexander V. Chernikov 	MPASS(TAILQ_EMPTY(&module_list));
3813e5d0784SAlexander V. Chernikov 
382*ee507b70SGleb Smirnoff 	genl_unregister_family(family_id);
3833e5d0784SAlexander V. Chernikov }
3843e5d0784SAlexander V. Chernikov 
3853e5d0784SAlexander V. Chernikov static int
ktest_modevent(module_t mod,int type,void * unused)3863e5d0784SAlexander V. Chernikov ktest_modevent(module_t mod, int type, void *unused)
3873e5d0784SAlexander V. Chernikov {
3883e5d0784SAlexander V. Chernikov 	int error = 0;
3893e5d0784SAlexander V. Chernikov 
3903e5d0784SAlexander V. Chernikov 	switch (type) {
3913e5d0784SAlexander V. Chernikov 	case MOD_LOAD:
3923e5d0784SAlexander V. Chernikov 		ktest_nl_register();
3933e5d0784SAlexander V. Chernikov 		break;
3943e5d0784SAlexander V. Chernikov 	case MOD_UNLOAD:
3953e5d0784SAlexander V. Chernikov 		ktest_nl_unregister();
3963e5d0784SAlexander V. Chernikov 		break;
3973e5d0784SAlexander V. Chernikov 	default:
3983e5d0784SAlexander V. Chernikov 		error = EOPNOTSUPP;
3993e5d0784SAlexander V. Chernikov 		break;
4003e5d0784SAlexander V. Chernikov 	}
4013e5d0784SAlexander V. Chernikov 	return (error);
4023e5d0784SAlexander V. Chernikov }
4033e5d0784SAlexander V. Chernikov 
4043e5d0784SAlexander V. Chernikov static moduledata_t ktestmod = {
4053e5d0784SAlexander V. Chernikov         "ktest",
4063e5d0784SAlexander V. Chernikov         ktest_modevent,
4073e5d0784SAlexander V. Chernikov         0
4083e5d0784SAlexander V. Chernikov };
4093e5d0784SAlexander V. Chernikov 
4103e5d0784SAlexander V. Chernikov DECLARE_MODULE(ktestmod, ktestmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
4113e5d0784SAlexander V. Chernikov MODULE_VERSION(ktestmod, 1);
4123e5d0784SAlexander V. Chernikov MODULE_DEPEND(ktestmod, netlink, 1, 1, 1);
4133e5d0784SAlexander V. Chernikov 
414