~lkcamp/patches

igt_kmod: add compatibility for KUnit v2 PROPOSED

Isabella Basso: 2
 igt_kmod: add compatibility for KUnit
 tests/kms_kunit: add KUnit damage helper test

 5 files changed, 247 insertions(+), 11 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~lkcamp/patches/patches/30163/mbox | git am -3
Learn more about email & git

[PATCH v2 1/2] igt_kmod: add compatibility for KUnit Export this patch

This adds functions for both executing the tests as well as parsing TAP
kmsg output.

Changes since v1:
- Correct handling of read to deal with partial buffer.

Signed-off-by: Isabella Basso <isabbasso@riseup.net>
---
 lib/igt_kmod.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_kmod.h |  13 ++++
 2 files changed, 182 insertions(+)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index cf7a3b22..5d8e8b94 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -345,6 +345,175 @@ igt_kmod_list_loaded(void)
	kmod_module_unref_list(list);
}

static void kmsg_parse_tap(int fd)
{
	char record[4096 + 1];
	const char *num_tests_str, *nok_str, *ok_str, *end_str_success, *end_str_fail;
	bool has_num = false;

	num_tests_str = "..";
	nok_str = "    not ok ";
	ok_str = "    ok ";
	end_str_fail = "not ok ";
	end_str_success = "ok ";

	if (fd == -1) {
		igt_warn("Unable to retrieve kernel log (from /dev/kmsg)\n");
		return;
	}

	record[sizeof(record) - 1] = '\0';

	for (;;) {
		const char *end, *result;
		ssize_t r;

		r = read(fd, record, sizeof(record) - 1);
		if (r < 0) {
			if (errno == EINTR)
				continue;

			if (errno == EPIPE) {
				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
				continue;
			}

			if (errno != EAGAIN)
				igt_warn("kmsg truncated: unknown error (%m)\n");

			break;
		}

		/* deal with partial string from read */
		if (record[r - 1] == NULL) {
			fseek(fd, 0, end);
			continue;
		}

		if (!has_num) {
			result = strstr(record, num_tests_str);
			if (result) {
				result += strlen(num_tests_str);
				end = strchrnul(result, '\n');
				igt_info("[IGT] running %.*s tests...\n",
					 (int)(end - result), result);
				has_num = true;
			}
			continue;
		}

		/* all results have 'ok' in them */
		result = strstr(record, "ok");

		if (result) {
			end = strchrnul(result, '\n');

			/* subtest succeded (i.e. ok_str) */
			result = strstr(record, ok_str);

			if (result) {
				result += strlen(ok_str);
				igt_info("[IGT] SUBTEST %.*s\n",
					 (int)(end - result), result);
				continue;
			}

			/* subtest failed (i.e. nok_str) */
			result = strstr(record, nok_str);

			if (result) {
				result += strlen(nok_str);
				igt_warn("[IGT] SUBTEST FAILED %.*s\n",
					 (int)(end - result), result);
				continue;
			}

			/* test ended (i.e. end_str_success) */
			result = strchr(record, ';') + 1;

			if (strncmp(result, end_str_success, strlen(end_str_success)) == 0) {
				igt_info("[IGT] TEST SUCCEEDED %.*s\n",
					 (int)(end - result), result);

				igt_success();
			}

			if (strncmp(result, end_str_fail, strlen(end_str_fail)) == 0) {
				igt_warn("[IGT] TEST FAILED %.*s\n",
					 (int)(end - result), result);
				igt_fail(IGT_EXIT_FAILURE);
			}
		}
	}
}

/**
 * igt_kunit: tests a kunit module
 * @mod_name: the name of the module
 * @options: options to load the module
 * @req_mods: array with additional dependencies to run the KUnit test
 * @req_mod_len: size of req_mods array
 *
 * Loads the kunit module, parses its dmesg output, then unloads it
 */
void igt_kunit(const char *mod_name, const char *options,
	       const char **req_mods, int req_mod_len)
{
	int kmsg_fd, cmi;
	struct igt_kselftest tst;
	const char *norm_name;

	/* get normalized module name */
	igt_require(igt_kselftest_init(&tst, mod_name) == 0);

	norm_name = kmod_module_get_name(tst.kmod);

	if (!igt_kmod_is_loaded(norm_name)) {
		/* The kunit module is required for running any kunit tests */
		if (!igt_kmod_is_loaded("kunit"))
			igt_require(igt_kmod_load("kunit", NULL) == 0);

		for (cmi = 0; cmi < req_mod_len; cmi++) {
			if (strcmp(req_mods[cmi], "kunit") == 0)
				continue;
			if (igt_kmod_load(req_mods[cmi], NULL))
				goto unload;
		}
	} else {
		/* Unload module if loaded (maybe previous run bailed out?) */
		igt_kmod_unload(mod_name, 0);
	}

	kmsg_fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);

	if (kmsg_fd < 0) {
		igt_warn("Could not open /dev/kmsg");
		goto unload;
	}

	if (lseek(kmsg_fd, 0, SEEK_END)) {
		igt_warn("Could not seek the end of /dev/kmsg");
		close(kmsg_fd);
		goto unload;
	}

	igt_require(igt_kmod_load(mod_name, options) == 0);

	kmsg_parse_tap(kmsg_fd);

	igt_kmod_unload(mod_name, 0);

	close(kmsg_fd);
