Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/tests/ktest.c
39475 views
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/stdarg.h>
38
#include <sys/priv.h>
39
40
#include <netlink/netlink.h>
41
#include <netlink/netlink_ctl.h>
42
#include <netlink/netlink_generic.h>
43
#include <netlink/netlink_message_parser.h>
44
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 int family_id;
364
static void
365
ktest_nl_register(void)
366
{
367
bool ret __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(family_id, ktest_cmds, nitems(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(family_id);
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
415