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