xref: /freebsd/sys/tests/ktest.c (revision f656a96020e83d1aa6d3b69f7885fc9e2cd684c9)
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 "opt_netlink.h"
293e5d0784SAlexander V. Chernikov 
303e5d0784SAlexander V. Chernikov #include <sys/param.h>
313e5d0784SAlexander V. Chernikov #include <sys/refcount.h>
323e5d0784SAlexander V. Chernikov #include <sys/types.h>
333e5d0784SAlexander V. Chernikov #include <sys/kernel.h>
343e5d0784SAlexander V. Chernikov #include <sys/lock.h>
35*f656a960SAlexander V. Chernikov #include <sys/mutex.h>
363e5d0784SAlexander V. Chernikov #include <sys/malloc.h>
373e5d0784SAlexander V. Chernikov #include <sys/module.h>
383e5d0784SAlexander V. Chernikov #include <sys/socket.h>
393e5d0784SAlexander V. Chernikov #include <sys/priv.h>
403e5d0784SAlexander V. Chernikov 
413e5d0784SAlexander V. Chernikov #include <netlink/netlink.h>
423e5d0784SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
433e5d0784SAlexander V. Chernikov #include <netlink/netlink_generic.h>
443e5d0784SAlexander V. Chernikov #include <netlink/netlink_message_parser.h>
453e5d0784SAlexander V. Chernikov 
463e5d0784SAlexander V. Chernikov #include <machine/stdarg.h>
473e5d0784SAlexander V. Chernikov #include <tests/ktest.h>
483e5d0784SAlexander V. Chernikov 
493e5d0784SAlexander V. Chernikov struct mtx ktest_mtx;
503e5d0784SAlexander V. Chernikov #define	KTEST_LOCK()		mtx_lock(&ktest_mtx)
513e5d0784SAlexander V. Chernikov #define	KTEST_UNLOCK()		mtx_unlock(&ktest_mtx)
523e5d0784SAlexander V. Chernikov #define	KTEST_LOCK_ASSERT()	mtx_assert(&ktest_mtx, MA_OWNED)
533e5d0784SAlexander V. Chernikov 
543e5d0784SAlexander V. Chernikov MTX_SYSINIT(ktest_mtx, &ktest_mtx, "ktest mutex", MTX_DEF);
553e5d0784SAlexander V. Chernikov 
563e5d0784SAlexander V. Chernikov struct ktest_module {
573e5d0784SAlexander V. Chernikov 	struct ktest_module_info	*info;
583e5d0784SAlexander V. Chernikov 	volatile u_int			refcount;
593e5d0784SAlexander V. Chernikov 	TAILQ_ENTRY(ktest_module)	entries;
603e5d0784SAlexander V. Chernikov };
613e5d0784SAlexander V. Chernikov static TAILQ_HEAD(, ktest_module) module_list = TAILQ_HEAD_INITIALIZER(module_list);
623e5d0784SAlexander V. Chernikov 
633e5d0784SAlexander V. Chernikov struct nl_ktest_parsed {
643e5d0784SAlexander V. Chernikov 	char		*mod_name;
653e5d0784SAlexander V. Chernikov 	char		*test_name;
663e5d0784SAlexander V. Chernikov 	struct nlattr	*test_meta;
673e5d0784SAlexander V. Chernikov };
683e5d0784SAlexander V. Chernikov 
693e5d0784SAlexander V. Chernikov #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
703e5d0784SAlexander V. Chernikov #define	_OUT(_field)	offsetof(struct nl_ktest_parsed, _field)
713e5d0784SAlexander V. Chernikov 
723e5d0784SAlexander V. Chernikov static const struct nlattr_parser nla_p_get[] = {
733e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_MOD_NAME, .off = _OUT(mod_name), .cb = nlattr_get_string },
743e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_TEST_NAME, .off = _OUT(test_name), .cb = nlattr_get_string },
753e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_TEST_META, .off = _OUT(test_meta), .cb = nlattr_get_nla },
763e5d0784SAlexander V. Chernikov };
773e5d0784SAlexander V. Chernikov static const struct nlfield_parser nlf_p_get[] = {
783e5d0784SAlexander V. Chernikov };
793e5d0784SAlexander V. Chernikov NL_DECLARE_PARSER(ktest_parser, struct genlmsghdr, nlf_p_get, nla_p_get);
803e5d0784SAlexander V. Chernikov #undef _IN
813e5d0784SAlexander V. Chernikov #undef _OUT
823e5d0784SAlexander V. Chernikov 
833e5d0784SAlexander V. Chernikov static bool
843e5d0784SAlexander V. Chernikov create_reply(struct nl_writer *nw, struct nlmsghdr *hdr, int cmd)
853e5d0784SAlexander V. Chernikov {
863e5d0784SAlexander V. Chernikov 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
873e5d0784SAlexander V. Chernikov 		return (false);
883e5d0784SAlexander V. Chernikov 
893e5d0784SAlexander V. Chernikov 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
903e5d0784SAlexander V. Chernikov 	ghdr_new->cmd = cmd;
913e5d0784SAlexander V. Chernikov 	ghdr_new->version = 0;
923e5d0784SAlexander V. Chernikov 	ghdr_new->reserved = 0;
933e5d0784SAlexander V. Chernikov 
943e5d0784SAlexander V. Chernikov 	return (true);
953e5d0784SAlexander V. Chernikov }
963e5d0784SAlexander V. Chernikov 
973e5d0784SAlexander V. Chernikov static int
983e5d0784SAlexander V. Chernikov dump_mod_test(struct nlmsghdr *hdr, struct nl_pstate *npt,
993e5d0784SAlexander V. Chernikov     struct ktest_module *mod, const struct ktest_test_info *test_info)
1003e5d0784SAlexander V. Chernikov {
1013e5d0784SAlexander V. Chernikov 	struct nl_writer *nw = npt->nw;
1023e5d0784SAlexander V. Chernikov 
1033e5d0784SAlexander V. Chernikov 	if (!create_reply(nw, hdr, KTEST_CMD_NEWTEST))
1043e5d0784SAlexander V. Chernikov 		goto enomem;
1053e5d0784SAlexander V. Chernikov 
1063e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_MOD_NAME, mod->info->name);
1073e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_TEST_NAME, test_info->name);
1083e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_TEST_DESCR, test_info->desc);
1093e5d0784SAlexander V. Chernikov 
1103e5d0784SAlexander V. Chernikov 	if (nlmsg_end(nw))
1113e5d0784SAlexander V. Chernikov 		return (0);
1123e5d0784SAlexander V. Chernikov enomem:
1133e5d0784SAlexander V. Chernikov 	nlmsg_abort(nw);
1143e5d0784SAlexander V. Chernikov 	return (ENOMEM);
1153e5d0784SAlexander V. Chernikov }
1163e5d0784SAlexander V. Chernikov 
1173e5d0784SAlexander V. Chernikov static int
1183e5d0784SAlexander V. Chernikov dump_mod_tests(struct nlmsghdr *hdr, struct nl_pstate *npt,
1193e5d0784SAlexander V. Chernikov     struct ktest_module *mod, struct nl_ktest_parsed *attrs)
1203e5d0784SAlexander V. Chernikov {
1213e5d0784SAlexander V. Chernikov 	for (int i = 0; i < mod->info->num_tests; i++) {
1223e5d0784SAlexander V. Chernikov 		const struct ktest_test_info *test_info = &mod->info->tests[i];
1233e5d0784SAlexander V. Chernikov 		if (attrs->test_name != NULL && strcmp(attrs->test_name, test_info->name))
1243e5d0784SAlexander V. Chernikov 			continue;
1253e5d0784SAlexander V. Chernikov 		int error = dump_mod_test(hdr, npt, mod, test_info);
1263e5d0784SAlexander V. Chernikov 		if (error != 0)
1273e5d0784SAlexander V. Chernikov 			return (error);
1283e5d0784SAlexander V. Chernikov 	}
1293e5d0784SAlexander V. Chernikov 
1303e5d0784SAlexander V. Chernikov 	return (0);
1313e5d0784SAlexander V. Chernikov }
1323e5d0784SAlexander V. Chernikov 
1333e5d0784SAlexander V. Chernikov static int
1343e5d0784SAlexander V. Chernikov dump_tests(struct nlmsghdr *hdr, struct nl_pstate *npt)
1353e5d0784SAlexander V. Chernikov {
1363e5d0784SAlexander V. Chernikov 	struct nl_ktest_parsed attrs = { };
1373e5d0784SAlexander V. Chernikov 	struct ktest_module *mod;
1383e5d0784SAlexander V. Chernikov 	int error;
1393e5d0784SAlexander V. Chernikov 
1403e5d0784SAlexander V. Chernikov 	error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
1413e5d0784SAlexander V. Chernikov 	if (error != 0)
1423e5d0784SAlexander V. Chernikov 		return (error);
1433e5d0784SAlexander V. Chernikov 
1443e5d0784SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_MULTI;
1453e5d0784SAlexander V. Chernikov 
1463e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
1473e5d0784SAlexander V. Chernikov 	TAILQ_FOREACH(mod, &module_list, entries) {
1483e5d0784SAlexander V. Chernikov 		if (attrs.mod_name && strcmp(attrs.mod_name, mod->info->name))
1493e5d0784SAlexander V. Chernikov 			continue;
1503e5d0784SAlexander V. Chernikov 		error = dump_mod_tests(hdr, npt, mod, &attrs);
1513e5d0784SAlexander V. Chernikov 		if (error != 0)
1523e5d0784SAlexander V. Chernikov 			break;
1533e5d0784SAlexander V. Chernikov 	}
1543e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
1553e5d0784SAlexander V. Chernikov 
1563e5d0784SAlexander V. Chernikov 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
1573e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
1583e5d0784SAlexander V. Chernikov 		return (ENOMEM);
1593e5d0784SAlexander V. Chernikov 	}
1603e5d0784SAlexander V. Chernikov 
1613e5d0784SAlexander V. Chernikov 	return (error);
1623e5d0784SAlexander V. Chernikov }
1633e5d0784SAlexander V. Chernikov 
1643e5d0784SAlexander V. Chernikov static int
1653e5d0784SAlexander V. Chernikov run_test(struct nlmsghdr *hdr, struct nl_pstate *npt)
1663e5d0784SAlexander V. Chernikov {
1673e5d0784SAlexander V. Chernikov 	struct nl_ktest_parsed attrs = { };
1683e5d0784SAlexander V. Chernikov 	struct ktest_module *mod;
1693e5d0784SAlexander V. Chernikov 	int error;
1703e5d0784SAlexander V. Chernikov 
1713e5d0784SAlexander V. Chernikov 	error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
1723e5d0784SAlexander V. Chernikov 	if (error != 0)
1733e5d0784SAlexander V. Chernikov 		return (error);
1743e5d0784SAlexander V. Chernikov 
1753e5d0784SAlexander V. Chernikov 	if (attrs.mod_name == NULL) {
1763e5d0784SAlexander V. Chernikov 		nlmsg_report_err_msg(npt, "KTEST_ATTR_MOD_NAME not set");
1773e5d0784SAlexander V. Chernikov 		return (EINVAL);
1783e5d0784SAlexander V. Chernikov 	}
1793e5d0784SAlexander V. Chernikov 
1803e5d0784SAlexander V. Chernikov 	if (attrs.test_name == NULL) {
1813e5d0784SAlexander V. Chernikov 		nlmsg_report_err_msg(npt, "KTEST_ATTR_TEST_NAME not set");
1823e5d0784SAlexander V. Chernikov 		return (EINVAL);
1833e5d0784SAlexander V. Chernikov 	}
1843e5d0784SAlexander V. Chernikov 
1853e5d0784SAlexander V. Chernikov 	const struct ktest_test_info *test = NULL;
1863e5d0784SAlexander V. Chernikov 
1873e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
1883e5d0784SAlexander V. Chernikov 	TAILQ_FOREACH(mod, &module_list, entries) {
1893e5d0784SAlexander V. Chernikov 		if (strcmp(attrs.mod_name, mod->info->name))
1903e5d0784SAlexander V. Chernikov 			continue;
1913e5d0784SAlexander V. Chernikov 
1923e5d0784SAlexander V. Chernikov 		const struct ktest_module_info *info = mod->info;
1933e5d0784SAlexander V. Chernikov 
1943e5d0784SAlexander V. Chernikov 		for (int i = 0; i < info->num_tests; i++) {
1953e5d0784SAlexander V. Chernikov 			const struct ktest_test_info *test_info = &info->tests[i];
1963e5d0784SAlexander V. Chernikov 
1973e5d0784SAlexander V. Chernikov 			if (!strcmp(attrs.test_name, test_info->name)) {
1983e5d0784SAlexander V. Chernikov 				test = test_info;
1993e5d0784SAlexander V. Chernikov 				break;
2003e5d0784SAlexander V. Chernikov 			}
2013e5d0784SAlexander V. Chernikov 		}
2023e5d0784SAlexander V. Chernikov 		break;
2033e5d0784SAlexander V. Chernikov 	}
2043e5d0784SAlexander V. Chernikov 	if (test != NULL)
2053e5d0784SAlexander V. Chernikov 		refcount_acquire(&mod->refcount);
2063e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
2073e5d0784SAlexander V. Chernikov 
2083e5d0784SAlexander V. Chernikov 	if (test == NULL)
2093e5d0784SAlexander V. Chernikov 		return (ESRCH);
2103e5d0784SAlexander V. Chernikov 
2113e5d0784SAlexander V. Chernikov 	/* Run the test */
2123e5d0784SAlexander V. Chernikov 	struct ktest_test_context ctx = {
2133e5d0784SAlexander V. Chernikov 		.npt = npt,
2143e5d0784SAlexander V. Chernikov 		.hdr = hdr,
2153e5d0784SAlexander V. Chernikov 		.buf = npt_alloc(npt, KTEST_MAX_BUF),
2163e5d0784SAlexander V. Chernikov 		.bufsize = KTEST_MAX_BUF,
2173e5d0784SAlexander V. Chernikov 	};
2183e5d0784SAlexander V. Chernikov 
2193e5d0784SAlexander V. Chernikov 	if (ctx.buf == NULL) {
2203e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "unable to allocate temporary buffer");
2213e5d0784SAlexander V. Chernikov 		return (ENOMEM);
2223e5d0784SAlexander V. Chernikov 	}
2233e5d0784SAlexander V. Chernikov 
2243e5d0784SAlexander V. Chernikov 	if (test->parse != NULL && attrs.test_meta != NULL) {
2253e5d0784SAlexander V. Chernikov 		error = test->parse(&ctx, attrs.test_meta);
2263e5d0784SAlexander V. Chernikov 		if (error != 0)
2273e5d0784SAlexander V. Chernikov 			return (error);
2283e5d0784SAlexander V. Chernikov 	}
2293e5d0784SAlexander V. Chernikov 
2303e5d0784SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_MULTI;
2313e5d0784SAlexander V. Chernikov 
2323e5d0784SAlexander V. Chernikov 	KTEST_LOG_LEVEL(&ctx, LOG_INFO, "start running %s", test->name);
2333e5d0784SAlexander V. Chernikov 	error = test->func(&ctx);
2343e5d0784SAlexander V. Chernikov 	KTEST_LOG_LEVEL(&ctx, LOG_INFO, "end running %s", test->name);
2353e5d0784SAlexander V. Chernikov 
2363e5d0784SAlexander V. Chernikov 	refcount_release(&mod->refcount);
2373e5d0784SAlexander V. Chernikov 
2383e5d0784SAlexander V. Chernikov 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
2393e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
2403e5d0784SAlexander V. Chernikov 		return (ENOMEM);
2413e5d0784SAlexander V. Chernikov 	}
2423e5d0784SAlexander V. Chernikov 
2433e5d0784SAlexander V. Chernikov 	return (error);
2443e5d0784SAlexander V. Chernikov }
2453e5d0784SAlexander V. Chernikov 
2463e5d0784SAlexander V. Chernikov 
2473e5d0784SAlexander V. Chernikov /* USER API */
2483e5d0784SAlexander V. Chernikov static void
2493e5d0784SAlexander V. Chernikov register_test_module(struct ktest_module_info *info)
2503e5d0784SAlexander V. Chernikov {
2513e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = malloc(sizeof(*mod), M_TEMP, M_WAITOK | M_ZERO);
2523e5d0784SAlexander V. Chernikov 
2533e5d0784SAlexander V. Chernikov 	mod->info = info;
2543e5d0784SAlexander V. Chernikov 	info->module_ptr = mod;
2553e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
2563e5d0784SAlexander V. Chernikov 	TAILQ_INSERT_TAIL(&module_list, mod, entries);
2573e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
2583e5d0784SAlexander V. Chernikov }
2593e5d0784SAlexander V. Chernikov 
2603e5d0784SAlexander V. Chernikov static void
2613e5d0784SAlexander V. Chernikov unregister_test_module(struct ktest_module_info *info)
2623e5d0784SAlexander V. Chernikov {
2633e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = info->module_ptr;
2643e5d0784SAlexander V. Chernikov 
2653e5d0784SAlexander V. Chernikov 	info->module_ptr = NULL;
2663e5d0784SAlexander V. Chernikov 
2673e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
2683e5d0784SAlexander V. Chernikov 	TAILQ_REMOVE(&module_list, mod, entries);
2693e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
2703e5d0784SAlexander V. Chernikov 
2713e5d0784SAlexander V. Chernikov 	free(mod, M_TEMP);
2723e5d0784SAlexander V. Chernikov }
2733e5d0784SAlexander V. Chernikov 
2743e5d0784SAlexander V. Chernikov static bool
2753e5d0784SAlexander V. Chernikov can_unregister(struct ktest_module_info *info)
2763e5d0784SAlexander V. Chernikov {
2773e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = info->module_ptr;
2783e5d0784SAlexander V. Chernikov 
2793e5d0784SAlexander V. Chernikov 	return (refcount_load(&mod->refcount) == 0);
2803e5d0784SAlexander V. Chernikov }
2813e5d0784SAlexander V. Chernikov 
2823e5d0784SAlexander V. Chernikov int
2833e5d0784SAlexander V. Chernikov ktest_default_modevent(module_t mod, int type, void *arg)
2843e5d0784SAlexander V. Chernikov {
2853e5d0784SAlexander V. Chernikov 	struct ktest_module_info *info = (struct ktest_module_info *)arg;
2863e5d0784SAlexander V. Chernikov 	int error = 0;
2873e5d0784SAlexander V. Chernikov 
2883e5d0784SAlexander V. Chernikov 	switch (type) {
2893e5d0784SAlexander V. Chernikov 	case MOD_LOAD:
2903e5d0784SAlexander V. Chernikov 		register_test_module(info);
2913e5d0784SAlexander V. Chernikov 		break;
2923e5d0784SAlexander V. Chernikov 	case MOD_UNLOAD:
2933e5d0784SAlexander V. Chernikov 		if (!can_unregister(info))
2943e5d0784SAlexander V. Chernikov 			return (EBUSY);
2953e5d0784SAlexander V. Chernikov 		unregister_test_module(info);
2963e5d0784SAlexander V. Chernikov 		break;
2973e5d0784SAlexander V. Chernikov 	default:
2983e5d0784SAlexander V. Chernikov 		error = EOPNOTSUPP;
2993e5d0784SAlexander V. Chernikov 		break;
3003e5d0784SAlexander V. Chernikov 	}
3013e5d0784SAlexander V. Chernikov 	return (error);
3023e5d0784SAlexander V. Chernikov }
3033e5d0784SAlexander V. Chernikov 
3043e5d0784SAlexander V. Chernikov bool
3053e5d0784SAlexander V. Chernikov ktest_start_msg(struct ktest_test_context *ctx)
3063e5d0784SAlexander V. Chernikov {
3073e5d0784SAlexander V. Chernikov 	return (create_reply(ctx->npt->nw, ctx->hdr, KTEST_CMD_NEWMESSAGE));
3083e5d0784SAlexander V. Chernikov }
3093e5d0784SAlexander V. Chernikov 
3103e5d0784SAlexander V. Chernikov void
3113e5d0784SAlexander V. Chernikov ktest_add_msg_meta(struct ktest_test_context *ctx, const char *func,
3123e5d0784SAlexander V. Chernikov     const char *fname, int line)
3133e5d0784SAlexander V. Chernikov {
3143e5d0784SAlexander V. Chernikov 	struct nl_writer *nw = ctx->npt->nw;
3153e5d0784SAlexander V. Chernikov 	struct timespec ts;
3163e5d0784SAlexander V. Chernikov 
3173e5d0784SAlexander V. Chernikov 	nanouptime(&ts);
3183e5d0784SAlexander V. Chernikov 	nlattr_add(nw, KTEST_MSG_ATTR_TS, sizeof(ts), &ts);
3193e5d0784SAlexander V. Chernikov 
3203e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_MSG_ATTR_FUNC, func);
3213e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_MSG_ATTR_FILE, fname);
3223e5d0784SAlexander V. Chernikov 	nlattr_add_u32(nw, KTEST_MSG_ATTR_LINE, line);
3233e5d0784SAlexander V. Chernikov }
3243e5d0784SAlexander V. Chernikov 
3253e5d0784SAlexander V. Chernikov void
3263e5d0784SAlexander V. Chernikov ktest_add_msg_text(struct ktest_test_context *ctx, int msg_level,
3273e5d0784SAlexander V. Chernikov     const char *fmt, ...)
3283e5d0784SAlexander V. Chernikov {
3293e5d0784SAlexander V. Chernikov 	va_list ap;
3303e5d0784SAlexander V. Chernikov 
3313e5d0784SAlexander V. Chernikov 	va_start(ap, fmt);
3323e5d0784SAlexander V. Chernikov 	vsnprintf(ctx->buf, ctx->bufsize, fmt, ap);
3333e5d0784SAlexander V. Chernikov 	va_end(ap);
3343e5d0784SAlexander V. Chernikov 
3353e5d0784SAlexander V. Chernikov 	nlattr_add_u8(ctx->npt->nw, KTEST_MSG_ATTR_LEVEL, msg_level);
3363e5d0784SAlexander V. Chernikov 	nlattr_add_string(ctx->npt->nw, KTEST_MSG_ATTR_TEXT, ctx->buf);
3373e5d0784SAlexander V. Chernikov }
3383e5d0784SAlexander V. Chernikov 
3393e5d0784SAlexander V. Chernikov void
3403e5d0784SAlexander V. Chernikov ktest_end_msg(struct ktest_test_context *ctx)
3413e5d0784SAlexander V. Chernikov {
3423e5d0784SAlexander V. Chernikov 	nlmsg_end(ctx->npt->nw);
3433e5d0784SAlexander V. Chernikov }
3443e5d0784SAlexander V. Chernikov 
3453e5d0784SAlexander V. Chernikov /* Module glue */
3463e5d0784SAlexander V. Chernikov 
3473e5d0784SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &ktest_parser };
3483e5d0784SAlexander V. Chernikov 
3493e5d0784SAlexander V. Chernikov static const struct genl_cmd ktest_cmds[] = {
3503e5d0784SAlexander V. Chernikov 	{
3513e5d0784SAlexander V. Chernikov 		.cmd_num = KTEST_CMD_LIST,
3523e5d0784SAlexander V. Chernikov 		.cmd_name = "KTEST_CMD_LIST",
3533e5d0784SAlexander V. Chernikov 		.cmd_cb = dump_tests,
3543e5d0784SAlexander V. Chernikov 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
3553e5d0784SAlexander V. Chernikov 	},
3563e5d0784SAlexander V. Chernikov 	{
3573e5d0784SAlexander V. Chernikov 		.cmd_num = KTEST_CMD_RUN,
3583e5d0784SAlexander V. Chernikov 		.cmd_name = "KTEST_CMD_RUN",
3593e5d0784SAlexander V. Chernikov 		.cmd_cb = run_test,
3603e5d0784SAlexander V. Chernikov 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
3613e5d0784SAlexander V. Chernikov 		.cmd_priv = PRIV_KLD_LOAD,
3623e5d0784SAlexander V. Chernikov 	},
3633e5d0784SAlexander V. Chernikov };
3643e5d0784SAlexander V. Chernikov 
3653e5d0784SAlexander V. Chernikov static void
3663e5d0784SAlexander V. Chernikov ktest_nl_register(void)
3673e5d0784SAlexander V. Chernikov {
3683e5d0784SAlexander V. Chernikov 	bool ret __diagused;
3693e5d0784SAlexander V. Chernikov 	int family_id __diagused;
3703e5d0784SAlexander V. Chernikov 
3713e5d0784SAlexander V. Chernikov 	NL_VERIFY_PARSERS(all_parsers);
3723e5d0784SAlexander V. Chernikov 	family_id = genl_register_family(KTEST_FAMILY_NAME, 0, 1, KTEST_CMD_MAX);
3733e5d0784SAlexander V. Chernikov 	MPASS(family_id != 0);
3743e5d0784SAlexander V. Chernikov 
3753e5d0784SAlexander V. Chernikov 	ret = genl_register_cmds(KTEST_FAMILY_NAME, ktest_cmds, NL_ARRAY_LEN(ktest_cmds));
3763e5d0784SAlexander V. Chernikov 	MPASS(ret);
3773e5d0784SAlexander V. Chernikov }
3783e5d0784SAlexander V. Chernikov 
3793e5d0784SAlexander V. Chernikov static void
3803e5d0784SAlexander V. Chernikov ktest_nl_unregister(void)
3813e5d0784SAlexander V. Chernikov {
3823e5d0784SAlexander V. Chernikov 	MPASS(TAILQ_EMPTY(&module_list));
3833e5d0784SAlexander V. Chernikov 
3843e5d0784SAlexander V. Chernikov 	genl_unregister_family(KTEST_FAMILY_NAME);
3853e5d0784SAlexander V. Chernikov }
3863e5d0784SAlexander V. Chernikov 
3873e5d0784SAlexander V. Chernikov static int
3883e5d0784SAlexander V. Chernikov ktest_modevent(module_t mod, int type, void *unused)
3893e5d0784SAlexander V. Chernikov {
3903e5d0784SAlexander V. Chernikov 	int error = 0;
3913e5d0784SAlexander V. Chernikov 
3923e5d0784SAlexander V. Chernikov 	switch (type) {
3933e5d0784SAlexander V. Chernikov 	case MOD_LOAD:
3943e5d0784SAlexander V. Chernikov 		ktest_nl_register();
3953e5d0784SAlexander V. Chernikov 		break;
3963e5d0784SAlexander V. Chernikov 	case MOD_UNLOAD:
3973e5d0784SAlexander V. Chernikov 		ktest_nl_unregister();
3983e5d0784SAlexander V. Chernikov 		break;
3993e5d0784SAlexander V. Chernikov 	default:
4003e5d0784SAlexander V. Chernikov 		error = EOPNOTSUPP;
4013e5d0784SAlexander V. Chernikov 		break;
4023e5d0784SAlexander V. Chernikov 	}
4033e5d0784SAlexander V. Chernikov 	return (error);
4043e5d0784SAlexander V. Chernikov }
4053e5d0784SAlexander V. Chernikov 
4063e5d0784SAlexander V. Chernikov static moduledata_t ktestmod = {
4073e5d0784SAlexander V. Chernikov         "ktest",
4083e5d0784SAlexander V. Chernikov         ktest_modevent,
4093e5d0784SAlexander V. Chernikov         0
4103e5d0784SAlexander V. Chernikov };
4113e5d0784SAlexander V. Chernikov 
4123e5d0784SAlexander V. Chernikov DECLARE_MODULE(ktestmod, ktestmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
4133e5d0784SAlexander V. Chernikov MODULE_VERSION(ktestmod, 1);
4143e5d0784SAlexander V. Chernikov MODULE_DEPEND(ktestmod, netlink, 1, 1, 1);
4153e5d0784SAlexander V. Chernikov 
416