xref: /freebsd/sys/tests/ktest.c (revision 3e5d0784b9b5296bda801add034b057ad68237f7)
1*3e5d0784SAlexander V. Chernikov /*-
2*3e5d0784SAlexander V. Chernikov  * SPDX-License-Identifier: BSD-2-Clause
3*3e5d0784SAlexander V. Chernikov  *
4*3e5d0784SAlexander V. Chernikov  * Copyright (c) 2023 Alexander V. Chernikov
5*3e5d0784SAlexander V. Chernikov  *
6*3e5d0784SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
7*3e5d0784SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
8*3e5d0784SAlexander V. Chernikov  * are met:
9*3e5d0784SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
10*3e5d0784SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
11*3e5d0784SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
12*3e5d0784SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
13*3e5d0784SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
14*3e5d0784SAlexander V. Chernikov  *
15*3e5d0784SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*3e5d0784SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*3e5d0784SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*3e5d0784SAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*3e5d0784SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*3e5d0784SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*3e5d0784SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*3e5d0784SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*3e5d0784SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*3e5d0784SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*3e5d0784SAlexander V. Chernikov  * SUCH DAMAGE.
26*3e5d0784SAlexander V. Chernikov  */
27*3e5d0784SAlexander V. Chernikov 
28*3e5d0784SAlexander V. Chernikov #include "opt_netlink.h"
29*3e5d0784SAlexander V. Chernikov 
30*3e5d0784SAlexander V. Chernikov #include <sys/param.h>
31*3e5d0784SAlexander V. Chernikov #include <sys/refcount.h>
32*3e5d0784SAlexander V. Chernikov #include <sys/types.h>
33*3e5d0784SAlexander V. Chernikov #include <sys/kernel.h>
34*3e5d0784SAlexander V. Chernikov #include <sys/lock.h>
35*3e5d0784SAlexander V. Chernikov #include <sys/malloc.h>
36*3e5d0784SAlexander V. Chernikov #include <sys/module.h>
37*3e5d0784SAlexander V. Chernikov #include <sys/socket.h>
38*3e5d0784SAlexander V. Chernikov #include <sys/priv.h>
39*3e5d0784SAlexander V. Chernikov 
40*3e5d0784SAlexander V. Chernikov #include <netlink/netlink.h>
41*3e5d0784SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
42*3e5d0784SAlexander V. Chernikov #include <netlink/netlink_generic.h>
43*3e5d0784SAlexander V. Chernikov #include <netlink/netlink_message_parser.h>
44*3e5d0784SAlexander V. Chernikov 
45*3e5d0784SAlexander V. Chernikov #include <machine/stdarg.h>
46*3e5d0784SAlexander V. Chernikov #include <tests/ktest.h>
47*3e5d0784SAlexander V. Chernikov 
48*3e5d0784SAlexander V. Chernikov struct mtx ktest_mtx;
49*3e5d0784SAlexander V. Chernikov #define	KTEST_LOCK()		mtx_lock(&ktest_mtx)
50*3e5d0784SAlexander V. Chernikov #define	KTEST_UNLOCK()		mtx_unlock(&ktest_mtx)
51*3e5d0784SAlexander V. Chernikov #define	KTEST_LOCK_ASSERT()	mtx_assert(&ktest_mtx, MA_OWNED)
52*3e5d0784SAlexander V. Chernikov 
53*3e5d0784SAlexander V. Chernikov MTX_SYSINIT(ktest_mtx, &ktest_mtx, "ktest mutex", MTX_DEF);
54*3e5d0784SAlexander V. Chernikov 
55*3e5d0784SAlexander V. Chernikov struct ktest_module {
56*3e5d0784SAlexander V. Chernikov 	struct ktest_module_info	*info;
57*3e5d0784SAlexander V. Chernikov 	volatile u_int			refcount;
58*3e5d0784SAlexander V. Chernikov 	TAILQ_ENTRY(ktest_module)	entries;
59*3e5d0784SAlexander V. Chernikov };
60*3e5d0784SAlexander V. Chernikov static TAILQ_HEAD(, ktest_module) module_list = TAILQ_HEAD_INITIALIZER(module_list);
61*3e5d0784SAlexander V. Chernikov 
62*3e5d0784SAlexander V. Chernikov struct nl_ktest_parsed {
63*3e5d0784SAlexander V. Chernikov 	char		*mod_name;
64*3e5d0784SAlexander V. Chernikov 	char		*test_name;
65*3e5d0784SAlexander V. Chernikov 	struct nlattr	*test_meta;
66*3e5d0784SAlexander V. Chernikov };
67*3e5d0784SAlexander V. Chernikov 
68*3e5d0784SAlexander V. Chernikov #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
69*3e5d0784SAlexander V. Chernikov #define	_OUT(_field)	offsetof(struct nl_ktest_parsed, _field)
70*3e5d0784SAlexander V. Chernikov 
71*3e5d0784SAlexander V. Chernikov static const struct nlattr_parser nla_p_get[] = {
72*3e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_MOD_NAME, .off = _OUT(mod_name), .cb = nlattr_get_string },
73*3e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_TEST_NAME, .off = _OUT(test_name), .cb = nlattr_get_string },
74*3e5d0784SAlexander V. Chernikov 	{ .type = KTEST_ATTR_TEST_META, .off = _OUT(test_meta), .cb = nlattr_get_nla },
75*3e5d0784SAlexander V. Chernikov };
76*3e5d0784SAlexander V. Chernikov static const struct nlfield_parser nlf_p_get[] = {
77*3e5d0784SAlexander V. Chernikov };
78*3e5d0784SAlexander V. Chernikov NL_DECLARE_PARSER(ktest_parser, struct genlmsghdr, nlf_p_get, nla_p_get);
79*3e5d0784SAlexander V. Chernikov #undef _IN
80*3e5d0784SAlexander V. Chernikov #undef _OUT
81*3e5d0784SAlexander V. Chernikov 
82*3e5d0784SAlexander V. Chernikov static bool
83*3e5d0784SAlexander V. Chernikov create_reply(struct nl_writer *nw, struct nlmsghdr *hdr, int cmd)
84*3e5d0784SAlexander V. Chernikov {
85*3e5d0784SAlexander V. Chernikov 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
86*3e5d0784SAlexander V. Chernikov 		return (false);
87*3e5d0784SAlexander V. Chernikov 
88*3e5d0784SAlexander V. Chernikov 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
89*3e5d0784SAlexander V. Chernikov 	ghdr_new->cmd = cmd;
90*3e5d0784SAlexander V. Chernikov 	ghdr_new->version = 0;
91*3e5d0784SAlexander V. Chernikov 	ghdr_new->reserved = 0;
92*3e5d0784SAlexander V. Chernikov 
93*3e5d0784SAlexander V. Chernikov 	return (true);
94*3e5d0784SAlexander V. Chernikov }
95*3e5d0784SAlexander V. Chernikov 
96*3e5d0784SAlexander V. Chernikov static int
97*3e5d0784SAlexander V. Chernikov dump_mod_test(struct nlmsghdr *hdr, struct nl_pstate *npt,
98*3e5d0784SAlexander V. Chernikov     struct ktest_module *mod, const struct ktest_test_info *test_info)
99*3e5d0784SAlexander V. Chernikov {
100*3e5d0784SAlexander V. Chernikov 	struct nl_writer *nw = npt->nw;
101*3e5d0784SAlexander V. Chernikov 
102*3e5d0784SAlexander V. Chernikov 	if (!create_reply(nw, hdr, KTEST_CMD_NEWTEST))
103*3e5d0784SAlexander V. Chernikov 		goto enomem;
104*3e5d0784SAlexander V. Chernikov 
105*3e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_MOD_NAME, mod->info->name);
106*3e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_TEST_NAME, test_info->name);
107*3e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_ATTR_TEST_DESCR, test_info->desc);
108*3e5d0784SAlexander V. Chernikov 
109*3e5d0784SAlexander V. Chernikov 	if (nlmsg_end(nw))
110*3e5d0784SAlexander V. Chernikov 		return (0);
111*3e5d0784SAlexander V. Chernikov enomem:
112*3e5d0784SAlexander V. Chernikov 	nlmsg_abort(nw);
113*3e5d0784SAlexander V. Chernikov 	return (ENOMEM);
114*3e5d0784SAlexander V. Chernikov }
115*3e5d0784SAlexander V. Chernikov 
116*3e5d0784SAlexander V. Chernikov static int
117*3e5d0784SAlexander V. Chernikov dump_mod_tests(struct nlmsghdr *hdr, struct nl_pstate *npt,
118*3e5d0784SAlexander V. Chernikov     struct ktest_module *mod, struct nl_ktest_parsed *attrs)
119*3e5d0784SAlexander V. Chernikov {
120*3e5d0784SAlexander V. Chernikov 	for (int i = 0; i < mod->info->num_tests; i++) {
121*3e5d0784SAlexander V. Chernikov 		const struct ktest_test_info *test_info = &mod->info->tests[i];
122*3e5d0784SAlexander V. Chernikov 		if (attrs->test_name != NULL && strcmp(attrs->test_name, test_info->name))
123*3e5d0784SAlexander V. Chernikov 			continue;
124*3e5d0784SAlexander V. Chernikov 		int error = dump_mod_test(hdr, npt, mod, test_info);
125*3e5d0784SAlexander V. Chernikov 		if (error != 0)
126*3e5d0784SAlexander V. Chernikov 			return (error);
127*3e5d0784SAlexander V. Chernikov 	}
128*3e5d0784SAlexander V. Chernikov 
129*3e5d0784SAlexander V. Chernikov 	return (0);
130*3e5d0784SAlexander V. Chernikov }
131*3e5d0784SAlexander V. Chernikov 
132*3e5d0784SAlexander V. Chernikov static int
133*3e5d0784SAlexander V. Chernikov dump_tests(struct nlmsghdr *hdr, struct nl_pstate *npt)
134*3e5d0784SAlexander V. Chernikov {
135*3e5d0784SAlexander V. Chernikov 	struct nl_ktest_parsed attrs = { };
136*3e5d0784SAlexander V. Chernikov 	struct ktest_module *mod;
137*3e5d0784SAlexander V. Chernikov 	int error;
138*3e5d0784SAlexander V. Chernikov 
139*3e5d0784SAlexander V. Chernikov 	error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
140*3e5d0784SAlexander V. Chernikov 	if (error != 0)
141*3e5d0784SAlexander V. Chernikov 		return (error);
142*3e5d0784SAlexander V. Chernikov 
143*3e5d0784SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_MULTI;
144*3e5d0784SAlexander V. Chernikov 
145*3e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
146*3e5d0784SAlexander V. Chernikov 	TAILQ_FOREACH(mod, &module_list, entries) {
147*3e5d0784SAlexander V. Chernikov 		if (attrs.mod_name && strcmp(attrs.mod_name, mod->info->name))
148*3e5d0784SAlexander V. Chernikov 			continue;
149*3e5d0784SAlexander V. Chernikov 		error = dump_mod_tests(hdr, npt, mod, &attrs);
150*3e5d0784SAlexander V. Chernikov 		if (error != 0)
151*3e5d0784SAlexander V. Chernikov 			break;
152*3e5d0784SAlexander V. Chernikov 	}
153*3e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
154*3e5d0784SAlexander V. Chernikov 
155*3e5d0784SAlexander V. Chernikov 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
156*3e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
157*3e5d0784SAlexander V. Chernikov 		return (ENOMEM);
158*3e5d0784SAlexander V. Chernikov 	}
159*3e5d0784SAlexander V. Chernikov 
160*3e5d0784SAlexander V. Chernikov 	return (error);
161*3e5d0784SAlexander V. Chernikov }
162*3e5d0784SAlexander V. Chernikov 
163*3e5d0784SAlexander V. Chernikov static int
164*3e5d0784SAlexander V. Chernikov run_test(struct nlmsghdr *hdr, struct nl_pstate *npt)
165*3e5d0784SAlexander V. Chernikov {
166*3e5d0784SAlexander V. Chernikov 	struct nl_ktest_parsed attrs = { };
167*3e5d0784SAlexander V. Chernikov 	struct ktest_module *mod;
168*3e5d0784SAlexander V. Chernikov 	int error;
169*3e5d0784SAlexander V. Chernikov 
170*3e5d0784SAlexander V. Chernikov 	error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
171*3e5d0784SAlexander V. Chernikov 	if (error != 0)
172*3e5d0784SAlexander V. Chernikov 		return (error);
173*3e5d0784SAlexander V. Chernikov 
174*3e5d0784SAlexander V. Chernikov 	if (attrs.mod_name == NULL) {
175*3e5d0784SAlexander V. Chernikov 		nlmsg_report_err_msg(npt, "KTEST_ATTR_MOD_NAME not set");
176*3e5d0784SAlexander V. Chernikov 		return (EINVAL);
177*3e5d0784SAlexander V. Chernikov 	}
178*3e5d0784SAlexander V. Chernikov 
179*3e5d0784SAlexander V. Chernikov 	if (attrs.test_name == NULL) {
180*3e5d0784SAlexander V. Chernikov 		nlmsg_report_err_msg(npt, "KTEST_ATTR_TEST_NAME not set");
181*3e5d0784SAlexander V. Chernikov 		return (EINVAL);
182*3e5d0784SAlexander V. Chernikov 	}
183*3e5d0784SAlexander V. Chernikov 
184*3e5d0784SAlexander V. Chernikov 	const struct ktest_test_info *test = NULL;
185*3e5d0784SAlexander V. Chernikov 
186*3e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
187*3e5d0784SAlexander V. Chernikov 	TAILQ_FOREACH(mod, &module_list, entries) {
188*3e5d0784SAlexander V. Chernikov 		if (strcmp(attrs.mod_name, mod->info->name))
189*3e5d0784SAlexander V. Chernikov 			continue;
190*3e5d0784SAlexander V. Chernikov 
191*3e5d0784SAlexander V. Chernikov 		const struct ktest_module_info *info = mod->info;
192*3e5d0784SAlexander V. Chernikov 
193*3e5d0784SAlexander V. Chernikov 		for (int i = 0; i < info->num_tests; i++) {
194*3e5d0784SAlexander V. Chernikov 			const struct ktest_test_info *test_info = &info->tests[i];
195*3e5d0784SAlexander V. Chernikov 
196*3e5d0784SAlexander V. Chernikov 			if (!strcmp(attrs.test_name, test_info->name)) {
197*3e5d0784SAlexander V. Chernikov 				test = test_info;
198*3e5d0784SAlexander V. Chernikov 				break;
199*3e5d0784SAlexander V. Chernikov 			}
200*3e5d0784SAlexander V. Chernikov 		}
201*3e5d0784SAlexander V. Chernikov 		break;
202*3e5d0784SAlexander V. Chernikov 	}
203*3e5d0784SAlexander V. Chernikov 	if (test != NULL)
204*3e5d0784SAlexander V. Chernikov 		refcount_acquire(&mod->refcount);
205*3e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
206*3e5d0784SAlexander V. Chernikov 
207*3e5d0784SAlexander V. Chernikov 	if (test == NULL)
208*3e5d0784SAlexander V. Chernikov 		return (ESRCH);
209*3e5d0784SAlexander V. Chernikov 
210*3e5d0784SAlexander V. Chernikov 	/* Run the test */
211*3e5d0784SAlexander V. Chernikov 	struct ktest_test_context ctx = {
212*3e5d0784SAlexander V. Chernikov 		.npt = npt,
213*3e5d0784SAlexander V. Chernikov 		.hdr = hdr,
214*3e5d0784SAlexander V. Chernikov 		.buf = npt_alloc(npt, KTEST_MAX_BUF),
215*3e5d0784SAlexander V. Chernikov 		.bufsize = KTEST_MAX_BUF,
216*3e5d0784SAlexander V. Chernikov 	};
217*3e5d0784SAlexander V. Chernikov 
218*3e5d0784SAlexander V. Chernikov 	if (ctx.buf == NULL) {
219*3e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "unable to allocate temporary buffer");
220*3e5d0784SAlexander V. Chernikov 		return (ENOMEM);
221*3e5d0784SAlexander V. Chernikov 	}
222*3e5d0784SAlexander V. Chernikov 
223*3e5d0784SAlexander V. Chernikov 	if (test->parse != NULL && attrs.test_meta != NULL) {
224*3e5d0784SAlexander V. Chernikov 		error = test->parse(&ctx, attrs.test_meta);
225*3e5d0784SAlexander V. Chernikov 		if (error != 0)
226*3e5d0784SAlexander V. Chernikov 			return (error);
227*3e5d0784SAlexander V. Chernikov 	}
228*3e5d0784SAlexander V. Chernikov 
229*3e5d0784SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_MULTI;
230*3e5d0784SAlexander V. Chernikov 
231*3e5d0784SAlexander V. Chernikov 	KTEST_LOG_LEVEL(&ctx, LOG_INFO, "start running %s", test->name);
232*3e5d0784SAlexander V. Chernikov 	error = test->func(&ctx);
233*3e5d0784SAlexander V. Chernikov 	KTEST_LOG_LEVEL(&ctx, LOG_INFO, "end running %s", test->name);
234*3e5d0784SAlexander V. Chernikov 
235*3e5d0784SAlexander V. Chernikov 	refcount_release(&mod->refcount);
236*3e5d0784SAlexander V. Chernikov 
237*3e5d0784SAlexander V. Chernikov 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
238*3e5d0784SAlexander V. Chernikov 		//NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
239*3e5d0784SAlexander V. Chernikov 		return (ENOMEM);
240*3e5d0784SAlexander V. Chernikov 	}
241*3e5d0784SAlexander V. Chernikov 
242*3e5d0784SAlexander V. Chernikov 	return (error);
243*3e5d0784SAlexander V. Chernikov }
244*3e5d0784SAlexander V. Chernikov 
245*3e5d0784SAlexander V. Chernikov 
246*3e5d0784SAlexander V. Chernikov /* USER API */
247*3e5d0784SAlexander V. Chernikov static void
248*3e5d0784SAlexander V. Chernikov register_test_module(struct ktest_module_info *info)
249*3e5d0784SAlexander V. Chernikov {
250*3e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = malloc(sizeof(*mod), M_TEMP, M_WAITOK | M_ZERO);
251*3e5d0784SAlexander V. Chernikov 
252*3e5d0784SAlexander V. Chernikov 	mod->info = info;
253*3e5d0784SAlexander V. Chernikov 	info->module_ptr = mod;
254*3e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
255*3e5d0784SAlexander V. Chernikov 	TAILQ_INSERT_TAIL(&module_list, mod, entries);
256*3e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
257*3e5d0784SAlexander V. Chernikov }
258*3e5d0784SAlexander V. Chernikov 
259*3e5d0784SAlexander V. Chernikov static void
260*3e5d0784SAlexander V. Chernikov unregister_test_module(struct ktest_module_info *info)
261*3e5d0784SAlexander V. Chernikov {
262*3e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = info->module_ptr;
263*3e5d0784SAlexander V. Chernikov 
264*3e5d0784SAlexander V. Chernikov 	info->module_ptr = NULL;
265*3e5d0784SAlexander V. Chernikov 
266*3e5d0784SAlexander V. Chernikov 	KTEST_LOCK();
267*3e5d0784SAlexander V. Chernikov 	TAILQ_REMOVE(&module_list, mod, entries);
268*3e5d0784SAlexander V. Chernikov 	KTEST_UNLOCK();
269*3e5d0784SAlexander V. Chernikov 
270*3e5d0784SAlexander V. Chernikov 	free(mod, M_TEMP);
271*3e5d0784SAlexander V. Chernikov }
272*3e5d0784SAlexander V. Chernikov 
273*3e5d0784SAlexander V. Chernikov static bool
274*3e5d0784SAlexander V. Chernikov can_unregister(struct ktest_module_info *info)
275*3e5d0784SAlexander V. Chernikov {
276*3e5d0784SAlexander V. Chernikov 	struct ktest_module *mod = info->module_ptr;
277*3e5d0784SAlexander V. Chernikov 
278*3e5d0784SAlexander V. Chernikov 	return (refcount_load(&mod->refcount) == 0);
279*3e5d0784SAlexander V. Chernikov }
280*3e5d0784SAlexander V. Chernikov 
281*3e5d0784SAlexander V. Chernikov int
282*3e5d0784SAlexander V. Chernikov ktest_default_modevent(module_t mod, int type, void *arg)
283*3e5d0784SAlexander V. Chernikov {
284*3e5d0784SAlexander V. Chernikov 	struct ktest_module_info *info = (struct ktest_module_info *)arg;
285*3e5d0784SAlexander V. Chernikov 	int error = 0;
286*3e5d0784SAlexander V. Chernikov 
287*3e5d0784SAlexander V. Chernikov 	switch (type) {
288*3e5d0784SAlexander V. Chernikov 	case MOD_LOAD:
289*3e5d0784SAlexander V. Chernikov 		register_test_module(info);
290*3e5d0784SAlexander V. Chernikov 		break;
291*3e5d0784SAlexander V. Chernikov 	case MOD_UNLOAD:
292*3e5d0784SAlexander V. Chernikov 		if (!can_unregister(info))
293*3e5d0784SAlexander V. Chernikov 			return (EBUSY);
294*3e5d0784SAlexander V. Chernikov 		unregister_test_module(info);
295*3e5d0784SAlexander V. Chernikov 		break;
296*3e5d0784SAlexander V. Chernikov 	default:
297*3e5d0784SAlexander V. Chernikov 		error = EOPNOTSUPP;
298*3e5d0784SAlexander V. Chernikov 		break;
299*3e5d0784SAlexander V. Chernikov 	}
300*3e5d0784SAlexander V. Chernikov 	return (error);
301*3e5d0784SAlexander V. Chernikov }
302*3e5d0784SAlexander V. Chernikov 
303*3e5d0784SAlexander V. Chernikov bool
304*3e5d0784SAlexander V. Chernikov ktest_start_msg(struct ktest_test_context *ctx)
305*3e5d0784SAlexander V. Chernikov {
306*3e5d0784SAlexander V. Chernikov 	return (create_reply(ctx->npt->nw, ctx->hdr, KTEST_CMD_NEWMESSAGE));
307*3e5d0784SAlexander V. Chernikov }
308*3e5d0784SAlexander V. Chernikov 
309*3e5d0784SAlexander V. Chernikov void
310*3e5d0784SAlexander V. Chernikov ktest_add_msg_meta(struct ktest_test_context *ctx, const char *func,
311*3e5d0784SAlexander V. Chernikov     const char *fname, int line)
312*3e5d0784SAlexander V. Chernikov {
313*3e5d0784SAlexander V. Chernikov 	struct nl_writer *nw = ctx->npt->nw;
314*3e5d0784SAlexander V. Chernikov 	struct timespec ts;
315*3e5d0784SAlexander V. Chernikov 
316*3e5d0784SAlexander V. Chernikov 	nanouptime(&ts);
317*3e5d0784SAlexander V. Chernikov 	nlattr_add(nw, KTEST_MSG_ATTR_TS, sizeof(ts), &ts);
318*3e5d0784SAlexander V. Chernikov 
319*3e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_MSG_ATTR_FUNC, func);
320*3e5d0784SAlexander V. Chernikov 	nlattr_add_string(nw, KTEST_MSG_ATTR_FILE, fname);
321*3e5d0784SAlexander V. Chernikov 	nlattr_add_u32(nw, KTEST_MSG_ATTR_LINE, line);
322*3e5d0784SAlexander V. Chernikov }
323*3e5d0784SAlexander V. Chernikov 
324*3e5d0784SAlexander V. Chernikov void
325*3e5d0784SAlexander V. Chernikov ktest_add_msg_text(struct ktest_test_context *ctx, int msg_level,
326*3e5d0784SAlexander V. Chernikov     const char *fmt, ...)
327*3e5d0784SAlexander V. Chernikov {
328*3e5d0784SAlexander V. Chernikov 	va_list ap;
329*3e5d0784SAlexander V. Chernikov 
330*3e5d0784SAlexander V. Chernikov 	va_start(ap, fmt);
331*3e5d0784SAlexander V. Chernikov 	vsnprintf(ctx->buf, ctx->bufsize, fmt, ap);
332*3e5d0784SAlexander V. Chernikov 	va_end(ap);
333*3e5d0784SAlexander V. Chernikov 
334*3e5d0784SAlexander V. Chernikov 	nlattr_add_u8(ctx->npt->nw, KTEST_MSG_ATTR_LEVEL, msg_level);
335*3e5d0784SAlexander V. Chernikov 	nlattr_add_string(ctx->npt->nw, KTEST_MSG_ATTR_TEXT, ctx->buf);
336*3e5d0784SAlexander V. Chernikov }
337*3e5d0784SAlexander V. Chernikov 
338*3e5d0784SAlexander V. Chernikov void
339*3e5d0784SAlexander V. Chernikov ktest_end_msg(struct ktest_test_context *ctx)
340*3e5d0784SAlexander V. Chernikov {
341*3e5d0784SAlexander V. Chernikov 	nlmsg_end(ctx->npt->nw);
342*3e5d0784SAlexander V. Chernikov }
343*3e5d0784SAlexander V. Chernikov 
344*3e5d0784SAlexander V. Chernikov /* Module glue */
345*3e5d0784SAlexander V. Chernikov 
346*3e5d0784SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &ktest_parser };
347*3e5d0784SAlexander V. Chernikov 
348*3e5d0784SAlexander V. Chernikov static const struct genl_cmd ktest_cmds[] = {
349*3e5d0784SAlexander V. Chernikov 	{
350*3e5d0784SAlexander V. Chernikov 		.cmd_num = KTEST_CMD_LIST,
351*3e5d0784SAlexander V. Chernikov 		.cmd_name = "KTEST_CMD_LIST",
352*3e5d0784SAlexander V. Chernikov 		.cmd_cb = dump_tests,
353*3e5d0784SAlexander V. Chernikov 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
354*3e5d0784SAlexander V. Chernikov 	},
355*3e5d0784SAlexander V. Chernikov 	{
356*3e5d0784SAlexander V. Chernikov 		.cmd_num = KTEST_CMD_RUN,
357*3e5d0784SAlexander V. Chernikov 		.cmd_name = "KTEST_CMD_RUN",
358*3e5d0784SAlexander V. Chernikov 		.cmd_cb = run_test,
359*3e5d0784SAlexander V. Chernikov 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
360*3e5d0784SAlexander V. Chernikov 		.cmd_priv = PRIV_KLD_LOAD,
361*3e5d0784SAlexander V. Chernikov 	},
362*3e5d0784SAlexander V. Chernikov };
363*3e5d0784SAlexander V. Chernikov 
364*3e5d0784SAlexander V. Chernikov static void
365*3e5d0784SAlexander V. Chernikov ktest_nl_register(void)
366*3e5d0784SAlexander V. Chernikov {
367*3e5d0784SAlexander V. Chernikov 	bool ret __diagused;
368*3e5d0784SAlexander V. Chernikov 	int family_id __diagused;
369*3e5d0784SAlexander V. Chernikov 
370*3e5d0784SAlexander V. Chernikov 	NL_VERIFY_PARSERS(all_parsers);
371*3e5d0784SAlexander V. Chernikov 	family_id = genl_register_family(KTEST_FAMILY_NAME, 0, 1, KTEST_CMD_MAX);
372*3e5d0784SAlexander V. Chernikov 	MPASS(family_id != 0);
373*3e5d0784SAlexander V. Chernikov 
374*3e5d0784SAlexander V. Chernikov 	ret = genl_register_cmds(KTEST_FAMILY_NAME, ktest_cmds, NL_ARRAY_LEN(ktest_cmds));
375*3e5d0784SAlexander V. Chernikov 	MPASS(ret);
376*3e5d0784SAlexander V. Chernikov }
377*3e5d0784SAlexander V. Chernikov 
378*3e5d0784SAlexander V. Chernikov static void
379*3e5d0784SAlexander V. Chernikov ktest_nl_unregister(void)
380*3e5d0784SAlexander V. Chernikov {
381*3e5d0784SAlexander V. Chernikov 	MPASS(TAILQ_EMPTY(&module_list));
382*3e5d0784SAlexander V. Chernikov 
383*3e5d0784SAlexander V. Chernikov 	genl_unregister_family(KTEST_FAMILY_NAME);
384*3e5d0784SAlexander V. Chernikov }
385*3e5d0784SAlexander V. Chernikov 
386*3e5d0784SAlexander V. Chernikov static int
387*3e5d0784SAlexander V. Chernikov ktest_modevent(module_t mod, int type, void *unused)
388*3e5d0784SAlexander V. Chernikov {
389*3e5d0784SAlexander V. Chernikov 	int error = 0;
390*3e5d0784SAlexander V. Chernikov 
391*3e5d0784SAlexander V. Chernikov 	switch (type) {
392*3e5d0784SAlexander V. Chernikov 	case MOD_LOAD:
393*3e5d0784SAlexander V. Chernikov 		ktest_nl_register();
394*3e5d0784SAlexander V. Chernikov 		break;
395*3e5d0784SAlexander V. Chernikov 	case MOD_UNLOAD:
396*3e5d0784SAlexander V. Chernikov 		ktest_nl_unregister();
397*3e5d0784SAlexander V. Chernikov 		break;
398*3e5d0784SAlexander V. Chernikov 	default:
399*3e5d0784SAlexander V. Chernikov 		error = EOPNOTSUPP;
400*3e5d0784SAlexander V. Chernikov 		break;
401*3e5d0784SAlexander V. Chernikov 	}
402*3e5d0784SAlexander V. Chernikov 	return (error);
403*3e5d0784SAlexander V. Chernikov }
404*3e5d0784SAlexander V. Chernikov 
405*3e5d0784SAlexander V. Chernikov static moduledata_t ktestmod = {
406*3e5d0784SAlexander V. Chernikov         "ktest",
407*3e5d0784SAlexander V. Chernikov         ktest_modevent,
408*3e5d0784SAlexander V. Chernikov         0
409*3e5d0784SAlexander V. Chernikov };
410*3e5d0784SAlexander V. Chernikov 
411*3e5d0784SAlexander V. Chernikov DECLARE_MODULE(ktestmod, ktestmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
412*3e5d0784SAlexander V. Chernikov MODULE_VERSION(ktestmod, 1);
413*3e5d0784SAlexander V. Chernikov MODULE_DEPEND(ktestmod, netlink, 1, 1, 1);
414*3e5d0784SAlexander V. Chernikov 
415