unload:
	igt_kmod_unload("kunit", 0);

	for (cmi--; cmi >= 0; cmi--) {
		if (strcmp(req_mods[cmi], "kunit") == 0)
			continue;
		igt_kmod_unload(req_mods[cmi], 0);
	}
}

/**
 * igt_i915_driver_load:
 * @opts: options to pass to i915 driver
diff --git a/lib/igt_kmod.h b/lib/igt_kmod.h
index 0898122b..60e7a750 100644
--- a/lib/igt_kmod.h
+++ b/lib/igt_kmod.h
@@ -28,6 +28,16 @@

#include "igt_list.h"

#define MAX_MOD_NAME_LEN 40

/**
 * ARRAY_SIZE:
 * @arr: static array
 *
 * Macro to compute the size of the static array @arr.
 */
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))

bool igt_kmod_is_loaded(const char *mod_name);
void igt_kmod_list_loaded(void);

@@ -36,6 +46,9 @@ bool igt_kmod_has_param(const char *mod_name, const char *param);
int igt_kmod_load(const char *mod_name, const char *opts);
int igt_kmod_unload(const char *mod_name, unsigned int flags);

void igt_kunit(const char *mod_name, const char *options,
	       const char **req_mods, int req_mod_len);

int igt_i915_driver_load(const char *opts);
int igt_i915_driver_unload(void);
int __igt_i915_driver_unload(const char **whom);
-- 
2.35.1

[PATCH v2 2/2] tests/kms_kunit: add KUnit damage helper test Export this patch

This adds a converted KUnit test module to IGT.

Signed-off-by: Isabella Basso <isabbasso@riseup.net>
---
 lib/igt_kmod.c    | 41 ++++++++++++++++++++++++++++++-----------
 tests/kms_kunit.c | 34 ++++++++++++++++++++++++++++++++++
 tests/meson.build |  1 +
 3 files changed, 65 insertions(+), 11 deletions(-)
 create mode 100644 tests/kms_kunit.c

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 5d8e8b94..14f1939d 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -364,12 +364,24 @@ static void kmsg_parse_tap(int fd)

	record[sizeof(record) - 1] = '\0';

	for (;;) {
	while (true) {
		const char *end, *result;
		ssize_t r;
		bool skip = true;

		r = read(fd, record, sizeof(record) - 1);
		if (r < 0) {
		if (!skip) {
			end = strchrnul(record, '\n');
		} else {
			end = NULL;
			skip = false;
		}

		r = read(fd, record,
			 end != NULL ?
				(int) (end - record) :
				sizeof(record) - 1);

		if (r <= 0) {
			if (errno == EINTR)
				continue;

@@ -384,19 +396,19 @@ static void kmsg_parse_tap(int fd)
			break;
		}

		/* deal with partial string from read */
		if (record[r - 1] == NULL) {
			fseek(fd, 0, end);
			continue;
		}

		if (!has_num) {
			result = strstr(record, num_tests_str);
			if (result) {
				result += strlen(num_tests_str);
				end = strchrnul(result, '\n');
				igt_info("[IGT] running %.*s tests...\n",
					 (int)(end - result), result);
				/* in case line > 4096 */
				if (end == NULL) {
					igt_info("%s\n", record);
				}
				else {
					igt_info("[IGT] running %.*s tests...\n",
						 (int)(end - result), result);
				}
				has_num = true;
			}
			continue;
@@ -407,6 +419,11 @@ static void kmsg_parse_tap(int fd)

		if (result) {
			end = strchrnul(result, '\n');
			/* in case line > 4096 */
			if (end == NULL) {
				igt_info("%s\n", record);
				continue;
			}

			/* subtest succeded (i.e. ok_str) */
			result = strstr(record, ok_str);
@@ -429,6 +446,7 @@ static void kmsg_parse_tap(int fd)
			}

			/* test ended (i.e. end_str_success) */
			/* we should skip the ; in the beginning of the string */
			result = strchr(record, ';') + 1;

			if (strncmp(result, end_str_success, strlen(end_str_success)) == 0) {
@@ -444,6 +462,7 @@ static void kmsg_parse_tap(int fd)
				igt_fail(IGT_EXIT_FAILURE);
			}
		}
		skip = true;
	}
}

diff --git a/tests/kms_kunit.c b/tests/kms_kunit.c
new file mode 100644
index 00000000..31566910
--- /dev/null
+++ b/tests/kms_kunit.c
@@ -0,0 +1,34 @@
/*
 * Copyright © 2021 Isabella Basso do Amaral <isabbasso@riseup.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <libkmod.h>

#include "igt.h"
#include "igt_kmod.h"

IGT_TEST_DESCRIPTION("KUnit DRM damage helper module testing");

igt_simple_main
{
	igt_kunit("test-drm_damage_helper", NULL, NULL, 0);
}
diff --git a/tests/meson.build b/tests/meson.build
index 3dbba7a1..6d3685c3 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -36,6 +36,7 @@ test_progs = [
	'kms_hdmi_inject',
	'kms_hdr',
	'kms_invalid_mode',
	'kms_kunit',
	'kms_lease',
	'kms_multipipe_modeset',
	'kms_panel_fitting',
-- 
2.35.1