]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/tests/ktest.c
linux(4): Regen for linux_nosys change
[FreeBSD/FreeBSD.git] / sys / tests / ktest.c
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