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