2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2023 Alexander V. Chernikov
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include "opt_netlink.h"
30 #include <sys/param.h>
31 #include <sys/refcount.h>
32 #include <sys/types.h>
33 #include <sys/kernel.h>
35 #include <sys/mutex.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/socket.h>
41 #include <netlink/netlink.h>
42 #include <netlink/netlink_ctl.h>
43 #include <netlink/netlink_generic.h>
44 #include <netlink/netlink_message_parser.h>
46 #include <machine/stdarg.h>
47 #include <tests/ktest.h>
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)
54 MTX_SYSINIT(ktest_mtx, &ktest_mtx, "ktest mutex", MTX_DEF);
57 struct ktest_module_info *info;
58 volatile u_int refcount;
59 TAILQ_ENTRY(ktest_module) entries;
61 static TAILQ_HEAD(, ktest_module) module_list = TAILQ_HEAD_INITIALIZER(module_list);
63 struct nl_ktest_parsed {
66 struct nlattr *test_meta;
69 #define _IN(_field) offsetof(struct genlmsghdr, _field)
70 #define _OUT(_field) offsetof(struct nl_ktest_parsed, _field)
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 },
77 static const struct nlfield_parser nlf_p_get[] = {
79 NL_DECLARE_PARSER(ktest_parser, struct genlmsghdr, nlf_p_get, nla_p_get);
84 create_reply(struct nl_writer *nw, struct nlmsghdr *hdr, int cmd)
86 if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
89 struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
91 ghdr_new->version = 0;
92 ghdr_new->reserved = 0;
98 dump_mod_test(struct nlmsghdr *hdr, struct nl_pstate *npt,
99 struct ktest_module *mod, const struct ktest_test_info *test_info)
101 struct nl_writer *nw = npt->nw;
103 if (!create_reply(nw, hdr, KTEST_CMD_NEWTEST))
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);
118 dump_mod_tests(struct nlmsghdr *hdr, struct nl_pstate *npt,
119 struct ktest_module *mod, struct nl_ktest_parsed *attrs)
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))
125 int error = dump_mod_test(hdr, npt, mod, test_info);
134 dump_tests(struct nlmsghdr *hdr, struct nl_pstate *npt)
136 struct nl_ktest_parsed attrs = { };
137 struct ktest_module *mod;
140 error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
144 hdr->nlmsg_flags |= NLM_F_MULTI;
147 TAILQ_FOREACH(mod, &module_list, entries) {
148 if (attrs.mod_name && strcmp(attrs.mod_name, mod->info->name))
150 error = dump_mod_tests(hdr, npt, mod, &attrs);
156 if (!nlmsg_end_dump(npt->nw, error, hdr)) {
157 //NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
165 run_test(struct nlmsghdr *hdr, struct nl_pstate *npt)
167 struct nl_ktest_parsed attrs = { };
168 struct ktest_module *mod;
171 error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs);
175 if (attrs.mod_name == NULL) {
176 nlmsg_report_err_msg(npt, "KTEST_ATTR_MOD_NAME not set");
180 if (attrs.test_name == NULL) {
181 nlmsg_report_err_msg(npt, "KTEST_ATTR_TEST_NAME not set");
185 const struct ktest_test_info *test = NULL;
188 TAILQ_FOREACH(mod, &module_list, entries) {
189 if (strcmp(attrs.mod_name, mod->info->name))
192 const struct ktest_module_info *info = mod->info;
194 for (int i = 0; i < info->num_tests; i++) {
195 const struct ktest_test_info *test_info = &info->tests[i];
197 if (!strcmp(attrs.test_name, test_info->name)) {
205 refcount_acquire(&mod->refcount);
212 struct ktest_test_context ctx = {
215 .buf = npt_alloc(npt, KTEST_MAX_BUF),
216 .bufsize = KTEST_MAX_BUF,
219 if (ctx.buf == NULL) {
220 //NL_LOG(LOG_DEBUG, "unable to allocate temporary buffer");
224 if (test->parse != NULL && attrs.test_meta != NULL) {
225 error = test->parse(&ctx, attrs.test_meta);
230 hdr->nlmsg_flags |= NLM_F_MULTI;
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);
236 refcount_release(&mod->refcount);
238 if (!nlmsg_end_dump(npt->nw, error, hdr)) {
239 //NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
249 register_test_module(struct ktest_module_info *info)
251 struct ktest_module *mod = malloc(sizeof(*mod), M_TEMP, M_WAITOK | M_ZERO);
254 info->module_ptr = mod;
256 TAILQ_INSERT_TAIL(&module_list, mod, entries);
261 unregister_test_module(struct ktest_module_info *info)
263 struct ktest_module *mod = info->module_ptr;
265 info->module_ptr = NULL;
268 TAILQ_REMOVE(&module_list, mod, entries);
275 can_unregister(struct ktest_module_info *info)
277 struct ktest_module *mod = info->module_ptr;
279 return (refcount_load(&mod->refcount) == 0);
283 ktest_default_modevent(module_t mod, int type, void *arg)
285 struct ktest_module_info *info = (struct ktest_module_info *)arg;
290 register_test_module(info);
293 if (!can_unregister(info))
295 unregister_test_module(info);
305 ktest_start_msg(struct ktest_test_context *ctx)
307 return (create_reply(ctx->npt->nw, ctx->hdr, KTEST_CMD_NEWMESSAGE));
311 ktest_add_msg_meta(struct ktest_test_context *ctx, const char *func,
312 const char *fname, int line)
314 struct nl_writer *nw = ctx->npt->nw;
318 nlattr_add(nw, KTEST_MSG_ATTR_TS, sizeof(ts), &ts);
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);
326 ktest_add_msg_text(struct ktest_test_context *ctx, int msg_level,
327 const char *fmt, ...)
332 vsnprintf(ctx->buf, ctx->bufsize, fmt, ap);
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);
340 ktest_end_msg(struct ktest_test_context *ctx)
342 nlmsg_end(ctx->npt->nw);
347 static const struct nlhdr_parser *all_parsers[] = { &ktest_parser };
349 static const struct genl_cmd ktest_cmds[] = {
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,
357 .cmd_num = KTEST_CMD_RUN,
358 .cmd_name = "KTEST_CMD_RUN",
360 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
361 .cmd_priv = PRIV_KLD_LOAD,
366 ktest_nl_register(void)
369 int family_id __diagused;
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);
375 ret = genl_register_cmds(KTEST_FAMILY_NAME, ktest_cmds, NL_ARRAY_LEN(ktest_cmds));
380 ktest_nl_unregister(void)
382 MPASS(TAILQ_EMPTY(&module_list));
384 genl_unregister_family(KTEST_FAMILY_NAME);
388 ktest_modevent(module_t mod, int type, void *unused)
397 ktest_nl_unregister();
406 static moduledata_t ktestmod = {
412 DECLARE_MODULE(ktestmod, ktestmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
413 MODULE_VERSION(ktestmod, 1);
414 MODULE_DEPEND(ktestmod, netlink, 1, 1, 1);