File Index
Files and directories
Sign in
FILE

executor.c

File preview with syntax highlighting.

linux-6.19.14/lib/kunit/executor.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#include <linux/reboot.h>
  4#include <kunit/test.h>
  5#include <kunit/attributes.h>
  6#include <linux/glob.h>
  7#include <linux/moduleparam.h>
  8
  9/*
 10 * These symbols point to the .kunit_test_suites section and are defined in
 11 * include/asm-generic/vmlinux.lds.h, and consequently must be extern.
 12 */
 13extern struct kunit_suite * const __kunit_suites_start[];
 14extern struct kunit_suite * const __kunit_suites_end[];
 15extern struct kunit_suite * const __kunit_init_suites_start[];
 16extern struct kunit_suite * const __kunit_init_suites_end[];
 17
 18static char *action_param;
 19
 20module_param_named(action, action_param, charp, 0400);
 21MODULE_PARM_DESC(action,
 22		 "Changes KUnit executor behavior, valid values are:\n"
 23		 "<none>: run the tests like normal\n"
 24		 "'list' to list test names instead of running them.\n"
 25		 "'list_attr' to list test names and attributes instead of running them.\n");
 26
 27const char *kunit_action(void)
 28{
 29	return action_param;
 30}
 31
 32/*
 33 * Run KUnit tests after initialization
 34 */
 35#ifdef CONFIG_KUNIT_AUTORUN_ENABLED
 36static bool autorun_param = true;
 37#else
 38static bool autorun_param;
 39#endif
 40module_param_named(autorun, autorun_param, bool, 0);
 41MODULE_PARM_DESC(autorun, "Run KUnit tests after initialization");
 42
 43bool kunit_autorun(void)
 44{
 45	return autorun_param;
 46}
 47
 48#define PARAM_FROM_CONFIG(config) (config[0] ? config : NULL)
 49
 50static char *filter_glob_param = PARAM_FROM_CONFIG(CONFIG_KUNIT_DEFAULT_FILTER_GLOB);
 51static char *filter_param = PARAM_FROM_CONFIG(CONFIG_KUNIT_DEFAULT_FILTER);
 52static char *filter_action_param = PARAM_FROM_CONFIG(CONFIG_KUNIT_DEFAULT_FILTER_ACTION);
 53
 54module_param_named(filter_glob, filter_glob_param, charp, 0600);
 55MODULE_PARM_DESC(filter_glob,
 56		"Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test");
 57module_param_named(filter, filter_param, charp, 0600);
 58MODULE_PARM_DESC(filter,
 59		"Filter which KUnit test suites/tests run at boot-time using attributes, e.g. speed>slow");
 60module_param_named(filter_action, filter_action_param, charp, 0600);
 61MODULE_PARM_DESC(filter_action,
 62		"Changes behavior of filtered tests using attributes, valid values are:\n"
 63		"<none>: do not run filtered tests as normal\n"
 64		"'skip': skip all filtered tests instead so tests will appear in output\n");
 65
 66const char *kunit_filter_glob(void)
 67{
 68	return filter_glob_param;
 69}
 70
 71char *kunit_filter(void)
 72{
 73	return filter_param;
 74}
 75
 76char *kunit_filter_action(void)
 77{
 78	return filter_action_param;
 79}
 80
 81/* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */
 82struct kunit_glob_filter {
 83	char *suite_glob;
 84	char *test_glob;
 85};
 86
 87/* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */
 88static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed,
 89				    const char *filter_glob)
 90{
 91	const char *period = strchr(filter_glob, '.');
 92
 93	if (!period) {
 94		parsed->suite_glob = kstrdup(filter_glob, GFP_KERNEL);
 95		if (!parsed->suite_glob)
 96			return -ENOMEM;
 97		parsed->test_glob = NULL;
 98		return 0;
 99	}
100
101	parsed->suite_glob = kstrndup(filter_glob, period - filter_glob, GFP_KERNEL);
102	if (!parsed->suite_glob)
103		return -ENOMEM;
104
105	parsed->test_glob = kstrdup(period + 1, GFP_KERNEL);
106	if (!parsed->test_glob) {
107		kfree(parsed->suite_glob);
108		return -ENOMEM;
109	}
110
111	return 0;
112}
113
114/* Create a copy of suite with only tests that match test_glob. */
115static struct kunit_suite *
116kunit_filter_glob_tests(const struct kunit_suite *const suite, const char *test_glob)
117{
118	int n = 0;
119	struct kunit_case *filtered, *test_case;
120	struct kunit_suite *copy;
121
122	kunit_suite_for_each_test_case(suite, test_case) {
123		if (!test_glob || glob_match(test_glob, test_case->name))
124			++n;
125	}
126
127	if (n == 0)
128		return NULL;
129
130	copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL);
131	if (!copy)
132		return ERR_PTR(-ENOMEM);
133
134	filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
135	if (!filtered) {
136		kfree(copy);
137		return ERR_PTR(-ENOMEM);
138	}
139
140	n = 0;
141	kunit_suite_for_each_test_case(suite, test_case) {
142		if (!test_glob || glob_match(test_glob, test_case->name))
143			filtered[n++] = *test_case;
144	}
145
146	copy->test_cases = filtered;
147	return copy;
148}
149
150void kunit_free_suite_set(struct kunit_suite_set suite_set)
151{
152	struct kunit_suite * const *suites;
153
154	for (suites = suite_set.start; suites < suite_set.end; suites++) {
155		kfree((*suites)->test_cases);
156		kfree(*suites);
157	}
158	kfree(suite_set.start);
159}
160
161/*
162 * Filter and reallocate test suites. Must return the filtered test suites set
163 * allocated at a valid virtual address or NULL in case of error.
164 */
165struct kunit_suite_set
166kunit_filter_suites(const struct kunit_suite_set *suite_set,
167		    const char *filter_glob,
168		    char *filters,
169		    char *filter_action,
170		    int *err)
171{
172	int i, j, k;
173	int filter_count = 0;
174	struct kunit_suite **copy, **copy_start, *filtered_suite, *new_filtered_suite;
175	struct kunit_suite_set filtered = {NULL, NULL};
176	struct kunit_glob_filter parsed_glob;
177	struct kunit_attr_filter *parsed_filters = NULL;
178	struct kunit_suite * const *suites;
179
180	const size_t max = suite_set->end - suite_set->start;
181
182	copy = kcalloc(max, sizeof(*copy), GFP_KERNEL);
183	if (!copy) { /* won't be able to run anything, return an empty set */
184		return filtered;
185	}
186	copy_start = copy;
187
188	if (filter_glob) {
189		*err = kunit_parse_glob_filter(&parsed_glob, filter_glob);
190		if (*err)
191			goto free_copy;
192	}
193
194	/* Parse attribute filters */
195	if (filters) {
196		filter_count = kunit_get_filter_count(filters);
197		parsed_filters = kcalloc(filter_count, sizeof(*parsed_filters), GFP_KERNEL);
198		if (!parsed_filters) {
199			*err = -ENOMEM;
200			goto free_parsed_glob;
201		}
202		for (j = 0; j < filter_count; j++)
203			parsed_filters[j] = kunit_next_attr_filter(&filters, err);
204		if (*err)
205			goto free_parsed_filters;
206	}
207
208	for (i = 0; &suite_set->start[i] != suite_set->end; i++) {
209		filtered_suite = suite_set->start[i];
210		if (filter_glob) {
211			if (!glob_match(parsed_glob.suite_glob, filtered_suite->name))
212				continue;
213			filtered_suite = kunit_filter_glob_tests(filtered_suite,
214					parsed_glob.test_glob);
215			if (IS_ERR(filtered_suite)) {
216				*err = PTR_ERR(filtered_suite);
217				goto free_filtered_suite;
218			}
219		}
220		if (filter_count > 0 && parsed_filters != NULL) {
221			for (k = 0; k < filter_count; k++) {
222				new_filtered_suite = kunit_filter_attr_tests(filtered_suite,
223						parsed_filters[k], filter_action, err);
224
225				/* Free previous copy of suite */
226				if (k > 0 || filter_glob) {
227					kfree(filtered_suite->test_cases);
228					kfree(filtered_suite);
229				}
230
231				filtered_suite = new_filtered_suite;
232
233				if (*err)
234					goto free_filtered_suite;
235
236				if (IS_ERR(filtered_suite)) {
237					*err = PTR_ERR(filtered_suite);
238					goto free_filtered_suite;
239				}
240				if (!filtered_suite)
241					break;
242			}
243		}
244
245		if (!filtered_suite)
246			continue;
247
248		*copy++ = filtered_suite;
249	}
250	filtered.start = copy_start;
251	filtered.end = copy;
252
253free_filtered_suite:
254	if (*err) {
255		for (suites = copy_start; suites < copy; suites++) {
256			kfree((*suites)->test_cases);
257			kfree(*suites);
258		}
259	}
260
261free_parsed_filters:
262	if (filter_count)
263		kfree(parsed_filters);
264
265free_parsed_glob:
266	if (filter_glob) {
267		kfree(parsed_glob.suite_glob);
268		kfree(parsed_glob.test_glob);
269	}
270
271free_copy:
272	if (*err)
273		kfree(copy_start);
274
275	return filtered;
276}
277
278void kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin)
279{
280	size_t num_suites = suite_set->end - suite_set->start;
281	bool autorun = kunit_autorun();
282
283	if (autorun && (builtin || num_suites)) {
284		pr_info("KTAP version 1\n");
285		pr_info("1..%zu\n", num_suites);
286	}
287
288	__kunit_test_suites_init(suite_set->start, num_suites, autorun);
289}
290
291void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
292{
293	struct kunit_suite * const *suites;
294	struct kunit_case *test_case;
295
296	/* Hack: print a ktap header so kunit.py can find the start of KUnit output. */
297	pr_info("KTAP version 1\n");
298
299	for (suites = suite_set->start; suites < suite_set->end; suites++) {
300		/* Print suite name and suite attributes */
301		pr_info("%s\n", (*suites)->name);
302		if (include_attr)
303			kunit_print_attr((void *)(*suites), false, 0);
304
305		/* Print test case name and attributes in suite */
306		kunit_suite_for_each_test_case((*suites), test_case) {
307			pr_info("%s.%s\n", (*suites)->name, test_case->name);
308			if (include_attr)
309				kunit_print_attr((void *)test_case, true, 0);
310		}
311	}
312}
313
314struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
315		struct kunit_suite_set suite_set)
316{
317	struct kunit_suite_set total_suite_set = {NULL, NULL};
318	struct kunit_suite **total_suite_start = NULL;
319	size_t init_num_suites, num_suites, suite_size;
320	int i = 0;
321
322	init_num_suites = init_suite_set.end - init_suite_set.start;
323	num_suites = suite_set.end - suite_set.start;
324	suite_size = sizeof(suite_set.start);
325
326	/* Allocate memory for array of all kunit suites */
327	total_suite_start = kmalloc_array(init_num_suites + num_suites, suite_size, GFP_KERNEL);
328	if (!total_suite_start)
329		return total_suite_set;
330
331	/* Append and mark init suites and then append all other kunit suites */
332	memcpy(total_suite_start, init_suite_set.start, init_num_suites * suite_size);
333	for (i = 0; i < init_num_suites; i++)
334		total_suite_start[i]->is_init = true;
335
336	memcpy(total_suite_start + init_num_suites, suite_set.start, num_suites * suite_size);
337
338	/* Set kunit suite set start and end */
339	total_suite_set.start = total_suite_start;
340	total_suite_set.end = total_suite_start + (init_num_suites + num_suites);
341
342	return total_suite_set;
343}
344
345#if IS_BUILTIN(CONFIG_KUNIT)
346
347static char *kunit_shutdown;
348core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
349
350static void kunit_handle_shutdown(void)
351{
352	if (!kunit_shutdown)
353		return;
354
355	if (!strcmp(kunit_shutdown, "poweroff"))
356		kernel_power_off();
357	else if (!strcmp(kunit_shutdown, "halt"))
358		kernel_halt();
359	else if (!strcmp(kunit_shutdown, "reboot"))
360		kernel_restart(NULL);
361
362}
363
364int kunit_run_all_tests(void)
365{
366	struct kunit_suite_set suite_set = {NULL, NULL};
367	struct kunit_suite_set filtered_suite_set = {NULL, NULL};
368	struct kunit_suite_set init_suite_set = {
369		__kunit_init_suites_start, __kunit_init_suites_end,
370	};
371	struct kunit_suite_set normal_suite_set = {
372		__kunit_suites_start, __kunit_suites_end,
373	};
374	size_t init_num_suites = init_suite_set.end - init_suite_set.start;
375	int err = 0;
376
377	if (init_num_suites > 0) {
378		suite_set = kunit_merge_suite_sets(init_suite_set, normal_suite_set);
379		if (!suite_set.start)
380			goto out;
381	} else
382		suite_set = normal_suite_set;
383
384	if (!kunit_enabled()) {
385		pr_info("kunit: disabled\n");
386		goto free_out;
387	}
388
389	if (filter_glob_param || filter_param) {
390		filtered_suite_set = kunit_filter_suites(&suite_set, filter_glob_param,
391				filter_param, filter_action_param, &err);
392
393		/* Free original suite set before using filtered suite set */
394		if (init_num_suites > 0)
395			kfree(suite_set.start);
396		suite_set = filtered_suite_set;
397
398		if (err) {
399			pr_err("kunit executor: error filtering suites: %d\n", err);
400			goto free_out;
401		}
402	}
403
404	if (!action_param)
405		kunit_exec_run_tests(&suite_set, true);
406	else if (strcmp(action_param, "list") == 0)
407		kunit_exec_list_tests(&suite_set, false);
408	else if (strcmp(action_param, "list_attr") == 0)
409		kunit_exec_list_tests(&suite_set, true);
410	else
411		pr_err("kunit executor: unknown action '%s'\n", action_param);
412
413free_out:
414	if (filter_glob_param || filter_param)
415		kunit_free_suite_set(suite_set);
416	else if (init_num_suites > 0)
417		/* Don't use kunit_free_suite_set because suites aren't individually allocated */
418		kfree(suite_set.start);
419
420out:
421	kunit_handle_shutdown();
422	return err;
423}
424
425#if IS_BUILTIN(CONFIG_KUNIT_TEST)
426#include "executor_test.c"
427#endif
428
429#endif /* IS_BUILTIN(CONFIG_KUNIT) */
~/ Read-only file preview.