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