All of lore.kernel.org
 help / color / mirror / Atom feed
* [Intel-gfx] [PATCH i-g-t v2 00/11] Kunit fixes and improvements
@ 2023-10-09 12:27 ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

v2: Add a new patch that provides all results cleanup helper,
  - split out changes in handling of modprobe errors and kernel taints from
    "Fetch a list of test cases in advance" to separate patches (Kamil),
  - prepare for KTAP parsing after modprobe completed in a separate
    patch,
  - drop other modprobe and kernel taint related changes from the series,
  - fix some string duplicates referenced from filtered out test cases not
    freed,
  - always pass last result to next dynamic sub-subtest, fetch first
    result right after loading the kunit test module for execution,
  - still break the loop of test cases on unexpected return codes from
    kunit_kmsg_get_result(),
  - fix typos (Kamil),
  - update commit descriptions.

Janusz Krzysztofik (11):
  lib/kunit: Fix handling of potential errors from F_GETFL
  lib/kunit: Be more verbose on errors
  lib/kunit: Fix misplaced igt_kunit() doc
  lib/kunit: Parse KTAP report from the main process thread
  lib/kunit: Omit suite name prefix if the same as subtest name
  tests/kms_selftest: Let subtest names match suite names
  lib/ktap: Drop workaround for missing top level KTAP headers
  lib/kunit: Provide all results cleanup helper
  lib/kunit: Prepare for KTAP parsing after modprobe completed
  lib/kunit: Fetch a list of test cases in advance
  lib/kunit: Execute kunit test cases only when needed

 lib/igt_kmod.c              | 418 ++++++++++++++++++++++----
 lib/igt_ktap.c              | 580 ------------------------------------
 lib/igt_ktap.h              |  22 --
 lib/tests/igt_ktap_parser.c |   3 +-
 tests/kms_selftest.c        |  37 +--
 5 files changed, 374 insertions(+), 686 deletions(-)

-- 
2.42.0


^ permalink raw reply	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 00/11] Kunit fixes and improvements
@ 2023-10-09 12:27 ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

v2: Add a new patch that provides all results cleanup helper,
  - split out changes in handling of modprobe errors and kernel taints from
    "Fetch a list of test cases in advance" to separate patches (Kamil),
  - prepare for KTAP parsing after modprobe completed in a separate
    patch,
  - drop other modprobe and kernel taint related changes from the series,
  - fix some string duplicates referenced from filtered out test cases not
    freed,
  - always pass last result to next dynamic sub-subtest, fetch first
    result right after loading the kunit test module for execution,
  - still break the loop of test cases on unexpected return codes from
    kunit_kmsg_get_result(),
  - fix typos (Kamil),
  - update commit descriptions.

Janusz Krzysztofik (11):
  lib/kunit: Fix handling of potential errors from F_GETFL
  lib/kunit: Be more verbose on errors
  lib/kunit: Fix misplaced igt_kunit() doc
  lib/kunit: Parse KTAP report from the main process thread
  lib/kunit: Omit suite name prefix if the same as subtest name
  tests/kms_selftest: Let subtest names match suite names
  lib/ktap: Drop workaround for missing top level KTAP headers
  lib/kunit: Provide all results cleanup helper
  lib/kunit: Prepare for KTAP parsing after modprobe completed
  lib/kunit: Fetch a list of test cases in advance
  lib/kunit: Execute kunit test cases only when needed

 lib/igt_kmod.c              | 418 ++++++++++++++++++++++----
 lib/igt_ktap.c              | 580 ------------------------------------
 lib/igt_ktap.h              |  22 --
 lib/tests/igt_ktap_parser.c |   3 +-
 tests/kms_selftest.c        |  37 +--
 5 files changed, 374 insertions(+), 686 deletions(-)

-- 
2.42.0


^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 00/11] Kunit fixes and improvements
@ 2023-10-09 12:27 ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

v2: Add a new patch that provides all results cleanup helper,
  - split out changes in handling of modprobe errors and kernel taints from
    "Fetch a list of test cases in advance" to separate patches (Kamil),
  - prepare for KTAP parsing after modprobe completed in a separate
    patch,
  - drop other modprobe and kernel taint related changes from the series,
  - fix some string duplicates referenced from filtered out test cases not
    freed,
  - always pass last result to next dynamic sub-subtest, fetch first
    result right after loading the kunit test module for execution,
  - still break the loop of test cases on unexpected return codes from
    kunit_kmsg_get_result(),
  - fix typos (Kamil),
  - update commit descriptions.

Janusz Krzysztofik (11):
  lib/kunit: Fix handling of potential errors from F_GETFL
  lib/kunit: Be more verbose on errors
  lib/kunit: Fix misplaced igt_kunit() doc
  lib/kunit: Parse KTAP report from the main process thread
  lib/kunit: Omit suite name prefix if the same as subtest name
  tests/kms_selftest: Let subtest names match suite names
  lib/ktap: Drop workaround for missing top level KTAP headers
  lib/kunit: Provide all results cleanup helper
  lib/kunit: Prepare for KTAP parsing after modprobe completed
  lib/kunit: Fetch a list of test cases in advance
  lib/kunit: Execute kunit test cases only when needed

 lib/igt_kmod.c              | 418 ++++++++++++++++++++++----
 lib/igt_ktap.c              | 580 ------------------------------------
 lib/igt_ktap.h              |  22 --
 lib/tests/igt_ktap_parser.c |   3 +-
 tests/kms_selftest.c        |  37 +--
 5 files changed, 374 insertions(+), 686 deletions(-)

-- 
2.42.0

^ permalink raw reply	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 01/11] lib/kunit: Fix handling of potential errors from F_GETFL
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Function fcntl(..., F_GETFL, ...) that returns file status flags may also
return a negative error code.  Handle that error instead of blindly using
the returned value as flags.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index d98e6c5f9e..05ff178b27 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -783,8 +783,8 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
 
-	flags = fcntl(tst->kmsg, F_GETFL, 0) & ~O_NONBLOCK;
-	igt_skip_on_f(fcntl(tst->kmsg, F_SETFL, flags) == -1,
+	igt_skip_on((flags = fcntl(tst->kmsg, F_GETFL, 0), flags < 0));
+	igt_skip_on_f(fcntl(tst->kmsg, F_SETFL, flags & ~O_NONBLOCK) == -1,
 		      "Could not set /dev/kmsg to blocking mode\n");
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 01/11] lib/kunit: Fix handling of potential errors from F_GETFL
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

Function fcntl(..., F_GETFL, ...) that returns file status flags may also
return a negative error code.  Handle that error instead of blindly using
the returned value as flags.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index d98e6c5f9e..05ff178b27 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -783,8 +783,8 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
 
-	flags = fcntl(tst->kmsg, F_GETFL, 0) & ~O_NONBLOCK;
-	igt_skip_on_f(fcntl(tst->kmsg, F_SETFL, flags) == -1,
+	igt_skip_on((flags = fcntl(tst->kmsg, F_GETFL, 0), flags < 0));
+	igt_skip_on_f(fcntl(tst->kmsg, F_SETFL, flags & ~O_NONBLOCK) == -1,
 		      "Could not set /dev/kmsg to blocking mode\n");
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 01/11] lib/kunit: Fix handling of potential errors from F_GETFL
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Function fcntl(..., F_GETFL, ...) that returns file status flags may also
return a negative error code.  Handle that error instead of blindly using
the returned value as flags.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index d98e6c5f9e..05ff178b27 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -783,8 +783,8 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
 
-	flags = fcntl(tst->kmsg, F_GETFL, 0) & ~O_NONBLOCK;
-	igt_skip_on_f(fcntl(tst->kmsg, F_SETFL, flags) == -1,
+	igt_skip_on((flags = fcntl(tst->kmsg, F_GETFL, 0), flags < 0));
+	igt_skip_on_f(fcntl(tst->kmsg, F_SETFL, flags & ~O_NONBLOCK) == -1,
 		      "Could not set /dev/kmsg to blocking mode\n");
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 02/11] lib/kunit: Be more verbose on errors
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Use a more verbose variant of igt_fail() when failing a dynamic sub-
subtest on kernel taint.  Also, print a debug message on string
duplication failure.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 05ff178b27..df0e650d49 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -834,7 +834,7 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 			if (!pthread_tryjoin_np(modprobe_thread, NULL))
 				igt_assert_eq(modprobe.err, 0);
 
-			igt_fail_on(igt_kernel_tainted(&taints));
+			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
 		free(result);
@@ -861,7 +861,7 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 */
 	if (!name) {
 		name = strdup(module_name);
-		if (name) {
+		if (!igt_debug_on(!name)) {
 			char *suffix = strstr(name, "_test");
 
 			if (!suffix)
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 02/11] lib/kunit: Be more verbose on errors
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

Use a more verbose variant of igt_fail() when failing a dynamic sub-
subtest on kernel taint.  Also, print a debug message on string
duplication failure.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 05ff178b27..df0e650d49 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -834,7 +834,7 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 			if (!pthread_tryjoin_np(modprobe_thread, NULL))
 				igt_assert_eq(modprobe.err, 0);
 
-			igt_fail_on(igt_kernel_tainted(&taints));
+			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
 		free(result);
@@ -861,7 +861,7 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 */
 	if (!name) {
 		name = strdup(module_name);
-		if (name) {
+		if (!igt_debug_on(!name)) {
 			char *suffix = strstr(name, "_test");
 
 			if (!suffix)
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 02/11] lib/kunit: Be more verbose on errors
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Use a more verbose variant of igt_fail() when failing a dynamic sub-
subtest on kernel taint.  Also, print a debug message on string
duplication failure.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 05ff178b27..df0e650d49 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -834,7 +834,7 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 			if (!pthread_tryjoin_np(modprobe_thread, NULL))
 				igt_assert_eq(modprobe.err, 0);
 
-			igt_fail_on(igt_kernel_tainted(&taints));
+			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
 		free(result);
@@ -861,7 +861,7 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 */
 	if (!name) {
 		name = strdup(module_name);
-		if (name) {
+		if (!igt_debug_on(!name)) {
 			char *suffix = strstr(name, "_test");
 
 			if (!suffix)
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 03/11] lib/kunit: Fix misplaced igt_kunit() doc
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

When igt_kunit() was converted to a helper and wrapped with a new function
promoted to take the name and role of the library API, related
documentation was left unchanged and still placed in front the demoted
function.  Update that documentation and move it to where it now belongs.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index df0e650d49..426ae5b26f 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -762,15 +762,6 @@ static void *modprobe_task(void *arg)
 	return NULL;
 }
 
-/**
- * igt_kunit:
- * @module_name: the name of the module
- * @opts: options to load the module
- *
- * Loads the test module, parses its (k)tap dmesg output, then unloads it
- *
- * Returns: IGT default codes
- */
 static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 {
 	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
@@ -849,6 +840,14 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 	igt_skip_on_f(ret, "KTAP parser failed\n");
 }
 
+/**
+ * igt_kunit:
+ * @module_name: the name of the module
+ * @name: the name of subtest, if different from that derived from module name
+ * @opts: options to load the module
+ *
+ * Loads the test module, parses its (k)tap dmesg output, then unloads it
+ */
 void igt_kunit(const char *module_name, const char *name, const char *opts)
 {
 	struct igt_ktest tst = { .kmsg = -1, };
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 03/11] lib/kunit: Fix misplaced igt_kunit() doc
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

When igt_kunit() was converted to a helper and wrapped with a new function
promoted to take the name and role of the library API, related
documentation was left unchanged and still placed in front the demoted
function.  Update that documentation and move it to where it now belongs.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index df0e650d49..426ae5b26f 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -762,15 +762,6 @@ static void *modprobe_task(void *arg)
 	return NULL;
 }
 
-/**
- * igt_kunit:
- * @module_name: the name of the module
- * @opts: options to load the module
- *
- * Loads the test module, parses its (k)tap dmesg output, then unloads it
- *
- * Returns: IGT default codes
- */
 static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 {
 	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
@@ -849,6 +840,14 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 	igt_skip_on_f(ret, "KTAP parser failed\n");
 }
 
+/**
+ * igt_kunit:
+ * @module_name: the name of the module
+ * @name: the name of subtest, if different from that derived from module name
+ * @opts: options to load the module
+ *
+ * Loads the test module, parses its (k)tap dmesg output, then unloads it
+ */
 void igt_kunit(const char *module_name, const char *name, const char *opts)
 {
 	struct igt_ktest tst = { .kmsg = -1, };
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 03/11] lib/kunit: Fix misplaced igt_kunit() doc
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

When igt_kunit() was converted to a helper and wrapped with a new function
promoted to take the name and role of the library API, related
documentation was left unchanged and still placed in front the demoted
function.  Update that documentation and move it to where it now belongs.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 lib/igt_kmod.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index df0e650d49..426ae5b26f 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -762,15 +762,6 @@ static void *modprobe_task(void *arg)
 	return NULL;
 }
 
-/**
- * igt_kunit:
- * @module_name: the name of the module
- * @opts: options to load the module
- *
- * Loads the test module, parses its (k)tap dmesg output, then unloads it
- *
- * Returns: IGT default codes
- */
 static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 {
 	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
@@ -849,6 +840,14 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 	igt_skip_on_f(ret, "KTAP parser failed\n");
 }
 
+/**
+ * igt_kunit:
+ * @module_name: the name of the module
+ * @name: the name of subtest, if different from that derived from module name
+ * @opts: options to load the module
+ *
+ * Loads the test module, parses its (k)tap dmesg output, then unloads it
+ */
 void igt_kunit(const char *module_name, const char *name, const char *opts)
 {
 	struct igt_ktest tst = { .kmsg = -1, };
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

There was an attempt to parse KTAP reports in the background while a kunit
test module is loading.  However, since dynamic sub-subtests can be
executed only from the main thread, that attempt was not quite successful,
as IGT results from all executed kunit test cases were generated only
after loading of kunit test module completed.

Now that the parser maintains its state and we can call it separately for
each input line of a KTAP report, it is perfectly possible to call the
parser from the main thread while the module is loading in the background,
and convert results from kunit test cases immediately to results of IGT
dynamic sub-subtests by running an igt_dynamic() section for each result
as soon as returned by the parser.

Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
result obtained from igt_ktap_parse() called from the main thread.

Also, drop no longer needed functions from igt_ktap soruces.

v3: Fix ktap structure not freed on lseek error,
  - fix initial SIGCHLD handler not restored,
  - fix missing handling of potential errors returned by sigaction,
  - fix potential race of read() vs. ptherad_kill(), use robust mutex for
    synchronization with modprobe thread,
  - fix potentially illegal use of igt_assert() called outside of
    dynamic sub-subtest section,
  - fix unsupported exit code potentially passed to igt_fail(),
  - no need to fail a dynamic sub-subtest on potential KTAP parser error
    after a valid result from the parser has been processed,
  - fix trailing newlines missing from error messages,
  - add more debug statements,
  - integrate common code around kunit_result_free() into it.
v2: Interrupt blocking read() on modprobe failure.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
---
 lib/igt_kmod.c | 261 +++++++++++++++++++----
 lib/igt_ktap.c | 568 -------------------------------------------------
 lib/igt_ktap.h |  22 --
 3 files changed, 222 insertions(+), 629 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 426ae5b26f..7bca4cdaab 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016 Intel Corporation
+ * Copyright © 2016-2023 Intel Corporation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -26,7 +26,12 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/utsname.h>
+#include <unistd.h>
+
+#include "assembler/brw_compat.h"	/* [un]likely() */
 
 #include "igt_aux.h"
 #include "igt_core.h"
@@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
 }
 
 struct modprobe_data {
+	pthread_t parent;
 	struct kmod_module *kmod;
 	const char *opts;
 	int err;
+	pthread_mutex_t lock;
+	pthread_t thread;
 };
 
 static void *modprobe_task(void *arg)
@@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
 
 	data->err = modprobe(data->kmod, data->opts);
 
+	if (igt_debug_on(data->err)) {
+		int err;
+
+		while (err = pthread_mutex_trylock(&data->lock),
+		       err && !igt_debug_on(err != EBUSY))
+			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
+	} else {
+		/* let main thread use mutex to detect modprobe completion */
+		igt_debug_on(pthread_mutex_lock(&data->lock));
+	}
+
 	return NULL;
 }
 
+static void kunit_sigchld_handler(int signal)
+{
+	return;
+}
+
+static int kunit_kmsg_result_get(struct igt_list_head *results,
+				 struct modprobe_data *modprobe,
+				 int fd, struct igt_ktap_results *ktap)
+{
+	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
+			 *saved;
+	char record[BUF_LEN + 1], *buf;
+	unsigned long taints;
+	int ret;
+
+	do {
+		int err;
+
+		if (igt_debug_on(igt_kernel_tainted(&taints)))
+			return -ENOTRECOVERABLE;
+
+		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
+		if (err == -1)
+			return -errno;
+		else if (unlikely(err))
+			return err;
+
+		err = pthread_mutex_lock(&modprobe->lock);
+		switch (err) {
+		case EOWNERDEAD:
+			/* leave the mutex unrecoverable */
+			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+			__attribute__ ((fallthrough));
+		case ENOTRECOVERABLE:
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+			if (igt_debug_on(modprobe->err))
+				return modprobe->err;
+			break;
+		case 0:
+			break;
+		default:
+			igt_debug("pthread_mutex_lock() error: %d\n", err);
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+			return -err;
+		}
+
+		ret = read(fd, record, BUF_LEN);
+
+		if (!err) {
+			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+		}
+
+		if (igt_debug_on(!ret))
+			return -ENODATA;
+		if (igt_debug_on(ret == -1))
+			return -errno;
+		if (unlikely(igt_debug_on(ret < 0)))
+			break;
+
+		/* skip kmsg continuation lines */
+		if (igt_debug_on(*record == ' '))
+			continue;
+
+		/* NULL-terminate the record */
+		record[ret] = '\0';
+
+		/* detect start of log message, continue if not found */
+		buf = strchrnul(record, ';');
+		if (igt_debug_on(*buf == '\0'))
+			continue;
+		buf++;
+
+		ret = igt_ktap_parse(buf, ktap);
+		if (!ret || igt_debug_on(ret != -EINPROGRESS))
+			break;
+	} while (igt_list_empty(results));
+
+	return ret;
+}
+
+static void kunit_result_free(struct igt_ktap_result **r,
+			      char **suite_name, char **case_name)
+{
+	if (!*r)
+		return;
+
+	igt_list_del(&(*r)->link);
+
+	if ((*r)->suite_name != *suite_name) {
+		free(*suite_name);
+		*suite_name = (*r)->suite_name;
+	}
+
+	if ((*r)->case_name != *case_name) {
+		free(*case_name);
+		*case_name = (*r)->case_name;
+	}
+
+	free((*r)->msg);
+	free(*r);
+	*r = NULL;
+}
+
 static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 {
-	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
-	struct kmod_module *kunit_kmod;
-	bool is_builtin;
-	struct ktap_test_results *results;
-	pthread_t modprobe_thread;
+	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
+	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *r, *rn;
+	struct igt_ktap_results *ktap;
+	pthread_mutexattr_t attr;
+	IGT_LIST_HEAD(results);
 	unsigned long taints;
 	int flags, ret;
 
@@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
-	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
-	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
-	kmod_module_unref(kunit_kmod);
+	igt_skip_on(pthread_mutexattr_init(&attr));
+	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
+	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
 
-	results = ktap_parser_start(tst->kmsg, is_builtin);
+	ktap = igt_ktap_alloc(&results);
+	igt_require(ktap);
 
-	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
+	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
 					modprobe_task, &modprobe))) {
-		ktap_parser_cancel();
-		igt_ignore_warn(ktap_parser_stop());
+		igt_ktap_free(ktap);
 		igt_skip("Failed to create a modprobe thread\n");
 	}
 
-	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
-	{
-		struct ktap_test_results_element *result;
-
-		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
-			ktap_parser_cancel();
+	do {
+		ret = kunit_kmsg_result_get(&results, &modprobe,
+					    tst->kmsg, ktap);
+		if (igt_debug_on(ret && ret != -EINPROGRESS))
 			break;
-		}
 
-		if (igt_kernel_tainted(&taints)) {
-			ktap_parser_cancel();
-			pthread_cancel(modprobe_thread);
+		if (igt_debug_on(igt_list_empty(&results)))
 			break;
-		}
 
-		pthread_mutex_lock(&results->mutex);
-		if (igt_list_empty(&results->list)) {
-			pthread_mutex_unlock(&results->mutex);
-			continue;
-		}
+		r = igt_list_first_entry(&results, r, link);
 
-		result = igt_list_first_entry(&results->list, result, link);
+		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
+			if (r->code == IGT_EXIT_INVALID) {
+				/* parametrized test case, get actual result */
+				kunit_result_free(&r, &suite_name, &case_name);
 
-		igt_list_del(&result->link);
-		pthread_mutex_unlock(&results->mutex);
+				igt_assert(igt_list_empty(&results));
 
-		igt_dynamic(result->test_name) {
-			igt_assert(READ_ONCE(result->passed));
+				ret = kunit_kmsg_result_get(&results, &modprobe,
+							    tst->kmsg, ktap);
+				if (ret != -EINPROGRESS)
+					igt_fail_on(ret);
+
+				igt_fail_on(igt_list_empty(&results));
+
+				r = igt_list_first_entry(&results, r, link);
+
+				igt_fail_on_f(strcmp(r->suite_name, suite_name),
+					      "suite_name expected: %s, got: %s\n",
+					      suite_name, r->suite_name);
+				igt_fail_on_f(strcmp(r->case_name, case_name),
+					      "case_name expected: %s, got: %s\n",
+					      case_name, r->case_name);
+			}
 
-			if (!pthread_tryjoin_np(modprobe_thread, NULL))
+			igt_assert_neq(r->code, IGT_EXIT_INVALID);
+
+			if (r->msg && *r->msg) {
+				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
+					      "%s\n", r->msg);
+				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
+					      "%s\n", r->msg);
+				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
+					      "%s\n", r->msg);
+			} else {
+				igt_skip_on(r->code == IGT_EXIT_SKIP);
+				igt_fail_on(r->code == IGT_EXIT_FAILURE);
+				if (r->code == IGT_EXIT_ABORT)
+					igt_fail(r->code);
+			}
+			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
+
+			switch (pthread_mutex_lock(&modprobe.lock)) {
+			case 0:
+				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+				break;
+			case EOWNERDEAD:
+				/* leave the mutex unrecoverable */
+				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+				__attribute__ ((fallthrough));
+			case ENOTRECOVERABLE:
 				igt_assert_eq(modprobe.err, 0);
+				break;
+			default:
+				igt_debug("pthread_mutex_lock() failed\n");
+				break;
+			}
 
 			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
-		free(result);
+		kunit_result_free(&r, &suite_name, &case_name);
+
+	} while (ret == -EINPROGRESS);
+
+	igt_list_for_each_entry_safe(r, rn, &results, link)
+		kunit_result_free(&r, &suite_name, &case_name);
+
+	free(case_name);
+	free(suite_name);
+
+	switch (pthread_mutex_lock(&modprobe.lock)) {
+	case 0:
+		igt_debug_on(pthread_cancel(modprobe.thread));
+		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+		igt_debug_on(pthread_join(modprobe.thread, NULL));
+		break;
+	case EOWNERDEAD:
+		/* leave the mutex unrecoverable */
+		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+		break;
+	case ENOTRECOVERABLE:
+		break;
+	default:
+		igt_debug("pthread_mutex_lock() failed\n");
+		igt_debug_on(pthread_join(modprobe.thread, NULL));
+		break;
 	}
 
-	pthread_join(modprobe_thread, NULL);
-
-	ret = ktap_parser_stop();
+	igt_ktap_free(ktap);
 
 	igt_skip_on(modprobe.err);
 	igt_skip_on(igt_kernel_tainted(&taints));
diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
index 5eac102417..53a6c63288 100644
--- a/lib/igt_ktap.c
+++ b/lib/igt_ktap.c
@@ -4,17 +4,11 @@
  * Copyright © 2023 Intel Corporation
  */
 
-#include <ctype.h>
-#include <limits.h>
-#include <libkmod.h>
-#include <pthread.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
-#include "igt_aux.h"
 #include "igt_core.h"
 #include "igt_ktap.h"
 #include "igt_list.h"
@@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
 {
 	free(ktap);
 }
-
-#define DELIMITER "-"
-
-struct ktap_parser_args {
-	int fd;
-	bool is_builtin;
-	int ret;
-} ktap_args;
-
-static struct ktap_test_results results;
-
-static int log_to_end(enum igt_log_level level, int fd,
-		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
-
-/**
- * log_to_end:
- * @level: #igt_log_level
- * @record: record to store the read data
- * @format: format string
- * @...: optional arguments used in the format string
- *
- * This is an altered version of the generic structured logging helper function
- * igt_log capable of reading to the end of a given line.
- *
- * Returns: 0 for success, or -2 if there's an error reading from the file
- */
-static int log_to_end(enum igt_log_level level, int fd,
-		      char *record, const char *format, ...)
-{
-	va_list args;
-	const char *lend;
-
-	/* Cutoff after newline character, in order to not display garbage */
-	char *cutoff = strchr(record, '\n');
-	if (cutoff) {
-		if (cutoff - record < BUF_LEN)
-			cutoff[1] = '\0';
-	}
-
-	va_start(args, format);
-	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
-	va_end(args);
-
-	lend = strchrnul(record, '\n');
-	while (*lend == '\0') {
-		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
-
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("an error occurred while reading kmsg: %m\n");
-
-			return -2;
-		}
-
-		lend = strchrnul(record, '\n');
-	}
-	return 0;
-}
-
-/**
- * lookup_value:
- * @haystack: the string to search in
- * @needle: the string to search for
- *
- * Returns: the value of the needle in the haystack, or -1 if not found.
- */
-static long lookup_value(const char *haystack, const char *needle)
-{
-	const char *needle_rptr;
-	char *needle_end;
-	long num;
-
-	needle_rptr = strcasestr(haystack, needle);
-
-	if (needle_rptr == NULL)
-		return -1;
-
-	/* Skip search string and whitespaces after it */
-	needle_rptr += strlen(needle);
-
-	num = strtol(needle_rptr, &needle_end, 10);
-
-	if (needle_rptr == needle_end)
-		return -1;
-
-	if (num == LONG_MIN || num == LONG_MAX)
-		return 0;
-
-	return num > 0 ? num : 0;
-}
-
-/**
- * tap_version_present:
- * @record: buffer with tap data
- * @print_info: whether tap version should be printed or not
- *
- * Returns:
- * 0 if not found
- * 1 if found
- */
-static int tap_version_present(char* record, bool print_info)
-{
-	/*
-	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
-	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
-	 *
-	 * but actually isn't, as it currently depends on the KUnit module
-	 * being built-in, so we can't rely on it every time
-	 */
-	const char *version_rptr = strcasestr(record, "TAP version ");
-	char *cutoff;
-
-	if (version_rptr == NULL)
-		return 0;
-
-	/* Cutoff after newline character, in order to not display garbage */
-	cutoff = strchr(version_rptr, '\n');
-	if (cutoff)
-		cutoff[0] = '\0';
-
-	if (print_info)
-		igt_info("%s\n", version_rptr);
-
-	return 1;
-}
-
-/**
- * find_next_tap_subtest:
- * @fd: file descriptor
- * @record: buffer used to read fd
- * @is_builtin: whether KUnit is built-in or not
- *
- * Returns:
- * 0 if there's missing information
- * -1 if not found
- * -2 if there are problems while reading the file.
- * any other value corresponds to the amount of cases of the next (sub)test
- */
-static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
-{
-	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
-	long test_count;
-	char *cutoff;
-
-	test_name[0] = '\0';
-	test_name[BUF_LEN] = '\0';
-
-	test_lookup_str = " subtest: ";
-	subtest_lookup_str = " test: ";
-
-	if (!tap_version_present(record, true))
-		return -1;
-
-	if (is_builtin) {
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("an error occurred while reading kmsg: %m\n");
-
-			return -2;
-		}
-	}
-
-	name_rptr = strcasestr(record, test_lookup_str);
-	if (name_rptr != NULL) {
-		name_rptr += strlen(test_lookup_str);
-	} else {
-		name_rptr = strcasestr(record, subtest_lookup_str);
-		if (name_rptr != NULL)
-			name_rptr += strlen(subtest_lookup_str);
-	}
-
-	if (name_rptr == NULL) {
-		if (!is_builtin)
-			/* We've probably found nothing */
-			return -1;
-		igt_info("Missing test name\n");
-	} else {
-		strncpy(test_name, name_rptr, BUF_LEN);
-		/* Cutoff after newline character, in order to not display garbage */
-		cutoff = strchr(test_name, '\n');
-		if (cutoff)
-			cutoff[0] = '\0';
-
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("unknown error reading kmsg (%m)\n");
-
-			return -2;
-		}
-
-		/* Now we can be sure we found tests */
-		if (!is_builtin)
-			igt_info("KUnit is not built-in, skipping version check...\n");
-	}
-
-	/*
-	 * Total test count will almost always appear as 0..N at the beginning
-	 * of a run, so we use it to reliably identify a new run
-	 */
-	test_count = lookup_value(record, "..");
-
-	if (test_count <= 0) {
-		igt_info("Missing test count\n");
-		if (test_name[0] == '\0')
-			return 0;
-		if (log_to_end(IGT_LOG_INFO, fd, record,
-				"Running some tests in: %s\n",
-				test_name) < 0)
-			return -2;
-		return 0;
-	} else if (test_name[0] == '\0') {
-		igt_info("Running %ld tests...\n", test_count);
-		return 0;
-	}
-
-	if (log_to_end(IGT_LOG_INFO, fd, record,
-			"Executing %ld tests in: %s\n",
-			test_count, test_name) < 0)
-		return -2;
-
-	return test_count;
-}
-
-/**
- * parse_kmsg_for_tap:
- * @fd: file descriptor
- * @record: buffer used to read fd
- * @test_name: buffer to store the test name
- *
- * Returns:
- * 1 if no results were found
- * 0 if a test succeded
- * -1 if a test failed
- * -2 if there are problems reading the file
- */
-static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
-{
-	const char *lstart, *ok_lookup_str, *nok_lookup_str,
-	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
-	char *test_name_end;
-
-	ok_lookup_str = "ok ";
-	nok_lookup_str = "not ok ";
-
-	lstart = strchrnul(record, ';');
-
-	if (*lstart == '\0') {
-		igt_warn("kmsg truncated: output malformed (%m)\n");
-		return -2;
-	}
-
-	lstart++;
-	while (isspace(*lstart))
-		lstart++;
-
-	nok_rptr = strstr(lstart, nok_lookup_str);
-	if (nok_rptr != NULL) {
-		nok_rptr += strlen(nok_lookup_str);
-		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
-			nok_rptr++;
-		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
-		while (!isspace(*test_name_end))
-			test_name_end++;
-		*test_name_end = '\0';
-		if (log_to_end(IGT_LOG_WARN, fd, record,
-			       "%s", lstart) < 0)
-			return -2;
-		return -1;
-	}
-
-	comment_start = strchrnul(lstart, '#');
-
-	/* Check if we're still in a subtest */
-	if (*comment_start != '\0') {
-		comment_start++;
-		value_parse_start = comment_start;
-
-		if (lookup_value(value_parse_start, "fail: ") > 0) {
-			if (log_to_end(IGT_LOG_WARN, fd, record,
-				       "%s", lstart) < 0)
-				return -2;
-			return -1;
-		}
-	}
-
-	ok_rptr = strstr(lstart, ok_lookup_str);
-	if (ok_rptr != NULL) {
-		ok_rptr += strlen(ok_lookup_str);
-		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
-			ok_rptr++;
-		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
-		while (!isspace(*test_name_end))
-			test_name_end++;
-		*test_name_end = '\0';
-		return 0;
-	}
-
-	return 1;
-}
-
-/**
- * parse_tap_level:
- * @fd: file descriptor
- * @base_test_name: test_name from upper recursion level
- * @test_count: test_count of this level
- * @failed_tests: top level failed_tests pointer
- * @found_tests: top level found_tests pointer
- * @is_builtin: whether the KUnit module is built-in or not
- *
- * Returns:
- * 0 if succeded
- * -1 if error occurred
- */
-__maybe_unused
-static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
-			   bool *found_tests, bool is_builtin)
-{
-	char record[BUF_LEN + 1];
-	struct ktap_test_results_element *r;
-	int internal_test_count;
-	char test_name[BUF_LEN + 1];
-	char base_test_name_for_next_level[BUF_LEN + 1];
-
-	for (int i = 0; i < test_count; i++) {
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("error reading kmsg (%m)\n");
-
-			return -1;
-		}
-
-		/* Sublevel found */
-		if (tap_version_present(record, false))
-		{
-			internal_test_count = find_next_tap_subtest(fd, record, test_name,
-								    is_builtin);
-			switch (internal_test_count) {
-			case -2:
-				/* No more data to read */
-				return -1;
-			case -1:
-				/* No test found */
-				return -1;
-			case 0:
-				/* Tests found, but they're missing info */
-				*found_tests = true;
-				return -1;
-			default:
-				*found_tests = true;
-
-				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
-				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
-				    base_test_name_for_next_level[0])
-					strncat(base_test_name_for_next_level, DELIMITER,
-						BUF_LEN - strlen(base_test_name_for_next_level));
-				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
-				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
-
-				if (parse_tap_level(fd, base_test_name_for_next_level,
-						    internal_test_count, failed_tests, found_tests,
-						    is_builtin) == -1)
-					return -1;
-				break;
-			}
-		}
-
-		switch (parse_kmsg_for_tap(fd, record, test_name)) {
-		case -2:
-			return -1;
-		case -1:
-			*failed_tests = true;
-
-			r = malloc(sizeof(*r));
-
-			memcpy(r->test_name, base_test_name, BUF_LEN);
-			if (strlen(r->test_name) < BUF_LEN - 1)
-				if (r->test_name[0])
-					strncat(r->test_name, DELIMITER,
-						BUF_LEN - strlen(r->test_name));
-			memcpy(r->test_name + strlen(r->test_name), test_name,
-			       BUF_LEN - strlen(r->test_name));
-			r->test_name[BUF_LEN] = '\0';
-
-			r->passed = false;
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&r->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-
-			test_name[0] = '\0';
-			break;
-		case 0:
-			r = malloc(sizeof(*r));
-
-			memcpy(r->test_name, base_test_name, BUF_LEN);
-			if (strlen(r->test_name) < BUF_LEN - 1)
-				if (r->test_name[0])
-					strncat(r->test_name, DELIMITER,
-						BUF_LEN - strlen(r->test_name));
-			memcpy(r->test_name + strlen(r->test_name), test_name,
-			       BUF_LEN - strlen(r->test_name));
-			r->test_name[BUF_LEN] = '\0';
-
-			r->passed = true;
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&r->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-
-			test_name[0] = '\0';
-			break;
-		default:
-			break;
-		}
-	}
-	return 0;
-}
-
-/**
- * igt_ktap_parser:
- *
- * This function parses the output of a ktap script and passes it to main thread.
- */
-void *igt_ktap_parser(void *unused)
-{
-	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
-	struct igt_ktap_results *ktap = NULL;
-	int fd = ktap_args.fd;
-	IGT_LIST_HEAD(list);
-	int err;
-
-	ktap = igt_ktap_alloc(&list);
-	if (igt_debug_on(!ktap))
-		goto igt_ktap_parser_end;
-
-	while (err = read(fd, record, BUF_LEN), err > 0) {
-		struct igt_ktap_result *r, *rn;
-
-		/* skip kmsg continuation lines */
-		if (igt_debug_on(*record == ' '))
-			continue;
-
-		/* NULL-terminate the record */
-		record[err] = '\0';
-
-		/* detect start of log message, continue if not found */
-		buf = strchrnul(record, ';');
-		if (igt_debug_on(*buf == '\0'))
-			continue;
-		buf++;
-
-		err = igt_ktap_parse(buf, ktap);
-
-		/* parsing error */
-		if (err && err != -EINPROGRESS)
-			goto igt_ktap_parser_end;
-
-		igt_list_for_each_entry_safe(r, rn, &list, link) {
-			struct ktap_test_results_element *result = NULL;
-			int code = r->code;
-
-			if (code != IGT_EXIT_INVALID)
-				result = calloc(1, sizeof(*result));
-
-			if (result) {
-				snprintf(result->test_name, sizeof(result->test_name),
-					 "%s-%s", r->suite_name, r->case_name);
-
-				if (code == IGT_EXIT_SUCCESS)
-					result->passed = true;
-			}
-
-			igt_list_del(&r->link);
-			if (r->suite_name != suite_name) {
-				free(suite_name);
-				suite_name = r->suite_name;
-			}
-			if (r->case_name != case_name) {
-				free(case_name);
-				case_name = r->case_name;
-			}
-			free(r->msg);
-			free(r);
-
-			/*
-			 * no extra result record expected on start
-			 * of parametrized test case -- skip it
-			 */
-			if (code == IGT_EXIT_INVALID)
-				continue;
-
-			if (!result) {
-				err = -ENOMEM;
-				goto igt_ktap_parser_end;
-			}
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&result->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-		}
-
-		/* end of KTAP report */
-		if (!err)
-			goto igt_ktap_parser_end;
-	}
-
-	if (err < 0) {
-		if (errno == EPIPE)
-			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-		else
-			igt_warn("error reading kmsg (%m)\n");
-	}
-
-igt_ktap_parser_end:
-	free(suite_name);
-	free(case_name);
-
-	if (!err)
-		ktap_args.ret = IGT_EXIT_SUCCESS;
-
-	results.still_running = false;
-
-	if (ktap)
-		igt_ktap_free(ktap);
-
-	return NULL;
-}
-
-static pthread_t ktap_parser_thread;
-
-struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
-{
-	IGT_INIT_LIST_HEAD(&results.list);
-	pthread_mutex_init(&results.mutex, NULL);
-	results.still_running = true;
-
-	ktap_args.fd = fd;
-	ktap_args.is_builtin = is_builtin;
-	ktap_args.ret = IGT_EXIT_FAILURE;
-	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
-
-	return &results;
-}
-
-void ktap_parser_cancel(void)
-{
-	pthread_cancel(ktap_parser_thread);
-}
-
-int ktap_parser_stop(void)
-{
-	pthread_join(ktap_parser_thread, NULL);
-	return ktap_args.ret;
-}
diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
index 6f8da3eab6..c422636bfc 100644
--- a/lib/igt_ktap.h
+++ b/lib/igt_ktap.h
@@ -27,8 +27,6 @@
 
 #define BUF_LEN 4096
 
-#include <pthread.h>
-
 #include "igt_list.h"
 
 struct igt_ktap_result {
@@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
 int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
 void igt_ktap_free(struct igt_ktap_results *ktap);
 
-void *igt_ktap_parser(void *unused);
-
-typedef struct ktap_test_results_element {
-	char test_name[BUF_LEN + 1];
-	bool passed;
-	struct igt_list_head link;
-} ktap_test_results_element;
-
-struct ktap_test_results {
-	struct igt_list_head list;
-	pthread_mutex_t mutex;
-	bool still_running;
-};
-
-
-
-struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
-void ktap_parser_cancel(void);
-int ktap_parser_stop(void);
-
 #endif /* IGT_KTAP_H */
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

There was an attempt to parse KTAP reports in the background while a kunit
test module is loading.  However, since dynamic sub-subtests can be
executed only from the main thread, that attempt was not quite successful,
as IGT results from all executed kunit test cases were generated only
after loading of kunit test module completed.

Now that the parser maintains its state and we can call it separately for
each input line of a KTAP report, it is perfectly possible to call the
parser from the main thread while the module is loading in the background,
and convert results from kunit test cases immediately to results of IGT
dynamic sub-subtests by running an igt_dynamic() section for each result
as soon as returned by the parser.

Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
result obtained from igt_ktap_parse() called from the main thread.

Also, drop no longer needed functions from igt_ktap soruces.

v3: Fix ktap structure not freed on lseek error,
  - fix initial SIGCHLD handler not restored,
  - fix missing handling of potential errors returned by sigaction,
  - fix potential race of read() vs. ptherad_kill(), use robust mutex for
    synchronization with modprobe thread,
  - fix potentially illegal use of igt_assert() called outside of
    dynamic sub-subtest section,
  - fix unsupported exit code potentially passed to igt_fail(),
  - no need to fail a dynamic sub-subtest on potential KTAP parser error
    after a valid result from the parser has been processed,
  - fix trailing newlines missing from error messages,
  - add more debug statements,
  - integrate common code around kunit_result_free() into it.
v2: Interrupt blocking read() on modprobe failure.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
---
 lib/igt_kmod.c | 261 +++++++++++++++++++----
 lib/igt_ktap.c | 568 -------------------------------------------------
 lib/igt_ktap.h |  22 --
 3 files changed, 222 insertions(+), 629 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 426ae5b26f..7bca4cdaab 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016 Intel Corporation
+ * Copyright © 2016-2023 Intel Corporation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -26,7 +26,12 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/utsname.h>
+#include <unistd.h>
+
+#include "assembler/brw_compat.h"	/* [un]likely() */
 
 #include "igt_aux.h"
 #include "igt_core.h"
@@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
 }
 
 struct modprobe_data {
+	pthread_t parent;
 	struct kmod_module *kmod;
 	const char *opts;
 	int err;
+	pthread_mutex_t lock;
+	pthread_t thread;
 };
 
 static void *modprobe_task(void *arg)
@@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
 
 	data->err = modprobe(data->kmod, data->opts);
 
+	if (igt_debug_on(data->err)) {
+		int err;
+
+		while (err = pthread_mutex_trylock(&data->lock),
+		       err && !igt_debug_on(err != EBUSY))
+			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
+	} else {
+		/* let main thread use mutex to detect modprobe completion */
+		igt_debug_on(pthread_mutex_lock(&data->lock));
+	}
+
 	return NULL;
 }
 
+static void kunit_sigchld_handler(int signal)
+{
+	return;
+}
+
+static int kunit_kmsg_result_get(struct igt_list_head *results,
+				 struct modprobe_data *modprobe,
+				 int fd, struct igt_ktap_results *ktap)
+{
+	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
+			 *saved;
+	char record[BUF_LEN + 1], *buf;
+	unsigned long taints;
+	int ret;
+
+	do {
+		int err;
+
+		if (igt_debug_on(igt_kernel_tainted(&taints)))
+			return -ENOTRECOVERABLE;
+
+		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
+		if (err == -1)
+			return -errno;
+		else if (unlikely(err))
+			return err;
+
+		err = pthread_mutex_lock(&modprobe->lock);
+		switch (err) {
+		case EOWNERDEAD:
+			/* leave the mutex unrecoverable */
+			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+			__attribute__ ((fallthrough));
+		case ENOTRECOVERABLE:
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+			if (igt_debug_on(modprobe->err))
+				return modprobe->err;
+			break;
+		case 0:
+			break;
+		default:
+			igt_debug("pthread_mutex_lock() error: %d\n", err);
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+			return -err;
+		}
+
+		ret = read(fd, record, BUF_LEN);
+
+		if (!err) {
+			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+		}
+
+		if (igt_debug_on(!ret))
+			return -ENODATA;
+		if (igt_debug_on(ret == -1))
+			return -errno;
+		if (unlikely(igt_debug_on(ret < 0)))
+			break;
+
+		/* skip kmsg continuation lines */
+		if (igt_debug_on(*record == ' '))
+			continue;
+
+		/* NULL-terminate the record */
+		record[ret] = '\0';
+
+		/* detect start of log message, continue if not found */
+		buf = strchrnul(record, ';');
+		if (igt_debug_on(*buf == '\0'))
+			continue;
+		buf++;
+
+		ret = igt_ktap_parse(buf, ktap);
+		if (!ret || igt_debug_on(ret != -EINPROGRESS))
+			break;
+	} while (igt_list_empty(results));
+
+	return ret;
+}
+
+static void kunit_result_free(struct igt_ktap_result **r,
+			      char **suite_name, char **case_name)
+{
+	if (!*r)
+		return;
+
+	igt_list_del(&(*r)->link);
+
+	if ((*r)->suite_name != *suite_name) {
+		free(*suite_name);
+		*suite_name = (*r)->suite_name;
+	}
+
+	if ((*r)->case_name != *case_name) {
+		free(*case_name);
+		*case_name = (*r)->case_name;
+	}
+
+	free((*r)->msg);
+	free(*r);
+	*r = NULL;
+}
+
 static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 {
-	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
-	struct kmod_module *kunit_kmod;
-	bool is_builtin;
-	struct ktap_test_results *results;
-	pthread_t modprobe_thread;
+	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
+	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *r, *rn;
+	struct igt_ktap_results *ktap;
+	pthread_mutexattr_t attr;
+	IGT_LIST_HEAD(results);
 	unsigned long taints;
 	int flags, ret;
 
@@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
-	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
-	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
-	kmod_module_unref(kunit_kmod);
+	igt_skip_on(pthread_mutexattr_init(&attr));
+	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
+	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
 
-	results = ktap_parser_start(tst->kmsg, is_builtin);
+	ktap = igt_ktap_alloc(&results);
+	igt_require(ktap);
 
-	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
+	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
 					modprobe_task, &modprobe))) {
-		ktap_parser_cancel();
-		igt_ignore_warn(ktap_parser_stop());
+		igt_ktap_free(ktap);
 		igt_skip("Failed to create a modprobe thread\n");
 	}
 
-	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
-	{
-		struct ktap_test_results_element *result;
-
-		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
-			ktap_parser_cancel();
+	do {
+		ret = kunit_kmsg_result_get(&results, &modprobe,
+					    tst->kmsg, ktap);
+		if (igt_debug_on(ret && ret != -EINPROGRESS))
 			break;
-		}
 
-		if (igt_kernel_tainted(&taints)) {
-			ktap_parser_cancel();
-			pthread_cancel(modprobe_thread);
+		if (igt_debug_on(igt_list_empty(&results)))
 			break;
-		}
 
-		pthread_mutex_lock(&results->mutex);
-		if (igt_list_empty(&results->list)) {
-			pthread_mutex_unlock(&results->mutex);
-			continue;
-		}
+		r = igt_list_first_entry(&results, r, link);
 
-		result = igt_list_first_entry(&results->list, result, link);
+		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
+			if (r->code == IGT_EXIT_INVALID) {
+				/* parametrized test case, get actual result */
+				kunit_result_free(&r, &suite_name, &case_name);
 
-		igt_list_del(&result->link);
-		pthread_mutex_unlock(&results->mutex);
+				igt_assert(igt_list_empty(&results));
 
-		igt_dynamic(result->test_name) {
-			igt_assert(READ_ONCE(result->passed));
+				ret = kunit_kmsg_result_get(&results, &modprobe,
+							    tst->kmsg, ktap);
+				if (ret != -EINPROGRESS)
+					igt_fail_on(ret);
+
+				igt_fail_on(igt_list_empty(&results));
+
+				r = igt_list_first_entry(&results, r, link);
+
+				igt_fail_on_f(strcmp(r->suite_name, suite_name),
+					      "suite_name expected: %s, got: %s\n",
+					      suite_name, r->suite_name);
+				igt_fail_on_f(strcmp(r->case_name, case_name),
+					      "case_name expected: %s, got: %s\n",
+					      case_name, r->case_name);
+			}
 
-			if (!pthread_tryjoin_np(modprobe_thread, NULL))
+			igt_assert_neq(r->code, IGT_EXIT_INVALID);
+
+			if (r->msg && *r->msg) {
+				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
+					      "%s\n", r->msg);
+				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
+					      "%s\n", r->msg);
+				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
+					      "%s\n", r->msg);
+			} else {
+				igt_skip_on(r->code == IGT_EXIT_SKIP);
+				igt_fail_on(r->code == IGT_EXIT_FAILURE);
+				if (r->code == IGT_EXIT_ABORT)
+					igt_fail(r->code);
+			}
+			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
+
+			switch (pthread_mutex_lock(&modprobe.lock)) {
+			case 0:
+				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+				break;
+			case EOWNERDEAD:
+				/* leave the mutex unrecoverable */
+				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+				__attribute__ ((fallthrough));
+			case ENOTRECOVERABLE:
 				igt_assert_eq(modprobe.err, 0);
+				break;
+			default:
+				igt_debug("pthread_mutex_lock() failed\n");
+				break;
+			}
 
 			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
-		free(result);
+		kunit_result_free(&r, &suite_name, &case_name);
+
+	} while (ret == -EINPROGRESS);
+
+	igt_list_for_each_entry_safe(r, rn, &results, link)
+		kunit_result_free(&r, &suite_name, &case_name);
+
+	free(case_name);
+	free(suite_name);
+
+	switch (pthread_mutex_lock(&modprobe.lock)) {
+	case 0:
+		igt_debug_on(pthread_cancel(modprobe.thread));
+		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+		igt_debug_on(pthread_join(modprobe.thread, NULL));
+		break;
+	case EOWNERDEAD:
+		/* leave the mutex unrecoverable */
+		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+		break;
+	case ENOTRECOVERABLE:
+		break;
+	default:
+		igt_debug("pthread_mutex_lock() failed\n");
+		igt_debug_on(pthread_join(modprobe.thread, NULL));
+		break;
 	}
 
-	pthread_join(modprobe_thread, NULL);
-
-	ret = ktap_parser_stop();
+	igt_ktap_free(ktap);
 
 	igt_skip_on(modprobe.err);
 	igt_skip_on(igt_kernel_tainted(&taints));
diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
index 5eac102417..53a6c63288 100644
--- a/lib/igt_ktap.c
+++ b/lib/igt_ktap.c
@@ -4,17 +4,11 @@
  * Copyright © 2023 Intel Corporation
  */
 
-#include <ctype.h>
-#include <limits.h>
-#include <libkmod.h>
-#include <pthread.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
-#include "igt_aux.h"
 #include "igt_core.h"
 #include "igt_ktap.h"
 #include "igt_list.h"
@@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
 {
 	free(ktap);
 }
-
-#define DELIMITER "-"
-
-struct ktap_parser_args {
-	int fd;
-	bool is_builtin;
-	int ret;
-} ktap_args;
-
-static struct ktap_test_results results;
-
-static int log_to_end(enum igt_log_level level, int fd,
-		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
-
-/**
- * log_to_end:
- * @level: #igt_log_level
- * @record: record to store the read data
- * @format: format string
- * @...: optional arguments used in the format string
- *
- * This is an altered version of the generic structured logging helper function
- * igt_log capable of reading to the end of a given line.
- *
- * Returns: 0 for success, or -2 if there's an error reading from the file
- */
-static int log_to_end(enum igt_log_level level, int fd,
-		      char *record, const char *format, ...)
-{
-	va_list args;
-	const char *lend;
-
-	/* Cutoff after newline character, in order to not display garbage */
-	char *cutoff = strchr(record, '\n');
-	if (cutoff) {
-		if (cutoff - record < BUF_LEN)
-			cutoff[1] = '\0';
-	}
-
-	va_start(args, format);
-	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
-	va_end(args);
-
-	lend = strchrnul(record, '\n');
-	while (*lend == '\0') {
-		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
-
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("an error occurred while reading kmsg: %m\n");
-
-			return -2;
-		}
-
-		lend = strchrnul(record, '\n');
-	}
-	return 0;
-}
-
-/**
- * lookup_value:
- * @haystack: the string to search in
- * @needle: the string to search for
- *
- * Returns: the value of the needle in the haystack, or -1 if not found.
- */
-static long lookup_value(const char *haystack, const char *needle)
-{
-	const char *needle_rptr;
-	char *needle_end;
-	long num;
-
-	needle_rptr = strcasestr(haystack, needle);
-
-	if (needle_rptr == NULL)
-		return -1;
-
-	/* Skip search string and whitespaces after it */
-	needle_rptr += strlen(needle);
-
-	num = strtol(needle_rptr, &needle_end, 10);
-
-	if (needle_rptr == needle_end)
-		return -1;
-
-	if (num == LONG_MIN || num == LONG_MAX)
-		return 0;
-
-	return num > 0 ? num : 0;
-}
-
-/**
- * tap_version_present:
- * @record: buffer with tap data
- * @print_info: whether tap version should be printed or not
- *
- * Returns:
- * 0 if not found
- * 1 if found
- */
-static int tap_version_present(char* record, bool print_info)
-{
-	/*
-	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
-	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
-	 *
-	 * but actually isn't, as it currently depends on the KUnit module
-	 * being built-in, so we can't rely on it every time
-	 */
-	const char *version_rptr = strcasestr(record, "TAP version ");
-	char *cutoff;
-
-	if (version_rptr == NULL)
-		return 0;
-
-	/* Cutoff after newline character, in order to not display garbage */
-	cutoff = strchr(version_rptr, '\n');
-	if (cutoff)
-		cutoff[0] = '\0';
-
-	if (print_info)
-		igt_info("%s\n", version_rptr);
-
-	return 1;
-}
-
-/**
- * find_next_tap_subtest:
- * @fd: file descriptor
- * @record: buffer used to read fd
- * @is_builtin: whether KUnit is built-in or not
- *
- * Returns:
- * 0 if there's missing information
- * -1 if not found
- * -2 if there are problems while reading the file.
- * any other value corresponds to the amount of cases of the next (sub)test
- */
-static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
-{
-	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
-	long test_count;
-	char *cutoff;
-
-	test_name[0] = '\0';
-	test_name[BUF_LEN] = '\0';
-
-	test_lookup_str = " subtest: ";
-	subtest_lookup_str = " test: ";
-
-	if (!tap_version_present(record, true))
-		return -1;
-
-	if (is_builtin) {
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("an error occurred while reading kmsg: %m\n");
-
-			return -2;
-		}
-	}
-
-	name_rptr = strcasestr(record, test_lookup_str);
-	if (name_rptr != NULL) {
-		name_rptr += strlen(test_lookup_str);
-	} else {
-		name_rptr = strcasestr(record, subtest_lookup_str);
-		if (name_rptr != NULL)
-			name_rptr += strlen(subtest_lookup_str);
-	}
-
-	if (name_rptr == NULL) {
-		if (!is_builtin)
-			/* We've probably found nothing */
-			return -1;
-		igt_info("Missing test name\n");
-	} else {
-		strncpy(test_name, name_rptr, BUF_LEN);
-		/* Cutoff after newline character, in order to not display garbage */
-		cutoff = strchr(test_name, '\n');
-		if (cutoff)
-			cutoff[0] = '\0';
-
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("unknown error reading kmsg (%m)\n");
-
-			return -2;
-		}
-
-		/* Now we can be sure we found tests */
-		if (!is_builtin)
-			igt_info("KUnit is not built-in, skipping version check...\n");
-	}
-
-	/*
-	 * Total test count will almost always appear as 0..N at the beginning
-	 * of a run, so we use it to reliably identify a new run
-	 */
-	test_count = lookup_value(record, "..");
-
-	if (test_count <= 0) {
-		igt_info("Missing test count\n");
-		if (test_name[0] == '\0')
-			return 0;
-		if (log_to_end(IGT_LOG_INFO, fd, record,
-				"Running some tests in: %s\n",
-				test_name) < 0)
-			return -2;
-		return 0;
-	} else if (test_name[0] == '\0') {
-		igt_info("Running %ld tests...\n", test_count);
-		return 0;
-	}
-
-	if (log_to_end(IGT_LOG_INFO, fd, record,
-			"Executing %ld tests in: %s\n",
-			test_count, test_name) < 0)
-		return -2;
-
-	return test_count;
-}
-
-/**
- * parse_kmsg_for_tap:
- * @fd: file descriptor
- * @record: buffer used to read fd
- * @test_name: buffer to store the test name
- *
- * Returns:
- * 1 if no results were found
- * 0 if a test succeded
- * -1 if a test failed
- * -2 if there are problems reading the file
- */
-static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
-{
-	const char *lstart, *ok_lookup_str, *nok_lookup_str,
-	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
-	char *test_name_end;
-
-	ok_lookup_str = "ok ";
-	nok_lookup_str = "not ok ";
-
-	lstart = strchrnul(record, ';');
-
-	if (*lstart == '\0') {
-		igt_warn("kmsg truncated: output malformed (%m)\n");
-		return -2;
-	}
-
-	lstart++;
-	while (isspace(*lstart))
-		lstart++;
-
-	nok_rptr = strstr(lstart, nok_lookup_str);
-	if (nok_rptr != NULL) {
-		nok_rptr += strlen(nok_lookup_str);
-		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
-			nok_rptr++;
-		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
-		while (!isspace(*test_name_end))
-			test_name_end++;
-		*test_name_end = '\0';
-		if (log_to_end(IGT_LOG_WARN, fd, record,
-			       "%s", lstart) < 0)
-			return -2;
-		return -1;
-	}
-
-	comment_start = strchrnul(lstart, '#');
-
-	/* Check if we're still in a subtest */
-	if (*comment_start != '\0') {
-		comment_start++;
-		value_parse_start = comment_start;
-
-		if (lookup_value(value_parse_start, "fail: ") > 0) {
-			if (log_to_end(IGT_LOG_WARN, fd, record,
-				       "%s", lstart) < 0)
-				return -2;
-			return -1;
-		}
-	}
-
-	ok_rptr = strstr(lstart, ok_lookup_str);
-	if (ok_rptr != NULL) {
-		ok_rptr += strlen(ok_lookup_str);
-		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
-			ok_rptr++;
-		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
-		while (!isspace(*test_name_end))
-			test_name_end++;
-		*test_name_end = '\0';
-		return 0;
-	}
-
-	return 1;
-}
-
-/**
- * parse_tap_level:
- * @fd: file descriptor
- * @base_test_name: test_name from upper recursion level
- * @test_count: test_count of this level
- * @failed_tests: top level failed_tests pointer
- * @found_tests: top level found_tests pointer
- * @is_builtin: whether the KUnit module is built-in or not
- *
- * Returns:
- * 0 if succeded
- * -1 if error occurred
- */
-__maybe_unused
-static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
-			   bool *found_tests, bool is_builtin)
-{
-	char record[BUF_LEN + 1];
-	struct ktap_test_results_element *r;
-	int internal_test_count;
-	char test_name[BUF_LEN + 1];
-	char base_test_name_for_next_level[BUF_LEN + 1];
-
-	for (int i = 0; i < test_count; i++) {
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("error reading kmsg (%m)\n");
-
-			return -1;
-		}
-
-		/* Sublevel found */
-		if (tap_version_present(record, false))
-		{
-			internal_test_count = find_next_tap_subtest(fd, record, test_name,
-								    is_builtin);
-			switch (internal_test_count) {
-			case -2:
-				/* No more data to read */
-				return -1;
-			case -1:
-				/* No test found */
-				return -1;
-			case 0:
-				/* Tests found, but they're missing info */
-				*found_tests = true;
-				return -1;
-			default:
-				*found_tests = true;
-
-				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
-				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
-				    base_test_name_for_next_level[0])
-					strncat(base_test_name_for_next_level, DELIMITER,
-						BUF_LEN - strlen(base_test_name_for_next_level));
-				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
-				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
-
-				if (parse_tap_level(fd, base_test_name_for_next_level,
-						    internal_test_count, failed_tests, found_tests,
-						    is_builtin) == -1)
-					return -1;
-				break;
-			}
-		}
-
-		switch (parse_kmsg_for_tap(fd, record, test_name)) {
-		case -2:
-			return -1;
-		case -1:
-			*failed_tests = true;
-
-			r = malloc(sizeof(*r));
-
-			memcpy(r->test_name, base_test_name, BUF_LEN);
-			if (strlen(r->test_name) < BUF_LEN - 1)
-				if (r->test_name[0])
-					strncat(r->test_name, DELIMITER,
-						BUF_LEN - strlen(r->test_name));
-			memcpy(r->test_name + strlen(r->test_name), test_name,
-			       BUF_LEN - strlen(r->test_name));
-			r->test_name[BUF_LEN] = '\0';
-
-			r->passed = false;
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&r->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-
-			test_name[0] = '\0';
-			break;
-		case 0:
-			r = malloc(sizeof(*r));
-
-			memcpy(r->test_name, base_test_name, BUF_LEN);
-			if (strlen(r->test_name) < BUF_LEN - 1)
-				if (r->test_name[0])
-					strncat(r->test_name, DELIMITER,
-						BUF_LEN - strlen(r->test_name));
-			memcpy(r->test_name + strlen(r->test_name), test_name,
-			       BUF_LEN - strlen(r->test_name));
-			r->test_name[BUF_LEN] = '\0';
-
-			r->passed = true;
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&r->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-
-			test_name[0] = '\0';
-			break;
-		default:
-			break;
-		}
-	}
-	return 0;
-}
-
-/**
- * igt_ktap_parser:
- *
- * This function parses the output of a ktap script and passes it to main thread.
- */
-void *igt_ktap_parser(void *unused)
-{
-	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
-	struct igt_ktap_results *ktap = NULL;
-	int fd = ktap_args.fd;
-	IGT_LIST_HEAD(list);
-	int err;
-
-	ktap = igt_ktap_alloc(&list);
-	if (igt_debug_on(!ktap))
-		goto igt_ktap_parser_end;
-
-	while (err = read(fd, record, BUF_LEN), err > 0) {
-		struct igt_ktap_result *r, *rn;
-
-		/* skip kmsg continuation lines */
-		if (igt_debug_on(*record == ' '))
-			continue;
-
-		/* NULL-terminate the record */
-		record[err] = '\0';
-
-		/* detect start of log message, continue if not found */
-		buf = strchrnul(record, ';');
-		if (igt_debug_on(*buf == '\0'))
-			continue;
-		buf++;
-
-		err = igt_ktap_parse(buf, ktap);
-
-		/* parsing error */
-		if (err && err != -EINPROGRESS)
-			goto igt_ktap_parser_end;
-
-		igt_list_for_each_entry_safe(r, rn, &list, link) {
-			struct ktap_test_results_element *result = NULL;
-			int code = r->code;
-
-			if (code != IGT_EXIT_INVALID)
-				result = calloc(1, sizeof(*result));
-
-			if (result) {
-				snprintf(result->test_name, sizeof(result->test_name),
-					 "%s-%s", r->suite_name, r->case_name);
-
-				if (code == IGT_EXIT_SUCCESS)
-					result->passed = true;
-			}
-
-			igt_list_del(&r->link);
-			if (r->suite_name != suite_name) {
-				free(suite_name);
-				suite_name = r->suite_name;
-			}
-			if (r->case_name != case_name) {
-				free(case_name);
-				case_name = r->case_name;
-			}
-			free(r->msg);
-			free(r);
-
-			/*
-			 * no extra result record expected on start
-			 * of parametrized test case -- skip it
-			 */
-			if (code == IGT_EXIT_INVALID)
-				continue;
-
-			if (!result) {
-				err = -ENOMEM;
-				goto igt_ktap_parser_end;
-			}
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&result->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-		}
-
-		/* end of KTAP report */
-		if (!err)
-			goto igt_ktap_parser_end;
-	}
-
-	if (err < 0) {
-		if (errno == EPIPE)
-			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-		else
-			igt_warn("error reading kmsg (%m)\n");
-	}
-
-igt_ktap_parser_end:
-	free(suite_name);
-	free(case_name);
-
-	if (!err)
-		ktap_args.ret = IGT_EXIT_SUCCESS;
-
-	results.still_running = false;
-
-	if (ktap)
-		igt_ktap_free(ktap);
-
-	return NULL;
-}
-
-static pthread_t ktap_parser_thread;
-
-struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
-{
-	IGT_INIT_LIST_HEAD(&results.list);
-	pthread_mutex_init(&results.mutex, NULL);
-	results.still_running = true;
-
-	ktap_args.fd = fd;
-	ktap_args.is_builtin = is_builtin;
-	ktap_args.ret = IGT_EXIT_FAILURE;
-	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
-
-	return &results;
-}
-
-void ktap_parser_cancel(void)
-{
-	pthread_cancel(ktap_parser_thread);
-}
-
-int ktap_parser_stop(void)
-{
-	pthread_join(ktap_parser_thread, NULL);
-	return ktap_args.ret;
-}
diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
index 6f8da3eab6..c422636bfc 100644
--- a/lib/igt_ktap.h
+++ b/lib/igt_ktap.h
@@ -27,8 +27,6 @@
 
 #define BUF_LEN 4096
 
-#include <pthread.h>
-
 #include "igt_list.h"
 
 struct igt_ktap_result {
@@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
 int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
 void igt_ktap_free(struct igt_ktap_results *ktap);
 
-void *igt_ktap_parser(void *unused);
-
-typedef struct ktap_test_results_element {
-	char test_name[BUF_LEN + 1];
-	bool passed;
-	struct igt_list_head link;
-} ktap_test_results_element;
-
-struct ktap_test_results {
-	struct igt_list_head list;
-	pthread_mutex_t mutex;
-	bool still_running;
-};
-
-
-
-struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
-void ktap_parser_cancel(void);
-int ktap_parser_stop(void);
-
 #endif /* IGT_KTAP_H */
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

There was an attempt to parse KTAP reports in the background while a kunit
test module is loading.  However, since dynamic sub-subtests can be
executed only from the main thread, that attempt was not quite successful,
as IGT results from all executed kunit test cases were generated only
after loading of kunit test module completed.

Now that the parser maintains its state and we can call it separately for
each input line of a KTAP report, it is perfectly possible to call the
parser from the main thread while the module is loading in the background,
and convert results from kunit test cases immediately to results of IGT
dynamic sub-subtests by running an igt_dynamic() section for each result
as soon as returned by the parser.

Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
result obtained from igt_ktap_parse() called from the main thread.

Also, drop no longer needed functions from igt_ktap soruces.

v3: Fix ktap structure not freed on lseek error,
  - fix initial SIGCHLD handler not restored,
  - fix missing handling of potential errors returned by sigaction,
  - fix potential race of read() vs. ptherad_kill(), use robust mutex for
    synchronization with modprobe thread,
  - fix potentially illegal use of igt_assert() called outside of
    dynamic sub-subtest section,
  - fix unsupported exit code potentially passed to igt_fail(),
  - no need to fail a dynamic sub-subtest on potential KTAP parser error
    after a valid result from the parser has been processed,
  - fix trailing newlines missing from error messages,
  - add more debug statements,
  - integrate common code around kunit_result_free() into it.
v2: Interrupt blocking read() on modprobe failure.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
---
 lib/igt_kmod.c | 261 +++++++++++++++++++----
 lib/igt_ktap.c | 568 -------------------------------------------------
 lib/igt_ktap.h |  22 --
 3 files changed, 222 insertions(+), 629 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 426ae5b26f..7bca4cdaab 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016 Intel Corporation
+ * Copyright © 2016-2023 Intel Corporation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -26,7 +26,12 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/utsname.h>
+#include <unistd.h>
+
+#include "assembler/brw_compat.h"	/* [un]likely() */
 
 #include "igt_aux.h"
 #include "igt_core.h"
@@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
 }
 
 struct modprobe_data {
+	pthread_t parent;
 	struct kmod_module *kmod;
 	const char *opts;
 	int err;
+	pthread_mutex_t lock;
+	pthread_t thread;
 };
 
 static void *modprobe_task(void *arg)
@@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
 
 	data->err = modprobe(data->kmod, data->opts);
 
+	if (igt_debug_on(data->err)) {
+		int err;
+
+		while (err = pthread_mutex_trylock(&data->lock),
+		       err && !igt_debug_on(err != EBUSY))
+			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
+	} else {
+		/* let main thread use mutex to detect modprobe completion */
+		igt_debug_on(pthread_mutex_lock(&data->lock));
+	}
+
 	return NULL;
 }
 
+static void kunit_sigchld_handler(int signal)
+{
+	return;
+}
+
+static int kunit_kmsg_result_get(struct igt_list_head *results,
+				 struct modprobe_data *modprobe,
+				 int fd, struct igt_ktap_results *ktap)
+{
+	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
+			 *saved;
+	char record[BUF_LEN + 1], *buf;
+	unsigned long taints;
+	int ret;
+
+	do {
+		int err;
+
+		if (igt_debug_on(igt_kernel_tainted(&taints)))
+			return -ENOTRECOVERABLE;
+
+		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
+		if (err == -1)
+			return -errno;
+		else if (unlikely(err))
+			return err;
+
+		err = pthread_mutex_lock(&modprobe->lock);
+		switch (err) {
+		case EOWNERDEAD:
+			/* leave the mutex unrecoverable */
+			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+			__attribute__ ((fallthrough));
+		case ENOTRECOVERABLE:
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+			if (igt_debug_on(modprobe->err))
+				return modprobe->err;
+			break;
+		case 0:
+			break;
+		default:
+			igt_debug("pthread_mutex_lock() error: %d\n", err);
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+			return -err;
+		}
+
+		ret = read(fd, record, BUF_LEN);
+
+		if (!err) {
+			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+		}
+
+		if (igt_debug_on(!ret))
+			return -ENODATA;
+		if (igt_debug_on(ret == -1))
+			return -errno;
+		if (unlikely(igt_debug_on(ret < 0)))
+			break;
+
+		/* skip kmsg continuation lines */
+		if (igt_debug_on(*record == ' '))
+			continue;
+
+		/* NULL-terminate the record */
+		record[ret] = '\0';
+
+		/* detect start of log message, continue if not found */
+		buf = strchrnul(record, ';');
+		if (igt_debug_on(*buf == '\0'))
+			continue;
+		buf++;
+
+		ret = igt_ktap_parse(buf, ktap);
+		if (!ret || igt_debug_on(ret != -EINPROGRESS))
+			break;
+	} while (igt_list_empty(results));
+
+	return ret;
+}
+
+static void kunit_result_free(struct igt_ktap_result **r,
+			      char **suite_name, char **case_name)
+{
+	if (!*r)
+		return;
+
+	igt_list_del(&(*r)->link);
+
+	if ((*r)->suite_name != *suite_name) {
+		free(*suite_name);
+		*suite_name = (*r)->suite_name;
+	}
+
+	if ((*r)->case_name != *case_name) {
+		free(*case_name);
+		*case_name = (*r)->case_name;
+	}
+
+	free((*r)->msg);
+	free(*r);
+	*r = NULL;
+}
+
 static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 {
-	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
-	struct kmod_module *kunit_kmod;
-	bool is_builtin;
-	struct ktap_test_results *results;
-	pthread_t modprobe_thread;
+	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
+	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *r, *rn;
+	struct igt_ktap_results *ktap;
+	pthread_mutexattr_t attr;
+	IGT_LIST_HEAD(results);
 	unsigned long taints;
 	int flags, ret;
 
@@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
-	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
-	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
-	kmod_module_unref(kunit_kmod);
+	igt_skip_on(pthread_mutexattr_init(&attr));
+	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
+	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
 
-	results = ktap_parser_start(tst->kmsg, is_builtin);
+	ktap = igt_ktap_alloc(&results);
+	igt_require(ktap);
 
-	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
+	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
 					modprobe_task, &modprobe))) {
-		ktap_parser_cancel();
-		igt_ignore_warn(ktap_parser_stop());
+		igt_ktap_free(ktap);
 		igt_skip("Failed to create a modprobe thread\n");
 	}
 
-	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
-	{
-		struct ktap_test_results_element *result;
-
-		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
-			ktap_parser_cancel();
+	do {
+		ret = kunit_kmsg_result_get(&results, &modprobe,
+					    tst->kmsg, ktap);
+		if (igt_debug_on(ret && ret != -EINPROGRESS))
 			break;
-		}
 
-		if (igt_kernel_tainted(&taints)) {
-			ktap_parser_cancel();
-			pthread_cancel(modprobe_thread);
+		if (igt_debug_on(igt_list_empty(&results)))
 			break;
-		}
 
-		pthread_mutex_lock(&results->mutex);
-		if (igt_list_empty(&results->list)) {
-			pthread_mutex_unlock(&results->mutex);
-			continue;
-		}
+		r = igt_list_first_entry(&results, r, link);
 
-		result = igt_list_first_entry(&results->list, result, link);
+		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
+			if (r->code == IGT_EXIT_INVALID) {
+				/* parametrized test case, get actual result */
+				kunit_result_free(&r, &suite_name, &case_name);
 
-		igt_list_del(&result->link);
-		pthread_mutex_unlock(&results->mutex);
+				igt_assert(igt_list_empty(&results));
 
-		igt_dynamic(result->test_name) {
-			igt_assert(READ_ONCE(result->passed));
+				ret = kunit_kmsg_result_get(&results, &modprobe,
+							    tst->kmsg, ktap);
+				if (ret != -EINPROGRESS)
+					igt_fail_on(ret);
+
+				igt_fail_on(igt_list_empty(&results));
+
+				r = igt_list_first_entry(&results, r, link);
+
+				igt_fail_on_f(strcmp(r->suite_name, suite_name),
+					      "suite_name expected: %s, got: %s\n",
+					      suite_name, r->suite_name);
+				igt_fail_on_f(strcmp(r->case_name, case_name),
+					      "case_name expected: %s, got: %s\n",
+					      case_name, r->case_name);
+			}
 
-			if (!pthread_tryjoin_np(modprobe_thread, NULL))
+			igt_assert_neq(r->code, IGT_EXIT_INVALID);
+
+			if (r->msg && *r->msg) {
+				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
+					      "%s\n", r->msg);
+				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
+					      "%s\n", r->msg);
+				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
+					      "%s\n", r->msg);
+			} else {
+				igt_skip_on(r->code == IGT_EXIT_SKIP);
+				igt_fail_on(r->code == IGT_EXIT_FAILURE);
+				if (r->code == IGT_EXIT_ABORT)
+					igt_fail(r->code);
+			}
+			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
+
+			switch (pthread_mutex_lock(&modprobe.lock)) {
+			case 0:
+				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+				break;
+			case EOWNERDEAD:
+				/* leave the mutex unrecoverable */
+				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+				__attribute__ ((fallthrough));
+			case ENOTRECOVERABLE:
 				igt_assert_eq(modprobe.err, 0);
+				break;
+			default:
+				igt_debug("pthread_mutex_lock() failed\n");
+				break;
+			}
 
 			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
-		free(result);
+		kunit_result_free(&r, &suite_name, &case_name);
+
+	} while (ret == -EINPROGRESS);
+
+	igt_list_for_each_entry_safe(r, rn, &results, link)
+		kunit_result_free(&r, &suite_name, &case_name);
+
+	free(case_name);
+	free(suite_name);
+
+	switch (pthread_mutex_lock(&modprobe.lock)) {
+	case 0:
+		igt_debug_on(pthread_cancel(modprobe.thread));
+		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+		igt_debug_on(pthread_join(modprobe.thread, NULL));
+		break;
+	case EOWNERDEAD:
+		/* leave the mutex unrecoverable */
+		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+		break;
+	case ENOTRECOVERABLE:
+		break;
+	default:
+		igt_debug("pthread_mutex_lock() failed\n");
+		igt_debug_on(pthread_join(modprobe.thread, NULL));
+		break;
 	}
 
-	pthread_join(modprobe_thread, NULL);
-
-	ret = ktap_parser_stop();
+	igt_ktap_free(ktap);
 
 	igt_skip_on(modprobe.err);
 	igt_skip_on(igt_kernel_tainted(&taints));
diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
index 5eac102417..53a6c63288 100644
--- a/lib/igt_ktap.c
+++ b/lib/igt_ktap.c
@@ -4,17 +4,11 @@
  * Copyright © 2023 Intel Corporation
  */
 
-#include <ctype.h>
-#include <limits.h>
-#include <libkmod.h>
-#include <pthread.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
-#include "igt_aux.h"
 #include "igt_core.h"
 #include "igt_ktap.h"
 #include "igt_list.h"
@@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
 {
 	free(ktap);
 }
-
-#define DELIMITER "-"
-
-struct ktap_parser_args {
-	int fd;
-	bool is_builtin;
-	int ret;
-} ktap_args;
-
-static struct ktap_test_results results;
-
-static int log_to_end(enum igt_log_level level, int fd,
-		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
-
-/**
- * log_to_end:
- * @level: #igt_log_level
- * @record: record to store the read data
- * @format: format string
- * @...: optional arguments used in the format string
- *
- * This is an altered version of the generic structured logging helper function
- * igt_log capable of reading to the end of a given line.
- *
- * Returns: 0 for success, or -2 if there's an error reading from the file
- */
-static int log_to_end(enum igt_log_level level, int fd,
-		      char *record, const char *format, ...)
-{
-	va_list args;
-	const char *lend;
-
-	/* Cutoff after newline character, in order to not display garbage */
-	char *cutoff = strchr(record, '\n');
-	if (cutoff) {
-		if (cutoff - record < BUF_LEN)
-			cutoff[1] = '\0';
-	}
-
-	va_start(args, format);
-	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
-	va_end(args);
-
-	lend = strchrnul(record, '\n');
-	while (*lend == '\0') {
-		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
-
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("an error occurred while reading kmsg: %m\n");
-
-			return -2;
-		}
-
-		lend = strchrnul(record, '\n');
-	}
-	return 0;
-}
-
-/**
- * lookup_value:
- * @haystack: the string to search in
- * @needle: the string to search for
- *
- * Returns: the value of the needle in the haystack, or -1 if not found.
- */
-static long lookup_value(const char *haystack, const char *needle)
-{
-	const char *needle_rptr;
-	char *needle_end;
-	long num;
-
-	needle_rptr = strcasestr(haystack, needle);
-
-	if (needle_rptr == NULL)
-		return -1;
-
-	/* Skip search string and whitespaces after it */
-	needle_rptr += strlen(needle);
-
-	num = strtol(needle_rptr, &needle_end, 10);
-
-	if (needle_rptr == needle_end)
-		return -1;
-
-	if (num == LONG_MIN || num == LONG_MAX)
-		return 0;
-
-	return num > 0 ? num : 0;
-}
-
-/**
- * tap_version_present:
- * @record: buffer with tap data
- * @print_info: whether tap version should be printed or not
- *
- * Returns:
- * 0 if not found
- * 1 if found
- */
-static int tap_version_present(char* record, bool print_info)
-{
-	/*
-	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
-	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
-	 *
-	 * but actually isn't, as it currently depends on the KUnit module
-	 * being built-in, so we can't rely on it every time
-	 */
-	const char *version_rptr = strcasestr(record, "TAP version ");
-	char *cutoff;
-
-	if (version_rptr == NULL)
-		return 0;
-
-	/* Cutoff after newline character, in order to not display garbage */
-	cutoff = strchr(version_rptr, '\n');
-	if (cutoff)
-		cutoff[0] = '\0';
-
-	if (print_info)
-		igt_info("%s\n", version_rptr);
-
-	return 1;
-}
-
-/**
- * find_next_tap_subtest:
- * @fd: file descriptor
- * @record: buffer used to read fd
- * @is_builtin: whether KUnit is built-in or not
- *
- * Returns:
- * 0 if there's missing information
- * -1 if not found
- * -2 if there are problems while reading the file.
- * any other value corresponds to the amount of cases of the next (sub)test
- */
-static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
-{
-	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
-	long test_count;
-	char *cutoff;
-
-	test_name[0] = '\0';
-	test_name[BUF_LEN] = '\0';
-
-	test_lookup_str = " subtest: ";
-	subtest_lookup_str = " test: ";
-
-	if (!tap_version_present(record, true))
-		return -1;
-
-	if (is_builtin) {
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("an error occurred while reading kmsg: %m\n");
-
-			return -2;
-		}
-	}
-
-	name_rptr = strcasestr(record, test_lookup_str);
-	if (name_rptr != NULL) {
-		name_rptr += strlen(test_lookup_str);
-	} else {
-		name_rptr = strcasestr(record, subtest_lookup_str);
-		if (name_rptr != NULL)
-			name_rptr += strlen(subtest_lookup_str);
-	}
-
-	if (name_rptr == NULL) {
-		if (!is_builtin)
-			/* We've probably found nothing */
-			return -1;
-		igt_info("Missing test name\n");
-	} else {
-		strncpy(test_name, name_rptr, BUF_LEN);
-		/* Cutoff after newline character, in order to not display garbage */
-		cutoff = strchr(test_name, '\n');
-		if (cutoff)
-			cutoff[0] = '\0';
-
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("unknown error reading kmsg (%m)\n");
-
-			return -2;
-		}
-
-		/* Now we can be sure we found tests */
-		if (!is_builtin)
-			igt_info("KUnit is not built-in, skipping version check...\n");
-	}
-
-	/*
-	 * Total test count will almost always appear as 0..N at the beginning
-	 * of a run, so we use it to reliably identify a new run
-	 */
-	test_count = lookup_value(record, "..");
-
-	if (test_count <= 0) {
-		igt_info("Missing test count\n");
-		if (test_name[0] == '\0')
-			return 0;
-		if (log_to_end(IGT_LOG_INFO, fd, record,
-				"Running some tests in: %s\n",
-				test_name) < 0)
-			return -2;
-		return 0;
-	} else if (test_name[0] == '\0') {
-		igt_info("Running %ld tests...\n", test_count);
-		return 0;
-	}
-
-	if (log_to_end(IGT_LOG_INFO, fd, record,
-			"Executing %ld tests in: %s\n",
-			test_count, test_name) < 0)
-		return -2;
-
-	return test_count;
-}
-
-/**
- * parse_kmsg_for_tap:
- * @fd: file descriptor
- * @record: buffer used to read fd
- * @test_name: buffer to store the test name
- *
- * Returns:
- * 1 if no results were found
- * 0 if a test succeded
- * -1 if a test failed
- * -2 if there are problems reading the file
- */
-static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
-{
-	const char *lstart, *ok_lookup_str, *nok_lookup_str,
-	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
-	char *test_name_end;
-
-	ok_lookup_str = "ok ";
-	nok_lookup_str = "not ok ";
-
-	lstart = strchrnul(record, ';');
-
-	if (*lstart == '\0') {
-		igt_warn("kmsg truncated: output malformed (%m)\n");
-		return -2;
-	}
-
-	lstart++;
-	while (isspace(*lstart))
-		lstart++;
-
-	nok_rptr = strstr(lstart, nok_lookup_str);
-	if (nok_rptr != NULL) {
-		nok_rptr += strlen(nok_lookup_str);
-		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
-			nok_rptr++;
-		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
-		while (!isspace(*test_name_end))
-			test_name_end++;
-		*test_name_end = '\0';
-		if (log_to_end(IGT_LOG_WARN, fd, record,
-			       "%s", lstart) < 0)
-			return -2;
-		return -1;
-	}
-
-	comment_start = strchrnul(lstart, '#');
-
-	/* Check if we're still in a subtest */
-	if (*comment_start != '\0') {
-		comment_start++;
-		value_parse_start = comment_start;
-
-		if (lookup_value(value_parse_start, "fail: ") > 0) {
-			if (log_to_end(IGT_LOG_WARN, fd, record,
-				       "%s", lstart) < 0)
-				return -2;
-			return -1;
-		}
-	}
-
-	ok_rptr = strstr(lstart, ok_lookup_str);
-	if (ok_rptr != NULL) {
-		ok_rptr += strlen(ok_lookup_str);
-		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
-			ok_rptr++;
-		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
-		while (!isspace(*test_name_end))
-			test_name_end++;
-		*test_name_end = '\0';
-		return 0;
-	}
-
-	return 1;
-}
-
-/**
- * parse_tap_level:
- * @fd: file descriptor
- * @base_test_name: test_name from upper recursion level
- * @test_count: test_count of this level
- * @failed_tests: top level failed_tests pointer
- * @found_tests: top level found_tests pointer
- * @is_builtin: whether the KUnit module is built-in or not
- *
- * Returns:
- * 0 if succeded
- * -1 if error occurred
- */
-__maybe_unused
-static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
-			   bool *found_tests, bool is_builtin)
-{
-	char record[BUF_LEN + 1];
-	struct ktap_test_results_element *r;
-	int internal_test_count;
-	char test_name[BUF_LEN + 1];
-	char base_test_name_for_next_level[BUF_LEN + 1];
-
-	for (int i = 0; i < test_count; i++) {
-		if (read(fd, record, BUF_LEN) < 0) {
-			if (errno == EPIPE)
-				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-			else
-				igt_warn("error reading kmsg (%m)\n");
-
-			return -1;
-		}
-
-		/* Sublevel found */
-		if (tap_version_present(record, false))
-		{
-			internal_test_count = find_next_tap_subtest(fd, record, test_name,
-								    is_builtin);
-			switch (internal_test_count) {
-			case -2:
-				/* No more data to read */
-				return -1;
-			case -1:
-				/* No test found */
-				return -1;
-			case 0:
-				/* Tests found, but they're missing info */
-				*found_tests = true;
-				return -1;
-			default:
-				*found_tests = true;
-
-				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
-				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
-				    base_test_name_for_next_level[0])
-					strncat(base_test_name_for_next_level, DELIMITER,
-						BUF_LEN - strlen(base_test_name_for_next_level));
-				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
-				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
-
-				if (parse_tap_level(fd, base_test_name_for_next_level,
-						    internal_test_count, failed_tests, found_tests,
-						    is_builtin) == -1)
-					return -1;
-				break;
-			}
-		}
-
-		switch (parse_kmsg_for_tap(fd, record, test_name)) {
-		case -2:
-			return -1;
-		case -1:
-			*failed_tests = true;
-
-			r = malloc(sizeof(*r));
-
-			memcpy(r->test_name, base_test_name, BUF_LEN);
-			if (strlen(r->test_name) < BUF_LEN - 1)
-				if (r->test_name[0])
-					strncat(r->test_name, DELIMITER,
-						BUF_LEN - strlen(r->test_name));
-			memcpy(r->test_name + strlen(r->test_name), test_name,
-			       BUF_LEN - strlen(r->test_name));
-			r->test_name[BUF_LEN] = '\0';
-
-			r->passed = false;
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&r->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-
-			test_name[0] = '\0';
-			break;
-		case 0:
-			r = malloc(sizeof(*r));
-
-			memcpy(r->test_name, base_test_name, BUF_LEN);
-			if (strlen(r->test_name) < BUF_LEN - 1)
-				if (r->test_name[0])
-					strncat(r->test_name, DELIMITER,
-						BUF_LEN - strlen(r->test_name));
-			memcpy(r->test_name + strlen(r->test_name), test_name,
-			       BUF_LEN - strlen(r->test_name));
-			r->test_name[BUF_LEN] = '\0';
-
-			r->passed = true;
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&r->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-
-			test_name[0] = '\0';
-			break;
-		default:
-			break;
-		}
-	}
-	return 0;
-}
-
-/**
- * igt_ktap_parser:
- *
- * This function parses the output of a ktap script and passes it to main thread.
- */
-void *igt_ktap_parser(void *unused)
-{
-	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
-	struct igt_ktap_results *ktap = NULL;
-	int fd = ktap_args.fd;
-	IGT_LIST_HEAD(list);
-	int err;
-
-	ktap = igt_ktap_alloc(&list);
-	if (igt_debug_on(!ktap))
-		goto igt_ktap_parser_end;
-
-	while (err = read(fd, record, BUF_LEN), err > 0) {
-		struct igt_ktap_result *r, *rn;
-
-		/* skip kmsg continuation lines */
-		if (igt_debug_on(*record == ' '))
-			continue;
-
-		/* NULL-terminate the record */
-		record[err] = '\0';
-
-		/* detect start of log message, continue if not found */
-		buf = strchrnul(record, ';');
-		if (igt_debug_on(*buf == '\0'))
-			continue;
-		buf++;
-
-		err = igt_ktap_parse(buf, ktap);
-
-		/* parsing error */
-		if (err && err != -EINPROGRESS)
-			goto igt_ktap_parser_end;
-
-		igt_list_for_each_entry_safe(r, rn, &list, link) {
-			struct ktap_test_results_element *result = NULL;
-			int code = r->code;
-
-			if (code != IGT_EXIT_INVALID)
-				result = calloc(1, sizeof(*result));
-
-			if (result) {
-				snprintf(result->test_name, sizeof(result->test_name),
-					 "%s-%s", r->suite_name, r->case_name);
-
-				if (code == IGT_EXIT_SUCCESS)
-					result->passed = true;
-			}
-
-			igt_list_del(&r->link);
-			if (r->suite_name != suite_name) {
-				free(suite_name);
-				suite_name = r->suite_name;
-			}
-			if (r->case_name != case_name) {
-				free(case_name);
-				case_name = r->case_name;
-			}
-			free(r->msg);
-			free(r);
-
-			/*
-			 * no extra result record expected on start
-			 * of parametrized test case -- skip it
-			 */
-			if (code == IGT_EXIT_INVALID)
-				continue;
-
-			if (!result) {
-				err = -ENOMEM;
-				goto igt_ktap_parser_end;
-			}
-
-			pthread_mutex_lock(&results.mutex);
-			igt_list_add_tail(&result->link, &results.list);
-			pthread_mutex_unlock(&results.mutex);
-		}
-
-		/* end of KTAP report */
-		if (!err)
-			goto igt_ktap_parser_end;
-	}
-
-	if (err < 0) {
-		if (errno == EPIPE)
-			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
-		else
-			igt_warn("error reading kmsg (%m)\n");
-	}
-
-igt_ktap_parser_end:
-	free(suite_name);
-	free(case_name);
-
-	if (!err)
-		ktap_args.ret = IGT_EXIT_SUCCESS;
-
-	results.still_running = false;
-
-	if (ktap)
-		igt_ktap_free(ktap);
-
-	return NULL;
-}
-
-static pthread_t ktap_parser_thread;
-
-struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
-{
-	IGT_INIT_LIST_HEAD(&results.list);
-	pthread_mutex_init(&results.mutex, NULL);
-	results.still_running = true;
-
-	ktap_args.fd = fd;
-	ktap_args.is_builtin = is_builtin;
-	ktap_args.ret = IGT_EXIT_FAILURE;
-	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
-
-	return &results;
-}
-
-void ktap_parser_cancel(void)
-{
-	pthread_cancel(ktap_parser_thread);
-}
-
-int ktap_parser_stop(void)
-{
-	pthread_join(ktap_parser_thread, NULL);
-	return ktap_args.ret;
-}
diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
index 6f8da3eab6..c422636bfc 100644
--- a/lib/igt_ktap.h
+++ b/lib/igt_ktap.h
@@ -27,8 +27,6 @@
 
 #define BUF_LEN 4096
 
-#include <pthread.h>
-
 #include "igt_list.h"
 
 struct igt_ktap_result {
@@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
 int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
 void igt_ktap_free(struct igt_ktap_results *ktap);
 
-void *igt_ktap_parser(void *unused);
-
-typedef struct ktap_test_results_element {
-	char test_name[BUF_LEN + 1];
-	bool passed;
-	struct igt_list_head link;
-} ktap_test_results_element;
-
-struct ktap_test_results {
-	struct igt_list_head list;
-	pthread_mutex_t mutex;
-	bool still_running;
-};
-
-
-
-struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
-void ktap_parser_cancel(void);
-int ktap_parser_stop(void);
-
 #endif /* IGT_KTAP_H */
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 05/11] lib/kunit: Omit suite name prefix if the same as subtest name
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Kunit test modules usually contain one test suite, named after the module
name with the trailing "_test" or "_kunit" suffix omitted.  Since we
follow the same convention when we derive subtest names from module names,
there is a great chance that those two names match.  Take this into
account when composing names for IGT dynamic sub-subtest names and drop
the leading test suite name component when it is the same as subtest name.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 lib/igt_kmod.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 7bca4cdaab..387efbb59f 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -885,7 +885,8 @@ static void kunit_result_free(struct igt_ktap_result **r,
 	*r = NULL;
 }
 
-static void __igt_kunit(struct igt_ktest *tst, const char *opts)
+static void
+__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 {
 	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
@@ -928,7 +929,11 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 		r = igt_list_first_entry(&results, r, link);
 
-		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
+		igt_dynamic_f("%s%s%s",
+			      strcmp(r->suite_name, name) ?  r->suite_name : "",
+			      strcmp(r->suite_name, name) ? "-" : "",
+			      r->case_name) {
+
 			if (r->code == IGT_EXIT_INVALID) {
 				/* parametrized test case, get actual result */
 				kunit_result_free(&r, &suite_name, &case_name);
@@ -1069,7 +1074,7 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 * and for documentation.
 	 */
 	igt_subtest_with_dynamic(name)
-		__igt_kunit(&tst, opts);
+		__igt_kunit(&tst, name, opts);
 
 	igt_fixture
 		igt_ktest_end(&tst);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 05/11] lib/kunit: Omit suite name prefix if the same as subtest name
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

Kunit test modules usually contain one test suite, named after the module
name with the trailing "_test" or "_kunit" suffix omitted.  Since we
follow the same convention when we derive subtest names from module names,
there is a great chance that those two names match.  Take this into
account when composing names for IGT dynamic sub-subtest names and drop
the leading test suite name component when it is the same as subtest name.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 lib/igt_kmod.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 7bca4cdaab..387efbb59f 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -885,7 +885,8 @@ static void kunit_result_free(struct igt_ktap_result **r,
 	*r = NULL;
 }
 
-static void __igt_kunit(struct igt_ktest *tst, const char *opts)
+static void
+__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 {
 	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
@@ -928,7 +929,11 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 		r = igt_list_first_entry(&results, r, link);
 
-		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
+		igt_dynamic_f("%s%s%s",
+			      strcmp(r->suite_name, name) ?  r->suite_name : "",
+			      strcmp(r->suite_name, name) ? "-" : "",
+			      r->case_name) {
+
 			if (r->code == IGT_EXIT_INVALID) {
 				/* parametrized test case, get actual result */
 				kunit_result_free(&r, &suite_name, &case_name);
@@ -1069,7 +1074,7 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 * and for documentation.
 	 */
 	igt_subtest_with_dynamic(name)
-		__igt_kunit(&tst, opts);
+		__igt_kunit(&tst, name, opts);
 
 	igt_fixture
 		igt_ktest_end(&tst);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 05/11] lib/kunit: Omit suite name prefix if the same as subtest name
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Kunit test modules usually contain one test suite, named after the module
name with the trailing "_test" or "_kunit" suffix omitted.  Since we
follow the same convention when we derive subtest names from module names,
there is a great chance that those two names match.  Take this into
account when composing names for IGT dynamic sub-subtest names and drop
the leading test suite name component when it is the same as subtest name.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 lib/igt_kmod.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 7bca4cdaab..387efbb59f 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -885,7 +885,8 @@ static void kunit_result_free(struct igt_ktap_result **r,
 	*r = NULL;
 }
 
-static void __igt_kunit(struct igt_ktest *tst, const char *opts)
+static void
+__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 {
 	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
@@ -928,7 +929,11 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
 
 		r = igt_list_first_entry(&results, r, link);
 
-		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
+		igt_dynamic_f("%s%s%s",
+			      strcmp(r->suite_name, name) ?  r->suite_name : "",
+			      strcmp(r->suite_name, name) ? "-" : "",
+			      r->case_name) {
+
 			if (r->code == IGT_EXIT_INVALID) {
 				/* parametrized test case, get actual result */
 				kunit_result_free(&r, &suite_name, &case_name);
@@ -1069,7 +1074,7 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 * and for documentation.
 	 */
 	igt_subtest_with_dynamic(name)
-		__igt_kunit(&tst, opts);
+		__igt_kunit(&tst, name, opts);
 
 	igt_fixture
 		igt_ktest_end(&tst);
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 06/11] tests/kms_selftest: Let subtest names match suite names
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

There is a rule specified in Kunit Test Style and Nomenclature guidelines
[1] that states modules should be named after the test suite, followed by
_test.  Of course, that rule applies only to modules that provide one test
suite per module.

As long as that rule is obeyed by authors of Kunit test modules, there is
no need to hardcode related IGT subtest names in IGT source code.  We are
already able to derive subtest names from module names, with their _test
or _kunit suffixes stripped.  We may expect those names will match Kunit
suite names provided by the modules.

Drop custom subtest names from IGT Kunit tests that still use them.
However, keep the mechanism that allows us to provide a name that differs
from that derived from module name.  That will be required if we ever need
to support a kunit test module that provides multiple test suites (think
of i915 selftests code converted to kunit and the i915 module potentially
providing three test suites: mock, live and perf).

[1] https://docs.kernel.org/dev-tools/kunit/style.html

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 tests/kms_selftest.c | 37 ++++++++++++++++---------------------
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/tests/kms_selftest.c b/tests/kms_selftest.c
index 080ffdf2c0..6618dbe50b 100644
--- a/tests/kms_selftest.c
+++ b/tests/kms_selftest.c
@@ -37,35 +37,30 @@
  *
  * arg[1]:
  *
- * @drm_cmdline:            drm cmdline
- * @drm_damage:             drm damage
- * @drm_dp_mst:             drm dp mst
+ * @drm_cmdline_parser:     drm cmdline parser
+ * @drm_damage_helper:      drm damage helper
+ * @drm_dp_mst_helper:      drm dp mst helper
  * @drm_format_helper:      drm format helper
  * @drm_format:             drm format
- * @drm_plane:              drm plane
- * @framebuffer:            framebuffer
+ * @drm_plane_helper:       drm plane helper
+ * @drm_framebuffer:        drm framebuffer
  */
 
 IGT_TEST_DESCRIPTION("Basic sanity check of KMS selftests.");
 
-struct kms_kunittests {
-	const char *kunit;
-	const char *name;
-};
-
 igt_main
 {
-	static const struct kms_kunittests kunit_subtests[] = {
-		{ "drm_cmdline_parser_test",	"drm_cmdline" },
-		{ "drm_damage_helper_test",	"drm_damage" },
-		{ "drm_dp_mst_helper_test",	"drm_dp_mst" },
-		{ "drm_format_helper_test",	"drm_format_helper" },
-		{ "drm_format_test",		"drm_format" },
-		{ "drm_framebuffer_test",	"framebuffer" },
-		{ "drm_plane_helper_test",	"drm_plane" },
-		{ NULL, NULL}
+	static const char *kunit_subtests[] = {
+		"drm_cmdline_parser_test",
+		"drm_damage_helper_test",
+		"drm_dp_mst_helper_test",
+		"drm_format_helper_test",
+		"drm_format_test",
+		"drm_framebuffer_test",
+		"drm_plane_helper_test",
+		NULL,
 	};
 
-	for (int i = 0; kunit_subtests[i].kunit != NULL; i++)
-		igt_kunit(kunit_subtests[i].kunit, kunit_subtests[i].name, NULL);
+	for (int i = 0; kunit_subtests[i] != NULL; i++)
+		igt_kunit(kunit_subtests[i], NULL, NULL);
 }
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 06/11] tests/kms_selftest: Let subtest names match suite names
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

There is a rule specified in Kunit Test Style and Nomenclature guidelines
[1] that states modules should be named after the test suite, followed by
_test.  Of course, that rule applies only to modules that provide one test
suite per module.

As long as that rule is obeyed by authors of Kunit test modules, there is
no need to hardcode related IGT subtest names in IGT source code.  We are
already able to derive subtest names from module names, with their _test
or _kunit suffixes stripped.  We may expect those names will match Kunit
suite names provided by the modules.

Drop custom subtest names from IGT Kunit tests that still use them.
However, keep the mechanism that allows us to provide a name that differs
from that derived from module name.  That will be required if we ever need
to support a kunit test module that provides multiple test suites (think
of i915 selftests code converted to kunit and the i915 module potentially
providing three test suites: mock, live and perf).

[1] https://docs.kernel.org/dev-tools/kunit/style.html

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 tests/kms_selftest.c | 37 ++++++++++++++++---------------------
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/tests/kms_selftest.c b/tests/kms_selftest.c
index 080ffdf2c0..6618dbe50b 100644
--- a/tests/kms_selftest.c
+++ b/tests/kms_selftest.c
@@ -37,35 +37,30 @@
  *
  * arg[1]:
  *
- * @drm_cmdline:            drm cmdline
- * @drm_damage:             drm damage
- * @drm_dp_mst:             drm dp mst
+ * @drm_cmdline_parser:     drm cmdline parser
+ * @drm_damage_helper:      drm damage helper
+ * @drm_dp_mst_helper:      drm dp mst helper
  * @drm_format_helper:      drm format helper
  * @drm_format:             drm format
- * @drm_plane:              drm plane
- * @framebuffer:            framebuffer
+ * @drm_plane_helper:       drm plane helper
+ * @drm_framebuffer:        drm framebuffer
  */
 
 IGT_TEST_DESCRIPTION("Basic sanity check of KMS selftests.");
 
-struct kms_kunittests {
-	const char *kunit;
-	const char *name;
-};
-
 igt_main
 {
-	static const struct kms_kunittests kunit_subtests[] = {
-		{ "drm_cmdline_parser_test",	"drm_cmdline" },
-		{ "drm_damage_helper_test",	"drm_damage" },
-		{ "drm_dp_mst_helper_test",	"drm_dp_mst" },
-		{ "drm_format_helper_test",	"drm_format_helper" },
-		{ "drm_format_test",		"drm_format" },
-		{ "drm_framebuffer_test",	"framebuffer" },
-		{ "drm_plane_helper_test",	"drm_plane" },
-		{ NULL, NULL}
+	static const char *kunit_subtests[] = {
+		"drm_cmdline_parser_test",
+		"drm_damage_helper_test",
+		"drm_dp_mst_helper_test",
+		"drm_format_helper_test",
+		"drm_format_test",
+		"drm_framebuffer_test",
+		"drm_plane_helper_test",
+		NULL,
 	};
 
-	for (int i = 0; kunit_subtests[i].kunit != NULL; i++)
-		igt_kunit(kunit_subtests[i].kunit, kunit_subtests[i].name, NULL);
+	for (int i = 0; kunit_subtests[i] != NULL; i++)
+		igt_kunit(kunit_subtests[i], NULL, NULL);
 }
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 06/11] tests/kms_selftest: Let subtest names match suite names
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

There is a rule specified in Kunit Test Style and Nomenclature guidelines
[1] that states modules should be named after the test suite, followed by
_test.  Of course, that rule applies only to modules that provide one test
suite per module.

As long as that rule is obeyed by authors of Kunit test modules, there is
no need to hardcode related IGT subtest names in IGT source code.  We are
already able to derive subtest names from module names, with their _test
or _kunit suffixes stripped.  We may expect those names will match Kunit
suite names provided by the modules.

Drop custom subtest names from IGT Kunit tests that still use them.
However, keep the mechanism that allows us to provide a name that differs
from that derived from module name.  That will be required if we ever need
to support a kunit test module that provides multiple test suites (think
of i915 selftests code converted to kunit and the i915 module potentially
providing three test suites: mock, live and perf).

[1] https://docs.kernel.org/dev-tools/kunit/style.html

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
---
 tests/kms_selftest.c | 37 ++++++++++++++++---------------------
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/tests/kms_selftest.c b/tests/kms_selftest.c
index 080ffdf2c0..6618dbe50b 100644
--- a/tests/kms_selftest.c
+++ b/tests/kms_selftest.c
@@ -37,35 +37,30 @@
  *
  * arg[1]:
  *
- * @drm_cmdline:            drm cmdline
- * @drm_damage:             drm damage
- * @drm_dp_mst:             drm dp mst
+ * @drm_cmdline_parser:     drm cmdline parser
+ * @drm_damage_helper:      drm damage helper
+ * @drm_dp_mst_helper:      drm dp mst helper
  * @drm_format_helper:      drm format helper
  * @drm_format:             drm format
- * @drm_plane:              drm plane
- * @framebuffer:            framebuffer
+ * @drm_plane_helper:       drm plane helper
+ * @drm_framebuffer:        drm framebuffer
  */
 
 IGT_TEST_DESCRIPTION("Basic sanity check of KMS selftests.");
 
-struct kms_kunittests {
-	const char *kunit;
-	const char *name;
-};
-
 igt_main
 {
-	static const struct kms_kunittests kunit_subtests[] = {
-		{ "drm_cmdline_parser_test",	"drm_cmdline" },
-		{ "drm_damage_helper_test",	"drm_damage" },
-		{ "drm_dp_mst_helper_test",	"drm_dp_mst" },
-		{ "drm_format_helper_test",	"drm_format_helper" },
-		{ "drm_format_test",		"drm_format" },
-		{ "drm_framebuffer_test",	"framebuffer" },
-		{ "drm_plane_helper_test",	"drm_plane" },
-		{ NULL, NULL}
+	static const char *kunit_subtests[] = {
+		"drm_cmdline_parser_test",
+		"drm_damage_helper_test",
+		"drm_dp_mst_helper_test",
+		"drm_format_helper_test",
+		"drm_format_test",
+		"drm_framebuffer_test",
+		"drm_plane_helper_test",
+		NULL,
 	};
 
-	for (int i = 0; kunit_subtests[i].kunit != NULL; i++)
-		igt_kunit(kunit_subtests[i].kunit, kunit_subtests[i].name, NULL);
+	for (int i = 0; kunit_subtests[i] != NULL; i++)
+		igt_kunit(kunit_subtests[i], NULL, NULL);
 }
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 07/11] lib/ktap: Drop workaround for missing top level KTAP headers
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

A workaround was implemented in IGT KTAP parser so it could accepted KTAP
reports with missing top level KTAP version and test suite plan headers.
Since kernel side commit c95e7c05c139 ("kunit: Report the count of test
suites in a module"), included in the mainline kernel since v6.6-rc1, has
fixed that issue, that workaround is no longer needed.  Drop it.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_ktap.c              | 12 ------------
 lib/tests/igt_ktap_parser.c |  3 +--
 2 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
index 53a6c63288..7c52ba11ed 100644
--- a/lib/igt_ktap.c
+++ b/lib/igt_ktap.c
@@ -84,18 +84,6 @@ int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap)
 		   igt_debug_on(sscanf(buf,
 				       "%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
 				       &n, &len) == 1 && len == strlen(buf))) {
-		/*
-		 * TODO: drop the following workaround as soon as
-		 * kernel side issue of missing lines with top level
-		 * KTAP version and test suite plan is fixed.
-		 */
-		if (ktap->expect == KTAP_START) {
-			ktap->suite_count = 1;
-			ktap->suite_last = 0;
-			ktap->suite_name = NULL;
-			ktap->expect = SUITE_START;
-		}
-
 		if (igt_debug_on(ktap->expect != SUITE_START))
 			return -EPROTO;
 
diff --git a/lib/tests/igt_ktap_parser.c b/lib/tests/igt_ktap_parser.c
index 6357bdf6a5..476e14092f 100644
--- a/lib/tests/igt_ktap_parser.c
+++ b/lib/tests/igt_ktap_parser.c
@@ -190,8 +190,7 @@ static void ktap_top_version(void)
 
 	ktap = igt_ktap_alloc(&results);
 	igt_require(ktap);
-	/* TODO: change to -EPROTO as soon as related workaround is dropped */
-	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EINPROGRESS);
+	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EPROTO);
 	igt_ktap_free(ktap);
 
 	ktap = igt_ktap_alloc(&results);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 07/11] lib/ktap: Drop workaround for missing top level KTAP headers
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

A workaround was implemented in IGT KTAP parser so it could accepted KTAP
reports with missing top level KTAP version and test suite plan headers.
Since kernel side commit c95e7c05c139 ("kunit: Report the count of test
suites in a module"), included in the mainline kernel since v6.6-rc1, has
fixed that issue, that workaround is no longer needed.  Drop it.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_ktap.c              | 12 ------------
 lib/tests/igt_ktap_parser.c |  3 +--
 2 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
index 53a6c63288..7c52ba11ed 100644
--- a/lib/igt_ktap.c
+++ b/lib/igt_ktap.c
@@ -84,18 +84,6 @@ int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap)
 		   igt_debug_on(sscanf(buf,
 				       "%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
 				       &n, &len) == 1 && len == strlen(buf))) {
-		/*
-		 * TODO: drop the following workaround as soon as
-		 * kernel side issue of missing lines with top level
-		 * KTAP version and test suite plan is fixed.
-		 */
-		if (ktap->expect == KTAP_START) {
-			ktap->suite_count = 1;
-			ktap->suite_last = 0;
-			ktap->suite_name = NULL;
-			ktap->expect = SUITE_START;
-		}
-
 		if (igt_debug_on(ktap->expect != SUITE_START))
 			return -EPROTO;
 
diff --git a/lib/tests/igt_ktap_parser.c b/lib/tests/igt_ktap_parser.c
index 6357bdf6a5..476e14092f 100644
--- a/lib/tests/igt_ktap_parser.c
+++ b/lib/tests/igt_ktap_parser.c
@@ -190,8 +190,7 @@ static void ktap_top_version(void)
 
 	ktap = igt_ktap_alloc(&results);
 	igt_require(ktap);
-	/* TODO: change to -EPROTO as soon as related workaround is dropped */
-	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EINPROGRESS);
+	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EPROTO);
 	igt_ktap_free(ktap);
 
 	ktap = igt_ktap_alloc(&results);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 07/11] lib/ktap: Drop workaround for missing top level KTAP headers
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

A workaround was implemented in IGT KTAP parser so it could accepted KTAP
reports with missing top level KTAP version and test suite plan headers.
Since kernel side commit c95e7c05c139 ("kunit: Report the count of test
suites in a module"), included in the mainline kernel since v6.6-rc1, has
fixed that issue, that workaround is no longer needed.  Drop it.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_ktap.c              | 12 ------------
 lib/tests/igt_ktap_parser.c |  3 +--
 2 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
index 53a6c63288..7c52ba11ed 100644
--- a/lib/igt_ktap.c
+++ b/lib/igt_ktap.c
@@ -84,18 +84,6 @@ int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap)
 		   igt_debug_on(sscanf(buf,
 				       "%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
 				       &n, &len) == 1 && len == strlen(buf))) {
-		/*
-		 * TODO: drop the following workaround as soon as
-		 * kernel side issue of missing lines with top level
-		 * KTAP version and test suite plan is fixed.
-		 */
-		if (ktap->expect == KTAP_START) {
-			ktap->suite_count = 1;
-			ktap->suite_last = 0;
-			ktap->suite_name = NULL;
-			ktap->expect = SUITE_START;
-		}
-
 		if (igt_debug_on(ktap->expect != SUITE_START))
 			return -EPROTO;
 
diff --git a/lib/tests/igt_ktap_parser.c b/lib/tests/igt_ktap_parser.c
index 6357bdf6a5..476e14092f 100644
--- a/lib/tests/igt_ktap_parser.c
+++ b/lib/tests/igt_ktap_parser.c
@@ -190,8 +190,7 @@ static void ktap_top_version(void)
 
 	ktap = igt_ktap_alloc(&results);
 	igt_require(ktap);
-	/* TODO: change to -EPROTO as soon as related workaround is dropped */
-	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EINPROGRESS);
+	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EPROTO);
 	igt_ktap_free(ktap);
 
 	ktap = igt_ktap_alloc(&results);
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 08/11] lib/kunit: Provide all results cleanup helper
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Planned changes require a couple of loops around kunit_result_free().
Since we already have such loop, move it into a helper in preparation for
future uses.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 387efbb59f..fed0855c84 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -885,13 +885,25 @@ static void kunit_result_free(struct igt_ktap_result **r,
 	*r = NULL;
 }
 
+static void kunit_results_free(struct igt_list_head *results,
+			       char **suite_name, char **case_name)
+{
+	struct igt_ktap_result *r, *rn;
+
+	igt_list_for_each_entry_safe(r, rn, results, link)
+		kunit_result_free(&r, suite_name, case_name);
+
+	free(*case_name);
+	free(*suite_name);
+}
+
 static void
 __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 {
 	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
-	struct igt_ktap_result *r, *rn;
 	struct igt_ktap_results *ktap;
+	struct igt_ktap_result *r;
 	pthread_mutexattr_t attr;
 	IGT_LIST_HEAD(results);
 	unsigned long taints;
@@ -997,11 +1009,7 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	} while (ret == -EINPROGRESS);
 
-	igt_list_for_each_entry_safe(r, rn, &results, link)
-		kunit_result_free(&r, &suite_name, &case_name);
-
-	free(case_name);
-	free(suite_name);
+	kunit_results_free(&results, &suite_name, &case_name);
 
 	switch (pthread_mutex_lock(&modprobe.lock)) {
 	case 0:
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 08/11] lib/kunit: Provide all results cleanup helper
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

Planned changes require a couple of loops around kunit_result_free().
Since we already have such loop, move it into a helper in preparation for
future uses.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 387efbb59f..fed0855c84 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -885,13 +885,25 @@ static void kunit_result_free(struct igt_ktap_result **r,
 	*r = NULL;
 }
 
+static void kunit_results_free(struct igt_list_head *results,
+			       char **suite_name, char **case_name)
+{
+	struct igt_ktap_result *r, *rn;
+
+	igt_list_for_each_entry_safe(r, rn, results, link)
+		kunit_result_free(&r, suite_name, case_name);
+
+	free(*case_name);
+	free(*suite_name);
+}
+
 static void
 __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 {
 	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
-	struct igt_ktap_result *r, *rn;
 	struct igt_ktap_results *ktap;
+	struct igt_ktap_result *r;
 	pthread_mutexattr_t attr;
 	IGT_LIST_HEAD(results);
 	unsigned long taints;
@@ -997,11 +1009,7 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	} while (ret == -EINPROGRESS);
 
-	igt_list_for_each_entry_safe(r, rn, &results, link)
-		kunit_result_free(&r, &suite_name, &case_name);
-
-	free(case_name);
-	free(suite_name);
+	kunit_results_free(&results, &suite_name, &case_name);
 
 	switch (pthread_mutex_lock(&modprobe.lock)) {
 	case 0:
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 08/11] lib/kunit: Provide all results cleanup helper
@ 2023-10-09 12:27   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:27 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Planned changes require a couple of loops around kunit_result_free().
Since we already have such loop, move it into a helper in preparation for
future uses.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 387efbb59f..fed0855c84 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -885,13 +885,25 @@ static void kunit_result_free(struct igt_ktap_result **r,
 	*r = NULL;
 }
 
+static void kunit_results_free(struct igt_list_head *results,
+			       char **suite_name, char **case_name)
+{
+	struct igt_ktap_result *r, *rn;
+
+	igt_list_for_each_entry_safe(r, rn, results, link)
+		kunit_result_free(&r, suite_name, case_name);
+
+	free(*case_name);
+	free(*suite_name);
+}
+
 static void
 __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 {
 	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
-	struct igt_ktap_result *r, *rn;
 	struct igt_ktap_results *ktap;
+	struct igt_ktap_result *r;
 	pthread_mutexattr_t attr;
 	IGT_LIST_HEAD(results);
 	unsigned long taints;
@@ -997,11 +1009,7 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	} while (ret == -EINPROGRESS);
 
-	igt_list_for_each_entry_safe(r, rn, &results, link)
-		kunit_result_free(&r, &suite_name, &case_name);
-
-	free(case_name);
-	free(suite_name);
+	kunit_results_free(&results, &suite_name, &case_name);
 
 	switch (pthread_mutex_lock(&modprobe.lock)) {
 	case 0:
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

We are going to add support for reading a list of kunit test cases
provided by a kunit test module prior to executing those test cases.  That
will be done by first loading kunit modules in list only mode, then
reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
will be performed after the kunit test module is successfully loaded and
there will be no concurrently running modprobe thread, we need to make
synchronization of reads from /dev/kmsg with potential errors modprobe
thread optional.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
 1 file changed, 26 insertions(+), 24 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index fed0855c84..ed41aa1235 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct igt_list_head *results,
 		if (igt_debug_on(igt_kernel_tainted(&taints)))
 			return -ENOTRECOVERABLE;
 
-		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
-		if (err == -1)
-			return -errno;
-		else if (unlikely(err))
-			return err;
-
-		err = pthread_mutex_lock(&modprobe->lock);
-		switch (err) {
-		case EOWNERDEAD:
-			/* leave the mutex unrecoverable */
-			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
-			__attribute__ ((fallthrough));
-		case ENOTRECOVERABLE:
-			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
-			if (igt_debug_on(modprobe->err))
-				return modprobe->err;
-			break;
-		case 0:
-			break;
-		default:
-			igt_debug("pthread_mutex_lock() error: %d\n", err);
-			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
-			return -err;
+		if (modprobe) {
+			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
+			if (err == -1)
+				return -errno;
+			else if (unlikely(err))
+				return err;
+
+			err = pthread_mutex_lock(&modprobe->lock);
+			switch (err) {
+			case EOWNERDEAD:
+				/* leave the mutex unrecoverable */
+				igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+				__attribute__ ((fallthrough));
+			case ENOTRECOVERABLE:
+				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+				if (igt_debug_on(modprobe->err))
+					return modprobe->err;
+				break;
+			case 0:
+				break;
+			default:
+				igt_debug("pthread_mutex_lock() error: %d\n", err);
+				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+				return -err;
+			}
 		}
 
 		ret = read(fd, record, BUF_LEN);
 
-		if (!err) {
+		if (modprobe && !err) {
 			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
 			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
 		}
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

We are going to add support for reading a list of kunit test cases
provided by a kunit test module prior to executing those test cases.  That
will be done by first loading kunit modules in list only mode, then
reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
will be performed after the kunit test module is successfully loaded and
there will be no concurrently running modprobe thread, we need to make
synchronization of reads from /dev/kmsg with potential errors modprobe
thread optional.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
 1 file changed, 26 insertions(+), 24 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index fed0855c84..ed41aa1235 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct igt_list_head *results,
 		if (igt_debug_on(igt_kernel_tainted(&taints)))
 			return -ENOTRECOVERABLE;
 
-		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
-		if (err == -1)
-			return -errno;
-		else if (unlikely(err))
-			return err;
-
-		err = pthread_mutex_lock(&modprobe->lock);
-		switch (err) {
-		case EOWNERDEAD:
-			/* leave the mutex unrecoverable */
-			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
-			__attribute__ ((fallthrough));
-		case ENOTRECOVERABLE:
-			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
-			if (igt_debug_on(modprobe->err))
-				return modprobe->err;
-			break;
-		case 0:
-			break;
-		default:
-			igt_debug("pthread_mutex_lock() error: %d\n", err);
-			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
-			return -err;
+		if (modprobe) {
+			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
+			if (err == -1)
+				return -errno;
+			else if (unlikely(err))
+				return err;
+
+			err = pthread_mutex_lock(&modprobe->lock);
+			switch (err) {
+			case EOWNERDEAD:
+				/* leave the mutex unrecoverable */
+				igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+				__attribute__ ((fallthrough));
+			case ENOTRECOVERABLE:
+				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+				if (igt_debug_on(modprobe->err))
+					return modprobe->err;
+				break;
+			case 0:
+				break;
+			default:
+				igt_debug("pthread_mutex_lock() error: %d\n", err);
+				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+				return -err;
+			}
 		}
 
 		ret = read(fd, record, BUF_LEN);
 
-		if (!err) {
+		if (modprobe && !err) {
 			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
 			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
 		}
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

We are going to add support for reading a list of kunit test cases
provided by a kunit test module prior to executing those test cases.  That
will be done by first loading kunit modules in list only mode, then
reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
will be performed after the kunit test module is successfully loaded and
there will be no concurrently running modprobe thread, we need to make
synchronization of reads from /dev/kmsg with potential errors modprobe
thread optional.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
 1 file changed, 26 insertions(+), 24 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index fed0855c84..ed41aa1235 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct igt_list_head *results,
 		if (igt_debug_on(igt_kernel_tainted(&taints)))
 			return -ENOTRECOVERABLE;
 
-		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
-		if (err == -1)
-			return -errno;
-		else if (unlikely(err))
-			return err;
-
-		err = pthread_mutex_lock(&modprobe->lock);
-		switch (err) {
-		case EOWNERDEAD:
-			/* leave the mutex unrecoverable */
-			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
-			__attribute__ ((fallthrough));
-		case ENOTRECOVERABLE:
-			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
-			if (igt_debug_on(modprobe->err))
-				return modprobe->err;
-			break;
-		case 0:
-			break;
-		default:
-			igt_debug("pthread_mutex_lock() error: %d\n", err);
-			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
-			return -err;
+		if (modprobe) {
+			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
+			if (err == -1)
+				return -errno;
+			else if (unlikely(err))
+				return err;
+
+			err = pthread_mutex_lock(&modprobe->lock);
+			switch (err) {
+			case EOWNERDEAD:
+				/* leave the mutex unrecoverable */
+				igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
+				__attribute__ ((fallthrough));
+			case ENOTRECOVERABLE:
+				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+				if (igt_debug_on(modprobe->err))
+					return modprobe->err;
+				break;
+			case 0:
+				break;
+			default:
+				igt_debug("pthread_mutex_lock() error: %d\n", err);
+				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
+				return -err;
+			}
 		}
 
 		ret = read(fd, record, BUF_LEN);
 
-		if (!err) {
+		if (modprobe && !err) {
 			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
 			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
 		}
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 10/11] lib/kunit: Fetch a list of test cases in advance
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Recent improvements to the kernel kunit framework allow us to obtain a
list of test cases provided by a kunit test module without actually
running them.  Use that feature to get a list of expected test cases
before we enter a loop around igt_dynamic().  Once done, enter the
igt_dynamic() section for each consecutive test case immediately, even
before first line of a related KTAP report appears, then look for a result
from that test case.  That should make our IGT results output still better
synchronized with related kernel messages.

The list of test cases provided by a kunit test module can be obtained by
loading the kunit base module with specific options, then loading the test
module.  For that to be possible, take care of unloading the kunit base
module before each kunit subtest (I was wrong when in one of my previous
commit messages I suggested that on final unload of a kunit test module
the kunit base module is unloaded automatically as its dependency,
however, that didn't matter before, then no separate fix was required).
Since that module can then be left loaded with non-default options if an
error occurs, unload it explicitly before returning from igt_kunit().

There are two possible ways of getting a list of test cases: by loading
the base kunit module with action=list module option, or by filtering
out all test cases from being executed while asking for SKIP results from
those filtered out.  Since the latter provides regular KTAP report that we
can already parse perfectly, use it instead of trying to identify an
unstructured list of test cases of unknown length submitted by the former.

If an IGT test that calls igt_kunit() provides a subtest name then use
that name to filter out potential test cases that don't belong to the
named test suite from the list.

To avoid loading any modules if no subtest is going to be executed (e.g.,
if a nonexistent subtest has been requested), load the kunit modules in
list mode from inside the igt_subtest_with_dynamic() section.  In order to
be free to skip the whole subtest on unmet requirements that need to be
verified after that list has been already populated, clean it up from a
follow up igt_fixture section.

Since we may now finish processing of all test cases / dynamic sub-
subtests before KTAP parsing completes, don't fail if we exit the loop of
dynamic sub-subtests with -EINPROGRESS error code returned by the parser.

v2: Split out changes in handling of modprobe errors and kernel taints to
    separate patches (Kamil),
  - fix some string duplicates referenced from filtered out test cases not
    freed,
  - don't check if next result is needed before fetching one, obviously
    true in first dynamic sub-subtest, and we always free last result
    before looping to next sub-subtest,
  - still break the loop of test cases on unexpected return codes from
    kunit_kmsg_get_result(),
  - use kunit_results_free() helper,
  - fix typos (Kamil),
  - update commit description.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 169 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 132 insertions(+), 37 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index ed41aa1235..150fe49803 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -899,17 +899,15 @@ static void kunit_results_free(struct igt_list_head *results,
 	free(*suite_name);
 }
 
-static void
-__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
+static void kunit_get_tests(struct igt_list_head *tests,
+			    struct igt_ktest *tst,
+			    const char *filter,
+			    const char *opts)
 {
-	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *r, *rn;
 	struct igt_ktap_results *ktap;
-	struct igt_ktap_result *r;
-	pthread_mutexattr_t attr;
-	IGT_LIST_HEAD(results);
-	unsigned long taints;
-	int flags, ret;
+	int flags, err;
 
 	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
 
@@ -919,6 +917,70 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
+	/*
+	 * To get a list of test cases provided by a kunit test module, ask the
+	 * generic kunit module to respond with SKIP result for each test found.
+	 * We could also use action=list kunit parameter to get the listing,
+	 * however, parsing a KTAP report -- something that we already can do
+	 * perfectly -- seems to be more safe than extracting a test case list
+	 * of unknown length from /dev/kmsg.
+	 */
+	igt_skip_on(igt_kmod_load("kunit", "filter=module=none filter_action=skip"));
+	igt_skip_on(modprobe(tst->kmod, opts));
+
+	ktap = igt_ktap_alloc(tests);
+	igt_require(ktap);
+
+	do
+		err = kunit_kmsg_result_get(tests, NULL, tst->kmsg, ktap);
+	while (err == -EINPROGRESS);
+
+	igt_ktap_free(ktap);
+
+	if (!err)
+		igt_list_for_each_entry_safe(r, rn, tests, link) {
+			if (igt_debug_on(r->code != IGT_EXIT_SKIP)) {
+				err = r->code ?: -EPROTO;
+				break;
+			}
+
+			if (!filter)
+				continue;
+
+			if (strcmp(r->suite_name, filter))
+				kunit_result_free(&r, &case_name, &suite_name);
+		}
+
+	if (err) {
+		kunit_results_free(tests, &case_name, &suite_name);
+	} else {
+		free(suite_name);
+		free(case_name);
+	}
+
+	igt_skip_on(kmod_module_remove_module(tst->kmod, KMOD_REMOVE_FORCE));
+	igt_skip_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
+
+	igt_skip_on_f(err,
+		      "KTAP parser failed while getting a list of test cases\n");
+}
+
+static void __igt_kunit(struct igt_ktest *tst,
+			const char *name,
+			const char *opts,
+			struct igt_list_head *tests)
+{
+	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
+	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *t, *r = NULL;
+	struct igt_ktap_results *ktap;
+	pthread_mutexattr_t attr;
+	IGT_LIST_HEAD(results);
+	int ret = -EINPROGRESS;
+	unsigned long taints;
+
+	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
+
 	igt_skip_on(pthread_mutexattr_init(&attr));
 	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
 	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
@@ -932,37 +994,47 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 		igt_skip("Failed to create a modprobe thread\n");
 	}
 
-	do {
-		ret = kunit_kmsg_result_get(&results, &modprobe,
-					    tst->kmsg, ktap);
-		if (igt_debug_on(ret && ret != -EINPROGRESS))
-			break;
-
-		if (igt_debug_on(igt_list_empty(&results)))
-			break;
-
-		r = igt_list_first_entry(&results, r, link);
-
+	igt_list_for_each_entry(t, tests, link) {
 		igt_dynamic_f("%s%s%s",
-			      strcmp(r->suite_name, name) ?  r->suite_name : "",
-			      strcmp(r->suite_name, name) ? "-" : "",
-			      r->case_name) {
-
-			if (r->code == IGT_EXIT_INVALID) {
-				/* parametrized test case, get actual result */
-				kunit_result_free(&r, &suite_name, &case_name);
-
-				igt_assert(igt_list_empty(&results));
+			      strcmp(t->suite_name, name) ?  t->suite_name : "",
+			      strcmp(t->suite_name, name) ? "-" : "",
+			      t->case_name) {
 
+			if (igt_list_empty(&results)) {
+				igt_assert_eq(ret, -EINPROGRESS);
 				ret = kunit_kmsg_result_get(&results, &modprobe,
 							    tst->kmsg, ktap);
-				if (ret != -EINPROGRESS)
-					igt_fail_on(ret);
-
 				igt_fail_on(igt_list_empty(&results));
+			}
+
+			r = igt_list_first_entry(&results, r, link);
+
+			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
+					      "suite_name expected: %s, got: %s\n",
+					      t->suite_name, r->suite_name) ||
+			       igt_debug_on_f(strcmp(r->case_name, t->case_name),
+					      "case_name expected: %s, got: %s\n",
+					      t->case_name, r->case_name) ||
+			       r->code == IGT_EXIT_INVALID) {
+
+				int code = r->code;
+
+				kunit_result_free(&r, &suite_name, &case_name);
+				if (igt_list_empty(&results)) {
+					igt_assert_eq(ret, -EINPROGRESS);
+					ret = kunit_kmsg_result_get(&results,
+								    &modprobe,
+								    tst->kmsg,
+								    ktap);
+					igt_fail_on(igt_list_empty(&results));
+				}
 
 				r = igt_list_first_entry(&results, r, link);
 
+				if (code != IGT_EXIT_INVALID)
+					continue;
+
+				/* result from parametrized test case */
 				igt_fail_on_f(strcmp(r->suite_name, suite_name),
 					      "suite_name expected: %s, got: %s\n",
 					      suite_name, r->suite_name);
@@ -1009,7 +1081,9 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 		kunit_result_free(&r, &suite_name, &case_name);
 
-	} while (ret == -EINPROGRESS);
+		if (igt_debug_on(ret != -EINPROGRESS))
+			break;
+	}
 
 	kunit_results_free(&results, &suite_name, &case_name);
 
@@ -1035,7 +1109,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	igt_skip_on(modprobe.err);
 	igt_skip_on(igt_kernel_tainted(&taints));
-	igt_skip_on_f(ret, "KTAP parser failed\n");
+	if (ret != -EINPROGRESS)
+		igt_skip_on_f(ret, "KTAP parser failed\n");
 }
 
 /**
@@ -1049,7 +1124,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 void igt_kunit(const char *module_name, const char *name, const char *opts)
 {
 	struct igt_ktest tst = { .kmsg = -1, };
-
+	const char *filter = name;
+	IGT_LIST_HEAD(tests);
 
 	/*
 	 * If the caller (an IGT test) provides no subtest name then we
@@ -1074,6 +1150,15 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 
 		igt_skip_on(igt_ktest_init(&tst, module_name));
 		igt_skip_on(igt_ktest_begin(&tst));
+
+		/*
+		 * Since we need to load kunit base module with specific
+		 * options in order to get a list of test cases, make
+		 * sure that the module is not loaded.  However, since
+		 * unload may fail if kunit base module is not loaded,
+		 * ignore any failures, we'll fail later if still loaded.
+		 */
+		igt_ignore_warn(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
 	}
 
 	/*
@@ -1083,11 +1168,21 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 * proper namespace for dynamic subtests, with is required for CI
 	 * and for documentation.
 	 */
-	igt_subtest_with_dynamic(name)
-		__igt_kunit(&tst, name, opts);
+	igt_subtest_with_dynamic(name) {
+		kunit_get_tests(&tests, &tst, filter, opts);
+		igt_skip_on(igt_list_empty(&tests));
+
+		__igt_kunit(&tst, name, opts, &tests);
+	}
+
+	igt_fixture {
+		char *suite_name = NULL, *case_name = NULL;
+
+		kunit_results_free(&tests, &suite_name, &case_name);
 
-	igt_fixture
 		igt_ktest_end(&tst);
+		igt_debug_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
+	}
 
 	igt_ktest_fini(&tst);
 }
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 10/11] lib/kunit: Fetch a list of test cases in advance
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

Recent improvements to the kernel kunit framework allow us to obtain a
list of test cases provided by a kunit test module without actually
running them.  Use that feature to get a list of expected test cases
before we enter a loop around igt_dynamic().  Once done, enter the
igt_dynamic() section for each consecutive test case immediately, even
before first line of a related KTAP report appears, then look for a result
from that test case.  That should make our IGT results output still better
synchronized with related kernel messages.

The list of test cases provided by a kunit test module can be obtained by
loading the kunit base module with specific options, then loading the test
module.  For that to be possible, take care of unloading the kunit base
module before each kunit subtest (I was wrong when in one of my previous
commit messages I suggested that on final unload of a kunit test module
the kunit base module is unloaded automatically as its dependency,
however, that didn't matter before, then no separate fix was required).
Since that module can then be left loaded with non-default options if an
error occurs, unload it explicitly before returning from igt_kunit().

There are two possible ways of getting a list of test cases: by loading
the base kunit module with action=list module option, or by filtering
out all test cases from being executed while asking for SKIP results from
those filtered out.  Since the latter provides regular KTAP report that we
can already parse perfectly, use it instead of trying to identify an
unstructured list of test cases of unknown length submitted by the former.

If an IGT test that calls igt_kunit() provides a subtest name then use
that name to filter out potential test cases that don't belong to the
named test suite from the list.

To avoid loading any modules if no subtest is going to be executed (e.g.,
if a nonexistent subtest has been requested), load the kunit modules in
list mode from inside the igt_subtest_with_dynamic() section.  In order to
be free to skip the whole subtest on unmet requirements that need to be
verified after that list has been already populated, clean it up from a
follow up igt_fixture section.

Since we may now finish processing of all test cases / dynamic sub-
subtests before KTAP parsing completes, don't fail if we exit the loop of
dynamic sub-subtests with -EINPROGRESS error code returned by the parser.

v2: Split out changes in handling of modprobe errors and kernel taints to
    separate patches (Kamil),
  - fix some string duplicates referenced from filtered out test cases not
    freed,
  - don't check if next result is needed before fetching one, obviously
    true in first dynamic sub-subtest, and we always free last result
    before looping to next sub-subtest,
  - still break the loop of test cases on unexpected return codes from
    kunit_kmsg_get_result(),
  - use kunit_results_free() helper,
  - fix typos (Kamil),
  - update commit description.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 169 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 132 insertions(+), 37 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index ed41aa1235..150fe49803 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -899,17 +899,15 @@ static void kunit_results_free(struct igt_list_head *results,
 	free(*suite_name);
 }
 
-static void
-__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
+static void kunit_get_tests(struct igt_list_head *tests,
+			    struct igt_ktest *tst,
+			    const char *filter,
+			    const char *opts)
 {
-	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *r, *rn;
 	struct igt_ktap_results *ktap;
-	struct igt_ktap_result *r;
-	pthread_mutexattr_t attr;
-	IGT_LIST_HEAD(results);
-	unsigned long taints;
-	int flags, ret;
+	int flags, err;
 
 	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
 
@@ -919,6 +917,70 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
+	/*
+	 * To get a list of test cases provided by a kunit test module, ask the
+	 * generic kunit module to respond with SKIP result for each test found.
+	 * We could also use action=list kunit parameter to get the listing,
+	 * however, parsing a KTAP report -- something that we already can do
+	 * perfectly -- seems to be more safe than extracting a test case list
+	 * of unknown length from /dev/kmsg.
+	 */
+	igt_skip_on(igt_kmod_load("kunit", "filter=module=none filter_action=skip"));
+	igt_skip_on(modprobe(tst->kmod, opts));
+
+	ktap = igt_ktap_alloc(tests);
+	igt_require(ktap);
+
+	do
+		err = kunit_kmsg_result_get(tests, NULL, tst->kmsg, ktap);
+	while (err == -EINPROGRESS);
+
+	igt_ktap_free(ktap);
+
+	if (!err)
+		igt_list_for_each_entry_safe(r, rn, tests, link) {
+			if (igt_debug_on(r->code != IGT_EXIT_SKIP)) {
+				err = r->code ?: -EPROTO;
+				break;
+			}
+
+			if (!filter)
+				continue;
+
+			if (strcmp(r->suite_name, filter))
+				kunit_result_free(&r, &case_name, &suite_name);
+		}
+
+	if (err) {
+		kunit_results_free(tests, &case_name, &suite_name);
+	} else {
+		free(suite_name);
+		free(case_name);
+	}
+
+	igt_skip_on(kmod_module_remove_module(tst->kmod, KMOD_REMOVE_FORCE));
+	igt_skip_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
+
+	igt_skip_on_f(err,
+		      "KTAP parser failed while getting a list of test cases\n");
+}
+
+static void __igt_kunit(struct igt_ktest *tst,
+			const char *name,
+			const char *opts,
+			struct igt_list_head *tests)
+{
+	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
+	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *t, *r = NULL;
+	struct igt_ktap_results *ktap;
+	pthread_mutexattr_t attr;
+	IGT_LIST_HEAD(results);
+	int ret = -EINPROGRESS;
+	unsigned long taints;
+
+	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
+
 	igt_skip_on(pthread_mutexattr_init(&attr));
 	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
 	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
@@ -932,37 +994,47 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 		igt_skip("Failed to create a modprobe thread\n");
 	}
 
-	do {
-		ret = kunit_kmsg_result_get(&results, &modprobe,
-					    tst->kmsg, ktap);
-		if (igt_debug_on(ret && ret != -EINPROGRESS))
-			break;
-
-		if (igt_debug_on(igt_list_empty(&results)))
-			break;
-
-		r = igt_list_first_entry(&results, r, link);
-
+	igt_list_for_each_entry(t, tests, link) {
 		igt_dynamic_f("%s%s%s",
-			      strcmp(r->suite_name, name) ?  r->suite_name : "",
-			      strcmp(r->suite_name, name) ? "-" : "",
-			      r->case_name) {
-
-			if (r->code == IGT_EXIT_INVALID) {
-				/* parametrized test case, get actual result */
-				kunit_result_free(&r, &suite_name, &case_name);
-
-				igt_assert(igt_list_empty(&results));
+			      strcmp(t->suite_name, name) ?  t->suite_name : "",
+			      strcmp(t->suite_name, name) ? "-" : "",
+			      t->case_name) {
 
+			if (igt_list_empty(&results)) {
+				igt_assert_eq(ret, -EINPROGRESS);
 				ret = kunit_kmsg_result_get(&results, &modprobe,
 							    tst->kmsg, ktap);
-				if (ret != -EINPROGRESS)
-					igt_fail_on(ret);
-
 				igt_fail_on(igt_list_empty(&results));
+			}
+
+			r = igt_list_first_entry(&results, r, link);
+
+			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
+					      "suite_name expected: %s, got: %s\n",
+					      t->suite_name, r->suite_name) ||
+			       igt_debug_on_f(strcmp(r->case_name, t->case_name),
+					      "case_name expected: %s, got: %s\n",
+					      t->case_name, r->case_name) ||
+			       r->code == IGT_EXIT_INVALID) {
+
+				int code = r->code;
+
+				kunit_result_free(&r, &suite_name, &case_name);
+				if (igt_list_empty(&results)) {
+					igt_assert_eq(ret, -EINPROGRESS);
+					ret = kunit_kmsg_result_get(&results,
+								    &modprobe,
+								    tst->kmsg,
+								    ktap);
+					igt_fail_on(igt_list_empty(&results));
+				}
 
 				r = igt_list_first_entry(&results, r, link);
 
+				if (code != IGT_EXIT_INVALID)
+					continue;
+
+				/* result from parametrized test case */
 				igt_fail_on_f(strcmp(r->suite_name, suite_name),
 					      "suite_name expected: %s, got: %s\n",
 					      suite_name, r->suite_name);
@@ -1009,7 +1081,9 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 		kunit_result_free(&r, &suite_name, &case_name);
 
-	} while (ret == -EINPROGRESS);
+		if (igt_debug_on(ret != -EINPROGRESS))
+			break;
+	}
 
 	kunit_results_free(&results, &suite_name, &case_name);
 
@@ -1035,7 +1109,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	igt_skip_on(modprobe.err);
 	igt_skip_on(igt_kernel_tainted(&taints));
-	igt_skip_on_f(ret, "KTAP parser failed\n");
+	if (ret != -EINPROGRESS)
+		igt_skip_on_f(ret, "KTAP parser failed\n");
 }
 
 /**
@@ -1049,7 +1124,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 void igt_kunit(const char *module_name, const char *name, const char *opts)
 {
 	struct igt_ktest tst = { .kmsg = -1, };
-
+	const char *filter = name;
+	IGT_LIST_HEAD(tests);
 
 	/*
 	 * If the caller (an IGT test) provides no subtest name then we
@@ -1074,6 +1150,15 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 
 		igt_skip_on(igt_ktest_init(&tst, module_name));
 		igt_skip_on(igt_ktest_begin(&tst));
+
+		/*
+		 * Since we need to load kunit base module with specific
+		 * options in order to get a list of test cases, make
+		 * sure that the module is not loaded.  However, since
+		 * unload may fail if kunit base module is not loaded,
+		 * ignore any failures, we'll fail later if still loaded.
+		 */
+		igt_ignore_warn(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
 	}
 
 	/*
@@ -1083,11 +1168,21 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 * proper namespace for dynamic subtests, with is required for CI
 	 * and for documentation.
 	 */
-	igt_subtest_with_dynamic(name)
-		__igt_kunit(&tst, name, opts);
+	igt_subtest_with_dynamic(name) {
+		kunit_get_tests(&tests, &tst, filter, opts);
+		igt_skip_on(igt_list_empty(&tests));
+
+		__igt_kunit(&tst, name, opts, &tests);
+	}
+
+	igt_fixture {
+		char *suite_name = NULL, *case_name = NULL;
+
+		kunit_results_free(&tests, &suite_name, &case_name);
 
-	igt_fixture
 		igt_ktest_end(&tst);
+		igt_debug_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
+	}
 
 	igt_ktest_fini(&tst);
 }
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 10/11] lib/kunit: Fetch a list of test cases in advance
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Recent improvements to the kernel kunit framework allow us to obtain a
list of test cases provided by a kunit test module without actually
running them.  Use that feature to get a list of expected test cases
before we enter a loop around igt_dynamic().  Once done, enter the
igt_dynamic() section for each consecutive test case immediately, even
before first line of a related KTAP report appears, then look for a result
from that test case.  That should make our IGT results output still better
synchronized with related kernel messages.

The list of test cases provided by a kunit test module can be obtained by
loading the kunit base module with specific options, then loading the test
module.  For that to be possible, take care of unloading the kunit base
module before each kunit subtest (I was wrong when in one of my previous
commit messages I suggested that on final unload of a kunit test module
the kunit base module is unloaded automatically as its dependency,
however, that didn't matter before, then no separate fix was required).
Since that module can then be left loaded with non-default options if an
error occurs, unload it explicitly before returning from igt_kunit().

There are two possible ways of getting a list of test cases: by loading
the base kunit module with action=list module option, or by filtering
out all test cases from being executed while asking for SKIP results from
those filtered out.  Since the latter provides regular KTAP report that we
can already parse perfectly, use it instead of trying to identify an
unstructured list of test cases of unknown length submitted by the former.

If an IGT test that calls igt_kunit() provides a subtest name then use
that name to filter out potential test cases that don't belong to the
named test suite from the list.

To avoid loading any modules if no subtest is going to be executed (e.g.,
if a nonexistent subtest has been requested), load the kunit modules in
list mode from inside the igt_subtest_with_dynamic() section.  In order to
be free to skip the whole subtest on unmet requirements that need to be
verified after that list has been already populated, clean it up from a
follow up igt_fixture section.

Since we may now finish processing of all test cases / dynamic sub-
subtests before KTAP parsing completes, don't fail if we exit the loop of
dynamic sub-subtests with -EINPROGRESS error code returned by the parser.

v2: Split out changes in handling of modprobe errors and kernel taints to
    separate patches (Kamil),
  - fix some string duplicates referenced from filtered out test cases not
    freed,
  - don't check if next result is needed before fetching one, obviously
    true in first dynamic sub-subtest, and we always free last result
    before looping to next sub-subtest,
  - still break the loop of test cases on unexpected return codes from
    kunit_kmsg_get_result(),
  - use kunit_results_free() helper,
  - fix typos (Kamil),
  - update commit description.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 169 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 132 insertions(+), 37 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index ed41aa1235..150fe49803 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -899,17 +899,15 @@ static void kunit_results_free(struct igt_list_head *results,
 	free(*suite_name);
 }
 
-static void
-__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
+static void kunit_get_tests(struct igt_list_head *tests,
+			    struct igt_ktest *tst,
+			    const char *filter,
+			    const char *opts)
 {
-	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
 	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *r, *rn;
 	struct igt_ktap_results *ktap;
-	struct igt_ktap_result *r;
-	pthread_mutexattr_t attr;
-	IGT_LIST_HEAD(results);
-	unsigned long taints;
-	int flags, ret;
+	int flags, err;
 
 	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
 
@@ -919,6 +917,70 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
+	/*
+	 * To get a list of test cases provided by a kunit test module, ask the
+	 * generic kunit module to respond with SKIP result for each test found.
+	 * We could also use action=list kunit parameter to get the listing,
+	 * however, parsing a KTAP report -- something that we already can do
+	 * perfectly -- seems to be more safe than extracting a test case list
+	 * of unknown length from /dev/kmsg.
+	 */
+	igt_skip_on(igt_kmod_load("kunit", "filter=module=none filter_action=skip"));
+	igt_skip_on(modprobe(tst->kmod, opts));
+
+	ktap = igt_ktap_alloc(tests);
+	igt_require(ktap);
+
+	do
+		err = kunit_kmsg_result_get(tests, NULL, tst->kmsg, ktap);
+	while (err == -EINPROGRESS);
+
+	igt_ktap_free(ktap);
+
+	if (!err)
+		igt_list_for_each_entry_safe(r, rn, tests, link) {
+			if (igt_debug_on(r->code != IGT_EXIT_SKIP)) {
+				err = r->code ?: -EPROTO;
+				break;
+			}
+
+			if (!filter)
+				continue;
+
+			if (strcmp(r->suite_name, filter))
+				kunit_result_free(&r, &case_name, &suite_name);
+		}
+
+	if (err) {
+		kunit_results_free(tests, &case_name, &suite_name);
+	} else {
+		free(suite_name);
+		free(case_name);
+	}
+
+	igt_skip_on(kmod_module_remove_module(tst->kmod, KMOD_REMOVE_FORCE));
+	igt_skip_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
+
+	igt_skip_on_f(err,
+		      "KTAP parser failed while getting a list of test cases\n");
+}
+
+static void __igt_kunit(struct igt_ktest *tst,
+			const char *name,
+			const char *opts,
+			struct igt_list_head *tests)
+{
+	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
+	char *suite_name = NULL, *case_name = NULL;
+	struct igt_ktap_result *t, *r = NULL;
+	struct igt_ktap_results *ktap;
+	pthread_mutexattr_t attr;
+	IGT_LIST_HEAD(results);
+	int ret = -EINPROGRESS;
+	unsigned long taints;
+
+	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
+
 	igt_skip_on(pthread_mutexattr_init(&attr));
 	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
 	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
@@ -932,37 +994,47 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 		igt_skip("Failed to create a modprobe thread\n");
 	}
 
-	do {
-		ret = kunit_kmsg_result_get(&results, &modprobe,
-					    tst->kmsg, ktap);
-		if (igt_debug_on(ret && ret != -EINPROGRESS))
-			break;
-
-		if (igt_debug_on(igt_list_empty(&results)))
-			break;
-
-		r = igt_list_first_entry(&results, r, link);
-
+	igt_list_for_each_entry(t, tests, link) {
 		igt_dynamic_f("%s%s%s",
-			      strcmp(r->suite_name, name) ?  r->suite_name : "",
-			      strcmp(r->suite_name, name) ? "-" : "",
-			      r->case_name) {
-
-			if (r->code == IGT_EXIT_INVALID) {
-				/* parametrized test case, get actual result */
-				kunit_result_free(&r, &suite_name, &case_name);
-
-				igt_assert(igt_list_empty(&results));
+			      strcmp(t->suite_name, name) ?  t->suite_name : "",
+			      strcmp(t->suite_name, name) ? "-" : "",
+			      t->case_name) {
 
+			if (igt_list_empty(&results)) {
+				igt_assert_eq(ret, -EINPROGRESS);
 				ret = kunit_kmsg_result_get(&results, &modprobe,
 							    tst->kmsg, ktap);
-				if (ret != -EINPROGRESS)
-					igt_fail_on(ret);
-
 				igt_fail_on(igt_list_empty(&results));
+			}
+
+			r = igt_list_first_entry(&results, r, link);
+
+			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
+					      "suite_name expected: %s, got: %s\n",
+					      t->suite_name, r->suite_name) ||
+			       igt_debug_on_f(strcmp(r->case_name, t->case_name),
+					      "case_name expected: %s, got: %s\n",
+					      t->case_name, r->case_name) ||
+			       r->code == IGT_EXIT_INVALID) {
+
+				int code = r->code;
+
+				kunit_result_free(&r, &suite_name, &case_name);
+				if (igt_list_empty(&results)) {
+					igt_assert_eq(ret, -EINPROGRESS);
+					ret = kunit_kmsg_result_get(&results,
+								    &modprobe,
+								    tst->kmsg,
+								    ktap);
+					igt_fail_on(igt_list_empty(&results));
+				}
 
 				r = igt_list_first_entry(&results, r, link);
 
+				if (code != IGT_EXIT_INVALID)
+					continue;
+
+				/* result from parametrized test case */
 				igt_fail_on_f(strcmp(r->suite_name, suite_name),
 					      "suite_name expected: %s, got: %s\n",
 					      suite_name, r->suite_name);
@@ -1009,7 +1081,9 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 		kunit_result_free(&r, &suite_name, &case_name);
 
-	} while (ret == -EINPROGRESS);
+		if (igt_debug_on(ret != -EINPROGRESS))
+			break;
+	}
 
 	kunit_results_free(&results, &suite_name, &case_name);
 
@@ -1035,7 +1109,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 
 	igt_skip_on(modprobe.err);
 	igt_skip_on(igt_kernel_tainted(&taints));
-	igt_skip_on_f(ret, "KTAP parser failed\n");
+	if (ret != -EINPROGRESS)
+		igt_skip_on_f(ret, "KTAP parser failed\n");
 }
 
 /**
@@ -1049,7 +1124,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
 void igt_kunit(const char *module_name, const char *name, const char *opts)
 {
 	struct igt_ktest tst = { .kmsg = -1, };
-
+	const char *filter = name;
+	IGT_LIST_HEAD(tests);
 
 	/*
 	 * If the caller (an IGT test) provides no subtest name then we
@@ -1074,6 +1150,15 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 
 		igt_skip_on(igt_ktest_init(&tst, module_name));
 		igt_skip_on(igt_ktest_begin(&tst));
+
+		/*
+		 * Since we need to load kunit base module with specific
+		 * options in order to get a list of test cases, make
+		 * sure that the module is not loaded.  However, since
+		 * unload may fail if kunit base module is not loaded,
+		 * ignore any failures, we'll fail later if still loaded.
+		 */
+		igt_ignore_warn(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
 	}
 
 	/*
@@ -1083,11 +1168,21 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
 	 * proper namespace for dynamic subtests, with is required for CI
 	 * and for documentation.
 	 */
-	igt_subtest_with_dynamic(name)
-		__igt_kunit(&tst, name, opts);
+	igt_subtest_with_dynamic(name) {
+		kunit_get_tests(&tests, &tst, filter, opts);
+		igt_skip_on(igt_list_empty(&tests));
+
+		__igt_kunit(&tst, name, opts, &tests);
+	}
+
+	igt_fixture {
+		char *suite_name = NULL, *case_name = NULL;
+
+		kunit_results_free(&tests, &suite_name, &case_name);
 
-	igt_fixture
 		igt_ktest_end(&tst);
+		igt_debug_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
+	}
 
 	igt_ktest_fini(&tst);
 }
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-gfx] [PATCH i-g-t v2 11/11] lib/kunit: Execute kunit test cases only when needed
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

IGT user interface allows to request execution of only those dynamic sub-
subtests that match a user provided name pattern.  If the user pattern
doesn't match any names of test cases provided by a kunit test module used
with the subtest to be run then no results from any dynamic sub-subtests
will be reported.  Since we already know the list of test cases provided
by the kunit test module, there is no need to load that module to execute
them unless the user pattern matches at least one of those test cases.

Don't load the kunit test module in execute mode before entering the loop
of dynamic sub-subtests, and do that only from the first actually executed
dynamic sub-subtest.

v2: Always pass last result to next dynamic sub-subtest, fetch first
    result right after loading the kunit test module for execution.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 66 ++++++++++++++++++++++++++------------------------
 1 file changed, 35 insertions(+), 31 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 150fe49803..69915adc40 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -981,33 +981,37 @@ static void __igt_kunit(struct igt_ktest *tst,
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
-	igt_skip_on(pthread_mutexattr_init(&attr));
-	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
-	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
-
 	ktap = igt_ktap_alloc(&results);
 	igt_require(ktap);
 
-	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
-					modprobe_task, &modprobe))) {
-		igt_ktap_free(ktap);
-		igt_skip("Failed to create a modprobe thread\n");
-	}
-
 	igt_list_for_each_entry(t, tests, link) {
 		igt_dynamic_f("%s%s%s",
 			      strcmp(t->suite_name, name) ?  t->suite_name : "",
 			      strcmp(t->suite_name, name) ? "-" : "",
 			      t->case_name) {
 
-			if (igt_list_empty(&results)) {
+			if (!modprobe.thread) {
+				igt_assert_eq(pthread_mutexattr_init(&attr), 0);
+				igt_assert_eq(pthread_mutexattr_setrobust(&attr,
+							  PTHREAD_MUTEX_ROBUST),
+					      0);
+				igt_assert_eq(pthread_mutex_init(&modprobe.lock,
+								 &attr), 0);
+
+				modprobe.err = pthread_create(&modprobe.thread,
+							      NULL,
+							      modprobe_task,
+							      &modprobe);
+				igt_assert_eq(modprobe.err, 0);
+
+				igt_assert(igt_list_empty(&results));
 				igt_assert_eq(ret, -EINPROGRESS);
 				ret = kunit_kmsg_result_get(&results, &modprobe,
 							    tst->kmsg, ktap);
 				igt_fail_on(igt_list_empty(&results));
-			}
 
-			r = igt_list_first_entry(&results, r, link);
+				r = igt_list_first_entry(&results, r, link);
+			}
 
 			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
 					      "suite_name expected: %s, got: %s\n",
@@ -1079,30 +1083,30 @@ static void __igt_kunit(struct igt_ktest *tst,
 			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
-		kunit_result_free(&r, &suite_name, &case_name);
-
 		if (igt_debug_on(ret != -EINPROGRESS))
 			break;
 	}
 
 	kunit_results_free(&results, &suite_name, &case_name);
 
-	switch (pthread_mutex_lock(&modprobe.lock)) {
-	case 0:
-		igt_debug_on(pthread_cancel(modprobe.thread));
-		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
-		igt_debug_on(pthread_join(modprobe.thread, NULL));
-		break;
-	case EOWNERDEAD:
-		/* leave the mutex unrecoverable */
-		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
-		break;
-	case ENOTRECOVERABLE:
-		break;
-	default:
-		igt_debug("pthread_mutex_lock() failed\n");
-		igt_debug_on(pthread_join(modprobe.thread, NULL));
-		break;
+	if (modprobe.thread) {
+		switch (pthread_mutex_lock(&modprobe.lock)) {
+		case 0:
+			igt_debug_on(pthread_cancel(modprobe.thread));
+			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+			igt_debug_on(pthread_join(modprobe.thread, NULL));
+			break;
+		case EOWNERDEAD:
+			/* leave the mutex unrecoverable */
+			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+			break;
+		case ENOTRECOVERABLE:
+			break;
+		default:
+			igt_debug("pthread_mutex_lock() failed\n");
+			igt_debug_on(pthread_join(modprobe.thread, NULL));
+			break;
+		}
 	}
 
 	igt_ktap_free(ktap);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] [PATCH i-g-t v2 11/11] lib/kunit: Execute kunit test cases only when needed
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: Kamil Konieczny, Janusz Krzysztofik, intel-gfx, intel-xe

IGT user interface allows to request execution of only those dynamic sub-
subtests that match a user provided name pattern.  If the user pattern
doesn't match any names of test cases provided by a kunit test module used
with the subtest to be run then no results from any dynamic sub-subtests
will be reported.  Since we already know the list of test cases provided
by the kunit test module, there is no need to load that module to execute
them unless the user pattern matches at least one of those test cases.

Don't load the kunit test module in execute mode before entering the loop
of dynamic sub-subtests, and do that only from the first actually executed
dynamic sub-subtest.

v2: Always pass last result to next dynamic sub-subtest, fetch first
    result right after loading the kunit test module for execution.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 66 ++++++++++++++++++++++++++------------------------
 1 file changed, 35 insertions(+), 31 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 150fe49803..69915adc40 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -981,33 +981,37 @@ static void __igt_kunit(struct igt_ktest *tst,
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
-	igt_skip_on(pthread_mutexattr_init(&attr));
-	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
-	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
-
 	ktap = igt_ktap_alloc(&results);
 	igt_require(ktap);
 
-	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
-					modprobe_task, &modprobe))) {
-		igt_ktap_free(ktap);
-		igt_skip("Failed to create a modprobe thread\n");
-	}
-
 	igt_list_for_each_entry(t, tests, link) {
 		igt_dynamic_f("%s%s%s",
 			      strcmp(t->suite_name, name) ?  t->suite_name : "",
 			      strcmp(t->suite_name, name) ? "-" : "",
 			      t->case_name) {
 
-			if (igt_list_empty(&results)) {
+			if (!modprobe.thread) {
+				igt_assert_eq(pthread_mutexattr_init(&attr), 0);
+				igt_assert_eq(pthread_mutexattr_setrobust(&attr,
+							  PTHREAD_MUTEX_ROBUST),
+					      0);
+				igt_assert_eq(pthread_mutex_init(&modprobe.lock,
+								 &attr), 0);
+
+				modprobe.err = pthread_create(&modprobe.thread,
+							      NULL,
+							      modprobe_task,
+							      &modprobe);
+				igt_assert_eq(modprobe.err, 0);
+
+				igt_assert(igt_list_empty(&results));
 				igt_assert_eq(ret, -EINPROGRESS);
 				ret = kunit_kmsg_result_get(&results, &modprobe,
 							    tst->kmsg, ktap);
 				igt_fail_on(igt_list_empty(&results));
-			}
 
-			r = igt_list_first_entry(&results, r, link);
+				r = igt_list_first_entry(&results, r, link);
+			}
 
 			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
 					      "suite_name expected: %s, got: %s\n",
@@ -1079,30 +1083,30 @@ static void __igt_kunit(struct igt_ktest *tst,
 			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
-		kunit_result_free(&r, &suite_name, &case_name);
-
 		if (igt_debug_on(ret != -EINPROGRESS))
 			break;
 	}
 
 	kunit_results_free(&results, &suite_name, &case_name);
 
-	switch (pthread_mutex_lock(&modprobe.lock)) {
-	case 0:
-		igt_debug_on(pthread_cancel(modprobe.thread));
-		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
-		igt_debug_on(pthread_join(modprobe.thread, NULL));
-		break;
-	case EOWNERDEAD:
-		/* leave the mutex unrecoverable */
-		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
-		break;
-	case ENOTRECOVERABLE:
-		break;
-	default:
-		igt_debug("pthread_mutex_lock() failed\n");
-		igt_debug_on(pthread_join(modprobe.thread, NULL));
-		break;
+	if (modprobe.thread) {
+		switch (pthread_mutex_lock(&modprobe.lock)) {
+		case 0:
+			igt_debug_on(pthread_cancel(modprobe.thread));
+			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+			igt_debug_on(pthread_join(modprobe.thread, NULL));
+			break;
+		case EOWNERDEAD:
+			/* leave the mutex unrecoverable */
+			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+			break;
+		case ENOTRECOVERABLE:
+			break;
+		default:
+			igt_debug("pthread_mutex_lock() failed\n");
+			igt_debug_on(pthread_join(modprobe.thread, NULL));
+			break;
+		}
 	}
 
 	igt_ktap_free(ktap);
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [igt-dev] [PATCH i-g-t v2 11/11] lib/kunit: Execute kunit test cases only when needed
@ 2023-10-09 12:28   ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-09 12:28 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

IGT user interface allows to request execution of only those dynamic sub-
subtests that match a user provided name pattern.  If the user pattern
doesn't match any names of test cases provided by a kunit test module used
with the subtest to be run then no results from any dynamic sub-subtests
will be reported.  Since we already know the list of test cases provided
by the kunit test module, there is no need to load that module to execute
them unless the user pattern matches at least one of those test cases.

Don't load the kunit test module in execute mode before entering the loop
of dynamic sub-subtests, and do that only from the first actually executed
dynamic sub-subtest.

v2: Always pass last result to next dynamic sub-subtest, fetch first
    result right after loading the kunit test module for execution.

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
---
 lib/igt_kmod.c | 66 ++++++++++++++++++++++++++------------------------
 1 file changed, 35 insertions(+), 31 deletions(-)

diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index 150fe49803..69915adc40 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -981,33 +981,37 @@ static void __igt_kunit(struct igt_ktest *tst,
 
 	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
 
-	igt_skip_on(pthread_mutexattr_init(&attr));
-	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
-	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
-
 	ktap = igt_ktap_alloc(&results);
 	igt_require(ktap);
 
-	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
-					modprobe_task, &modprobe))) {
-		igt_ktap_free(ktap);
-		igt_skip("Failed to create a modprobe thread\n");
-	}
-
 	igt_list_for_each_entry(t, tests, link) {
 		igt_dynamic_f("%s%s%s",
 			      strcmp(t->suite_name, name) ?  t->suite_name : "",
 			      strcmp(t->suite_name, name) ? "-" : "",
 			      t->case_name) {
 
-			if (igt_list_empty(&results)) {
+			if (!modprobe.thread) {
+				igt_assert_eq(pthread_mutexattr_init(&attr), 0);
+				igt_assert_eq(pthread_mutexattr_setrobust(&attr,
+							  PTHREAD_MUTEX_ROBUST),
+					      0);
+				igt_assert_eq(pthread_mutex_init(&modprobe.lock,
+								 &attr), 0);
+
+				modprobe.err = pthread_create(&modprobe.thread,
+							      NULL,
+							      modprobe_task,
+							      &modprobe);
+				igt_assert_eq(modprobe.err, 0);
+
+				igt_assert(igt_list_empty(&results));
 				igt_assert_eq(ret, -EINPROGRESS);
 				ret = kunit_kmsg_result_get(&results, &modprobe,
 							    tst->kmsg, ktap);
 				igt_fail_on(igt_list_empty(&results));
-			}
 
-			r = igt_list_first_entry(&results, r, link);
+				r = igt_list_first_entry(&results, r, link);
+			}
 
 			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
 					      "suite_name expected: %s, got: %s\n",
@@ -1079,30 +1083,30 @@ static void __igt_kunit(struct igt_ktest *tst,
 			igt_assert_eq(igt_kernel_tainted(&taints), 0);
 		}
 
-		kunit_result_free(&r, &suite_name, &case_name);
-
 		if (igt_debug_on(ret != -EINPROGRESS))
 			break;
 	}
 
 	kunit_results_free(&results, &suite_name, &case_name);
 
-	switch (pthread_mutex_lock(&modprobe.lock)) {
-	case 0:
-		igt_debug_on(pthread_cancel(modprobe.thread));
-		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
-		igt_debug_on(pthread_join(modprobe.thread, NULL));
-		break;
-	case EOWNERDEAD:
-		/* leave the mutex unrecoverable */
-		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
-		break;
-	case ENOTRECOVERABLE:
-		break;
-	default:
-		igt_debug("pthread_mutex_lock() failed\n");
-		igt_debug_on(pthread_join(modprobe.thread, NULL));
-		break;
+	if (modprobe.thread) {
+		switch (pthread_mutex_lock(&modprobe.lock)) {
+		case 0:
+			igt_debug_on(pthread_cancel(modprobe.thread));
+			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+			igt_debug_on(pthread_join(modprobe.thread, NULL));
+			break;
+		case EOWNERDEAD:
+			/* leave the mutex unrecoverable */
+			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
+			break;
+		case ENOTRECOVERABLE:
+			break;
+		default:
+			igt_debug("pthread_mutex_lock() failed\n");
+			igt_debug_on(pthread_join(modprobe.thread, NULL));
+			break;
+		}
 	}
 
 	igt_ktap_free(ktap);
-- 
2.42.0

^ permalink raw reply related	[flat|nested] 77+ messages in thread

* [Intel-xe] ✗ CI.Patch_applied: failure for Kunit fixes and improvements (rev2)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (12 preceding siblings ...)
  (?)
@ 2023-10-09 12:48 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-09 12:48 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: intel-xe

== Series Details ==

Series: Kunit fixes and improvements (rev2)
URL   : https://patchwork.freedesktop.org/series/124555/
State : failure

== Summary ==

=== Applying kernel patches on branch 'drm-xe-next' with base: ===
Base commit: 973ab92d1 drm/xe: directly use pat_index for pte_encode
=== git am output follows ===
error: lib/igt_kmod.c: does not exist in index
hint: Use 'git am --show-current-patch' to see the failed patch
Applying: lib/kunit: Fix handling of potential errors from F_GETFL
Patch failed at 0001 lib/kunit: Fix handling of potential errors from F_GETFL
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".



^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✗ Fi.CI.BAT: failure for Kunit fixes and improvements (rev2)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (13 preceding siblings ...)
  (?)
@ 2023-10-09 15:48 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-09 15:48 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 7404 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev2)
URL   : https://patchwork.freedesktop.org/series/124554/
State : failure

== Summary ==

CI Bug Log - changes from IGT_7522 -> IGTPW_9951
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with IGTPW_9951 absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_9951, please notify your bug team (lgci.bug.filing@intel.com) to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html

Participating hosts (37 -> 35)
------------------------------

  Additional (1): bat-dg2-9 
  Missing    (3): bat-dg2-8 fi-snb-2520m fi-hsw-4770 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in IGTPW_9951:

### IGT changes ###

#### Possible regressions ####

  * igt@i915_module_load@load:
    - bat-adlp-6:         [PASS][1] -> [INCOMPLETE][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/bat-adlp-6/igt@i915_module_load@load.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-adlp-6/igt@i915_module_load@load.html

  
Known issues
------------

  Here are the changes found in IGTPW_9951 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_mmap@basic:
    - bat-dg2-9:          NOTRUN -> [SKIP][3] ([i915#4083])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@gem_mmap@basic.html

  * igt@gem_mmap_gtt@basic:
    - bat-dg2-9:          NOTRUN -> [SKIP][4] ([i915#4077]) +2 other tests skip
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@gem_mmap_gtt@basic.html

  * igt@gem_render_tiled_blits@basic:
    - bat-dg2-9:          NOTRUN -> [SKIP][5] ([i915#4079]) +1 other test skip
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@gem_render_tiled_blits@basic.html

  * igt@i915_pm_rps@basic-api:
    - bat-dg2-9:          NOTRUN -> [SKIP][6] ([i915#6621])
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@i915_pm_rps@basic-api.html

  * igt@kms_addfb_basic@addfb25-y-tiled-small-legacy:
    - bat-dg2-9:          NOTRUN -> [SKIP][7] ([i915#5190])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@addfb25-y-tiled-small-legacy.html

  * igt@kms_addfb_basic@basic-y-tiled-legacy:
    - bat-dg2-9:          NOTRUN -> [SKIP][8] ([i915#4215] / [i915#5190])
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@basic-y-tiled-legacy.html

  * igt@kms_addfb_basic@framebuffer-vs-set-tiling:
    - bat-dg2-9:          NOTRUN -> [SKIP][9] ([i915#4212]) +6 other tests skip
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@framebuffer-vs-set-tiling.html

  * igt@kms_addfb_basic@tile-pitch-mismatch:
    - bat-dg2-9:          NOTRUN -> [SKIP][10] ([i915#4212] / [i915#5608])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@tile-pitch-mismatch.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-legacy:
    - bat-dg2-9:          NOTRUN -> [SKIP][11] ([i915#4103] / [i915#4213] / [i915#5608]) +1 other test skip
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-legacy.html

  * igt@kms_force_connector_basic@force-load-detect:
    - bat-dg2-9:          NOTRUN -> [SKIP][12] ([fdo#109285])
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_force_connector_basic@force-load-detect.html

  * igt@kms_force_connector_basic@prune-stale-modes:
    - bat-dg2-9:          NOTRUN -> [SKIP][13] ([i915#5274])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_force_connector_basic@prune-stale-modes.html

  * igt@kms_psr@sprite_plane_onoff:
    - bat-dg2-9:          NOTRUN -> [SKIP][14] ([i915#1072]) +3 other tests skip
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_psr@sprite_plane_onoff.html

  * igt@kms_setmode@basic-clone-single-crtc:
    - bat-dg2-9:          NOTRUN -> [SKIP][15] ([i915#3555] / [i915#4098])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_setmode@basic-clone-single-crtc.html

  * igt@prime_vgem@basic-fence-flip:
    - bat-dg2-9:          NOTRUN -> [SKIP][16] ([i915#3708])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@prime_vgem@basic-fence-flip.html

  * igt@prime_vgem@basic-fence-mmap:
    - bat-dg2-9:          NOTRUN -> [SKIP][17] ([i915#3708] / [i915#4077]) +1 other test skip
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@prime_vgem@basic-fence-mmap.html

  * igt@prime_vgem@basic-write:
    - bat-dg2-9:          NOTRUN -> [SKIP][18] ([i915#3291] / [i915#3708]) +2 other tests skip
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@prime_vgem@basic-write.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
  [i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
  [i915#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077
  [i915#4079]: https://gitlab.freedesktop.org/drm/intel/issues/4079
  [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083
  [i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4212]: https://gitlab.freedesktop.org/drm/intel/issues/4212
  [i915#4213]: https://gitlab.freedesktop.org/drm/intel/issues/4213
  [i915#4215]: https://gitlab.freedesktop.org/drm/intel/issues/4215
  [i915#5190]: https://gitlab.freedesktop.org/drm/intel/issues/5190
  [i915#5274]: https://gitlab.freedesktop.org/drm/intel/issues/5274
  [i915#5354]: https://gitlab.freedesktop.org/drm/intel/issues/5354
  [i915#5608]: https://gitlab.freedesktop.org/drm/intel/issues/5608
  [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7522 -> IGTPW_9951

  CI-20190529: 20190529
  CI_DRM_13729: fe7c35e5be39964f7c0b83899b9d5d9355848544 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_9951: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html
  IGT_7522: 334a1178a36a1327dc7fbba43ab16d1f3a7d887b @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git


Testlist changes
----------------

+igt@kms_selftest@drm_cmdline_parser
+igt@kms_selftest@drm_damage_helper
+igt@kms_selftest@drm_dp_mst_helper
+igt@kms_selftest@drm_framebuffer
+igt@kms_selftest@drm_plane_helper
-igt@kms_selftest@drm_cmdline
-igt@kms_selftest@drm_damage
-igt@kms_selftest@drm_dp_mst
-igt@kms_selftest@drm_plane
-igt@kms_selftest@framebuffer

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html

[-- Attachment #2: Type: text/html, Size: 8825 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✗ CI.xeBAT: failure for Kunit fixes and improvements (rev2)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (14 preceding siblings ...)
  (?)
@ 2023-10-09 15:50 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-09 15:50 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 5330 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev2)
URL   : https://patchwork.freedesktop.org/series/124554/
State : failure

== Summary ==

CI Bug Log - changes from XEIGT_7522_BAT -> XEIGTPW_9951_BAT
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with XEIGTPW_9951_BAT absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in XEIGTPW_9951_BAT, please notify your bug team (lgci.bug.filing@intel.com) to allow them
  to document this new failure mode, which will reduce false positives in CI.

  

Participating hosts (3 -> 4)
------------------------------

  Additional (1): bat-adlp-7 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in XEIGTPW_9951_BAT:

### IGT changes ###

#### Possible regressions ####

  * igt@xe_live_ktest@migrate:
    - bat-pvc-2:          [PASS][1] -> [SKIP][2] +2 other tests skip
   [1]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7522/bat-pvc-2/igt@xe_live_ktest@migrate.html
   [2]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-pvc-2/igt@xe_live_ktest@migrate.html
    - bat-atsm-2:         [PASS][3] -> [SKIP][4] +2 other tests skip
   [3]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7522/bat-atsm-2/igt@xe_live_ktest@migrate.html
   [4]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-atsm-2/igt@xe_live_ktest@migrate.html

  
Known issues
------------

  Here are the changes found in XEIGTPW_9951_BAT that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@kms_addfb_basic@basic-y-tiled-legacy:
    - bat-adlp-7:         NOTRUN -> [FAIL][5] ([Intel XE#609]) +2 other tests fail
   [5]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-adlp-7/igt@kms_addfb_basic@basic-y-tiled-legacy.html

  * igt@kms_dsc@dsc-basic:
    - bat-adlp-7:         NOTRUN -> [SKIP][6] ([Intel XE#423])
   [6]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-adlp-7/igt@kms_dsc@dsc-basic.html

  * igt@kms_flip@basic-flip-vs-wf_vblank@d-edp1:
    - bat-adlp-7:         NOTRUN -> [FAIL][7] ([Intel XE#480]) +1 other test fail
   [7]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-adlp-7/igt@kms_flip@basic-flip-vs-wf_vblank@d-edp1.html

  * igt@kms_frontbuffer_tracking@basic:
    - bat-adlp-7:         NOTRUN -> [INCOMPLETE][8] ([Intel XE#632])
   [8]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-adlp-7/igt@kms_frontbuffer_tracking@basic.html

  * igt@xe_evict@evict-small-cm:
    - bat-adlp-7:         NOTRUN -> [SKIP][9] ([Intel XE#261] / [Intel XE#688]) +15 other tests skip
   [9]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-adlp-7/igt@xe_evict@evict-small-cm.html

  * igt@xe_exec_fault_mode@twice-userptr-invalidate-prefetch:
    - bat-adlp-7:         NOTRUN -> [SKIP][10] ([Intel XE#288]) +17 other tests skip
   [10]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-adlp-7/igt@xe_exec_fault_mode@twice-userptr-invalidate-prefetch.html

  * igt@xe_live_ktest@migrate:
    - bat-dg2-oem2:       [PASS][11] -> [SKIP][12] ([Intel XE#762]) +2 other tests skip
   [11]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7522/bat-dg2-oem2/igt@xe_live_ktest@migrate.html
   [12]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-dg2-oem2/igt@xe_live_ktest@migrate.html

  * igt@xe_mmap@vram:
    - bat-adlp-7:         NOTRUN -> [SKIP][13] ([Intel XE#263])
   [13]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-adlp-7/igt@xe_mmap@vram.html

  
#### Possible fixes ####

  * igt@xe_exec_reset@virtual-close-fd-no-exec:
    - bat-pvc-2:          [DMESG-WARN][14] ([Intel XE#696]) -> [PASS][15]
   [14]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7522/bat-pvc-2/igt@xe_exec_reset@virtual-close-fd-no-exec.html
   [15]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/bat-pvc-2/igt@xe_exec_reset@virtual-close-fd-no-exec.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [Intel XE#261]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/261
  [Intel XE#263]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/263
  [Intel XE#288]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/288
  [Intel XE#423]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/423
  [Intel XE#480]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/480
  [Intel XE#609]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/609
  [Intel XE#632]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/632
  [Intel XE#688]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/688
  [Intel XE#696]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/696
  [Intel XE#762]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/762


Build changes
-------------

  * IGT: IGT_7522 -> IGTPW_9951

  IGTPW_9951: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html
  IGT_7522: 334a1178a36a1327dc7fbba43ab16d1f3a7d887b @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  xe-422-973ab92d198430d6023aa21b93ce665193b00342: 973ab92d198430d6023aa21b93ce665193b00342

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9951/index.html

[-- Attachment #2: Type: text/html, Size: 6167 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 07/11] lib/ktap: Drop workaround for missing top level KTAP headers
  2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 17:09     ` Kamil Konieczny
  -1 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:09 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:58 +0200, Janusz Krzysztofik wrote:
> A workaround was implemented in IGT KTAP parser so it could accepted KTAP
> reports with missing top level KTAP version and test suite plan headers.
> Since kernel side commit c95e7c05c139 ("kunit: Report the count of test
> suites in a module"), included in the mainline kernel since v6.6-rc1, has
> fixed that issue, that workaround is no longer needed.  Drop it.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_ktap.c              | 12 ------------
>  lib/tests/igt_ktap_parser.c |  3 +--
>  2 files changed, 1 insertion(+), 14 deletions(-)
> 
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 53a6c63288..7c52ba11ed 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -84,18 +84,6 @@ int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap)
>  		   igt_debug_on(sscanf(buf,
>  				       "%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
>  				       &n, &len) == 1 && len == strlen(buf))) {
> -		/*
> -		 * TODO: drop the following workaround as soon as
> -		 * kernel side issue of missing lines with top level
> -		 * KTAP version and test suite plan is fixed.
> -		 */
> -		if (ktap->expect == KTAP_START) {
> -			ktap->suite_count = 1;
> -			ktap->suite_last = 0;
> -			ktap->suite_name = NULL;
> -			ktap->expect = SUITE_START;
> -		}
> -
>  		if (igt_debug_on(ktap->expect != SUITE_START))
>  			return -EPROTO;
>  
> diff --git a/lib/tests/igt_ktap_parser.c b/lib/tests/igt_ktap_parser.c
> index 6357bdf6a5..476e14092f 100644
> --- a/lib/tests/igt_ktap_parser.c
> +++ b/lib/tests/igt_ktap_parser.c
> @@ -190,8 +190,7 @@ static void ktap_top_version(void)
>  
>  	ktap = igt_ktap_alloc(&results);
>  	igt_require(ktap);
> -	/* TODO: change to -EPROTO as soon as related workaround is dropped */
> -	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EINPROGRESS);
> +	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EPROTO);
>  	igt_ktap_free(ktap);
>  
>  	ktap = igt_ktap_alloc(&results);
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 07/11] lib/ktap: Drop workaround for missing top level KTAP headers
@ 2023-10-09 17:09     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:09 UTC (permalink / raw)
  To: igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:58 +0200, Janusz Krzysztofik wrote:
> A workaround was implemented in IGT KTAP parser so it could accepted KTAP
> reports with missing top level KTAP version and test suite plan headers.
> Since kernel side commit c95e7c05c139 ("kunit: Report the count of test
> suites in a module"), included in the mainline kernel since v6.6-rc1, has
> fixed that issue, that workaround is no longer needed.  Drop it.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_ktap.c              | 12 ------------
>  lib/tests/igt_ktap_parser.c |  3 +--
>  2 files changed, 1 insertion(+), 14 deletions(-)
> 
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 53a6c63288..7c52ba11ed 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -84,18 +84,6 @@ int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap)
>  		   igt_debug_on(sscanf(buf,
>  				       "%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
>  				       &n, &len) == 1 && len == strlen(buf))) {
> -		/*
> -		 * TODO: drop the following workaround as soon as
> -		 * kernel side issue of missing lines with top level
> -		 * KTAP version and test suite plan is fixed.
> -		 */
> -		if (ktap->expect == KTAP_START) {
> -			ktap->suite_count = 1;
> -			ktap->suite_last = 0;
> -			ktap->suite_name = NULL;
> -			ktap->expect = SUITE_START;
> -		}
> -
>  		if (igt_debug_on(ktap->expect != SUITE_START))
>  			return -EPROTO;
>  
> diff --git a/lib/tests/igt_ktap_parser.c b/lib/tests/igt_ktap_parser.c
> index 6357bdf6a5..476e14092f 100644
> --- a/lib/tests/igt_ktap_parser.c
> +++ b/lib/tests/igt_ktap_parser.c
> @@ -190,8 +190,7 @@ static void ktap_top_version(void)
>  
>  	ktap = igt_ktap_alloc(&results);
>  	igt_require(ktap);
> -	/* TODO: change to -EPROTO as soon as related workaround is dropped */
> -	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EINPROGRESS);
> +	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EPROTO);
>  	igt_ktap_free(ktap);
>  
>  	ktap = igt_ktap_alloc(&results);
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 07/11] lib/ktap: Drop workaround for missing top level KTAP headers
@ 2023-10-09 17:09     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:09 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:58 +0200, Janusz Krzysztofik wrote:
> A workaround was implemented in IGT KTAP parser so it could accepted KTAP
> reports with missing top level KTAP version and test suite plan headers.
> Since kernel side commit c95e7c05c139 ("kunit: Report the count of test
> suites in a module"), included in the mainline kernel since v6.6-rc1, has
> fixed that issue, that workaround is no longer needed.  Drop it.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_ktap.c              | 12 ------------
>  lib/tests/igt_ktap_parser.c |  3 +--
>  2 files changed, 1 insertion(+), 14 deletions(-)
> 
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 53a6c63288..7c52ba11ed 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -84,18 +84,6 @@ int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap)
>  		   igt_debug_on(sscanf(buf,
>  				       "%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
>  				       &n, &len) == 1 && len == strlen(buf))) {
> -		/*
> -		 * TODO: drop the following workaround as soon as
> -		 * kernel side issue of missing lines with top level
> -		 * KTAP version and test suite plan is fixed.
> -		 */
> -		if (ktap->expect == KTAP_START) {
> -			ktap->suite_count = 1;
> -			ktap->suite_last = 0;
> -			ktap->suite_name = NULL;
> -			ktap->expect = SUITE_START;
> -		}
> -
>  		if (igt_debug_on(ktap->expect != SUITE_START))
>  			return -EPROTO;
>  
> diff --git a/lib/tests/igt_ktap_parser.c b/lib/tests/igt_ktap_parser.c
> index 6357bdf6a5..476e14092f 100644
> --- a/lib/tests/igt_ktap_parser.c
> +++ b/lib/tests/igt_ktap_parser.c
> @@ -190,8 +190,7 @@ static void ktap_top_version(void)
>  
>  	ktap = igt_ktap_alloc(&results);
>  	igt_require(ktap);
> -	/* TODO: change to -EPROTO as soon as related workaround is dropped */
> -	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EINPROGRESS);
> +	igt_assert_eq(igt_ktap_parse("    KTAP version 1\n", ktap), -EPROTO);
>  	igt_ktap_free(ktap);
>  
>  	ktap = igt_ktap_alloc(&results);
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 11/11] lib/kunit: Execute kunit test cases only when needed
  2023-10-09 12:28   ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 17:10     ` Kamil Konieczny
  -1 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:10 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:02 +0200, Janusz Krzysztofik wrote:
> IGT user interface allows to request execution of only those dynamic sub-
> subtests that match a user provided name pattern.  If the user pattern
> doesn't match any names of test cases provided by a kunit test module used
> with the subtest to be run then no results from any dynamic sub-subtests
> will be reported.  Since we already know the list of test cases provided
> by the kunit test module, there is no need to load that module to execute
> them unless the user pattern matches at least one of those test cases.
> 
> Don't load the kunit test module in execute mode before entering the loop
> of dynamic sub-subtests, and do that only from the first actually executed
> dynamic sub-subtest.
> 
> v2: Always pass last result to next dynamic sub-subtest, fetch first
>     result right after loading the kunit test module for execution.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 66 ++++++++++++++++++++++++++------------------------
>  1 file changed, 35 insertions(+), 31 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 150fe49803..69915adc40 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -981,33 +981,37 @@ static void __igt_kunit(struct igt_ktest *tst,
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(pthread_mutexattr_init(&attr));
> -	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> -	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> -
>  	ktap = igt_ktap_alloc(&results);
>  	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> -					modprobe_task, &modprobe))) {
> -		igt_ktap_free(ktap);
> -		igt_skip("Failed to create a modprobe thread\n");
> -	}
> -
>  	igt_list_for_each_entry(t, tests, link) {
>  		igt_dynamic_f("%s%s%s",
>  			      strcmp(t->suite_name, name) ?  t->suite_name : "",
>  			      strcmp(t->suite_name, name) ? "-" : "",
>  			      t->case_name) {
>  
> -			if (igt_list_empty(&results)) {
> +			if (!modprobe.thread) {
> +				igt_assert_eq(pthread_mutexattr_init(&attr), 0);
> +				igt_assert_eq(pthread_mutexattr_setrobust(&attr,
> +							  PTHREAD_MUTEX_ROBUST),
> +					      0);
> +				igt_assert_eq(pthread_mutex_init(&modprobe.lock,
> +								 &attr), 0);
> +
> +				modprobe.err = pthread_create(&modprobe.thread,
> +							      NULL,
> +							      modprobe_task,
> +							      &modprobe);
> +				igt_assert_eq(modprobe.err, 0);
> +
> +				igt_assert(igt_list_empty(&results));
>  				igt_assert_eq(ret, -EINPROGRESS);
>  				ret = kunit_kmsg_result_get(&results, &modprobe,
>  							    tst->kmsg, ktap);
>  				igt_fail_on(igt_list_empty(&results));
> -			}
>  
> -			r = igt_list_first_entry(&results, r, link);
> +				r = igt_list_first_entry(&results, r, link);
> +			}
>  
>  			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
>  					      "suite_name expected: %s, got: %s\n",
> @@ -1079,30 +1083,30 @@ static void __igt_kunit(struct igt_ktest *tst,
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		kunit_result_free(&r, &suite_name, &case_name);
> -
>  		if (igt_debug_on(ret != -EINPROGRESS))
>  			break;
>  	}
>  
>  	kunit_results_free(&results, &suite_name, &case_name);
>  
> -	switch (pthread_mutex_lock(&modprobe.lock)) {
> -	case 0:
> -		igt_debug_on(pthread_cancel(modprobe.thread));
> -		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> -		igt_debug_on(pthread_join(modprobe.thread, NULL));
> -		break;
> -	case EOWNERDEAD:
> -		/* leave the mutex unrecoverable */
> -		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> -		break;
> -	case ENOTRECOVERABLE:
> -		break;
> -	default:
> -		igt_debug("pthread_mutex_lock() failed\n");
> -		igt_debug_on(pthread_join(modprobe.thread, NULL));
> -		break;
> +	if (modprobe.thread) {
> +		switch (pthread_mutex_lock(&modprobe.lock)) {
> +		case 0:
> +			igt_debug_on(pthread_cancel(modprobe.thread));
> +			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +			igt_debug_on(pthread_join(modprobe.thread, NULL));
> +			break;
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +			break;
> +		case ENOTRECOVERABLE:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() failed\n");
> +			igt_debug_on(pthread_join(modprobe.thread, NULL));
> +			break;
> +		}
>  	}
>  
>  	igt_ktap_free(ktap);
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 11/11] lib/kunit: Execute kunit test cases only when needed
@ 2023-10-09 17:10     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:10 UTC (permalink / raw)
  To: igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:02 +0200, Janusz Krzysztofik wrote:
> IGT user interface allows to request execution of only those dynamic sub-
> subtests that match a user provided name pattern.  If the user pattern
> doesn't match any names of test cases provided by a kunit test module used
> with the subtest to be run then no results from any dynamic sub-subtests
> will be reported.  Since we already know the list of test cases provided
> by the kunit test module, there is no need to load that module to execute
> them unless the user pattern matches at least one of those test cases.
> 
> Don't load the kunit test module in execute mode before entering the loop
> of dynamic sub-subtests, and do that only from the first actually executed
> dynamic sub-subtest.
> 
> v2: Always pass last result to next dynamic sub-subtest, fetch first
>     result right after loading the kunit test module for execution.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 66 ++++++++++++++++++++++++++------------------------
>  1 file changed, 35 insertions(+), 31 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 150fe49803..69915adc40 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -981,33 +981,37 @@ static void __igt_kunit(struct igt_ktest *tst,
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(pthread_mutexattr_init(&attr));
> -	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> -	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> -
>  	ktap = igt_ktap_alloc(&results);
>  	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> -					modprobe_task, &modprobe))) {
> -		igt_ktap_free(ktap);
> -		igt_skip("Failed to create a modprobe thread\n");
> -	}
> -
>  	igt_list_for_each_entry(t, tests, link) {
>  		igt_dynamic_f("%s%s%s",
>  			      strcmp(t->suite_name, name) ?  t->suite_name : "",
>  			      strcmp(t->suite_name, name) ? "-" : "",
>  			      t->case_name) {
>  
> -			if (igt_list_empty(&results)) {
> +			if (!modprobe.thread) {
> +				igt_assert_eq(pthread_mutexattr_init(&attr), 0);
> +				igt_assert_eq(pthread_mutexattr_setrobust(&attr,
> +							  PTHREAD_MUTEX_ROBUST),
> +					      0);
> +				igt_assert_eq(pthread_mutex_init(&modprobe.lock,
> +								 &attr), 0);
> +
> +				modprobe.err = pthread_create(&modprobe.thread,
> +							      NULL,
> +							      modprobe_task,
> +							      &modprobe);
> +				igt_assert_eq(modprobe.err, 0);
> +
> +				igt_assert(igt_list_empty(&results));
>  				igt_assert_eq(ret, -EINPROGRESS);
>  				ret = kunit_kmsg_result_get(&results, &modprobe,
>  							    tst->kmsg, ktap);
>  				igt_fail_on(igt_list_empty(&results));
> -			}
>  
> -			r = igt_list_first_entry(&results, r, link);
> +				r = igt_list_first_entry(&results, r, link);
> +			}
>  
>  			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
>  					      "suite_name expected: %s, got: %s\n",
> @@ -1079,30 +1083,30 @@ static void __igt_kunit(struct igt_ktest *tst,
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		kunit_result_free(&r, &suite_name, &case_name);
> -
>  		if (igt_debug_on(ret != -EINPROGRESS))
>  			break;
>  	}
>  
>  	kunit_results_free(&results, &suite_name, &case_name);
>  
> -	switch (pthread_mutex_lock(&modprobe.lock)) {
> -	case 0:
> -		igt_debug_on(pthread_cancel(modprobe.thread));
> -		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> -		igt_debug_on(pthread_join(modprobe.thread, NULL));
> -		break;
> -	case EOWNERDEAD:
> -		/* leave the mutex unrecoverable */
> -		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> -		break;
> -	case ENOTRECOVERABLE:
> -		break;
> -	default:
> -		igt_debug("pthread_mutex_lock() failed\n");
> -		igt_debug_on(pthread_join(modprobe.thread, NULL));
> -		break;
> +	if (modprobe.thread) {
> +		switch (pthread_mutex_lock(&modprobe.lock)) {
> +		case 0:
> +			igt_debug_on(pthread_cancel(modprobe.thread));
> +			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +			igt_debug_on(pthread_join(modprobe.thread, NULL));
> +			break;
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +			break;
> +		case ENOTRECOVERABLE:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() failed\n");
> +			igt_debug_on(pthread_join(modprobe.thread, NULL));
> +			break;
> +		}
>  	}
>  
>  	igt_ktap_free(ktap);
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 11/11] lib/kunit: Execute kunit test cases only when needed
@ 2023-10-09 17:10     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:10 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:02 +0200, Janusz Krzysztofik wrote:
> IGT user interface allows to request execution of only those dynamic sub-
> subtests that match a user provided name pattern.  If the user pattern
> doesn't match any names of test cases provided by a kunit test module used
> with the subtest to be run then no results from any dynamic sub-subtests
> will be reported.  Since we already know the list of test cases provided
> by the kunit test module, there is no need to load that module to execute
> them unless the user pattern matches at least one of those test cases.
> 
> Don't load the kunit test module in execute mode before entering the loop
> of dynamic sub-subtests, and do that only from the first actually executed
> dynamic sub-subtest.
> 
> v2: Always pass last result to next dynamic sub-subtest, fetch first
>     result right after loading the kunit test module for execution.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 66 ++++++++++++++++++++++++++------------------------
>  1 file changed, 35 insertions(+), 31 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 150fe49803..69915adc40 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -981,33 +981,37 @@ static void __igt_kunit(struct igt_ktest *tst,
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(pthread_mutexattr_init(&attr));
> -	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> -	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> -
>  	ktap = igt_ktap_alloc(&results);
>  	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> -					modprobe_task, &modprobe))) {
> -		igt_ktap_free(ktap);
> -		igt_skip("Failed to create a modprobe thread\n");
> -	}
> -
>  	igt_list_for_each_entry(t, tests, link) {
>  		igt_dynamic_f("%s%s%s",
>  			      strcmp(t->suite_name, name) ?  t->suite_name : "",
>  			      strcmp(t->suite_name, name) ? "-" : "",
>  			      t->case_name) {
>  
> -			if (igt_list_empty(&results)) {
> +			if (!modprobe.thread) {
> +				igt_assert_eq(pthread_mutexattr_init(&attr), 0);
> +				igt_assert_eq(pthread_mutexattr_setrobust(&attr,
> +							  PTHREAD_MUTEX_ROBUST),
> +					      0);
> +				igt_assert_eq(pthread_mutex_init(&modprobe.lock,
> +								 &attr), 0);
> +
> +				modprobe.err = pthread_create(&modprobe.thread,
> +							      NULL,
> +							      modprobe_task,
> +							      &modprobe);
> +				igt_assert_eq(modprobe.err, 0);
> +
> +				igt_assert(igt_list_empty(&results));
>  				igt_assert_eq(ret, -EINPROGRESS);
>  				ret = kunit_kmsg_result_get(&results, &modprobe,
>  							    tst->kmsg, ktap);
>  				igt_fail_on(igt_list_empty(&results));
> -			}
>  
> -			r = igt_list_first_entry(&results, r, link);
> +				r = igt_list_first_entry(&results, r, link);
> +			}
>  
>  			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
>  					      "suite_name expected: %s, got: %s\n",
> @@ -1079,30 +1083,30 @@ static void __igt_kunit(struct igt_ktest *tst,
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		kunit_result_free(&r, &suite_name, &case_name);
> -
>  		if (igt_debug_on(ret != -EINPROGRESS))
>  			break;
>  	}
>  
>  	kunit_results_free(&results, &suite_name, &case_name);
>  
> -	switch (pthread_mutex_lock(&modprobe.lock)) {
> -	case 0:
> -		igt_debug_on(pthread_cancel(modprobe.thread));
> -		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> -		igt_debug_on(pthread_join(modprobe.thread, NULL));
> -		break;
> -	case EOWNERDEAD:
> -		/* leave the mutex unrecoverable */
> -		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> -		break;
> -	case ENOTRECOVERABLE:
> -		break;
> -	default:
> -		igt_debug("pthread_mutex_lock() failed\n");
> -		igt_debug_on(pthread_join(modprobe.thread, NULL));
> -		break;
> +	if (modprobe.thread) {
> +		switch (pthread_mutex_lock(&modprobe.lock)) {
> +		case 0:
> +			igt_debug_on(pthread_cancel(modprobe.thread));
> +			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +			igt_debug_on(pthread_join(modprobe.thread, NULL));
> +			break;
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +			break;
> +		case ENOTRECOVERABLE:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() failed\n");
> +			igt_debug_on(pthread_join(modprobe.thread, NULL));
> +			break;
> +		}
>  	}
>  
>  	igt_ktap_free(ktap);
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 08/11] lib/kunit: Provide all results cleanup helper
  2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 17:33     ` Kamil Konieczny
  -1 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:33 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:59 +0200, Janusz Krzysztofik wrote:
> Planned changes require a couple of loops around kunit_result_free().
> Since we already have such loop, move it into a helper in preparation for
> future uses.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 20 ++++++++++++++------
>  1 file changed, 14 insertions(+), 6 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 387efbb59f..fed0855c84 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -885,13 +885,25 @@ static void kunit_result_free(struct igt_ktap_result **r,
>  	*r = NULL;
>  }
>  
> +static void kunit_results_free(struct igt_list_head *results,
> +			       char **suite_name, char **case_name)
> +{
> +	struct igt_ktap_result *r, *rn;
> +
> +	igt_list_for_each_entry_safe(r, rn, results, link)
> +		kunit_result_free(&r, suite_name, case_name);
> +
> +	free(*case_name);
> +	free(*suite_name);
> +}
> +
>  static void
>  __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  {
>  	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
>  	char *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_result *r, *rn;
>  	struct igt_ktap_results *ktap;
> +	struct igt_ktap_result *r;
>  	pthread_mutexattr_t attr;
>  	IGT_LIST_HEAD(results);
>  	unsigned long taints;
> @@ -997,11 +1009,7 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	} while (ret == -EINPROGRESS);
>  
> -	igt_list_for_each_entry_safe(r, rn, &results, link)
> -		kunit_result_free(&r, &suite_name, &case_name);
> -
> -	free(case_name);
> -	free(suite_name);
> +	kunit_results_free(&results, &suite_name, &case_name);
>  
>  	switch (pthread_mutex_lock(&modprobe.lock)) {
>  	case 0:
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 08/11] lib/kunit: Provide all results cleanup helper
@ 2023-10-09 17:33     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:33 UTC (permalink / raw)
  To: igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:59 +0200, Janusz Krzysztofik wrote:
> Planned changes require a couple of loops around kunit_result_free().
> Since we already have such loop, move it into a helper in preparation for
> future uses.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 20 ++++++++++++++------
>  1 file changed, 14 insertions(+), 6 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 387efbb59f..fed0855c84 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -885,13 +885,25 @@ static void kunit_result_free(struct igt_ktap_result **r,
>  	*r = NULL;
>  }
>  
> +static void kunit_results_free(struct igt_list_head *results,
> +			       char **suite_name, char **case_name)
> +{
> +	struct igt_ktap_result *r, *rn;
> +
> +	igt_list_for_each_entry_safe(r, rn, results, link)
> +		kunit_result_free(&r, suite_name, case_name);
> +
> +	free(*case_name);
> +	free(*suite_name);
> +}
> +
>  static void
>  __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  {
>  	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
>  	char *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_result *r, *rn;
>  	struct igt_ktap_results *ktap;
> +	struct igt_ktap_result *r;
>  	pthread_mutexattr_t attr;
>  	IGT_LIST_HEAD(results);
>  	unsigned long taints;
> @@ -997,11 +1009,7 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	} while (ret == -EINPROGRESS);
>  
> -	igt_list_for_each_entry_safe(r, rn, &results, link)
> -		kunit_result_free(&r, &suite_name, &case_name);
> -
> -	free(case_name);
> -	free(suite_name);
> +	kunit_results_free(&results, &suite_name, &case_name);
>  
>  	switch (pthread_mutex_lock(&modprobe.lock)) {
>  	case 0:
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 08/11] lib/kunit: Provide all results cleanup helper
@ 2023-10-09 17:33     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:33 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:59 +0200, Janusz Krzysztofik wrote:
> Planned changes require a couple of loops around kunit_result_free().
> Since we already have such loop, move it into a helper in preparation for
> future uses.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 20 ++++++++++++++------
>  1 file changed, 14 insertions(+), 6 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 387efbb59f..fed0855c84 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -885,13 +885,25 @@ static void kunit_result_free(struct igt_ktap_result **r,
>  	*r = NULL;
>  }
>  
> +static void kunit_results_free(struct igt_list_head *results,
> +			       char **suite_name, char **case_name)
> +{
> +	struct igt_ktap_result *r, *rn;
> +
> +	igt_list_for_each_entry_safe(r, rn, results, link)
> +		kunit_result_free(&r, suite_name, case_name);
> +
> +	free(*case_name);
> +	free(*suite_name);
> +}
> +
>  static void
>  __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  {
>  	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
>  	char *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_result *r, *rn;
>  	struct igt_ktap_results *ktap;
> +	struct igt_ktap_result *r;
>  	pthread_mutexattr_t attr;
>  	IGT_LIST_HEAD(results);
>  	unsigned long taints;
> @@ -997,11 +1009,7 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	} while (ret == -EINPROGRESS);
>  
> -	igt_list_for_each_entry_safe(r, rn, &results, link)
> -		kunit_result_free(&r, &suite_name, &case_name);
> -
> -	free(case_name);
> -	free(suite_name);
> +	kunit_results_free(&results, &suite_name, &case_name);
>  
>  	switch (pthread_mutex_lock(&modprobe.lock)) {
>  	case 0:
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
  2023-10-09 12:28   ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 17:37     ` Kamil Konieczny
  -1 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:37 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:00 +0200, Janusz Krzysztofik wrote:
> We are going to add support for reading a list of kunit test cases
> provided by a kunit test module prior to executing those test cases.  That
--------------------------------------------------------------------- ^^
Two spaces, use only one.

> will be done by first loading kunit modules in list only mode, then
> reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
-------------------------------------------------------^^
Same here.

> will be performed after the kunit test module is successfully loaded and
> there will be no concurrently running modprobe thread, we need to make
> synchronization of reads from /dev/kmsg with potential errors modprobe
> thread optional.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
>  1 file changed, 26 insertions(+), 24 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index fed0855c84..ed41aa1235 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct igt_list_head *results,
>  		if (igt_debug_on(igt_kernel_tainted(&taints)))
>  			return -ENOTRECOVERABLE;
>  
> -		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> -		if (err == -1)
> -			return -errno;
> -		else if (unlikely(err))
> -			return err;
> -
> -		err = pthread_mutex_lock(&modprobe->lock);
> -		switch (err) {
> -		case EOWNERDEAD:
> -			/* leave the mutex unrecoverable */
> -			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> -			__attribute__ ((fallthrough));
> -		case ENOTRECOVERABLE:
> -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> -			if (igt_debug_on(modprobe->err))
> -				return modprobe->err;
> -			break;
> -		case 0:
> -			break;
> -		default:
> -			igt_debug("pthread_mutex_lock() error: %d\n", err);
> -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> -			return -err;
> +		if (modprobe) {
> +			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +			if (err == -1)
> +				return -errno;
> +			else if (unlikely(err))
> +				return err;
> +
> +			err = pthread_mutex_lock(&modprobe->lock);
> +			switch (err) {
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
> +				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +				if (igt_debug_on(modprobe->err))
> +					return modprobe->err;
> +				break;
> +			case 0:
> +				break;
> +			default:
> +				igt_debug("pthread_mutex_lock() error: %d\n", err);
> +				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +				return -err;
> +			}
>  		}
>  
>  		ret = read(fd, record, BUF_LEN);
>  
> -		if (!err) {
> +		if (modprobe && !err) {
>  			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
>  			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
>  		}
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
@ 2023-10-09 17:37     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:37 UTC (permalink / raw)
  To: igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:00 +0200, Janusz Krzysztofik wrote:
> We are going to add support for reading a list of kunit test cases
> provided by a kunit test module prior to executing those test cases.  That
--------------------------------------------------------------------- ^^
Two spaces, use only one.

> will be done by first loading kunit modules in list only mode, then
> reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
-------------------------------------------------------^^
Same here.

> will be performed after the kunit test module is successfully loaded and
> there will be no concurrently running modprobe thread, we need to make
> synchronization of reads from /dev/kmsg with potential errors modprobe
> thread optional.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
>  1 file changed, 26 insertions(+), 24 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index fed0855c84..ed41aa1235 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct igt_list_head *results,
>  		if (igt_debug_on(igt_kernel_tainted(&taints)))
>  			return -ENOTRECOVERABLE;
>  
> -		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> -		if (err == -1)
> -			return -errno;
> -		else if (unlikely(err))
> -			return err;
> -
> -		err = pthread_mutex_lock(&modprobe->lock);
> -		switch (err) {
> -		case EOWNERDEAD:
> -			/* leave the mutex unrecoverable */
> -			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> -			__attribute__ ((fallthrough));
> -		case ENOTRECOVERABLE:
> -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> -			if (igt_debug_on(modprobe->err))
> -				return modprobe->err;
> -			break;
> -		case 0:
> -			break;
> -		default:
> -			igt_debug("pthread_mutex_lock() error: %d\n", err);
> -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> -			return -err;
> +		if (modprobe) {
> +			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +			if (err == -1)
> +				return -errno;
> +			else if (unlikely(err))
> +				return err;
> +
> +			err = pthread_mutex_lock(&modprobe->lock);
> +			switch (err) {
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
> +				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +				if (igt_debug_on(modprobe->err))
> +					return modprobe->err;
> +				break;
> +			case 0:
> +				break;
> +			default:
> +				igt_debug("pthread_mutex_lock() error: %d\n", err);
> +				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +				return -err;
> +			}
>  		}
>  
>  		ret = read(fd, record, BUF_LEN);
>  
> -		if (!err) {
> +		if (modprobe && !err) {
>  			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
>  			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
>  		}
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
@ 2023-10-09 17:37     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:37 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:00 +0200, Janusz Krzysztofik wrote:
> We are going to add support for reading a list of kunit test cases
> provided by a kunit test module prior to executing those test cases.  That
--------------------------------------------------------------------- ^^
Two spaces, use only one.

> will be done by first loading kunit modules in list only mode, then
> reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
-------------------------------------------------------^^
Same here.

> will be performed after the kunit test module is successfully loaded and
> there will be no concurrently running modprobe thread, we need to make
> synchronization of reads from /dev/kmsg with potential errors modprobe
> thread optional.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>

Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> ---
>  lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
>  1 file changed, 26 insertions(+), 24 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index fed0855c84..ed41aa1235 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct igt_list_head *results,
>  		if (igt_debug_on(igt_kernel_tainted(&taints)))
>  			return -ENOTRECOVERABLE;
>  
> -		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> -		if (err == -1)
> -			return -errno;
> -		else if (unlikely(err))
> -			return err;
> -
> -		err = pthread_mutex_lock(&modprobe->lock);
> -		switch (err) {
> -		case EOWNERDEAD:
> -			/* leave the mutex unrecoverable */
> -			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> -			__attribute__ ((fallthrough));
> -		case ENOTRECOVERABLE:
> -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> -			if (igt_debug_on(modprobe->err))
> -				return modprobe->err;
> -			break;
> -		case 0:
> -			break;
> -		default:
> -			igt_debug("pthread_mutex_lock() error: %d\n", err);
> -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> -			return -err;
> +		if (modprobe) {
> +			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +			if (err == -1)
> +				return -errno;
> +			else if (unlikely(err))
> +				return err;
> +
> +			err = pthread_mutex_lock(&modprobe->lock);
> +			switch (err) {
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
> +				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +				if (igt_debug_on(modprobe->err))
> +					return modprobe->err;
> +				break;
> +			case 0:
> +				break;
> +			default:
> +				igt_debug("pthread_mutex_lock() error: %d\n", err);
> +				igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +				return -err;
> +			}
>  		}
>  
>  		ret = read(fd, record, BUF_LEN);
>  
> -		if (!err) {
> +		if (modprobe && !err) {
>  			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
>  			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
>  		}
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 10/11] lib/kunit: Fetch a list of test cases in advance
  2023-10-09 12:28   ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-09 17:45     ` Kamil Konieczny
  -1 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:45 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:01 +0200, Janusz Krzysztofik wrote:
> Recent improvements to the kernel kunit framework allow us to obtain a
> list of test cases provided by a kunit test module without actually
> running them.  Use that feature to get a list of expected test cases
> before we enter a loop around igt_dynamic().  Once done, enter the
----------------------------------------------^^
Two spaces, use one.

> igt_dynamic() section for each consecutive test case immediately, even
> before first line of a related KTAP report appears, then look for a result
> from that test case.  That should make our IGT results output still better
--------------------- ^^
Same here.

> synchronized with related kernel messages.
> 
> The list of test cases provided by a kunit test module can be obtained by
> loading the kunit base module with specific options, then loading the test
> module.  For that to be possible, take care of unloading the kunit base
--------------------- ^^
Same here.

> module before each kunit subtest (I was wrong when in one of my previous
> commit messages I suggested that on final unload of a kunit test module
> the kunit base module is unloaded automatically as its dependency,
> however, that didn't matter before, then no separate fix was required).
> Since that module can then be left loaded with non-default options if an
> error occurs, unload it explicitly before returning from igt_kunit().
> 
> There are two possible ways of getting a list of test cases: by loading
> the base kunit module with action=list module option, or by filtering
> out all test cases from being executed while asking for SKIP results from
> those filtered out.  Since the latter provides regular KTAP report that we
-------------------- ^^
Same here.

> can already parse perfectly, use it instead of trying to identify an
> unstructured list of test cases of unknown length submitted by the former.
> 
> If an IGT test that calls igt_kunit() provides a subtest name then use
> that name to filter out potential test cases that don't belong to the
> named test suite from the list.
> 
> To avoid loading any modules if no subtest is going to be executed (e.g.,
> if a nonexistent subtest has been requested), load the kunit modules in
> list mode from inside the igt_subtest_with_dynamic() section.  In order to
-------------------------------------------------------------- ^^
Same here.

> be free to skip the whole subtest on unmet requirements that need to be
> verified after that list has been already populated, clean it up from a
> follow up igt_fixture section.
> 
> Since we may now finish processing of all test cases / dynamic sub-
> subtests before KTAP parsing completes, don't fail if we exit the loop of
> dynamic sub-subtests with -EINPROGRESS error code returned by the parser.
> 
> v2: Split out changes in handling of modprobe errors and kernel taints to
>     separate patches (Kamil),
>   - fix some string duplicates referenced from filtered out test cases not
>     freed,
>   - don't check if next result is needed before fetching one, obviously
>     true in first dynamic sub-subtest, and we always free last result
>     before looping to next sub-subtest,
>   - still break the loop of test cases on unexpected return codes from
>     kunit_kmsg_get_result(),
>   - use kunit_results_free() helper,
>   - fix typos (Kamil),
>   - update commit description.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> ---
>  lib/igt_kmod.c | 169 ++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 132 insertions(+), 37 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index ed41aa1235..150fe49803 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -899,17 +899,15 @@ static void kunit_results_free(struct igt_list_head *results,
>  	free(*suite_name);
>  }
>  
> -static void
> -__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
> +static void kunit_get_tests(struct igt_list_head *tests,
> +			    struct igt_ktest *tst,
> +			    const char *filter,
> +			    const char *opts)
>  {
> -	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
>  	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
>  	struct igt_ktap_results *ktap;
> -	struct igt_ktap_result *r;
> -	pthread_mutexattr_t attr;
> -	IGT_LIST_HEAD(results);
> -	unsigned long taints;
> -	int flags, ret;
> +	int flags, err;
>  
>  	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
>  
> @@ -919,6 +917,70 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> +	/*
> +	 * To get a list of test cases provided by a kunit test module, ask the
> +	 * generic kunit module to respond with SKIP result for each test found.
> +	 * We could also use action=list kunit parameter to get the listing,
> +	 * however, parsing a KTAP report -- something that we already can do
> +	 * perfectly -- seems to be more safe than extracting a test case list
> +	 * of unknown length from /dev/kmsg.
> +	 */
> +	igt_skip_on(igt_kmod_load("kunit", "filter=module=none filter_action=skip"));
> +	igt_skip_on(modprobe(tst->kmod, opts));
> +
> +	ktap = igt_ktap_alloc(tests);
> +	igt_require(ktap);
> +
> +	do
> +		err = kunit_kmsg_result_get(tests, NULL, tst->kmsg, ktap);
> +	while (err == -EINPROGRESS);
> +
> +	igt_ktap_free(ktap);
> +
> +	if (!err)
> +		igt_list_for_each_entry_safe(r, rn, tests, link) {
> +			if (igt_debug_on(r->code != IGT_EXIT_SKIP)) {
> +				err = r->code ?: -EPROTO;
> +				break;
> +			}
> +
> +			if (!filter)
> +				continue;
> +
> +			if (strcmp(r->suite_name, filter))
> +				kunit_result_free(&r, &case_name, &suite_name);
> +		}
> +
> +	if (err) {
> +		kunit_results_free(tests, &case_name, &suite_name);
> +	} else {
> +		free(suite_name);
> +		free(case_name);
> +	}
> +
> +	igt_skip_on(kmod_module_remove_module(tst->kmod, KMOD_REMOVE_FORCE));
> +	igt_skip_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
> +
> +	igt_skip_on_f(err,
> +		      "KTAP parser failed while getting a list of test cases\n");
> +}
> +
> +static void __igt_kunit(struct igt_ktest *tst,
> +			const char *name,
> +			const char *opts,
> +			struct igt_list_head *tests)
> +{
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *t, *r = NULL;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
> +	int ret = -EINPROGRESS;
> +	unsigned long taints;
> +
> +	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> +
>  	igt_skip_on(pthread_mutexattr_init(&attr));
>  	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
>  	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> @@ -932,37 +994,47 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	do {
> -		ret = kunit_kmsg_result_get(&results, &modprobe,
> -					    tst->kmsg, ktap);
> -		if (igt_debug_on(ret && ret != -EINPROGRESS))
> -			break;
> -
> -		if (igt_debug_on(igt_list_empty(&results)))
> -			break;
> -
> -		r = igt_list_first_entry(&results, r, link);
> -
> +	igt_list_for_each_entry(t, tests, link) {
>  		igt_dynamic_f("%s%s%s",
> -			      strcmp(r->suite_name, name) ?  r->suite_name : "",
> -			      strcmp(r->suite_name, name) ? "-" : "",
> -			      r->case_name) {
> -
> -			if (r->code == IGT_EXIT_INVALID) {
> -				/* parametrized test case, get actual result */
> -				kunit_result_free(&r, &suite_name, &case_name);
> -
> -				igt_assert(igt_list_empty(&results));
> +			      strcmp(t->suite_name, name) ?  t->suite_name : "",
> +			      strcmp(t->suite_name, name) ? "-" : "",
> +			      t->case_name) {
>  
> +			if (igt_list_empty(&results)) {
> +				igt_assert_eq(ret, -EINPROGRESS);
>  				ret = kunit_kmsg_result_get(&results, &modprobe,
>  							    tst->kmsg, ktap);
> -				if (ret != -EINPROGRESS)
> -					igt_fail_on(ret);
> -
>  				igt_fail_on(igt_list_empty(&results));
> +			}
> +
> +			r = igt_list_first_entry(&results, r, link);
> +
> +			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      t->suite_name, r->suite_name) ||
> +			       igt_debug_on_f(strcmp(r->case_name, t->case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      t->case_name, r->case_name) ||
> +			       r->code == IGT_EXIT_INVALID) {
> +
> +				int code = r->code;
> +
> +				kunit_result_free(&r, &suite_name, &case_name);
> +				if (igt_list_empty(&results)) {
> +					igt_assert_eq(ret, -EINPROGRESS);
> +					ret = kunit_kmsg_result_get(&results,
> +								    &modprobe,
> +								    tst->kmsg,
> +								    ktap);
> +					igt_fail_on(igt_list_empty(&results));
> +				}
>  
>  				r = igt_list_first_entry(&results, r, link);
>  
> +				if (code != IGT_EXIT_INVALID)
> +					continue;
> +
> +				/* result from parametrized test case */
>  				igt_fail_on_f(strcmp(r->suite_name, suite_name),
>  					      "suite_name expected: %s, got: %s\n",
>  					      suite_name, r->suite_name);
> @@ -1009,7 +1081,9 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  		kunit_result_free(&r, &suite_name, &case_name);
>  
> -	} while (ret == -EINPROGRESS);
> +		if (igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	}
>  
>  	kunit_results_free(&results, &suite_name, &case_name);
>  
> @@ -1035,7 +1109,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> -	igt_skip_on_f(ret, "KTAP parser failed\n");
> +	if (ret != -EINPROGRESS)
> +		igt_skip_on_f(ret, "KTAP parser failed\n");
>  }
>  
>  /**
> @@ -1049,7 +1124,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  void igt_kunit(const char *module_name, const char *name, const char *opts)
>  {
>  	struct igt_ktest tst = { .kmsg = -1, };
> -
> +	const char *filter = name;
> +	IGT_LIST_HEAD(tests);
>  
>  	/*
>  	 * If the caller (an IGT test) provides no subtest name then we
> @@ -1074,6 +1150,15 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
>  
>  		igt_skip_on(igt_ktest_init(&tst, module_name));
>  		igt_skip_on(igt_ktest_begin(&tst));
> +
> +		/*
> +		 * Since we need to load kunit base module with specific
> +		 * options in order to get a list of test cases, make
> +		 * sure that the module is not loaded.  However, since
--------------------------------------------- ^^
Same here.

Acked-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> +		 * unload may fail if kunit base module is not loaded,
> +		 * ignore any failures, we'll fail later if still loaded.
> +		 */
> +		igt_ignore_warn(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
>  	}
>  
>  	/*
> @@ -1083,11 +1168,21 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
>  	 * proper namespace for dynamic subtests, with is required for CI
>  	 * and for documentation.
>  	 */
> -	igt_subtest_with_dynamic(name)
> -		__igt_kunit(&tst, name, opts);
> +	igt_subtest_with_dynamic(name) {
> +		kunit_get_tests(&tests, &tst, filter, opts);
> +		igt_skip_on(igt_list_empty(&tests));
> +
> +		__igt_kunit(&tst, name, opts, &tests);
> +	}
> +
> +	igt_fixture {
> +		char *suite_name = NULL, *case_name = NULL;
> +
> +		kunit_results_free(&tests, &suite_name, &case_name);
>  
> -	igt_fixture
>  		igt_ktest_end(&tst);
> +		igt_debug_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
> +	}
>  
>  	igt_ktest_fini(&tst);
>  }
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 10/11] lib/kunit: Fetch a list of test cases in advance
@ 2023-10-09 17:45     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:45 UTC (permalink / raw)
  To: igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:01 +0200, Janusz Krzysztofik wrote:
> Recent improvements to the kernel kunit framework allow us to obtain a
> list of test cases provided by a kunit test module without actually
> running them.  Use that feature to get a list of expected test cases
> before we enter a loop around igt_dynamic().  Once done, enter the
----------------------------------------------^^
Two spaces, use one.

> igt_dynamic() section for each consecutive test case immediately, even
> before first line of a related KTAP report appears, then look for a result
> from that test case.  That should make our IGT results output still better
--------------------- ^^
Same here.

> synchronized with related kernel messages.
> 
> The list of test cases provided by a kunit test module can be obtained by
> loading the kunit base module with specific options, then loading the test
> module.  For that to be possible, take care of unloading the kunit base
--------------------- ^^
Same here.

> module before each kunit subtest (I was wrong when in one of my previous
> commit messages I suggested that on final unload of a kunit test module
> the kunit base module is unloaded automatically as its dependency,
> however, that didn't matter before, then no separate fix was required).
> Since that module can then be left loaded with non-default options if an
> error occurs, unload it explicitly before returning from igt_kunit().
> 
> There are two possible ways of getting a list of test cases: by loading
> the base kunit module with action=list module option, or by filtering
> out all test cases from being executed while asking for SKIP results from
> those filtered out.  Since the latter provides regular KTAP report that we
-------------------- ^^
Same here.

> can already parse perfectly, use it instead of trying to identify an
> unstructured list of test cases of unknown length submitted by the former.
> 
> If an IGT test that calls igt_kunit() provides a subtest name then use
> that name to filter out potential test cases that don't belong to the
> named test suite from the list.
> 
> To avoid loading any modules if no subtest is going to be executed (e.g.,
> if a nonexistent subtest has been requested), load the kunit modules in
> list mode from inside the igt_subtest_with_dynamic() section.  In order to
-------------------------------------------------------------- ^^
Same here.

> be free to skip the whole subtest on unmet requirements that need to be
> verified after that list has been already populated, clean it up from a
> follow up igt_fixture section.
> 
> Since we may now finish processing of all test cases / dynamic sub-
> subtests before KTAP parsing completes, don't fail if we exit the loop of
> dynamic sub-subtests with -EINPROGRESS error code returned by the parser.
> 
> v2: Split out changes in handling of modprobe errors and kernel taints to
>     separate patches (Kamil),
>   - fix some string duplicates referenced from filtered out test cases not
>     freed,
>   - don't check if next result is needed before fetching one, obviously
>     true in first dynamic sub-subtest, and we always free last result
>     before looping to next sub-subtest,
>   - still break the loop of test cases on unexpected return codes from
>     kunit_kmsg_get_result(),
>   - use kunit_results_free() helper,
>   - fix typos (Kamil),
>   - update commit description.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> ---
>  lib/igt_kmod.c | 169 ++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 132 insertions(+), 37 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index ed41aa1235..150fe49803 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -899,17 +899,15 @@ static void kunit_results_free(struct igt_list_head *results,
>  	free(*suite_name);
>  }
>  
> -static void
> -__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
> +static void kunit_get_tests(struct igt_list_head *tests,
> +			    struct igt_ktest *tst,
> +			    const char *filter,
> +			    const char *opts)
>  {
> -	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
>  	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
>  	struct igt_ktap_results *ktap;
> -	struct igt_ktap_result *r;
> -	pthread_mutexattr_t attr;
> -	IGT_LIST_HEAD(results);
> -	unsigned long taints;
> -	int flags, ret;
> +	int flags, err;
>  
>  	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
>  
> @@ -919,6 +917,70 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> +	/*
> +	 * To get a list of test cases provided by a kunit test module, ask the
> +	 * generic kunit module to respond with SKIP result for each test found.
> +	 * We could also use action=list kunit parameter to get the listing,
> +	 * however, parsing a KTAP report -- something that we already can do
> +	 * perfectly -- seems to be more safe than extracting a test case list
> +	 * of unknown length from /dev/kmsg.
> +	 */
> +	igt_skip_on(igt_kmod_load("kunit", "filter=module=none filter_action=skip"));
> +	igt_skip_on(modprobe(tst->kmod, opts));
> +
> +	ktap = igt_ktap_alloc(tests);
> +	igt_require(ktap);
> +
> +	do
> +		err = kunit_kmsg_result_get(tests, NULL, tst->kmsg, ktap);
> +	while (err == -EINPROGRESS);
> +
> +	igt_ktap_free(ktap);
> +
> +	if (!err)
> +		igt_list_for_each_entry_safe(r, rn, tests, link) {
> +			if (igt_debug_on(r->code != IGT_EXIT_SKIP)) {
> +				err = r->code ?: -EPROTO;
> +				break;
> +			}
> +
> +			if (!filter)
> +				continue;
> +
> +			if (strcmp(r->suite_name, filter))
> +				kunit_result_free(&r, &case_name, &suite_name);
> +		}
> +
> +	if (err) {
> +		kunit_results_free(tests, &case_name, &suite_name);
> +	} else {
> +		free(suite_name);
> +		free(case_name);
> +	}
> +
> +	igt_skip_on(kmod_module_remove_module(tst->kmod, KMOD_REMOVE_FORCE));
> +	igt_skip_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
> +
> +	igt_skip_on_f(err,
> +		      "KTAP parser failed while getting a list of test cases\n");
> +}
> +
> +static void __igt_kunit(struct igt_ktest *tst,
> +			const char *name,
> +			const char *opts,
> +			struct igt_list_head *tests)
> +{
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *t, *r = NULL;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
> +	int ret = -EINPROGRESS;
> +	unsigned long taints;
> +
> +	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> +
>  	igt_skip_on(pthread_mutexattr_init(&attr));
>  	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
>  	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> @@ -932,37 +994,47 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	do {
> -		ret = kunit_kmsg_result_get(&results, &modprobe,
> -					    tst->kmsg, ktap);
> -		if (igt_debug_on(ret && ret != -EINPROGRESS))
> -			break;
> -
> -		if (igt_debug_on(igt_list_empty(&results)))
> -			break;
> -
> -		r = igt_list_first_entry(&results, r, link);
> -
> +	igt_list_for_each_entry(t, tests, link) {
>  		igt_dynamic_f("%s%s%s",
> -			      strcmp(r->suite_name, name) ?  r->suite_name : "",
> -			      strcmp(r->suite_name, name) ? "-" : "",
> -			      r->case_name) {
> -
> -			if (r->code == IGT_EXIT_INVALID) {
> -				/* parametrized test case, get actual result */
> -				kunit_result_free(&r, &suite_name, &case_name);
> -
> -				igt_assert(igt_list_empty(&results));
> +			      strcmp(t->suite_name, name) ?  t->suite_name : "",
> +			      strcmp(t->suite_name, name) ? "-" : "",
> +			      t->case_name) {
>  
> +			if (igt_list_empty(&results)) {
> +				igt_assert_eq(ret, -EINPROGRESS);
>  				ret = kunit_kmsg_result_get(&results, &modprobe,
>  							    tst->kmsg, ktap);
> -				if (ret != -EINPROGRESS)
> -					igt_fail_on(ret);
> -
>  				igt_fail_on(igt_list_empty(&results));
> +			}
> +
> +			r = igt_list_first_entry(&results, r, link);
> +
> +			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      t->suite_name, r->suite_name) ||
> +			       igt_debug_on_f(strcmp(r->case_name, t->case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      t->case_name, r->case_name) ||
> +			       r->code == IGT_EXIT_INVALID) {
> +
> +				int code = r->code;
> +
> +				kunit_result_free(&r, &suite_name, &case_name);
> +				if (igt_list_empty(&results)) {
> +					igt_assert_eq(ret, -EINPROGRESS);
> +					ret = kunit_kmsg_result_get(&results,
> +								    &modprobe,
> +								    tst->kmsg,
> +								    ktap);
> +					igt_fail_on(igt_list_empty(&results));
> +				}
>  
>  				r = igt_list_first_entry(&results, r, link);
>  
> +				if (code != IGT_EXIT_INVALID)
> +					continue;
> +
> +				/* result from parametrized test case */
>  				igt_fail_on_f(strcmp(r->suite_name, suite_name),
>  					      "suite_name expected: %s, got: %s\n",
>  					      suite_name, r->suite_name);
> @@ -1009,7 +1081,9 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  		kunit_result_free(&r, &suite_name, &case_name);
>  
> -	} while (ret == -EINPROGRESS);
> +		if (igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	}
>  
>  	kunit_results_free(&results, &suite_name, &case_name);
>  
> @@ -1035,7 +1109,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> -	igt_skip_on_f(ret, "KTAP parser failed\n");
> +	if (ret != -EINPROGRESS)
> +		igt_skip_on_f(ret, "KTAP parser failed\n");
>  }
>  
>  /**
> @@ -1049,7 +1124,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  void igt_kunit(const char *module_name, const char *name, const char *opts)
>  {
>  	struct igt_ktest tst = { .kmsg = -1, };
> -
> +	const char *filter = name;
> +	IGT_LIST_HEAD(tests);
>  
>  	/*
>  	 * If the caller (an IGT test) provides no subtest name then we
> @@ -1074,6 +1150,15 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
>  
>  		igt_skip_on(igt_ktest_init(&tst, module_name));
>  		igt_skip_on(igt_ktest_begin(&tst));
> +
> +		/*
> +		 * Since we need to load kunit base module with specific
> +		 * options in order to get a list of test cases, make
> +		 * sure that the module is not loaded.  However, since
--------------------------------------------- ^^
Same here.

Acked-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> +		 * unload may fail if kunit base module is not loaded,
> +		 * ignore any failures, we'll fail later if still loaded.
> +		 */
> +		igt_ignore_warn(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
>  	}
>  
>  	/*
> @@ -1083,11 +1168,21 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
>  	 * proper namespace for dynamic subtests, with is required for CI
>  	 * and for documentation.
>  	 */
> -	igt_subtest_with_dynamic(name)
> -		__igt_kunit(&tst, name, opts);
> +	igt_subtest_with_dynamic(name) {
> +		kunit_get_tests(&tests, &tst, filter, opts);
> +		igt_skip_on(igt_list_empty(&tests));
> +
> +		__igt_kunit(&tst, name, opts, &tests);
> +	}
> +
> +	igt_fixture {
> +		char *suite_name = NULL, *case_name = NULL;
> +
> +		kunit_results_free(&tests, &suite_name, &case_name);
>  
> -	igt_fixture
>  		igt_ktest_end(&tst);
> +		igt_debug_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
> +	}
>  
>  	igt_ktest_fini(&tst);
>  }
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 10/11] lib/kunit: Fetch a list of test cases in advance
@ 2023-10-09 17:45     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-09 17:45 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:28:01 +0200, Janusz Krzysztofik wrote:
> Recent improvements to the kernel kunit framework allow us to obtain a
> list of test cases provided by a kunit test module without actually
> running them.  Use that feature to get a list of expected test cases
> before we enter a loop around igt_dynamic().  Once done, enter the
----------------------------------------------^^
Two spaces, use one.

> igt_dynamic() section for each consecutive test case immediately, even
> before first line of a related KTAP report appears, then look for a result
> from that test case.  That should make our IGT results output still better
--------------------- ^^
Same here.

> synchronized with related kernel messages.
> 
> The list of test cases provided by a kunit test module can be obtained by
> loading the kunit base module with specific options, then loading the test
> module.  For that to be possible, take care of unloading the kunit base
--------------------- ^^
Same here.

> module before each kunit subtest (I was wrong when in one of my previous
> commit messages I suggested that on final unload of a kunit test module
> the kunit base module is unloaded automatically as its dependency,
> however, that didn't matter before, then no separate fix was required).
> Since that module can then be left loaded with non-default options if an
> error occurs, unload it explicitly before returning from igt_kunit().
> 
> There are two possible ways of getting a list of test cases: by loading
> the base kunit module with action=list module option, or by filtering
> out all test cases from being executed while asking for SKIP results from
> those filtered out.  Since the latter provides regular KTAP report that we
-------------------- ^^
Same here.

> can already parse perfectly, use it instead of trying to identify an
> unstructured list of test cases of unknown length submitted by the former.
> 
> If an IGT test that calls igt_kunit() provides a subtest name then use
> that name to filter out potential test cases that don't belong to the
> named test suite from the list.
> 
> To avoid loading any modules if no subtest is going to be executed (e.g.,
> if a nonexistent subtest has been requested), load the kunit modules in
> list mode from inside the igt_subtest_with_dynamic() section.  In order to
-------------------------------------------------------------- ^^
Same here.

> be free to skip the whole subtest on unmet requirements that need to be
> verified after that list has been already populated, clean it up from a
> follow up igt_fixture section.
> 
> Since we may now finish processing of all test cases / dynamic sub-
> subtests before KTAP parsing completes, don't fail if we exit the loop of
> dynamic sub-subtests with -EINPROGRESS error code returned by the parser.
> 
> v2: Split out changes in handling of modprobe errors and kernel taints to
>     separate patches (Kamil),
>   - fix some string duplicates referenced from filtered out test cases not
>     freed,
>   - don't check if next result is needed before fetching one, obviously
>     true in first dynamic sub-subtest, and we always free last result
>     before looping to next sub-subtest,
>   - still break the loop of test cases on unexpected return codes from
>     kunit_kmsg_get_result(),
>   - use kunit_results_free() helper,
>   - fix typos (Kamil),
>   - update commit description.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> ---
>  lib/igt_kmod.c | 169 ++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 132 insertions(+), 37 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index ed41aa1235..150fe49803 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -899,17 +899,15 @@ static void kunit_results_free(struct igt_list_head *results,
>  	free(*suite_name);
>  }
>  
> -static void
> -__igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
> +static void kunit_get_tests(struct igt_list_head *tests,
> +			    struct igt_ktest *tst,
> +			    const char *filter,
> +			    const char *opts)
>  {
> -	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
>  	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
>  	struct igt_ktap_results *ktap;
> -	struct igt_ktap_result *r;
> -	pthread_mutexattr_t attr;
> -	IGT_LIST_HEAD(results);
> -	unsigned long taints;
> -	int flags, ret;
> +	int flags, err;
>  
>  	igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n");
>  
> @@ -919,6 +917,70 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> +	/*
> +	 * To get a list of test cases provided by a kunit test module, ask the
> +	 * generic kunit module to respond with SKIP result for each test found.
> +	 * We could also use action=list kunit parameter to get the listing,
> +	 * however, parsing a KTAP report -- something that we already can do
> +	 * perfectly -- seems to be more safe than extracting a test case list
> +	 * of unknown length from /dev/kmsg.
> +	 */
> +	igt_skip_on(igt_kmod_load("kunit", "filter=module=none filter_action=skip"));
> +	igt_skip_on(modprobe(tst->kmod, opts));
> +
> +	ktap = igt_ktap_alloc(tests);
> +	igt_require(ktap);
> +
> +	do
> +		err = kunit_kmsg_result_get(tests, NULL, tst->kmsg, ktap);
> +	while (err == -EINPROGRESS);
> +
> +	igt_ktap_free(ktap);
> +
> +	if (!err)
> +		igt_list_for_each_entry_safe(r, rn, tests, link) {
> +			if (igt_debug_on(r->code != IGT_EXIT_SKIP)) {
> +				err = r->code ?: -EPROTO;
> +				break;
> +			}
> +
> +			if (!filter)
> +				continue;
> +
> +			if (strcmp(r->suite_name, filter))
> +				kunit_result_free(&r, &case_name, &suite_name);
> +		}
> +
> +	if (err) {
> +		kunit_results_free(tests, &case_name, &suite_name);
> +	} else {
> +		free(suite_name);
> +		free(case_name);
> +	}
> +
> +	igt_skip_on(kmod_module_remove_module(tst->kmod, KMOD_REMOVE_FORCE));
> +	igt_skip_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
> +
> +	igt_skip_on_f(err,
> +		      "KTAP parser failed while getting a list of test cases\n");
> +}
> +
> +static void __igt_kunit(struct igt_ktest *tst,
> +			const char *name,
> +			const char *opts,
> +			struct igt_list_head *tests)
> +{
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *t, *r = NULL;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
> +	int ret = -EINPROGRESS;
> +	unsigned long taints;
> +
> +	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> +
>  	igt_skip_on(pthread_mutexattr_init(&attr));
>  	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
>  	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> @@ -932,37 +994,47 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	do {
> -		ret = kunit_kmsg_result_get(&results, &modprobe,
> -					    tst->kmsg, ktap);
> -		if (igt_debug_on(ret && ret != -EINPROGRESS))
> -			break;
> -
> -		if (igt_debug_on(igt_list_empty(&results)))
> -			break;
> -
> -		r = igt_list_first_entry(&results, r, link);
> -
> +	igt_list_for_each_entry(t, tests, link) {
>  		igt_dynamic_f("%s%s%s",
> -			      strcmp(r->suite_name, name) ?  r->suite_name : "",
> -			      strcmp(r->suite_name, name) ? "-" : "",
> -			      r->case_name) {
> -
> -			if (r->code == IGT_EXIT_INVALID) {
> -				/* parametrized test case, get actual result */
> -				kunit_result_free(&r, &suite_name, &case_name);
> -
> -				igt_assert(igt_list_empty(&results));
> +			      strcmp(t->suite_name, name) ?  t->suite_name : "",
> +			      strcmp(t->suite_name, name) ? "-" : "",
> +			      t->case_name) {
>  
> +			if (igt_list_empty(&results)) {
> +				igt_assert_eq(ret, -EINPROGRESS);
>  				ret = kunit_kmsg_result_get(&results, &modprobe,
>  							    tst->kmsg, ktap);
> -				if (ret != -EINPROGRESS)
> -					igt_fail_on(ret);
> -
>  				igt_fail_on(igt_list_empty(&results));
> +			}
> +
> +			r = igt_list_first_entry(&results, r, link);
> +
> +			while (igt_debug_on_f(strcmp(r->suite_name, t->suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      t->suite_name, r->suite_name) ||
> +			       igt_debug_on_f(strcmp(r->case_name, t->case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      t->case_name, r->case_name) ||
> +			       r->code == IGT_EXIT_INVALID) {
> +
> +				int code = r->code;
> +
> +				kunit_result_free(&r, &suite_name, &case_name);
> +				if (igt_list_empty(&results)) {
> +					igt_assert_eq(ret, -EINPROGRESS);
> +					ret = kunit_kmsg_result_get(&results,
> +								    &modprobe,
> +								    tst->kmsg,
> +								    ktap);
> +					igt_fail_on(igt_list_empty(&results));
> +				}
>  
>  				r = igt_list_first_entry(&results, r, link);
>  
> +				if (code != IGT_EXIT_INVALID)
> +					continue;
> +
> +				/* result from parametrized test case */
>  				igt_fail_on_f(strcmp(r->suite_name, suite_name),
>  					      "suite_name expected: %s, got: %s\n",
>  					      suite_name, r->suite_name);
> @@ -1009,7 +1081,9 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  		kunit_result_free(&r, &suite_name, &case_name);
>  
> -	} while (ret == -EINPROGRESS);
> +		if (igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	}
>  
>  	kunit_results_free(&results, &suite_name, &case_name);
>  
> @@ -1035,7 +1109,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> -	igt_skip_on_f(ret, "KTAP parser failed\n");
> +	if (ret != -EINPROGRESS)
> +		igt_skip_on_f(ret, "KTAP parser failed\n");
>  }
>  
>  /**
> @@ -1049,7 +1124,8 @@ __igt_kunit(struct igt_ktest *tst, const char *name, const char *opts)
>  void igt_kunit(const char *module_name, const char *name, const char *opts)
>  {
>  	struct igt_ktest tst = { .kmsg = -1, };
> -
> +	const char *filter = name;
> +	IGT_LIST_HEAD(tests);
>  
>  	/*
>  	 * If the caller (an IGT test) provides no subtest name then we
> @@ -1074,6 +1150,15 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
>  
>  		igt_skip_on(igt_ktest_init(&tst, module_name));
>  		igt_skip_on(igt_ktest_begin(&tst));
> +
> +		/*
> +		 * Since we need to load kunit base module with specific
> +		 * options in order to get a list of test cases, make
> +		 * sure that the module is not loaded.  However, since
--------------------------------------------- ^^
Same here.

Acked-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>

> +		 * unload may fail if kunit base module is not loaded,
> +		 * ignore any failures, we'll fail later if still loaded.
> +		 */
> +		igt_ignore_warn(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
>  	}
>  
>  	/*
> @@ -1083,11 +1168,21 @@ void igt_kunit(const char *module_name, const char *name, const char *opts)
>  	 * proper namespace for dynamic subtests, with is required for CI
>  	 * and for documentation.
>  	 */
> -	igt_subtest_with_dynamic(name)
> -		__igt_kunit(&tst, name, opts);
> +	igt_subtest_with_dynamic(name) {
> +		kunit_get_tests(&tests, &tst, filter, opts);
> +		igt_skip_on(igt_list_empty(&tests));
> +
> +		__igt_kunit(&tst, name, opts, &tests);
> +	}
> +
> +	igt_fixture {
> +		char *suite_name = NULL, *case_name = NULL;
> +
> +		kunit_results_free(&tests, &suite_name, &case_name);
>  
> -	igt_fixture
>  		igt_ktest_end(&tst);
> +		igt_debug_on(igt_kmod_unload("kunit", KMOD_REMOVE_FORCE));
> +	}
>  
>  	igt_ktest_fini(&tst);
>  }
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
  2023-10-09 17:37     ` [Intel-xe] " Kamil Konieczny
  (?)
@ 2023-10-10 10:33       ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 10:33 UTC (permalink / raw)
  To: Kamil Konieczny, igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Kamil,

Thanks for review.

On Monday, 9 October 2023 19:37:31 CEST Kamil Konieczny wrote:
> Hi Janusz,
> On 2023-10-09 at 14:28:00 +0200, Janusz Krzysztofik wrote:
> > We are going to add support for reading a list of kunit test cases
> > provided by a kunit test module prior to executing those test cases.  That
> --------------------------------------------------------------------- ^^
> Two spaces, use only one.

I've discussed that point in details with Kamil and we've agreed that using 
two spaces for separating sentences can improve readability of a text usually 
printed with a fixed font, with no use of advance typesetting methods.

Thanks,
Janusz

> 
> > will be done by first loading kunit modules in list only mode, then
> > reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
> -------------------------------------------------------^^
> Same here.
> 
> > will be performed after the kunit test module is successfully loaded and
> > there will be no concurrently running modprobe thread, we need to make
> > synchronization of reads from /dev/kmsg with potential errors modprobe
> > thread optional.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> 
> Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
> 
> > ---
> >  lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
> >  1 file changed, 26 insertions(+), 24 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index fed0855c84..ed41aa1235 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct 
igt_list_head *results,
> >  		if (igt_debug_on(igt_kernel_tainted(&taints)))
> >  			return -ENOTRECOVERABLE;
> >  
> > -		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > -		if (err == -1)
> > -			return -errno;
> > -		else if (unlikely(err))
> > -			return err;
> > -
> > -		err = pthread_mutex_lock(&modprobe->lock);
> > -		switch (err) {
> > -		case EOWNERDEAD:
> > -			/* leave the mutex unrecoverable */
> > -			igt_debug_on(pthread_mutex_unlock(&modprobe-
>lock));
> > -			__attribute__ ((fallthrough));
> > -		case ENOTRECOVERABLE:
> > -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > -			if (igt_debug_on(modprobe->err))
> > -				return modprobe->err;
> > -			break;
> > -		case 0:
> > -			break;
> > -		default:
> > -			igt_debug("pthread_mutex_lock() error: %d\n", 
err);
> > -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > -			return -err;
> > +		if (modprobe) {
> > +			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, 
saved));
> > +			if (err == -1)
> > +				return -errno;
> > +			else if (unlikely(err))
> > +				return err;
> > +
> > +			err = pthread_mutex_lock(&modprobe->lock);
> > +			switch (err) {
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				
igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> > +				igt_debug_on(sigaction(SIGCHLD, saved, 
NULL));
> > +				if (igt_debug_on(modprobe->err))
> > +					return modprobe->err;
> > +				break;
> > +			case 0:
> > +				break;
> > +			default:
> > +				igt_debug("pthread_mutex_lock() error: 
%d\n", err);
> > +				igt_debug_on(sigaction(SIGCHLD, saved, 
NULL));
> > +				return -err;
> > +			}
> >  		}
> >  
> >  		ret = read(fd, record, BUF_LEN);
> >  
> > -		if (!err) {
> > +		if (modprobe && !err) {
> >  			igt_debug_on(pthread_mutex_unlock(&modprobe-
>lock));
> >  			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> >  		}
> 





^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
@ 2023-10-10 10:33       ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 10:33 UTC (permalink / raw)
  To: Kamil Konieczny, igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Kamil,

Thanks for review.

On Monday, 9 October 2023 19:37:31 CEST Kamil Konieczny wrote:
> Hi Janusz,
> On 2023-10-09 at 14:28:00 +0200, Janusz Krzysztofik wrote:
> > We are going to add support for reading a list of kunit test cases
> > provided by a kunit test module prior to executing those test cases.  That
> --------------------------------------------------------------------- ^^
> Two spaces, use only one.

I've discussed that point in details with Kamil and we've agreed that using 
two spaces for separating sentences can improve readability of a text usually 
printed with a fixed font, with no use of advance typesetting methods.

Thanks,
Janusz

> 
> > will be done by first loading kunit modules in list only mode, then
> > reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
> -------------------------------------------------------^^
> Same here.
> 
> > will be performed after the kunit test module is successfully loaded and
> > there will be no concurrently running modprobe thread, we need to make
> > synchronization of reads from /dev/kmsg with potential errors modprobe
> > thread optional.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> 
> Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
> 
> > ---
> >  lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
> >  1 file changed, 26 insertions(+), 24 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index fed0855c84..ed41aa1235 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct 
igt_list_head *results,
> >  		if (igt_debug_on(igt_kernel_tainted(&taints)))
> >  			return -ENOTRECOVERABLE;
> >  
> > -		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > -		if (err == -1)
> > -			return -errno;
> > -		else if (unlikely(err))
> > -			return err;
> > -
> > -		err = pthread_mutex_lock(&modprobe->lock);
> > -		switch (err) {
> > -		case EOWNERDEAD:
> > -			/* leave the mutex unrecoverable */
> > -			igt_debug_on(pthread_mutex_unlock(&modprobe-
>lock));
> > -			__attribute__ ((fallthrough));
> > -		case ENOTRECOVERABLE:
> > -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > -			if (igt_debug_on(modprobe->err))
> > -				return modprobe->err;
> > -			break;
> > -		case 0:
> > -			break;
> > -		default:
> > -			igt_debug("pthread_mutex_lock() error: %d\n", 
err);
> > -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > -			return -err;
> > +		if (modprobe) {
> > +			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, 
saved));
> > +			if (err == -1)
> > +				return -errno;
> > +			else if (unlikely(err))
> > +				return err;
> > +
> > +			err = pthread_mutex_lock(&modprobe->lock);
> > +			switch (err) {
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				
igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> > +				igt_debug_on(sigaction(SIGCHLD, saved, 
NULL));
> > +				if (igt_debug_on(modprobe->err))
> > +					return modprobe->err;
> > +				break;
> > +			case 0:
> > +				break;
> > +			default:
> > +				igt_debug("pthread_mutex_lock() error: 
%d\n", err);
> > +				igt_debug_on(sigaction(SIGCHLD, saved, 
NULL));
> > +				return -err;
> > +			}
> >  		}
> >  
> >  		ret = read(fd, record, BUF_LEN);
> >  
> > -		if (!err) {
> > +		if (modprobe && !err) {
> >  			igt_debug_on(pthread_mutex_unlock(&modprobe-
>lock));
> >  			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> >  		}
> 





^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed
@ 2023-10-10 10:33       ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 10:33 UTC (permalink / raw)
  To: Kamil Konieczny, igt-dev; +Cc: intel-gfx, intel-xe

Hi Kamil,

Thanks for review.

On Monday, 9 October 2023 19:37:31 CEST Kamil Konieczny wrote:
> Hi Janusz,
> On 2023-10-09 at 14:28:00 +0200, Janusz Krzysztofik wrote:
> > We are going to add support for reading a list of kunit test cases
> > provided by a kunit test module prior to executing those test cases.  That
> --------------------------------------------------------------------- ^^
> Two spaces, use only one.

I've discussed that point in details with Kamil and we've agreed that using 
two spaces for separating sentences can improve readability of a text usually 
printed with a fixed font, with no use of advance typesetting methods.

Thanks,
Janusz

> 
> > will be done by first loading kunit modules in list only mode, then
> > reading the list from /dev/kmsg with our KTAP parser.  Since that parsing
> -------------------------------------------------------^^
> Same here.
> 
> > will be performed after the kunit test module is successfully loaded and
> > there will be no concurrently running modprobe thread, we need to make
> > synchronization of reads from /dev/kmsg with potential errors modprobe
> > thread optional.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> 
> Reviewed-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
> 
> > ---
> >  lib/igt_kmod.c | 50 ++++++++++++++++++++++++++------------------------
> >  1 file changed, 26 insertions(+), 24 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index fed0855c84..ed41aa1235 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -802,34 +802,36 @@ static int kunit_kmsg_result_get(struct 
igt_list_head *results,
> >  		if (igt_debug_on(igt_kernel_tainted(&taints)))
> >  			return -ENOTRECOVERABLE;
> >  
> > -		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > -		if (err == -1)
> > -			return -errno;
> > -		else if (unlikely(err))
> > -			return err;
> > -
> > -		err = pthread_mutex_lock(&modprobe->lock);
> > -		switch (err) {
> > -		case EOWNERDEAD:
> > -			/* leave the mutex unrecoverable */
> > -			igt_debug_on(pthread_mutex_unlock(&modprobe-
>lock));
> > -			__attribute__ ((fallthrough));
> > -		case ENOTRECOVERABLE:
> > -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > -			if (igt_debug_on(modprobe->err))
> > -				return modprobe->err;
> > -			break;
> > -		case 0:
> > -			break;
> > -		default:
> > -			igt_debug("pthread_mutex_lock() error: %d\n", 
err);
> > -			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > -			return -err;
> > +		if (modprobe) {
> > +			err = igt_debug_on(sigaction(SIGCHLD, &sigchld, 
saved));
> > +			if (err == -1)
> > +				return -errno;
> > +			else if (unlikely(err))
> > +				return err;
> > +
> > +			err = pthread_mutex_lock(&modprobe->lock);
> > +			switch (err) {
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				
igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> > +				igt_debug_on(sigaction(SIGCHLD, saved, 
NULL));
> > +				if (igt_debug_on(modprobe->err))
> > +					return modprobe->err;
> > +				break;
> > +			case 0:
> > +				break;
> > +			default:
> > +				igt_debug("pthread_mutex_lock() error: 
%d\n", err);
> > +				igt_debug_on(sigaction(SIGCHLD, saved, 
NULL));
> > +				return -err;
> > +			}
> >  		}
> >  
> >  		ret = read(fd, record, BUF_LEN);
> >  
> > -		if (!err) {
> > +		if (modprobe && !err) {
> >  			igt_debug_on(pthread_mutex_unlock(&modprobe-
>lock));
> >  			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> >  		}
> 




^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✓ Fi.CI.BAT: success for Kunit fixes and improvements (rev2)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (15 preceding siblings ...)
  (?)
@ 2023-10-10 11:15 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-10 11:15 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 6993 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev2)
URL   : https://patchwork.freedesktop.org/series/124554/
State : success

== Summary ==

CI Bug Log - changes from IGT_7522 -> IGTPW_9951
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html

Participating hosts (37 -> 35)
------------------------------

  Additional (1): bat-dg2-9 
  Missing    (3): bat-dg2-8 fi-snb-2520m fi-hsw-4770 

Known issues
------------

  Here are the changes found in IGTPW_9951 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_mmap@basic:
    - bat-dg2-9:          NOTRUN -> [SKIP][1] ([i915#4083])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@gem_mmap@basic.html

  * igt@gem_mmap_gtt@basic:
    - bat-dg2-9:          NOTRUN -> [SKIP][2] ([i915#4077]) +2 other tests skip
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@gem_mmap_gtt@basic.html

  * igt@gem_render_tiled_blits@basic:
    - bat-dg2-9:          NOTRUN -> [SKIP][3] ([i915#4079]) +1 other test skip
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@gem_render_tiled_blits@basic.html

  * igt@i915_module_load@load:
    - bat-adlp-6:         [PASS][4] -> [INCOMPLETE][5] ([i915#8449])
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/bat-adlp-6/igt@i915_module_load@load.html
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-adlp-6/igt@i915_module_load@load.html

  * igt@i915_pm_rps@basic-api:
    - bat-dg2-9:          NOTRUN -> [SKIP][6] ([i915#6621])
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@i915_pm_rps@basic-api.html

  * igt@kms_addfb_basic@addfb25-y-tiled-small-legacy:
    - bat-dg2-9:          NOTRUN -> [SKIP][7] ([i915#5190])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@addfb25-y-tiled-small-legacy.html

  * igt@kms_addfb_basic@basic-y-tiled-legacy:
    - bat-dg2-9:          NOTRUN -> [SKIP][8] ([i915#4215] / [i915#5190])
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@basic-y-tiled-legacy.html

  * igt@kms_addfb_basic@framebuffer-vs-set-tiling:
    - bat-dg2-9:          NOTRUN -> [SKIP][9] ([i915#4212]) +6 other tests skip
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@framebuffer-vs-set-tiling.html

  * igt@kms_addfb_basic@tile-pitch-mismatch:
    - bat-dg2-9:          NOTRUN -> [SKIP][10] ([i915#4212] / [i915#5608])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_addfb_basic@tile-pitch-mismatch.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-legacy:
    - bat-dg2-9:          NOTRUN -> [SKIP][11] ([i915#4103] / [i915#4213] / [i915#5608]) +1 other test skip
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-legacy.html

  * igt@kms_force_connector_basic@force-load-detect:
    - bat-dg2-9:          NOTRUN -> [SKIP][12] ([fdo#109285])
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_force_connector_basic@force-load-detect.html

  * igt@kms_force_connector_basic@prune-stale-modes:
    - bat-dg2-9:          NOTRUN -> [SKIP][13] ([i915#5274])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_force_connector_basic@prune-stale-modes.html

  * igt@kms_psr@sprite_plane_onoff:
    - bat-dg2-9:          NOTRUN -> [SKIP][14] ([i915#1072]) +3 other tests skip
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_psr@sprite_plane_onoff.html

  * igt@kms_setmode@basic-clone-single-crtc:
    - bat-dg2-9:          NOTRUN -> [SKIP][15] ([i915#3555] / [i915#4098])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@kms_setmode@basic-clone-single-crtc.html

  * igt@prime_vgem@basic-fence-flip:
    - bat-dg2-9:          NOTRUN -> [SKIP][16] ([i915#3708])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@prime_vgem@basic-fence-flip.html

  * igt@prime_vgem@basic-fence-mmap:
    - bat-dg2-9:          NOTRUN -> [SKIP][17] ([i915#3708] / [i915#4077]) +1 other test skip
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@prime_vgem@basic-fence-mmap.html

  * igt@prime_vgem@basic-write:
    - bat-dg2-9:          NOTRUN -> [SKIP][18] ([i915#3291] / [i915#3708]) +2 other tests skip
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/bat-dg2-9/igt@prime_vgem@basic-write.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
  [i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
  [i915#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077
  [i915#4079]: https://gitlab.freedesktop.org/drm/intel/issues/4079
  [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083
  [i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4212]: https://gitlab.freedesktop.org/drm/intel/issues/4212
  [i915#4213]: https://gitlab.freedesktop.org/drm/intel/issues/4213
  [i915#4215]: https://gitlab.freedesktop.org/drm/intel/issues/4215
  [i915#5190]: https://gitlab.freedesktop.org/drm/intel/issues/5190
  [i915#5274]: https://gitlab.freedesktop.org/drm/intel/issues/5274
  [i915#5354]: https://gitlab.freedesktop.org/drm/intel/issues/5354
  [i915#5608]: https://gitlab.freedesktop.org/drm/intel/issues/5608
  [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621
  [i915#8449]: https://gitlab.freedesktop.org/drm/intel/issues/8449


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7522 -> IGTPW_9951

  CI-20190529: 20190529
  CI_DRM_13729: fe7c35e5be39964f7c0b83899b9d5d9355848544 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_9951: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html
  IGT_7522: 334a1178a36a1327dc7fbba43ab16d1f3a7d887b @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git


Testlist changes
----------------

+igt@kms_selftest@drm_cmdline_parser
+igt@kms_selftest@drm_damage_helper
+igt@kms_selftest@drm_dp_mst_helper
+igt@kms_selftest@drm_framebuffer
+igt@kms_selftest@drm_plane_helper
-igt@kms_selftest@drm_cmdline
-igt@kms_selftest@drm_damage
-igt@kms_selftest@drm_dp_mst
-igt@kms_selftest@drm_plane
-igt@kms_selftest@framebuffer

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html

[-- Attachment #2: Type: text/html, Size: 8402 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
  2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-10 13:33     ` Mauro Carvalho Chehab
  -1 siblings, 0 replies; 77+ messages in thread
From: Mauro Carvalho Chehab @ 2023-10-10 13:33 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev, intel-gfx, Mauro Carvalho Chehab, intel-xe

On Mon,  9 Oct 2023 14:27:55 +0200
Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com> wrote:

> There was an attempt to parse KTAP reports in the background while a kunit
> test module is loading.  However, since dynamic sub-subtests can be
> executed only from the main thread, that attempt was not quite successful,
> as IGT results from all executed kunit test cases were generated only
> after loading of kunit test module completed.
> 
> Now that the parser maintains its state and we can call it separately for
> each input line of a KTAP report, it is perfectly possible to call the
> parser from the main thread while the module is loading in the background,
> and convert results from kunit test cases immediately to results of IGT
> dynamic sub-subtests by running an igt_dynamic() section for each result
> as soon as returned by the parser.
> 
> Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> result obtained from igt_ktap_parse() called from the main thread.
> 
> Also, drop no longer needed functions from igt_ktap soruces.
> 
> v3: Fix ktap structure not freed on lseek error,
>   - fix initial SIGCHLD handler not restored,
>   - fix missing handling of potential errors returned by sigaction,
>   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
>     synchronization with modprobe thread,
>   - fix potentially illegal use of igt_assert() called outside of
>     dynamic sub-subtest section,
>   - fix unsupported exit code potentially passed to igt_fail(),
>   - no need to fail a dynamic sub-subtest on potential KTAP parser error
>     after a valid result from the parser has been processed,
>   - fix trailing newlines missing from error messages,
>   - add more debug statements,
>   - integrate common code around kunit_result_free() into it.
> v2: Interrupt blocking read() on modprobe failure.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> ---
>  lib/igt_kmod.c | 261 +++++++++++++++++++----
>  lib/igt_ktap.c | 568 -------------------------------------------------
>  lib/igt_ktap.h |  22 --
>  3 files changed, 222 insertions(+), 629 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 426ae5b26f..7bca4cdaab 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright © 2016 Intel Corporation
> + * Copyright © 2016-2023 Intel Corporation
>   *
>   * Permission is hereby granted, free of charge, to any person obtaining a
>   * copy of this software and associated documentation files (the "Software"),
> @@ -26,7 +26,12 @@
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <pthread.h>
> +#include <stdlib.h>
> +#include <string.h>
>  #include <sys/utsname.h>
> +#include <unistd.h>
> +
> +#include "assembler/brw_compat.h"	/* [un]likely() */
>  
>  #include "igt_aux.h"
>  #include "igt_core.h"
> @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
>  }
>  
>  struct modprobe_data {
> +	pthread_t parent;
>  	struct kmod_module *kmod;
>  	const char *opts;
>  	int err;
> +	pthread_mutex_t lock;
> +	pthread_t thread;
>  };
>  
>  static void *modprobe_task(void *arg)
> @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
>  
>  	data->err = modprobe(data->kmod, data->opts);
>  
> +	if (igt_debug_on(data->err)) {
> +		int err;
> +
> +		while (err = pthread_mutex_trylock(&data->lock),
> +		       err && !igt_debug_on(err != EBUSY))
> +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));

I guess you need here an "igt_debug_on_once", as it doesn't make
sense to have a (potentially) endless loop here printing error
messages.

the other changes LGTM.

> +	} else {
> +		/* let main thread use mutex to detect modprobe completion */
> +		igt_debug_on(pthread_mutex_lock(&data->lock));
> +	}
> +
>  	return NULL;
>  }
>  
> +static void kunit_sigchld_handler(int signal)
> +{
> +	return;
> +}
> +
> +static int kunit_kmsg_result_get(struct igt_list_head *results,
> +				 struct modprobe_data *modprobe,
> +				 int fd, struct igt_ktap_results *ktap)
> +{
> +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> +			 *saved;
> +	char record[BUF_LEN + 1], *buf;
> +	unsigned long taints;
> +	int ret;
> +
> +	do {
> +		int err;
> +
> +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> +			return -ENOTRECOVERABLE;
> +
> +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +		if (err == -1)
> +			return -errno;
> +		else if (unlikely(err))
> +			return err;
> +
> +		err = pthread_mutex_lock(&modprobe->lock);
> +		switch (err) {
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			__attribute__ ((fallthrough));
> +		case ENOTRECOVERABLE:
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			if (igt_debug_on(modprobe->err))
> +				return modprobe->err;
> +			break;
> +		case 0:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			return -err;
> +		}
> +
> +		ret = read(fd, record, BUF_LEN);
> +
> +		if (!err) {
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +		}
> +
> +		if (igt_debug_on(!ret))
> +			return -ENODATA;
> +		if (igt_debug_on(ret == -1))
> +			return -errno;
> +		if (unlikely(igt_debug_on(ret < 0)))
> +			break;
> +
> +		/* skip kmsg continuation lines */
> +		if (igt_debug_on(*record == ' '))
> +			continue;
> +
> +		/* NULL-terminate the record */
> +		record[ret] = '\0';
> +
> +		/* detect start of log message, continue if not found */
> +		buf = strchrnul(record, ';');
> +		if (igt_debug_on(*buf == '\0'))
> +			continue;
> +		buf++;
> +
> +		ret = igt_ktap_parse(buf, ktap);
> +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	} while (igt_list_empty(results));
> +
> +	return ret;
> +}
> +
> +static void kunit_result_free(struct igt_ktap_result **r,
> +			      char **suite_name, char **case_name)
> +{
> +	if (!*r)
> +		return;
> +
> +	igt_list_del(&(*r)->link);
> +
> +	if ((*r)->suite_name != *suite_name) {
> +		free(*suite_name);
> +		*suite_name = (*r)->suite_name;
> +	}
> +
> +	if ((*r)->case_name != *case_name) {
> +		free(*case_name);
> +		*case_name = (*r)->case_name;
> +	}
> +
> +	free((*r)->msg);
> +	free(*r);
> +	*r = NULL;
> +}
> +
>  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  {
> -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> -	struct kmod_module *kunit_kmod;
> -	bool is_builtin;
> -	struct ktap_test_results *results;
> -	pthread_t modprobe_thread;
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
>  	unsigned long taints;
>  	int flags, ret;
>  
> @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> -	kmod_module_unref(kunit_kmod);
> +	igt_skip_on(pthread_mutexattr_init(&attr));
> +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
>  
> -	results = ktap_parser_start(tst->kmsg, is_builtin);
> +	ktap = igt_ktap_alloc(&results);
> +	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
>  					modprobe_task, &modprobe))) {
> -		ktap_parser_cancel();
> -		igt_ignore_warn(ktap_parser_stop());
> +		igt_ktap_free(ktap);
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> -	{
> -		struct ktap_test_results_element *result;
> -
> -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> -			ktap_parser_cancel();
> +	do {
> +		ret = kunit_kmsg_result_get(&results, &modprobe,
> +					    tst->kmsg, ktap);
> +		if (igt_debug_on(ret && ret != -EINPROGRESS))
>  			break;
> -		}
>  
> -		if (igt_kernel_tainted(&taints)) {
> -			ktap_parser_cancel();
> -			pthread_cancel(modprobe_thread);
> +		if (igt_debug_on(igt_list_empty(&results)))
>  			break;
> -		}
>  
> -		pthread_mutex_lock(&results->mutex);
> -		if (igt_list_empty(&results->list)) {
> -			pthread_mutex_unlock(&results->mutex);
> -			continue;
> -		}
> +		r = igt_list_first_entry(&results, r, link);
>  
> -		result = igt_list_first_entry(&results->list, result, link);
> +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> +			if (r->code == IGT_EXIT_INVALID) {
> +				/* parametrized test case, get actual result */
> +				kunit_result_free(&r, &suite_name, &case_name);
>  
> -		igt_list_del(&result->link);
> -		pthread_mutex_unlock(&results->mutex);
> +				igt_assert(igt_list_empty(&results));
>  
> -		igt_dynamic(result->test_name) {
> -			igt_assert(READ_ONCE(result->passed));
> +				ret = kunit_kmsg_result_get(&results, &modprobe,
> +							    tst->kmsg, ktap);
> +				if (ret != -EINPROGRESS)
> +					igt_fail_on(ret);
> +
> +				igt_fail_on(igt_list_empty(&results));
> +
> +				r = igt_list_first_entry(&results, r, link);
> +
> +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      suite_name, r->suite_name);
> +				igt_fail_on_f(strcmp(r->case_name, case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      case_name, r->case_name);
> +			}
>  
> -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> +
> +			if (r->msg && *r->msg) {
> +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> +					      "%s\n", r->msg);
> +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> +					      "%s\n", r->msg);
> +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> +					      "%s\n", r->msg);
> +			} else {
> +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> +				if (r->code == IGT_EXIT_ABORT)
> +					igt_fail(r->code);
> +			}
> +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> +
> +			switch (pthread_mutex_lock(&modprobe.lock)) {
> +			case 0:
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				break;
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
>  				igt_assert_eq(modprobe.err, 0);
> +				break;
> +			default:
> +				igt_debug("pthread_mutex_lock() failed\n");
> +				break;
> +			}
>  
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		free(result);
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	} while (ret == -EINPROGRESS);
> +
> +	igt_list_for_each_entry_safe(r, rn, &results, link)
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	free(case_name);
> +	free(suite_name);
> +
> +	switch (pthread_mutex_lock(&modprobe.lock)) {
> +	case 0:
> +		igt_debug_on(pthread_cancel(modprobe.thread));
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
> +	case EOWNERDEAD:
> +		/* leave the mutex unrecoverable */
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		break;
> +	case ENOTRECOVERABLE:
> +		break;
> +	default:
> +		igt_debug("pthread_mutex_lock() failed\n");
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
>  	}
>  
> -	pthread_join(modprobe_thread, NULL);
> -
> -	ret = ktap_parser_stop();
> +	igt_ktap_free(ktap);
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 5eac102417..53a6c63288 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -4,17 +4,11 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> -#include <ctype.h>
> -#include <limits.h>
> -#include <libkmod.h>
> -#include <pthread.h>
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> -#include <unistd.h>
>  
> -#include "igt_aux.h"
>  #include "igt_core.h"
>  #include "igt_ktap.h"
>  #include "igt_list.h"
> @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
>  {
>  	free(ktap);
>  }
> -
> -#define DELIMITER "-"
> -
> -struct ktap_parser_args {
> -	int fd;
> -	bool is_builtin;
> -	int ret;
> -} ktap_args;
> -
> -static struct ktap_test_results results;
> -
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> -
> -/**
> - * log_to_end:
> - * @level: #igt_log_level
> - * @record: record to store the read data
> - * @format: format string
> - * @...: optional arguments used in the format string
> - *
> - * This is an altered version of the generic structured logging helper function
> - * igt_log capable of reading to the end of a given line.
> - *
> - * Returns: 0 for success, or -2 if there's an error reading from the file
> - */
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...)
> -{
> -	va_list args;
> -	const char *lend;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	char *cutoff = strchr(record, '\n');
> -	if (cutoff) {
> -		if (cutoff - record < BUF_LEN)
> -			cutoff[1] = '\0';
> -	}
> -
> -	va_start(args, format);
> -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> -	va_end(args);
> -
> -	lend = strchrnul(record, '\n');
> -	while (*lend == '\0') {
> -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -
> -		lend = strchrnul(record, '\n');
> -	}
> -	return 0;
> -}
> -
> -/**
> - * lookup_value:
> - * @haystack: the string to search in
> - * @needle: the string to search for
> - *
> - * Returns: the value of the needle in the haystack, or -1 if not found.
> - */
> -static long lookup_value(const char *haystack, const char *needle)
> -{
> -	const char *needle_rptr;
> -	char *needle_end;
> -	long num;
> -
> -	needle_rptr = strcasestr(haystack, needle);
> -
> -	if (needle_rptr == NULL)
> -		return -1;
> -
> -	/* Skip search string and whitespaces after it */
> -	needle_rptr += strlen(needle);
> -
> -	num = strtol(needle_rptr, &needle_end, 10);
> -
> -	if (needle_rptr == needle_end)
> -		return -1;
> -
> -	if (num == LONG_MIN || num == LONG_MAX)
> -		return 0;
> -
> -	return num > 0 ? num : 0;
> -}
> -
> -/**
> - * tap_version_present:
> - * @record: buffer with tap data
> - * @print_info: whether tap version should be printed or not
> - *
> - * Returns:
> - * 0 if not found
> - * 1 if found
> - */
> -static int tap_version_present(char* record, bool print_info)
> -{
> -	/*
> -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> -	 *
> -	 * but actually isn't, as it currently depends on the KUnit module
> -	 * being built-in, so we can't rely on it every time
> -	 */
> -	const char *version_rptr = strcasestr(record, "TAP version ");
> -	char *cutoff;
> -
> -	if (version_rptr == NULL)
> -		return 0;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	cutoff = strchr(version_rptr, '\n');
> -	if (cutoff)
> -		cutoff[0] = '\0';
> -
> -	if (print_info)
> -		igt_info("%s\n", version_rptr);
> -
> -	return 1;
> -}
> -
> -/**
> - * find_next_tap_subtest:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @is_builtin: whether KUnit is built-in or not
> - *
> - * Returns:
> - * 0 if there's missing information
> - * -1 if not found
> - * -2 if there are problems while reading the file.
> - * any other value corresponds to the amount of cases of the next (sub)test
> - */
> -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> -{
> -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> -	long test_count;
> -	char *cutoff;
> -
> -	test_name[0] = '\0';
> -	test_name[BUF_LEN] = '\0';
> -
> -	test_lookup_str = " subtest: ";
> -	subtest_lookup_str = " test: ";
> -
> -	if (!tap_version_present(record, true))
> -		return -1;
> -
> -	if (is_builtin) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -	}
> -
> -	name_rptr = strcasestr(record, test_lookup_str);
> -	if (name_rptr != NULL) {
> -		name_rptr += strlen(test_lookup_str);
> -	} else {
> -		name_rptr = strcasestr(record, subtest_lookup_str);
> -		if (name_rptr != NULL)
> -			name_rptr += strlen(subtest_lookup_str);
> -	}
> -
> -	if (name_rptr == NULL) {
> -		if (!is_builtin)
> -			/* We've probably found nothing */
> -			return -1;
> -		igt_info("Missing test name\n");
> -	} else {
> -		strncpy(test_name, name_rptr, BUF_LEN);
> -		/* Cutoff after newline character, in order to not display garbage */
> -		cutoff = strchr(test_name, '\n');
> -		if (cutoff)
> -			cutoff[0] = '\0';
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("unknown error reading kmsg (%m)\n");
> -
> -			return -2;
> -		}
> -
> -		/* Now we can be sure we found tests */
> -		if (!is_builtin)
> -			igt_info("KUnit is not built-in, skipping version check...\n");
> -	}
> -
> -	/*
> -	 * Total test count will almost always appear as 0..N at the beginning
> -	 * of a run, so we use it to reliably identify a new run
> -	 */
> -	test_count = lookup_value(record, "..");
> -
> -	if (test_count <= 0) {
> -		igt_info("Missing test count\n");
> -		if (test_name[0] == '\0')
> -			return 0;
> -		if (log_to_end(IGT_LOG_INFO, fd, record,
> -				"Running some tests in: %s\n",
> -				test_name) < 0)
> -			return -2;
> -		return 0;
> -	} else if (test_name[0] == '\0') {
> -		igt_info("Running %ld tests...\n", test_count);
> -		return 0;
> -	}
> -
> -	if (log_to_end(IGT_LOG_INFO, fd, record,
> -			"Executing %ld tests in: %s\n",
> -			test_count, test_name) < 0)
> -		return -2;
> -
> -	return test_count;
> -}
> -
> -/**
> - * parse_kmsg_for_tap:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @test_name: buffer to store the test name
> - *
> - * Returns:
> - * 1 if no results were found
> - * 0 if a test succeded
> - * -1 if a test failed
> - * -2 if there are problems reading the file
> - */
> -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> -{
> -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> -	char *test_name_end;
> -
> -	ok_lookup_str = "ok ";
> -	nok_lookup_str = "not ok ";
> -
> -	lstart = strchrnul(record, ';');
> -
> -	if (*lstart == '\0') {
> -		igt_warn("kmsg truncated: output malformed (%m)\n");
> -		return -2;
> -	}
> -
> -	lstart++;
> -	while (isspace(*lstart))
> -		lstart++;
> -
> -	nok_rptr = strstr(lstart, nok_lookup_str);
> -	if (nok_rptr != NULL) {
> -		nok_rptr += strlen(nok_lookup_str);
> -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> -			nok_rptr++;
> -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		if (log_to_end(IGT_LOG_WARN, fd, record,
> -			       "%s", lstart) < 0)
> -			return -2;
> -		return -1;
> -	}
> -
> -	comment_start = strchrnul(lstart, '#');
> -
> -	/* Check if we're still in a subtest */
> -	if (*comment_start != '\0') {
> -		comment_start++;
> -		value_parse_start = comment_start;
> -
> -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> -			if (log_to_end(IGT_LOG_WARN, fd, record,
> -				       "%s", lstart) < 0)
> -				return -2;
> -			return -1;
> -		}
> -	}
> -
> -	ok_rptr = strstr(lstart, ok_lookup_str);
> -	if (ok_rptr != NULL) {
> -		ok_rptr += strlen(ok_lookup_str);
> -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> -			ok_rptr++;
> -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		return 0;
> -	}
> -
> -	return 1;
> -}
> -
> -/**
> - * parse_tap_level:
> - * @fd: file descriptor
> - * @base_test_name: test_name from upper recursion level
> - * @test_count: test_count of this level
> - * @failed_tests: top level failed_tests pointer
> - * @found_tests: top level found_tests pointer
> - * @is_builtin: whether the KUnit module is built-in or not
> - *
> - * Returns:
> - * 0 if succeded
> - * -1 if error occurred
> - */
> -__maybe_unused
> -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> -			   bool *found_tests, bool is_builtin)
> -{
> -	char record[BUF_LEN + 1];
> -	struct ktap_test_results_element *r;
> -	int internal_test_count;
> -	char test_name[BUF_LEN + 1];
> -	char base_test_name_for_next_level[BUF_LEN + 1];
> -
> -	for (int i = 0; i < test_count; i++) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("error reading kmsg (%m)\n");
> -
> -			return -1;
> -		}
> -
> -		/* Sublevel found */
> -		if (tap_version_present(record, false))
> -		{
> -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> -								    is_builtin);
> -			switch (internal_test_count) {
> -			case -2:
> -				/* No more data to read */
> -				return -1;
> -			case -1:
> -				/* No test found */
> -				return -1;
> -			case 0:
> -				/* Tests found, but they're missing info */
> -				*found_tests = true;
> -				return -1;
> -			default:
> -				*found_tests = true;
> -
> -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> -				    base_test_name_for_next_level[0])
> -					strncat(base_test_name_for_next_level, DELIMITER,
> -						BUF_LEN - strlen(base_test_name_for_next_level));
> -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> -
> -				if (parse_tap_level(fd, base_test_name_for_next_level,
> -						    internal_test_count, failed_tests, found_tests,
> -						    is_builtin) == -1)
> -					return -1;
> -				break;
> -			}
> -		}
> -
> -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> -		case -2:
> -			return -1;
> -		case -1:
> -			*failed_tests = true;
> -
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = false;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		case 0:
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = true;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		default:
> -			break;
> -		}
> -	}
> -	return 0;
> -}
> -
> -/**
> - * igt_ktap_parser:
> - *
> - * This function parses the output of a ktap script and passes it to main thread.
> - */
> -void *igt_ktap_parser(void *unused)
> -{
> -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_results *ktap = NULL;
> -	int fd = ktap_args.fd;
> -	IGT_LIST_HEAD(list);
> -	int err;
> -
> -	ktap = igt_ktap_alloc(&list);
> -	if (igt_debug_on(!ktap))
> -		goto igt_ktap_parser_end;
> -
> -	while (err = read(fd, record, BUF_LEN), err > 0) {
> -		struct igt_ktap_result *r, *rn;
> -
> -		/* skip kmsg continuation lines */
> -		if (igt_debug_on(*record == ' '))
> -			continue;
> -
> -		/* NULL-terminate the record */
> -		record[err] = '\0';
> -
> -		/* detect start of log message, continue if not found */
> -		buf = strchrnul(record, ';');
> -		if (igt_debug_on(*buf == '\0'))
> -			continue;
> -		buf++;
> -
> -		err = igt_ktap_parse(buf, ktap);
> -
> -		/* parsing error */
> -		if (err && err != -EINPROGRESS)
> -			goto igt_ktap_parser_end;
> -
> -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> -			struct ktap_test_results_element *result = NULL;
> -			int code = r->code;
> -
> -			if (code != IGT_EXIT_INVALID)
> -				result = calloc(1, sizeof(*result));
> -
> -			if (result) {
> -				snprintf(result->test_name, sizeof(result->test_name),
> -					 "%s-%s", r->suite_name, r->case_name);
> -
> -				if (code == IGT_EXIT_SUCCESS)
> -					result->passed = true;
> -			}
> -
> -			igt_list_del(&r->link);
> -			if (r->suite_name != suite_name) {
> -				free(suite_name);
> -				suite_name = r->suite_name;
> -			}
> -			if (r->case_name != case_name) {
> -				free(case_name);
> -				case_name = r->case_name;
> -			}
> -			free(r->msg);
> -			free(r);
> -
> -			/*
> -			 * no extra result record expected on start
> -			 * of parametrized test case -- skip it
> -			 */
> -			if (code == IGT_EXIT_INVALID)
> -				continue;
> -
> -			if (!result) {
> -				err = -ENOMEM;
> -				goto igt_ktap_parser_end;
> -			}
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&result->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -		}
> -
> -		/* end of KTAP report */
> -		if (!err)
> -			goto igt_ktap_parser_end;
> -	}
> -
> -	if (err < 0) {
> -		if (errno == EPIPE)
> -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -		else
> -			igt_warn("error reading kmsg (%m)\n");
> -	}
> -
> -igt_ktap_parser_end:
> -	free(suite_name);
> -	free(case_name);
> -
> -	if (!err)
> -		ktap_args.ret = IGT_EXIT_SUCCESS;
> -
> -	results.still_running = false;
> -
> -	if (ktap)
> -		igt_ktap_free(ktap);
> -
> -	return NULL;
> -}
> -
> -static pthread_t ktap_parser_thread;
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> -{
> -	IGT_INIT_LIST_HEAD(&results.list);
> -	pthread_mutex_init(&results.mutex, NULL);
> -	results.still_running = true;
> -
> -	ktap_args.fd = fd;
> -	ktap_args.is_builtin = is_builtin;
> -	ktap_args.ret = IGT_EXIT_FAILURE;
> -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> -
> -	return &results;
> -}
> -
> -void ktap_parser_cancel(void)
> -{
> -	pthread_cancel(ktap_parser_thread);
> -}
> -
> -int ktap_parser_stop(void)
> -{
> -	pthread_join(ktap_parser_thread, NULL);
> -	return ktap_args.ret;
> -}
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index 6f8da3eab6..c422636bfc 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -27,8 +27,6 @@
>  
>  #define BUF_LEN 4096
>  
> -#include <pthread.h>
> -
>  #include "igt_list.h"
>  
>  struct igt_ktap_result {
> @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
>  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
>  void igt_ktap_free(struct igt_ktap_results *ktap);
>  
> -void *igt_ktap_parser(void *unused);
> -
> -typedef struct ktap_test_results_element {
> -	char test_name[BUF_LEN + 1];
> -	bool passed;
> -	struct igt_list_head link;
> -} ktap_test_results_element;
> -
> -struct ktap_test_results {
> -	struct igt_list_head list;
> -	pthread_mutex_t mutex;
> -	bool still_running;
> -};
> -
> -
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> -void ktap_parser_cancel(void);
> -int ktap_parser_stop(void);
> -
>  #endif /* IGT_KTAP_H */

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-10 13:33     ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 77+ messages in thread
From: Mauro Carvalho Chehab @ 2023-10-10 13:33 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev, intel-gfx, intel-xe

On Mon,  9 Oct 2023 14:27:55 +0200
Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com> wrote:

> There was an attempt to parse KTAP reports in the background while a kunit
> test module is loading.  However, since dynamic sub-subtests can be
> executed only from the main thread, that attempt was not quite successful,
> as IGT results from all executed kunit test cases were generated only
> after loading of kunit test module completed.
> 
> Now that the parser maintains its state and we can call it separately for
> each input line of a KTAP report, it is perfectly possible to call the
> parser from the main thread while the module is loading in the background,
> and convert results from kunit test cases immediately to results of IGT
> dynamic sub-subtests by running an igt_dynamic() section for each result
> as soon as returned by the parser.
> 
> Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> result obtained from igt_ktap_parse() called from the main thread.
> 
> Also, drop no longer needed functions from igt_ktap soruces.
> 
> v3: Fix ktap structure not freed on lseek error,
>   - fix initial SIGCHLD handler not restored,
>   - fix missing handling of potential errors returned by sigaction,
>   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
>     synchronization with modprobe thread,
>   - fix potentially illegal use of igt_assert() called outside of
>     dynamic sub-subtest section,
>   - fix unsupported exit code potentially passed to igt_fail(),
>   - no need to fail a dynamic sub-subtest on potential KTAP parser error
>     after a valid result from the parser has been processed,
>   - fix trailing newlines missing from error messages,
>   - add more debug statements,
>   - integrate common code around kunit_result_free() into it.
> v2: Interrupt blocking read() on modprobe failure.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> ---
>  lib/igt_kmod.c | 261 +++++++++++++++++++----
>  lib/igt_ktap.c | 568 -------------------------------------------------
>  lib/igt_ktap.h |  22 --
>  3 files changed, 222 insertions(+), 629 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 426ae5b26f..7bca4cdaab 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright © 2016 Intel Corporation
> + * Copyright © 2016-2023 Intel Corporation
>   *
>   * Permission is hereby granted, free of charge, to any person obtaining a
>   * copy of this software and associated documentation files (the "Software"),
> @@ -26,7 +26,12 @@
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <pthread.h>
> +#include <stdlib.h>
> +#include <string.h>
>  #include <sys/utsname.h>
> +#include <unistd.h>
> +
> +#include "assembler/brw_compat.h"	/* [un]likely() */
>  
>  #include "igt_aux.h"
>  #include "igt_core.h"
> @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
>  }
>  
>  struct modprobe_data {
> +	pthread_t parent;
>  	struct kmod_module *kmod;
>  	const char *opts;
>  	int err;
> +	pthread_mutex_t lock;
> +	pthread_t thread;
>  };
>  
>  static void *modprobe_task(void *arg)
> @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
>  
>  	data->err = modprobe(data->kmod, data->opts);
>  
> +	if (igt_debug_on(data->err)) {
> +		int err;
> +
> +		while (err = pthread_mutex_trylock(&data->lock),
> +		       err && !igt_debug_on(err != EBUSY))
> +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));

I guess you need here an "igt_debug_on_once", as it doesn't make
sense to have a (potentially) endless loop here printing error
messages.

the other changes LGTM.

> +	} else {
> +		/* let main thread use mutex to detect modprobe completion */
> +		igt_debug_on(pthread_mutex_lock(&data->lock));
> +	}
> +
>  	return NULL;
>  }
>  
> +static void kunit_sigchld_handler(int signal)
> +{
> +	return;
> +}
> +
> +static int kunit_kmsg_result_get(struct igt_list_head *results,
> +				 struct modprobe_data *modprobe,
> +				 int fd, struct igt_ktap_results *ktap)
> +{
> +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> +			 *saved;
> +	char record[BUF_LEN + 1], *buf;
> +	unsigned long taints;
> +	int ret;
> +
> +	do {
> +		int err;
> +
> +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> +			return -ENOTRECOVERABLE;
> +
> +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +		if (err == -1)
> +			return -errno;
> +		else if (unlikely(err))
> +			return err;
> +
> +		err = pthread_mutex_lock(&modprobe->lock);
> +		switch (err) {
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			__attribute__ ((fallthrough));
> +		case ENOTRECOVERABLE:
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			if (igt_debug_on(modprobe->err))
> +				return modprobe->err;
> +			break;
> +		case 0:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			return -err;
> +		}
> +
> +		ret = read(fd, record, BUF_LEN);
> +
> +		if (!err) {
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +		}
> +
> +		if (igt_debug_on(!ret))
> +			return -ENODATA;
> +		if (igt_debug_on(ret == -1))
> +			return -errno;
> +		if (unlikely(igt_debug_on(ret < 0)))
> +			break;
> +
> +		/* skip kmsg continuation lines */
> +		if (igt_debug_on(*record == ' '))
> +			continue;
> +
> +		/* NULL-terminate the record */
> +		record[ret] = '\0';
> +
> +		/* detect start of log message, continue if not found */
> +		buf = strchrnul(record, ';');
> +		if (igt_debug_on(*buf == '\0'))
> +			continue;
> +		buf++;
> +
> +		ret = igt_ktap_parse(buf, ktap);
> +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	} while (igt_list_empty(results));
> +
> +	return ret;
> +}
> +
> +static void kunit_result_free(struct igt_ktap_result **r,
> +			      char **suite_name, char **case_name)
> +{
> +	if (!*r)
> +		return;
> +
> +	igt_list_del(&(*r)->link);
> +
> +	if ((*r)->suite_name != *suite_name) {
> +		free(*suite_name);
> +		*suite_name = (*r)->suite_name;
> +	}
> +
> +	if ((*r)->case_name != *case_name) {
> +		free(*case_name);
> +		*case_name = (*r)->case_name;
> +	}
> +
> +	free((*r)->msg);
> +	free(*r);
> +	*r = NULL;
> +}
> +
>  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  {
> -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> -	struct kmod_module *kunit_kmod;
> -	bool is_builtin;
> -	struct ktap_test_results *results;
> -	pthread_t modprobe_thread;
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
>  	unsigned long taints;
>  	int flags, ret;
>  
> @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> -	kmod_module_unref(kunit_kmod);
> +	igt_skip_on(pthread_mutexattr_init(&attr));
> +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
>  
> -	results = ktap_parser_start(tst->kmsg, is_builtin);
> +	ktap = igt_ktap_alloc(&results);
> +	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
>  					modprobe_task, &modprobe))) {
> -		ktap_parser_cancel();
> -		igt_ignore_warn(ktap_parser_stop());
> +		igt_ktap_free(ktap);
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> -	{
> -		struct ktap_test_results_element *result;
> -
> -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> -			ktap_parser_cancel();
> +	do {
> +		ret = kunit_kmsg_result_get(&results, &modprobe,
> +					    tst->kmsg, ktap);
> +		if (igt_debug_on(ret && ret != -EINPROGRESS))
>  			break;
> -		}
>  
> -		if (igt_kernel_tainted(&taints)) {
> -			ktap_parser_cancel();
> -			pthread_cancel(modprobe_thread);
> +		if (igt_debug_on(igt_list_empty(&results)))
>  			break;
> -		}
>  
> -		pthread_mutex_lock(&results->mutex);
> -		if (igt_list_empty(&results->list)) {
> -			pthread_mutex_unlock(&results->mutex);
> -			continue;
> -		}
> +		r = igt_list_first_entry(&results, r, link);
>  
> -		result = igt_list_first_entry(&results->list, result, link);
> +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> +			if (r->code == IGT_EXIT_INVALID) {
> +				/* parametrized test case, get actual result */
> +				kunit_result_free(&r, &suite_name, &case_name);
>  
> -		igt_list_del(&result->link);
> -		pthread_mutex_unlock(&results->mutex);
> +				igt_assert(igt_list_empty(&results));
>  
> -		igt_dynamic(result->test_name) {
> -			igt_assert(READ_ONCE(result->passed));
> +				ret = kunit_kmsg_result_get(&results, &modprobe,
> +							    tst->kmsg, ktap);
> +				if (ret != -EINPROGRESS)
> +					igt_fail_on(ret);
> +
> +				igt_fail_on(igt_list_empty(&results));
> +
> +				r = igt_list_first_entry(&results, r, link);
> +
> +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      suite_name, r->suite_name);
> +				igt_fail_on_f(strcmp(r->case_name, case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      case_name, r->case_name);
> +			}
>  
> -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> +
> +			if (r->msg && *r->msg) {
> +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> +					      "%s\n", r->msg);
> +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> +					      "%s\n", r->msg);
> +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> +					      "%s\n", r->msg);
> +			} else {
> +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> +				if (r->code == IGT_EXIT_ABORT)
> +					igt_fail(r->code);
> +			}
> +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> +
> +			switch (pthread_mutex_lock(&modprobe.lock)) {
> +			case 0:
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				break;
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
>  				igt_assert_eq(modprobe.err, 0);
> +				break;
> +			default:
> +				igt_debug("pthread_mutex_lock() failed\n");
> +				break;
> +			}
>  
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		free(result);
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	} while (ret == -EINPROGRESS);
> +
> +	igt_list_for_each_entry_safe(r, rn, &results, link)
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	free(case_name);
> +	free(suite_name);
> +
> +	switch (pthread_mutex_lock(&modprobe.lock)) {
> +	case 0:
> +		igt_debug_on(pthread_cancel(modprobe.thread));
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
> +	case EOWNERDEAD:
> +		/* leave the mutex unrecoverable */
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		break;
> +	case ENOTRECOVERABLE:
> +		break;
> +	default:
> +		igt_debug("pthread_mutex_lock() failed\n");
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
>  	}
>  
> -	pthread_join(modprobe_thread, NULL);
> -
> -	ret = ktap_parser_stop();
> +	igt_ktap_free(ktap);
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 5eac102417..53a6c63288 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -4,17 +4,11 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> -#include <ctype.h>
> -#include <limits.h>
> -#include <libkmod.h>
> -#include <pthread.h>
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> -#include <unistd.h>
>  
> -#include "igt_aux.h"
>  #include "igt_core.h"
>  #include "igt_ktap.h"
>  #include "igt_list.h"
> @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
>  {
>  	free(ktap);
>  }
> -
> -#define DELIMITER "-"
> -
> -struct ktap_parser_args {
> -	int fd;
> -	bool is_builtin;
> -	int ret;
> -} ktap_args;
> -
> -static struct ktap_test_results results;
> -
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> -
> -/**
> - * log_to_end:
> - * @level: #igt_log_level
> - * @record: record to store the read data
> - * @format: format string
> - * @...: optional arguments used in the format string
> - *
> - * This is an altered version of the generic structured logging helper function
> - * igt_log capable of reading to the end of a given line.
> - *
> - * Returns: 0 for success, or -2 if there's an error reading from the file
> - */
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...)
> -{
> -	va_list args;
> -	const char *lend;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	char *cutoff = strchr(record, '\n');
> -	if (cutoff) {
> -		if (cutoff - record < BUF_LEN)
> -			cutoff[1] = '\0';
> -	}
> -
> -	va_start(args, format);
> -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> -	va_end(args);
> -
> -	lend = strchrnul(record, '\n');
> -	while (*lend == '\0') {
> -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -
> -		lend = strchrnul(record, '\n');
> -	}
> -	return 0;
> -}
> -
> -/**
> - * lookup_value:
> - * @haystack: the string to search in
> - * @needle: the string to search for
> - *
> - * Returns: the value of the needle in the haystack, or -1 if not found.
> - */
> -static long lookup_value(const char *haystack, const char *needle)
> -{
> -	const char *needle_rptr;
> -	char *needle_end;
> -	long num;
> -
> -	needle_rptr = strcasestr(haystack, needle);
> -
> -	if (needle_rptr == NULL)
> -		return -1;
> -
> -	/* Skip search string and whitespaces after it */
> -	needle_rptr += strlen(needle);
> -
> -	num = strtol(needle_rptr, &needle_end, 10);
> -
> -	if (needle_rptr == needle_end)
> -		return -1;
> -
> -	if (num == LONG_MIN || num == LONG_MAX)
> -		return 0;
> -
> -	return num > 0 ? num : 0;
> -}
> -
> -/**
> - * tap_version_present:
> - * @record: buffer with tap data
> - * @print_info: whether tap version should be printed or not
> - *
> - * Returns:
> - * 0 if not found
> - * 1 if found
> - */
> -static int tap_version_present(char* record, bool print_info)
> -{
> -	/*
> -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> -	 *
> -	 * but actually isn't, as it currently depends on the KUnit module
> -	 * being built-in, so we can't rely on it every time
> -	 */
> -	const char *version_rptr = strcasestr(record, "TAP version ");
> -	char *cutoff;
> -
> -	if (version_rptr == NULL)
> -		return 0;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	cutoff = strchr(version_rptr, '\n');
> -	if (cutoff)
> -		cutoff[0] = '\0';
> -
> -	if (print_info)
> -		igt_info("%s\n", version_rptr);
> -
> -	return 1;
> -}
> -
> -/**
> - * find_next_tap_subtest:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @is_builtin: whether KUnit is built-in or not
> - *
> - * Returns:
> - * 0 if there's missing information
> - * -1 if not found
> - * -2 if there are problems while reading the file.
> - * any other value corresponds to the amount of cases of the next (sub)test
> - */
> -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> -{
> -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> -	long test_count;
> -	char *cutoff;
> -
> -	test_name[0] = '\0';
> -	test_name[BUF_LEN] = '\0';
> -
> -	test_lookup_str = " subtest: ";
> -	subtest_lookup_str = " test: ";
> -
> -	if (!tap_version_present(record, true))
> -		return -1;
> -
> -	if (is_builtin) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -	}
> -
> -	name_rptr = strcasestr(record, test_lookup_str);
> -	if (name_rptr != NULL) {
> -		name_rptr += strlen(test_lookup_str);
> -	} else {
> -		name_rptr = strcasestr(record, subtest_lookup_str);
> -		if (name_rptr != NULL)
> -			name_rptr += strlen(subtest_lookup_str);
> -	}
> -
> -	if (name_rptr == NULL) {
> -		if (!is_builtin)
> -			/* We've probably found nothing */
> -			return -1;
> -		igt_info("Missing test name\n");
> -	} else {
> -		strncpy(test_name, name_rptr, BUF_LEN);
> -		/* Cutoff after newline character, in order to not display garbage */
> -		cutoff = strchr(test_name, '\n');
> -		if (cutoff)
> -			cutoff[0] = '\0';
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("unknown error reading kmsg (%m)\n");
> -
> -			return -2;
> -		}
> -
> -		/* Now we can be sure we found tests */
> -		if (!is_builtin)
> -			igt_info("KUnit is not built-in, skipping version check...\n");
> -	}
> -
> -	/*
> -	 * Total test count will almost always appear as 0..N at the beginning
> -	 * of a run, so we use it to reliably identify a new run
> -	 */
> -	test_count = lookup_value(record, "..");
> -
> -	if (test_count <= 0) {
> -		igt_info("Missing test count\n");
> -		if (test_name[0] == '\0')
> -			return 0;
> -		if (log_to_end(IGT_LOG_INFO, fd, record,
> -				"Running some tests in: %s\n",
> -				test_name) < 0)
> -			return -2;
> -		return 0;
> -	} else if (test_name[0] == '\0') {
> -		igt_info("Running %ld tests...\n", test_count);
> -		return 0;
> -	}
> -
> -	if (log_to_end(IGT_LOG_INFO, fd, record,
> -			"Executing %ld tests in: %s\n",
> -			test_count, test_name) < 0)
> -		return -2;
> -
> -	return test_count;
> -}
> -
> -/**
> - * parse_kmsg_for_tap:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @test_name: buffer to store the test name
> - *
> - * Returns:
> - * 1 if no results were found
> - * 0 if a test succeded
> - * -1 if a test failed
> - * -2 if there are problems reading the file
> - */
> -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> -{
> -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> -	char *test_name_end;
> -
> -	ok_lookup_str = "ok ";
> -	nok_lookup_str = "not ok ";
> -
> -	lstart = strchrnul(record, ';');
> -
> -	if (*lstart == '\0') {
> -		igt_warn("kmsg truncated: output malformed (%m)\n");
> -		return -2;
> -	}
> -
> -	lstart++;
> -	while (isspace(*lstart))
> -		lstart++;
> -
> -	nok_rptr = strstr(lstart, nok_lookup_str);
> -	if (nok_rptr != NULL) {
> -		nok_rptr += strlen(nok_lookup_str);
> -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> -			nok_rptr++;
> -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		if (log_to_end(IGT_LOG_WARN, fd, record,
> -			       "%s", lstart) < 0)
> -			return -2;
> -		return -1;
> -	}
> -
> -	comment_start = strchrnul(lstart, '#');
> -
> -	/* Check if we're still in a subtest */
> -	if (*comment_start != '\0') {
> -		comment_start++;
> -		value_parse_start = comment_start;
> -
> -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> -			if (log_to_end(IGT_LOG_WARN, fd, record,
> -				       "%s", lstart) < 0)
> -				return -2;
> -			return -1;
> -		}
> -	}
> -
> -	ok_rptr = strstr(lstart, ok_lookup_str);
> -	if (ok_rptr != NULL) {
> -		ok_rptr += strlen(ok_lookup_str);
> -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> -			ok_rptr++;
> -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		return 0;
> -	}
> -
> -	return 1;
> -}
> -
> -/**
> - * parse_tap_level:
> - * @fd: file descriptor
> - * @base_test_name: test_name from upper recursion level
> - * @test_count: test_count of this level
> - * @failed_tests: top level failed_tests pointer
> - * @found_tests: top level found_tests pointer
> - * @is_builtin: whether the KUnit module is built-in or not
> - *
> - * Returns:
> - * 0 if succeded
> - * -1 if error occurred
> - */
> -__maybe_unused
> -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> -			   bool *found_tests, bool is_builtin)
> -{
> -	char record[BUF_LEN + 1];
> -	struct ktap_test_results_element *r;
> -	int internal_test_count;
> -	char test_name[BUF_LEN + 1];
> -	char base_test_name_for_next_level[BUF_LEN + 1];
> -
> -	for (int i = 0; i < test_count; i++) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("error reading kmsg (%m)\n");
> -
> -			return -1;
> -		}
> -
> -		/* Sublevel found */
> -		if (tap_version_present(record, false))
> -		{
> -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> -								    is_builtin);
> -			switch (internal_test_count) {
> -			case -2:
> -				/* No more data to read */
> -				return -1;
> -			case -1:
> -				/* No test found */
> -				return -1;
> -			case 0:
> -				/* Tests found, but they're missing info */
> -				*found_tests = true;
> -				return -1;
> -			default:
> -				*found_tests = true;
> -
> -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> -				    base_test_name_for_next_level[0])
> -					strncat(base_test_name_for_next_level, DELIMITER,
> -						BUF_LEN - strlen(base_test_name_for_next_level));
> -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> -
> -				if (parse_tap_level(fd, base_test_name_for_next_level,
> -						    internal_test_count, failed_tests, found_tests,
> -						    is_builtin) == -1)
> -					return -1;
> -				break;
> -			}
> -		}
> -
> -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> -		case -2:
> -			return -1;
> -		case -1:
> -			*failed_tests = true;
> -
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = false;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		case 0:
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = true;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		default:
> -			break;
> -		}
> -	}
> -	return 0;
> -}
> -
> -/**
> - * igt_ktap_parser:
> - *
> - * This function parses the output of a ktap script and passes it to main thread.
> - */
> -void *igt_ktap_parser(void *unused)
> -{
> -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_results *ktap = NULL;
> -	int fd = ktap_args.fd;
> -	IGT_LIST_HEAD(list);
> -	int err;
> -
> -	ktap = igt_ktap_alloc(&list);
> -	if (igt_debug_on(!ktap))
> -		goto igt_ktap_parser_end;
> -
> -	while (err = read(fd, record, BUF_LEN), err > 0) {
> -		struct igt_ktap_result *r, *rn;
> -
> -		/* skip kmsg continuation lines */
> -		if (igt_debug_on(*record == ' '))
> -			continue;
> -
> -		/* NULL-terminate the record */
> -		record[err] = '\0';
> -
> -		/* detect start of log message, continue if not found */
> -		buf = strchrnul(record, ';');
> -		if (igt_debug_on(*buf == '\0'))
> -			continue;
> -		buf++;
> -
> -		err = igt_ktap_parse(buf, ktap);
> -
> -		/* parsing error */
> -		if (err && err != -EINPROGRESS)
> -			goto igt_ktap_parser_end;
> -
> -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> -			struct ktap_test_results_element *result = NULL;
> -			int code = r->code;
> -
> -			if (code != IGT_EXIT_INVALID)
> -				result = calloc(1, sizeof(*result));
> -
> -			if (result) {
> -				snprintf(result->test_name, sizeof(result->test_name),
> -					 "%s-%s", r->suite_name, r->case_name);
> -
> -				if (code == IGT_EXIT_SUCCESS)
> -					result->passed = true;
> -			}
> -
> -			igt_list_del(&r->link);
> -			if (r->suite_name != suite_name) {
> -				free(suite_name);
> -				suite_name = r->suite_name;
> -			}
> -			if (r->case_name != case_name) {
> -				free(case_name);
> -				case_name = r->case_name;
> -			}
> -			free(r->msg);
> -			free(r);
> -
> -			/*
> -			 * no extra result record expected on start
> -			 * of parametrized test case -- skip it
> -			 */
> -			if (code == IGT_EXIT_INVALID)
> -				continue;
> -
> -			if (!result) {
> -				err = -ENOMEM;
> -				goto igt_ktap_parser_end;
> -			}
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&result->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -		}
> -
> -		/* end of KTAP report */
> -		if (!err)
> -			goto igt_ktap_parser_end;
> -	}
> -
> -	if (err < 0) {
> -		if (errno == EPIPE)
> -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -		else
> -			igt_warn("error reading kmsg (%m)\n");
> -	}
> -
> -igt_ktap_parser_end:
> -	free(suite_name);
> -	free(case_name);
> -
> -	if (!err)
> -		ktap_args.ret = IGT_EXIT_SUCCESS;
> -
> -	results.still_running = false;
> -
> -	if (ktap)
> -		igt_ktap_free(ktap);
> -
> -	return NULL;
> -}
> -
> -static pthread_t ktap_parser_thread;
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> -{
> -	IGT_INIT_LIST_HEAD(&results.list);
> -	pthread_mutex_init(&results.mutex, NULL);
> -	results.still_running = true;
> -
> -	ktap_args.fd = fd;
> -	ktap_args.is_builtin = is_builtin;
> -	ktap_args.ret = IGT_EXIT_FAILURE;
> -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> -
> -	return &results;
> -}
> -
> -void ktap_parser_cancel(void)
> -{
> -	pthread_cancel(ktap_parser_thread);
> -}
> -
> -int ktap_parser_stop(void)
> -{
> -	pthread_join(ktap_parser_thread, NULL);
> -	return ktap_args.ret;
> -}
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index 6f8da3eab6..c422636bfc 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -27,8 +27,6 @@
>  
>  #define BUF_LEN 4096
>  
> -#include <pthread.h>
> -
>  #include "igt_list.h"
>  
>  struct igt_ktap_result {
> @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
>  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
>  void igt_ktap_free(struct igt_ktap_results *ktap);
>  
> -void *igt_ktap_parser(void *unused);
> -
> -typedef struct ktap_test_results_element {
> -	char test_name[BUF_LEN + 1];
> -	bool passed;
> -	struct igt_list_head link;
> -} ktap_test_results_element;
> -
> -struct ktap_test_results {
> -	struct igt_list_head list;
> -	pthread_mutex_t mutex;
> -	bool still_running;
> -};
> -
> -
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> -void ktap_parser_cancel(void);
> -int ktap_parser_stop(void);
> -
>  #endif /* IGT_KTAP_H */

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-10 13:33     ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 77+ messages in thread
From: Mauro Carvalho Chehab @ 2023-10-10 13:33 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev, intel-gfx, intel-xe

On Mon,  9 Oct 2023 14:27:55 +0200
Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com> wrote:

> There was an attempt to parse KTAP reports in the background while a kunit
> test module is loading.  However, since dynamic sub-subtests can be
> executed only from the main thread, that attempt was not quite successful,
> as IGT results from all executed kunit test cases were generated only
> after loading of kunit test module completed.
> 
> Now that the parser maintains its state and we can call it separately for
> each input line of a KTAP report, it is perfectly possible to call the
> parser from the main thread while the module is loading in the background,
> and convert results from kunit test cases immediately to results of IGT
> dynamic sub-subtests by running an igt_dynamic() section for each result
> as soon as returned by the parser.
> 
> Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> result obtained from igt_ktap_parse() called from the main thread.
> 
> Also, drop no longer needed functions from igt_ktap soruces.
> 
> v3: Fix ktap structure not freed on lseek error,
>   - fix initial SIGCHLD handler not restored,
>   - fix missing handling of potential errors returned by sigaction,
>   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
>     synchronization with modprobe thread,
>   - fix potentially illegal use of igt_assert() called outside of
>     dynamic sub-subtest section,
>   - fix unsupported exit code potentially passed to igt_fail(),
>   - no need to fail a dynamic sub-subtest on potential KTAP parser error
>     after a valid result from the parser has been processed,
>   - fix trailing newlines missing from error messages,
>   - add more debug statements,
>   - integrate common code around kunit_result_free() into it.
> v2: Interrupt blocking read() on modprobe failure.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> ---
>  lib/igt_kmod.c | 261 +++++++++++++++++++----
>  lib/igt_ktap.c | 568 -------------------------------------------------
>  lib/igt_ktap.h |  22 --
>  3 files changed, 222 insertions(+), 629 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 426ae5b26f..7bca4cdaab 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright © 2016 Intel Corporation
> + * Copyright © 2016-2023 Intel Corporation
>   *
>   * Permission is hereby granted, free of charge, to any person obtaining a
>   * copy of this software and associated documentation files (the "Software"),
> @@ -26,7 +26,12 @@
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <pthread.h>
> +#include <stdlib.h>
> +#include <string.h>
>  #include <sys/utsname.h>
> +#include <unistd.h>
> +
> +#include "assembler/brw_compat.h"	/* [un]likely() */
>  
>  #include "igt_aux.h"
>  #include "igt_core.h"
> @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
>  }
>  
>  struct modprobe_data {
> +	pthread_t parent;
>  	struct kmod_module *kmod;
>  	const char *opts;
>  	int err;
> +	pthread_mutex_t lock;
> +	pthread_t thread;
>  };
>  
>  static void *modprobe_task(void *arg)
> @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
>  
>  	data->err = modprobe(data->kmod, data->opts);
>  
> +	if (igt_debug_on(data->err)) {
> +		int err;
> +
> +		while (err = pthread_mutex_trylock(&data->lock),
> +		       err && !igt_debug_on(err != EBUSY))
> +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));

I guess you need here an "igt_debug_on_once", as it doesn't make
sense to have a (potentially) endless loop here printing error
messages.

the other changes LGTM.

> +	} else {
> +		/* let main thread use mutex to detect modprobe completion */
> +		igt_debug_on(pthread_mutex_lock(&data->lock));
> +	}
> +
>  	return NULL;
>  }
>  
> +static void kunit_sigchld_handler(int signal)
> +{
> +	return;
> +}
> +
> +static int kunit_kmsg_result_get(struct igt_list_head *results,
> +				 struct modprobe_data *modprobe,
> +				 int fd, struct igt_ktap_results *ktap)
> +{
> +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> +			 *saved;
> +	char record[BUF_LEN + 1], *buf;
> +	unsigned long taints;
> +	int ret;
> +
> +	do {
> +		int err;
> +
> +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> +			return -ENOTRECOVERABLE;
> +
> +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +		if (err == -1)
> +			return -errno;
> +		else if (unlikely(err))
> +			return err;
> +
> +		err = pthread_mutex_lock(&modprobe->lock);
> +		switch (err) {
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			__attribute__ ((fallthrough));
> +		case ENOTRECOVERABLE:
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			if (igt_debug_on(modprobe->err))
> +				return modprobe->err;
> +			break;
> +		case 0:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			return -err;
> +		}
> +
> +		ret = read(fd, record, BUF_LEN);
> +
> +		if (!err) {
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +		}
> +
> +		if (igt_debug_on(!ret))
> +			return -ENODATA;
> +		if (igt_debug_on(ret == -1))
> +			return -errno;
> +		if (unlikely(igt_debug_on(ret < 0)))
> +			break;
> +
> +		/* skip kmsg continuation lines */
> +		if (igt_debug_on(*record == ' '))
> +			continue;
> +
> +		/* NULL-terminate the record */
> +		record[ret] = '\0';
> +
> +		/* detect start of log message, continue if not found */
> +		buf = strchrnul(record, ';');
> +		if (igt_debug_on(*buf == '\0'))
> +			continue;
> +		buf++;
> +
> +		ret = igt_ktap_parse(buf, ktap);
> +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	} while (igt_list_empty(results));
> +
> +	return ret;
> +}
> +
> +static void kunit_result_free(struct igt_ktap_result **r,
> +			      char **suite_name, char **case_name)
> +{
> +	if (!*r)
> +		return;
> +
> +	igt_list_del(&(*r)->link);
> +
> +	if ((*r)->suite_name != *suite_name) {
> +		free(*suite_name);
> +		*suite_name = (*r)->suite_name;
> +	}
> +
> +	if ((*r)->case_name != *case_name) {
> +		free(*case_name);
> +		*case_name = (*r)->case_name;
> +	}
> +
> +	free((*r)->msg);
> +	free(*r);
> +	*r = NULL;
> +}
> +
>  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  {
> -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> -	struct kmod_module *kunit_kmod;
> -	bool is_builtin;
> -	struct ktap_test_results *results;
> -	pthread_t modprobe_thread;
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
>  	unsigned long taints;
>  	int flags, ret;
>  
> @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> -	kmod_module_unref(kunit_kmod);
> +	igt_skip_on(pthread_mutexattr_init(&attr));
> +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
>  
> -	results = ktap_parser_start(tst->kmsg, is_builtin);
> +	ktap = igt_ktap_alloc(&results);
> +	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
>  					modprobe_task, &modprobe))) {
> -		ktap_parser_cancel();
> -		igt_ignore_warn(ktap_parser_stop());
> +		igt_ktap_free(ktap);
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> -	{
> -		struct ktap_test_results_element *result;
> -
> -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> -			ktap_parser_cancel();
> +	do {
> +		ret = kunit_kmsg_result_get(&results, &modprobe,
> +					    tst->kmsg, ktap);
> +		if (igt_debug_on(ret && ret != -EINPROGRESS))
>  			break;
> -		}
>  
> -		if (igt_kernel_tainted(&taints)) {
> -			ktap_parser_cancel();
> -			pthread_cancel(modprobe_thread);
> +		if (igt_debug_on(igt_list_empty(&results)))
>  			break;
> -		}
>  
> -		pthread_mutex_lock(&results->mutex);
> -		if (igt_list_empty(&results->list)) {
> -			pthread_mutex_unlock(&results->mutex);
> -			continue;
> -		}
> +		r = igt_list_first_entry(&results, r, link);
>  
> -		result = igt_list_first_entry(&results->list, result, link);
> +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> +			if (r->code == IGT_EXIT_INVALID) {
> +				/* parametrized test case, get actual result */
> +				kunit_result_free(&r, &suite_name, &case_name);
>  
> -		igt_list_del(&result->link);
> -		pthread_mutex_unlock(&results->mutex);
> +				igt_assert(igt_list_empty(&results));
>  
> -		igt_dynamic(result->test_name) {
> -			igt_assert(READ_ONCE(result->passed));
> +				ret = kunit_kmsg_result_get(&results, &modprobe,
> +							    tst->kmsg, ktap);
> +				if (ret != -EINPROGRESS)
> +					igt_fail_on(ret);
> +
> +				igt_fail_on(igt_list_empty(&results));
> +
> +				r = igt_list_first_entry(&results, r, link);
> +
> +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      suite_name, r->suite_name);
> +				igt_fail_on_f(strcmp(r->case_name, case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      case_name, r->case_name);
> +			}
>  
> -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> +
> +			if (r->msg && *r->msg) {
> +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> +					      "%s\n", r->msg);
> +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> +					      "%s\n", r->msg);
> +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> +					      "%s\n", r->msg);
> +			} else {
> +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> +				if (r->code == IGT_EXIT_ABORT)
> +					igt_fail(r->code);
> +			}
> +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> +
> +			switch (pthread_mutex_lock(&modprobe.lock)) {
> +			case 0:
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				break;
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
>  				igt_assert_eq(modprobe.err, 0);
> +				break;
> +			default:
> +				igt_debug("pthread_mutex_lock() failed\n");
> +				break;
> +			}
>  
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		free(result);
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	} while (ret == -EINPROGRESS);
> +
> +	igt_list_for_each_entry_safe(r, rn, &results, link)
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	free(case_name);
> +	free(suite_name);
> +
> +	switch (pthread_mutex_lock(&modprobe.lock)) {
> +	case 0:
> +		igt_debug_on(pthread_cancel(modprobe.thread));
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
> +	case EOWNERDEAD:
> +		/* leave the mutex unrecoverable */
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		break;
> +	case ENOTRECOVERABLE:
> +		break;
> +	default:
> +		igt_debug("pthread_mutex_lock() failed\n");
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
>  	}
>  
> -	pthread_join(modprobe_thread, NULL);
> -
> -	ret = ktap_parser_stop();
> +	igt_ktap_free(ktap);
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 5eac102417..53a6c63288 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -4,17 +4,11 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> -#include <ctype.h>
> -#include <limits.h>
> -#include <libkmod.h>
> -#include <pthread.h>
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> -#include <unistd.h>
>  
> -#include "igt_aux.h"
>  #include "igt_core.h"
>  #include "igt_ktap.h"
>  #include "igt_list.h"
> @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
>  {
>  	free(ktap);
>  }
> -
> -#define DELIMITER "-"
> -
> -struct ktap_parser_args {
> -	int fd;
> -	bool is_builtin;
> -	int ret;
> -} ktap_args;
> -
> -static struct ktap_test_results results;
> -
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> -
> -/**
> - * log_to_end:
> - * @level: #igt_log_level
> - * @record: record to store the read data
> - * @format: format string
> - * @...: optional arguments used in the format string
> - *
> - * This is an altered version of the generic structured logging helper function
> - * igt_log capable of reading to the end of a given line.
> - *
> - * Returns: 0 for success, or -2 if there's an error reading from the file
> - */
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...)
> -{
> -	va_list args;
> -	const char *lend;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	char *cutoff = strchr(record, '\n');
> -	if (cutoff) {
> -		if (cutoff - record < BUF_LEN)
> -			cutoff[1] = '\0';
> -	}
> -
> -	va_start(args, format);
> -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> -	va_end(args);
> -
> -	lend = strchrnul(record, '\n');
> -	while (*lend == '\0') {
> -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -
> -		lend = strchrnul(record, '\n');
> -	}
> -	return 0;
> -}
> -
> -/**
> - * lookup_value:
> - * @haystack: the string to search in
> - * @needle: the string to search for
> - *
> - * Returns: the value of the needle in the haystack, or -1 if not found.
> - */
> -static long lookup_value(const char *haystack, const char *needle)
> -{
> -	const char *needle_rptr;
> -	char *needle_end;
> -	long num;
> -
> -	needle_rptr = strcasestr(haystack, needle);
> -
> -	if (needle_rptr == NULL)
> -		return -1;
> -
> -	/* Skip search string and whitespaces after it */
> -	needle_rptr += strlen(needle);
> -
> -	num = strtol(needle_rptr, &needle_end, 10);
> -
> -	if (needle_rptr == needle_end)
> -		return -1;
> -
> -	if (num == LONG_MIN || num == LONG_MAX)
> -		return 0;
> -
> -	return num > 0 ? num : 0;
> -}
> -
> -/**
> - * tap_version_present:
> - * @record: buffer with tap data
> - * @print_info: whether tap version should be printed or not
> - *
> - * Returns:
> - * 0 if not found
> - * 1 if found
> - */
> -static int tap_version_present(char* record, bool print_info)
> -{
> -	/*
> -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> -	 *
> -	 * but actually isn't, as it currently depends on the KUnit module
> -	 * being built-in, so we can't rely on it every time
> -	 */
> -	const char *version_rptr = strcasestr(record, "TAP version ");
> -	char *cutoff;
> -
> -	if (version_rptr == NULL)
> -		return 0;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	cutoff = strchr(version_rptr, '\n');
> -	if (cutoff)
> -		cutoff[0] = '\0';
> -
> -	if (print_info)
> -		igt_info("%s\n", version_rptr);
> -
> -	return 1;
> -}
> -
> -/**
> - * find_next_tap_subtest:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @is_builtin: whether KUnit is built-in or not
> - *
> - * Returns:
> - * 0 if there's missing information
> - * -1 if not found
> - * -2 if there are problems while reading the file.
> - * any other value corresponds to the amount of cases of the next (sub)test
> - */
> -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> -{
> -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> -	long test_count;
> -	char *cutoff;
> -
> -	test_name[0] = '\0';
> -	test_name[BUF_LEN] = '\0';
> -
> -	test_lookup_str = " subtest: ";
> -	subtest_lookup_str = " test: ";
> -
> -	if (!tap_version_present(record, true))
> -		return -1;
> -
> -	if (is_builtin) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -	}
> -
> -	name_rptr = strcasestr(record, test_lookup_str);
> -	if (name_rptr != NULL) {
> -		name_rptr += strlen(test_lookup_str);
> -	} else {
> -		name_rptr = strcasestr(record, subtest_lookup_str);
> -		if (name_rptr != NULL)
> -			name_rptr += strlen(subtest_lookup_str);
> -	}
> -
> -	if (name_rptr == NULL) {
> -		if (!is_builtin)
> -			/* We've probably found nothing */
> -			return -1;
> -		igt_info("Missing test name\n");
> -	} else {
> -		strncpy(test_name, name_rptr, BUF_LEN);
> -		/* Cutoff after newline character, in order to not display garbage */
> -		cutoff = strchr(test_name, '\n');
> -		if (cutoff)
> -			cutoff[0] = '\0';
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("unknown error reading kmsg (%m)\n");
> -
> -			return -2;
> -		}
> -
> -		/* Now we can be sure we found tests */
> -		if (!is_builtin)
> -			igt_info("KUnit is not built-in, skipping version check...\n");
> -	}
> -
> -	/*
> -	 * Total test count will almost always appear as 0..N at the beginning
> -	 * of a run, so we use it to reliably identify a new run
> -	 */
> -	test_count = lookup_value(record, "..");
> -
> -	if (test_count <= 0) {
> -		igt_info("Missing test count\n");
> -		if (test_name[0] == '\0')
> -			return 0;
> -		if (log_to_end(IGT_LOG_INFO, fd, record,
> -				"Running some tests in: %s\n",
> -				test_name) < 0)
> -			return -2;
> -		return 0;
> -	} else if (test_name[0] == '\0') {
> -		igt_info("Running %ld tests...\n", test_count);
> -		return 0;
> -	}
> -
> -	if (log_to_end(IGT_LOG_INFO, fd, record,
> -			"Executing %ld tests in: %s\n",
> -			test_count, test_name) < 0)
> -		return -2;
> -
> -	return test_count;
> -}
> -
> -/**
> - * parse_kmsg_for_tap:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @test_name: buffer to store the test name
> - *
> - * Returns:
> - * 1 if no results were found
> - * 0 if a test succeded
> - * -1 if a test failed
> - * -2 if there are problems reading the file
> - */
> -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> -{
> -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> -	char *test_name_end;
> -
> -	ok_lookup_str = "ok ";
> -	nok_lookup_str = "not ok ";
> -
> -	lstart = strchrnul(record, ';');
> -
> -	if (*lstart == '\0') {
> -		igt_warn("kmsg truncated: output malformed (%m)\n");
> -		return -2;
> -	}
> -
> -	lstart++;
> -	while (isspace(*lstart))
> -		lstart++;
> -
> -	nok_rptr = strstr(lstart, nok_lookup_str);
> -	if (nok_rptr != NULL) {
> -		nok_rptr += strlen(nok_lookup_str);
> -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> -			nok_rptr++;
> -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		if (log_to_end(IGT_LOG_WARN, fd, record,
> -			       "%s", lstart) < 0)
> -			return -2;
> -		return -1;
> -	}
> -
> -	comment_start = strchrnul(lstart, '#');
> -
> -	/* Check if we're still in a subtest */
> -	if (*comment_start != '\0') {
> -		comment_start++;
> -		value_parse_start = comment_start;
> -
> -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> -			if (log_to_end(IGT_LOG_WARN, fd, record,
> -				       "%s", lstart) < 0)
> -				return -2;
> -			return -1;
> -		}
> -	}
> -
> -	ok_rptr = strstr(lstart, ok_lookup_str);
> -	if (ok_rptr != NULL) {
> -		ok_rptr += strlen(ok_lookup_str);
> -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> -			ok_rptr++;
> -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		return 0;
> -	}
> -
> -	return 1;
> -}
> -
> -/**
> - * parse_tap_level:
> - * @fd: file descriptor
> - * @base_test_name: test_name from upper recursion level
> - * @test_count: test_count of this level
> - * @failed_tests: top level failed_tests pointer
> - * @found_tests: top level found_tests pointer
> - * @is_builtin: whether the KUnit module is built-in or not
> - *
> - * Returns:
> - * 0 if succeded
> - * -1 if error occurred
> - */
> -__maybe_unused
> -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> -			   bool *found_tests, bool is_builtin)
> -{
> -	char record[BUF_LEN + 1];
> -	struct ktap_test_results_element *r;
> -	int internal_test_count;
> -	char test_name[BUF_LEN + 1];
> -	char base_test_name_for_next_level[BUF_LEN + 1];
> -
> -	for (int i = 0; i < test_count; i++) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("error reading kmsg (%m)\n");
> -
> -			return -1;
> -		}
> -
> -		/* Sublevel found */
> -		if (tap_version_present(record, false))
> -		{
> -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> -								    is_builtin);
> -			switch (internal_test_count) {
> -			case -2:
> -				/* No more data to read */
> -				return -1;
> -			case -1:
> -				/* No test found */
> -				return -1;
> -			case 0:
> -				/* Tests found, but they're missing info */
> -				*found_tests = true;
> -				return -1;
> -			default:
> -				*found_tests = true;
> -
> -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> -				    base_test_name_for_next_level[0])
> -					strncat(base_test_name_for_next_level, DELIMITER,
> -						BUF_LEN - strlen(base_test_name_for_next_level));
> -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> -
> -				if (parse_tap_level(fd, base_test_name_for_next_level,
> -						    internal_test_count, failed_tests, found_tests,
> -						    is_builtin) == -1)
> -					return -1;
> -				break;
> -			}
> -		}
> -
> -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> -		case -2:
> -			return -1;
> -		case -1:
> -			*failed_tests = true;
> -
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = false;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		case 0:
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = true;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		default:
> -			break;
> -		}
> -	}
> -	return 0;
> -}
> -
> -/**
> - * igt_ktap_parser:
> - *
> - * This function parses the output of a ktap script and passes it to main thread.
> - */
> -void *igt_ktap_parser(void *unused)
> -{
> -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_results *ktap = NULL;
> -	int fd = ktap_args.fd;
> -	IGT_LIST_HEAD(list);
> -	int err;
> -
> -	ktap = igt_ktap_alloc(&list);
> -	if (igt_debug_on(!ktap))
> -		goto igt_ktap_parser_end;
> -
> -	while (err = read(fd, record, BUF_LEN), err > 0) {
> -		struct igt_ktap_result *r, *rn;
> -
> -		/* skip kmsg continuation lines */
> -		if (igt_debug_on(*record == ' '))
> -			continue;
> -
> -		/* NULL-terminate the record */
> -		record[err] = '\0';
> -
> -		/* detect start of log message, continue if not found */
> -		buf = strchrnul(record, ';');
> -		if (igt_debug_on(*buf == '\0'))
> -			continue;
> -		buf++;
> -
> -		err = igt_ktap_parse(buf, ktap);
> -
> -		/* parsing error */
> -		if (err && err != -EINPROGRESS)
> -			goto igt_ktap_parser_end;
> -
> -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> -			struct ktap_test_results_element *result = NULL;
> -			int code = r->code;
> -
> -			if (code != IGT_EXIT_INVALID)
> -				result = calloc(1, sizeof(*result));
> -
> -			if (result) {
> -				snprintf(result->test_name, sizeof(result->test_name),
> -					 "%s-%s", r->suite_name, r->case_name);
> -
> -				if (code == IGT_EXIT_SUCCESS)
> -					result->passed = true;
> -			}
> -
> -			igt_list_del(&r->link);
> -			if (r->suite_name != suite_name) {
> -				free(suite_name);
> -				suite_name = r->suite_name;
> -			}
> -			if (r->case_name != case_name) {
> -				free(case_name);
> -				case_name = r->case_name;
> -			}
> -			free(r->msg);
> -			free(r);
> -
> -			/*
> -			 * no extra result record expected on start
> -			 * of parametrized test case -- skip it
> -			 */
> -			if (code == IGT_EXIT_INVALID)
> -				continue;
> -
> -			if (!result) {
> -				err = -ENOMEM;
> -				goto igt_ktap_parser_end;
> -			}
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&result->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -		}
> -
> -		/* end of KTAP report */
> -		if (!err)
> -			goto igt_ktap_parser_end;
> -	}
> -
> -	if (err < 0) {
> -		if (errno == EPIPE)
> -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -		else
> -			igt_warn("error reading kmsg (%m)\n");
> -	}
> -
> -igt_ktap_parser_end:
> -	free(suite_name);
> -	free(case_name);
> -
> -	if (!err)
> -		ktap_args.ret = IGT_EXIT_SUCCESS;
> -
> -	results.still_running = false;
> -
> -	if (ktap)
> -		igt_ktap_free(ktap);
> -
> -	return NULL;
> -}
> -
> -static pthread_t ktap_parser_thread;
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> -{
> -	IGT_INIT_LIST_HEAD(&results.list);
> -	pthread_mutex_init(&results.mutex, NULL);
> -	results.still_running = true;
> -
> -	ktap_args.fd = fd;
> -	ktap_args.is_builtin = is_builtin;
> -	ktap_args.ret = IGT_EXIT_FAILURE;
> -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> -
> -	return &results;
> -}
> -
> -void ktap_parser_cancel(void)
> -{
> -	pthread_cancel(ktap_parser_thread);
> -}
> -
> -int ktap_parser_stop(void)
> -{
> -	pthread_join(ktap_parser_thread, NULL);
> -	return ktap_args.ret;
> -}
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index 6f8da3eab6..c422636bfc 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -27,8 +27,6 @@
>  
>  #define BUF_LEN 4096
>  
> -#include <pthread.h>
> -
>  #include "igt_list.h"
>  
>  struct igt_ktap_result {
> @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
>  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
>  void igt_ktap_free(struct igt_ktap_results *ktap);
>  
> -void *igt_ktap_parser(void *unused);
> -
> -typedef struct ktap_test_results_element {
> -	char test_name[BUF_LEN + 1];
> -	bool passed;
> -	struct igt_list_head link;
> -} ktap_test_results_element;
> -
> -struct ktap_test_results {
> -	struct igt_list_head list;
> -	pthread_mutex_t mutex;
> -	bool still_running;
> -};
> -
> -
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> -void ktap_parser_cancel(void);
> -int ktap_parser_stop(void);
> -
>  #endif /* IGT_KTAP_H */

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
  2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-10 15:59     ` Kamil Konieczny
  -1 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-10 15:59 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> There was an attempt to parse KTAP reports in the background while a kunit
> test module is loading.  However, since dynamic sub-subtests can be
> executed only from the main thread, that attempt was not quite successful,
> as IGT results from all executed kunit test cases were generated only
> after loading of kunit test module completed.
> 
> Now that the parser maintains its state and we can call it separately for
> each input line of a KTAP report, it is perfectly possible to call the
> parser from the main thread while the module is loading in the background,
> and convert results from kunit test cases immediately to results of IGT
> dynamic sub-subtests by running an igt_dynamic() section for each result
> as soon as returned by the parser.
> 
> Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> result obtained from igt_ktap_parse() called from the main thread.
> 
> Also, drop no longer needed functions from igt_ktap soruces.
> 
> v3: Fix ktap structure not freed on lseek error,
>   - fix initial SIGCHLD handler not restored,
>   - fix missing handling of potential errors returned by sigaction,
>   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
>     synchronization with modprobe thread,
>   - fix potentially illegal use of igt_assert() called outside of
>     dynamic sub-subtest section,
>   - fix unsupported exit code potentially passed to igt_fail(),
>   - no need to fail a dynamic sub-subtest on potential KTAP parser error
>     after a valid result from the parser has been processed,
>   - fix trailing newlines missing from error messages,
>   - add more debug statements,
>   - integrate common code around kunit_result_free() into it.
> v2: Interrupt blocking read() on modprobe failure.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> ---
>  lib/igt_kmod.c | 261 +++++++++++++++++++----
>  lib/igt_ktap.c | 568 -------------------------------------------------
>  lib/igt_ktap.h |  22 --
>  3 files changed, 222 insertions(+), 629 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 426ae5b26f..7bca4cdaab 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright © 2016 Intel Corporation
> + * Copyright © 2016-2023 Intel Corporation
>   *
>   * Permission is hereby granted, free of charge, to any person obtaining a
>   * copy of this software and associated documentation files (the "Software"),
> @@ -26,7 +26,12 @@
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <pthread.h>
> +#include <stdlib.h>
> +#include <string.h>
>  #include <sys/utsname.h>
> +#include <unistd.h>
> +
> +#include "assembler/brw_compat.h"	/* [un]likely() */
------------ ^^^^^^^^^
Do we really need this?

>  
>  #include "igt_aux.h"
>  #include "igt_core.h"
> @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
>  }
>  
>  struct modprobe_data {
> +	pthread_t parent;
--- ^^^^^^^^^^^^^^^^
Please move it below to other related thread data.
Also consider a comment why(or for what purpose)
did you put this here.

>  	struct kmod_module *kmod;
>  	const char *opts;
>  	int err;
> +	pthread_mutex_t lock;
> +	pthread_t thread;
>  };
>  
>  static void *modprobe_task(void *arg)
> @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
>  
>  	data->err = modprobe(data->kmod, data->opts);
>  
> +	if (igt_debug_on(data->err)) {
> +		int err;
> +
> +		while (err = pthread_mutex_trylock(&data->lock),
> +		       err && !igt_debug_on(err != EBUSY))
> +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> +	} else {
> +		/* let main thread use mutex to detect modprobe completion */
> +		igt_debug_on(pthread_mutex_lock(&data->lock));
> +	}
> +
>  	return NULL;
>  }
>  
> +static void kunit_sigchld_handler(int signal)
> +{
> +	return;
--- ^^^^^^^
Why not removing this? checkpatch complains about return from void.

> +}
> +
> +static int kunit_kmsg_result_get(struct igt_list_head *results,
> +				 struct modprobe_data *modprobe,
> +				 int fd, struct igt_ktap_results *ktap)
> +{
> +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> +			 *saved;
> +	char record[BUF_LEN + 1], *buf;
> +	unsigned long taints;
> +	int ret;
> +
> +	do {
> +		int err;
> +
> +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> +			return -ENOTRECOVERABLE;
> +
> +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +		if (err == -1)
> +			return -errno;
> +		else if (unlikely(err))
> +			return err;
> +
> +		err = pthread_mutex_lock(&modprobe->lock);
> +		switch (err) {
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			__attribute__ ((fallthrough));
> +		case ENOTRECOVERABLE:
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			if (igt_debug_on(modprobe->err))
> +				return modprobe->err;

Why no 'return -err;' here?

> +			break;
> +		case 0:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			return -err;
> +		}
> +
> +		ret = read(fd, record, BUF_LEN);
> +
> +		if (!err) {
        ^^^^^^^^^^^
Looks strange here.

> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +		}
> +
> +		if (igt_debug_on(!ret))
> +			return -ENODATA;
> +		if (igt_debug_on(ret == -1))
> +			return -errno;
> +		if (unlikely(igt_debug_on(ret < 0)))
> +			break;
> +
> +		/* skip kmsg continuation lines */
> +		if (igt_debug_on(*record == ' '))
----------- ^^^^^^^^^^^^
Why debug here? imho it is not needed.

> +			continue;
> +
> +		/* NULL-terminate the record */
> +		record[ret] = '\0';
> +
> +		/* detect start of log message, continue if not found */
> +		buf = strchrnul(record, ';');
> +		if (igt_debug_on(*buf == '\0'))
----------- ^^^^^^^^^^^^
Same here, you just continue loop if no ';'?

> +			continue;
> +		buf++;
> +
> +		ret = igt_ktap_parse(buf, ktap);
> +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	} while (igt_list_empty(results));
> +
> +	return ret;
> +}
> +
> +static void kunit_result_free(struct igt_ktap_result **r,
> +			      char **suite_name, char **case_name)
> +{
> +	if (!*r)
> +		return;
> +
> +	igt_list_del(&(*r)->link);
> +
> +	if ((*r)->suite_name != *suite_name) {
> +		free(*suite_name);
> +		*suite_name = (*r)->suite_name;
> +	}
> +
> +	if ((*r)->case_name != *case_name) {
> +		free(*case_name);
> +		*case_name = (*r)->case_name;
> +	}
> +
> +	free((*r)->msg);
> +	free(*r);
> +	*r = NULL;
> +}
> +
>  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  {
> -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> -	struct kmod_module *kunit_kmod;
> -	bool is_builtin;
> -	struct ktap_test_results *results;
> -	pthread_t modprobe_thread;
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
>  	unsigned long taints;
>  	int flags, ret;
>  
> @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> -	kmod_module_unref(kunit_kmod);
> +	igt_skip_on(pthread_mutexattr_init(&attr));
> +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
>  
> -	results = ktap_parser_start(tst->kmsg, is_builtin);
> +	ktap = igt_ktap_alloc(&results);
> +	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
>  					modprobe_task, &modprobe))) {
> -		ktap_parser_cancel();
> -		igt_ignore_warn(ktap_parser_stop());
> +		igt_ktap_free(ktap);
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> -	{
> -		struct ktap_test_results_element *result;
> -
> -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> -			ktap_parser_cancel();
> +	do {
> +		ret = kunit_kmsg_result_get(&results, &modprobe,
> +					    tst->kmsg, ktap);
> +		if (igt_debug_on(ret && ret != -EINPROGRESS))
>  			break;
> -		}
>  
> -		if (igt_kernel_tainted(&taints)) {
> -			ktap_parser_cancel();
> -			pthread_cancel(modprobe_thread);
> +		if (igt_debug_on(igt_list_empty(&results)))
>  			break;
> -		}
>  
> -		pthread_mutex_lock(&results->mutex);
> -		if (igt_list_empty(&results->list)) {
> -			pthread_mutex_unlock(&results->mutex);
> -			continue;
> -		}
> +		r = igt_list_first_entry(&results, r, link);
>  
> -		result = igt_list_first_entry(&results->list, result, link);
> +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> +			if (r->code == IGT_EXIT_INVALID) {
> +				/* parametrized test case, get actual result */
> +				kunit_result_free(&r, &suite_name, &case_name);
>  
> -		igt_list_del(&result->link);
> -		pthread_mutex_unlock(&results->mutex);
> +				igt_assert(igt_list_empty(&results));
>  
> -		igt_dynamic(result->test_name) {
> -			igt_assert(READ_ONCE(result->passed));
> +				ret = kunit_kmsg_result_get(&results, &modprobe,
> +							    tst->kmsg, ktap);
> +				if (ret != -EINPROGRESS)
> +					igt_fail_on(ret);
> +
> +				igt_fail_on(igt_list_empty(&results));
> +
> +				r = igt_list_first_entry(&results, r, link);
> +
> +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      suite_name, r->suite_name);
> +				igt_fail_on_f(strcmp(r->case_name, case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      case_name, r->case_name);
> +			}
>  
> -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> +
> +			if (r->msg && *r->msg) {
> +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> +					      "%s\n", r->msg);
> +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> +					      "%s\n", r->msg);
> +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> +					      "%s\n", r->msg);
> +			} else {
> +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> +				if (r->code == IGT_EXIT_ABORT)
> +					igt_fail(r->code);
> +			}
> +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> +
> +			switch (pthread_mutex_lock(&modprobe.lock)) {
> +			case 0:
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				break;
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
>  				igt_assert_eq(modprobe.err, 0);
> +				break;

imho we should also break a do-while loop in case of error.

> +			default:
> +				igt_debug("pthread_mutex_lock() failed\n");
> +				break;
> +			}
>  
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		free(result);
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	} while (ret == -EINPROGRESS);
> +
> +	igt_list_for_each_entry_safe(r, rn, &results, link)
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	free(case_name);
> +	free(suite_name);
> +
> +	switch (pthread_mutex_lock(&modprobe.lock)) {
> +	case 0:
> +		igt_debug_on(pthread_cancel(modprobe.thread));
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
> +	case EOWNERDEAD:
> +		/* leave the mutex unrecoverable */
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		break;
> +	case ENOTRECOVERABLE:
> +		break;
> +	default:
> +		igt_debug("pthread_mutex_lock() failed\n");
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
>  	}
>  
> -	pthread_join(modprobe_thread, NULL);
> -
> -	ret = ktap_parser_stop();
> +	igt_ktap_free(ktap);
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 5eac102417..53a6c63288 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -4,17 +4,11 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> -#include <ctype.h>
> -#include <limits.h>
> -#include <libkmod.h>
> -#include <pthread.h>
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> -#include <unistd.h>
>  
> -#include "igt_aux.h"
>  #include "igt_core.h"
>  #include "igt_ktap.h"
>  #include "igt_list.h"
> @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
>  {
>  	free(ktap);
>  }
> -
> -#define DELIMITER "-"
> -
> -struct ktap_parser_args {
> -	int fd;
> -	bool is_builtin;
> -	int ret;
> -} ktap_args;
> -
> -static struct ktap_test_results results;
> -
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> -
> -/**
> - * log_to_end:
> - * @level: #igt_log_level
> - * @record: record to store the read data
> - * @format: format string
> - * @...: optional arguments used in the format string
> - *
> - * This is an altered version of the generic structured logging helper function
> - * igt_log capable of reading to the end of a given line.
> - *
> - * Returns: 0 for success, or -2 if there's an error reading from the file
> - */
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...)
> -{
> -	va_list args;
> -	const char *lend;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	char *cutoff = strchr(record, '\n');
> -	if (cutoff) {
> -		if (cutoff - record < BUF_LEN)
> -			cutoff[1] = '\0';
> -	}
> -
> -	va_start(args, format);
> -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> -	va_end(args);
> -
> -	lend = strchrnul(record, '\n');
> -	while (*lend == '\0') {
> -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -
> -		lend = strchrnul(record, '\n');
> -	}
> -	return 0;
> -}
> -
> -/**
> - * lookup_value:
> - * @haystack: the string to search in
> - * @needle: the string to search for
> - *
> - * Returns: the value of the needle in the haystack, or -1 if not found.
> - */
> -static long lookup_value(const char *haystack, const char *needle)
> -{
> -	const char *needle_rptr;
> -	char *needle_end;
> -	long num;
> -
> -	needle_rptr = strcasestr(haystack, needle);
> -
> -	if (needle_rptr == NULL)
> -		return -1;
> -
> -	/* Skip search string and whitespaces after it */
> -	needle_rptr += strlen(needle);
> -
> -	num = strtol(needle_rptr, &needle_end, 10);
> -
> -	if (needle_rptr == needle_end)
> -		return -1;
> -
> -	if (num == LONG_MIN || num == LONG_MAX)
> -		return 0;
> -
> -	return num > 0 ? num : 0;
> -}
> -
> -/**
> - * tap_version_present:
> - * @record: buffer with tap data
> - * @print_info: whether tap version should be printed or not
> - *
> - * Returns:
> - * 0 if not found
> - * 1 if found
> - */
> -static int tap_version_present(char* record, bool print_info)
> -{
> -	/*
> -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines

Could you save these two lines comment in some other place?

Regards,
Kamil

> -	 *
> -	 * but actually isn't, as it currently depends on the KUnit module
> -	 * being built-in, so we can't rely on it every time
> -	 */
> -	const char *version_rptr = strcasestr(record, "TAP version ");
> -	char *cutoff;
> -
> -	if (version_rptr == NULL)
> -		return 0;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	cutoff = strchr(version_rptr, '\n');
> -	if (cutoff)
> -		cutoff[0] = '\0';
> -
> -	if (print_info)
> -		igt_info("%s\n", version_rptr);
> -
> -	return 1;
> -}
> -
> -/**
> - * find_next_tap_subtest:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @is_builtin: whether KUnit is built-in or not
> - *
> - * Returns:
> - * 0 if there's missing information
> - * -1 if not found
> - * -2 if there are problems while reading the file.
> - * any other value corresponds to the amount of cases of the next (sub)test
> - */
> -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> -{
> -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> -	long test_count;
> -	char *cutoff;
> -
> -	test_name[0] = '\0';
> -	test_name[BUF_LEN] = '\0';
> -
> -	test_lookup_str = " subtest: ";
> -	subtest_lookup_str = " test: ";
> -
> -	if (!tap_version_present(record, true))
> -		return -1;
> -
> -	if (is_builtin) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -	}
> -
> -	name_rptr = strcasestr(record, test_lookup_str);
> -	if (name_rptr != NULL) {
> -		name_rptr += strlen(test_lookup_str);
> -	} else {
> -		name_rptr = strcasestr(record, subtest_lookup_str);
> -		if (name_rptr != NULL)
> -			name_rptr += strlen(subtest_lookup_str);
> -	}
> -
> -	if (name_rptr == NULL) {
> -		if (!is_builtin)
> -			/* We've probably found nothing */
> -			return -1;
> -		igt_info("Missing test name\n");
> -	} else {
> -		strncpy(test_name, name_rptr, BUF_LEN);
> -		/* Cutoff after newline character, in order to not display garbage */
> -		cutoff = strchr(test_name, '\n');
> -		if (cutoff)
> -			cutoff[0] = '\0';
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("unknown error reading kmsg (%m)\n");
> -
> -			return -2;
> -		}
> -
> -		/* Now we can be sure we found tests */
> -		if (!is_builtin)
> -			igt_info("KUnit is not built-in, skipping version check...\n");
> -	}
> -
> -	/*
> -	 * Total test count will almost always appear as 0..N at the beginning
> -	 * of a run, so we use it to reliably identify a new run
> -	 */
> -	test_count = lookup_value(record, "..");
> -
> -	if (test_count <= 0) {
> -		igt_info("Missing test count\n");
> -		if (test_name[0] == '\0')
> -			return 0;
> -		if (log_to_end(IGT_LOG_INFO, fd, record,
> -				"Running some tests in: %s\n",
> -				test_name) < 0)
> -			return -2;
> -		return 0;
> -	} else if (test_name[0] == '\0') {
> -		igt_info("Running %ld tests...\n", test_count);
> -		return 0;
> -	}
> -
> -	if (log_to_end(IGT_LOG_INFO, fd, record,
> -			"Executing %ld tests in: %s\n",
> -			test_count, test_name) < 0)
> -		return -2;
> -
> -	return test_count;
> -}
> -
> -/**
> - * parse_kmsg_for_tap:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @test_name: buffer to store the test name
> - *
> - * Returns:
> - * 1 if no results were found
> - * 0 if a test succeded
> - * -1 if a test failed
> - * -2 if there are problems reading the file
> - */
> -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> -{
> -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> -	char *test_name_end;
> -
> -	ok_lookup_str = "ok ";
> -	nok_lookup_str = "not ok ";
> -
> -	lstart = strchrnul(record, ';');
> -
> -	if (*lstart == '\0') {
> -		igt_warn("kmsg truncated: output malformed (%m)\n");
> -		return -2;
> -	}
> -
> -	lstart++;
> -	while (isspace(*lstart))
> -		lstart++;
> -
> -	nok_rptr = strstr(lstart, nok_lookup_str);
> -	if (nok_rptr != NULL) {
> -		nok_rptr += strlen(nok_lookup_str);
> -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> -			nok_rptr++;
> -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		if (log_to_end(IGT_LOG_WARN, fd, record,
> -			       "%s", lstart) < 0)
> -			return -2;
> -		return -1;
> -	}
> -
> -	comment_start = strchrnul(lstart, '#');
> -
> -	/* Check if we're still in a subtest */
> -	if (*comment_start != '\0') {
> -		comment_start++;
> -		value_parse_start = comment_start;
> -
> -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> -			if (log_to_end(IGT_LOG_WARN, fd, record,
> -				       "%s", lstart) < 0)
> -				return -2;
> -			return -1;
> -		}
> -	}
> -
> -	ok_rptr = strstr(lstart, ok_lookup_str);
> -	if (ok_rptr != NULL) {
> -		ok_rptr += strlen(ok_lookup_str);
> -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> -			ok_rptr++;
> -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		return 0;
> -	}
> -
> -	return 1;
> -}
> -
> -/**
> - * parse_tap_level:
> - * @fd: file descriptor
> - * @base_test_name: test_name from upper recursion level
> - * @test_count: test_count of this level
> - * @failed_tests: top level failed_tests pointer
> - * @found_tests: top level found_tests pointer
> - * @is_builtin: whether the KUnit module is built-in or not
> - *
> - * Returns:
> - * 0 if succeded
> - * -1 if error occurred
> - */
> -__maybe_unused
> -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> -			   bool *found_tests, bool is_builtin)
> -{
> -	char record[BUF_LEN + 1];
> -	struct ktap_test_results_element *r;
> -	int internal_test_count;
> -	char test_name[BUF_LEN + 1];
> -	char base_test_name_for_next_level[BUF_LEN + 1];
> -
> -	for (int i = 0; i < test_count; i++) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("error reading kmsg (%m)\n");
> -
> -			return -1;
> -		}
> -
> -		/* Sublevel found */
> -		if (tap_version_present(record, false))
> -		{
> -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> -								    is_builtin);
> -			switch (internal_test_count) {
> -			case -2:
> -				/* No more data to read */
> -				return -1;
> -			case -1:
> -				/* No test found */
> -				return -1;
> -			case 0:
> -				/* Tests found, but they're missing info */
> -				*found_tests = true;
> -				return -1;
> -			default:
> -				*found_tests = true;
> -
> -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> -				    base_test_name_for_next_level[0])
> -					strncat(base_test_name_for_next_level, DELIMITER,
> -						BUF_LEN - strlen(base_test_name_for_next_level));
> -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> -
> -				if (parse_tap_level(fd, base_test_name_for_next_level,
> -						    internal_test_count, failed_tests, found_tests,
> -						    is_builtin) == -1)
> -					return -1;
> -				break;
> -			}
> -		}
> -
> -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> -		case -2:
> -			return -1;
> -		case -1:
> -			*failed_tests = true;
> -
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = false;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		case 0:
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = true;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		default:
> -			break;
> -		}
> -	}
> -	return 0;
> -}
> -
> -/**
> - * igt_ktap_parser:
> - *
> - * This function parses the output of a ktap script and passes it to main thread.
> - */
> -void *igt_ktap_parser(void *unused)
> -{
> -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_results *ktap = NULL;
> -	int fd = ktap_args.fd;
> -	IGT_LIST_HEAD(list);
> -	int err;
> -
> -	ktap = igt_ktap_alloc(&list);
> -	if (igt_debug_on(!ktap))
> -		goto igt_ktap_parser_end;
> -
> -	while (err = read(fd, record, BUF_LEN), err > 0) {
> -		struct igt_ktap_result *r, *rn;
> -
> -		/* skip kmsg continuation lines */
> -		if (igt_debug_on(*record == ' '))
> -			continue;
> -
> -		/* NULL-terminate the record */
> -		record[err] = '\0';
> -
> -		/* detect start of log message, continue if not found */
> -		buf = strchrnul(record, ';');
> -		if (igt_debug_on(*buf == '\0'))
> -			continue;
> -		buf++;
> -
> -		err = igt_ktap_parse(buf, ktap);
> -
> -		/* parsing error */
> -		if (err && err != -EINPROGRESS)
> -			goto igt_ktap_parser_end;
> -
> -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> -			struct ktap_test_results_element *result = NULL;
> -			int code = r->code;
> -
> -			if (code != IGT_EXIT_INVALID)
> -				result = calloc(1, sizeof(*result));
> -
> -			if (result) {
> -				snprintf(result->test_name, sizeof(result->test_name),
> -					 "%s-%s", r->suite_name, r->case_name);
> -
> -				if (code == IGT_EXIT_SUCCESS)
> -					result->passed = true;
> -			}
> -
> -			igt_list_del(&r->link);
> -			if (r->suite_name != suite_name) {
> -				free(suite_name);
> -				suite_name = r->suite_name;
> -			}
> -			if (r->case_name != case_name) {
> -				free(case_name);
> -				case_name = r->case_name;
> -			}
> -			free(r->msg);
> -			free(r);
> -
> -			/*
> -			 * no extra result record expected on start
> -			 * of parametrized test case -- skip it
> -			 */
> -			if (code == IGT_EXIT_INVALID)
> -				continue;
> -
> -			if (!result) {
> -				err = -ENOMEM;
> -				goto igt_ktap_parser_end;
> -			}
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&result->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -		}
> -
> -		/* end of KTAP report */
> -		if (!err)
> -			goto igt_ktap_parser_end;
> -	}
> -
> -	if (err < 0) {
> -		if (errno == EPIPE)
> -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -		else
> -			igt_warn("error reading kmsg (%m)\n");
> -	}
> -
> -igt_ktap_parser_end:
> -	free(suite_name);
> -	free(case_name);
> -
> -	if (!err)
> -		ktap_args.ret = IGT_EXIT_SUCCESS;
> -
> -	results.still_running = false;
> -
> -	if (ktap)
> -		igt_ktap_free(ktap);
> -
> -	return NULL;
> -}
> -
> -static pthread_t ktap_parser_thread;
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> -{
> -	IGT_INIT_LIST_HEAD(&results.list);
> -	pthread_mutex_init(&results.mutex, NULL);
> -	results.still_running = true;
> -
> -	ktap_args.fd = fd;
> -	ktap_args.is_builtin = is_builtin;
> -	ktap_args.ret = IGT_EXIT_FAILURE;
> -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> -
> -	return &results;
> -}
> -
> -void ktap_parser_cancel(void)
> -{
> -	pthread_cancel(ktap_parser_thread);
> -}
> -
> -int ktap_parser_stop(void)
> -{
> -	pthread_join(ktap_parser_thread, NULL);
> -	return ktap_args.ret;
> -}
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index 6f8da3eab6..c422636bfc 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -27,8 +27,6 @@
>  
>  #define BUF_LEN 4096
>  
> -#include <pthread.h>
> -
>  #include "igt_list.h"
>  
>  struct igt_ktap_result {
> @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
>  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
>  void igt_ktap_free(struct igt_ktap_results *ktap);
>  
> -void *igt_ktap_parser(void *unused);
> -
> -typedef struct ktap_test_results_element {
> -	char test_name[BUF_LEN + 1];
> -	bool passed;
> -	struct igt_list_head link;
> -} ktap_test_results_element;
> -
> -struct ktap_test_results {
> -	struct igt_list_head list;
> -	pthread_mutex_t mutex;
> -	bool still_running;
> -};
> -
> -
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> -void ktap_parser_cancel(void);
> -int ktap_parser_stop(void);
> -
>  #endif /* IGT_KTAP_H */
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-10 15:59     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-10 15:59 UTC (permalink / raw)
  To: igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> There was an attempt to parse KTAP reports in the background while a kunit
> test module is loading.  However, since dynamic sub-subtests can be
> executed only from the main thread, that attempt was not quite successful,
> as IGT results from all executed kunit test cases were generated only
> after loading of kunit test module completed.
> 
> Now that the parser maintains its state and we can call it separately for
> each input line of a KTAP report, it is perfectly possible to call the
> parser from the main thread while the module is loading in the background,
> and convert results from kunit test cases immediately to results of IGT
> dynamic sub-subtests by running an igt_dynamic() section for each result
> as soon as returned by the parser.
> 
> Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> result obtained from igt_ktap_parse() called from the main thread.
> 
> Also, drop no longer needed functions from igt_ktap soruces.
> 
> v3: Fix ktap structure not freed on lseek error,
>   - fix initial SIGCHLD handler not restored,
>   - fix missing handling of potential errors returned by sigaction,
>   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
>     synchronization with modprobe thread,
>   - fix potentially illegal use of igt_assert() called outside of
>     dynamic sub-subtest section,
>   - fix unsupported exit code potentially passed to igt_fail(),
>   - no need to fail a dynamic sub-subtest on potential KTAP parser error
>     after a valid result from the parser has been processed,
>   - fix trailing newlines missing from error messages,
>   - add more debug statements,
>   - integrate common code around kunit_result_free() into it.
> v2: Interrupt blocking read() on modprobe failure.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> ---
>  lib/igt_kmod.c | 261 +++++++++++++++++++----
>  lib/igt_ktap.c | 568 -------------------------------------------------
>  lib/igt_ktap.h |  22 --
>  3 files changed, 222 insertions(+), 629 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 426ae5b26f..7bca4cdaab 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright © 2016 Intel Corporation
> + * Copyright © 2016-2023 Intel Corporation
>   *
>   * Permission is hereby granted, free of charge, to any person obtaining a
>   * copy of this software and associated documentation files (the "Software"),
> @@ -26,7 +26,12 @@
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <pthread.h>
> +#include <stdlib.h>
> +#include <string.h>
>  #include <sys/utsname.h>
> +#include <unistd.h>
> +
> +#include "assembler/brw_compat.h"	/* [un]likely() */
------------ ^^^^^^^^^
Do we really need this?

>  
>  #include "igt_aux.h"
>  #include "igt_core.h"
> @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
>  }
>  
>  struct modprobe_data {
> +	pthread_t parent;
--- ^^^^^^^^^^^^^^^^
Please move it below to other related thread data.
Also consider a comment why(or for what purpose)
did you put this here.

>  	struct kmod_module *kmod;
>  	const char *opts;
>  	int err;
> +	pthread_mutex_t lock;
> +	pthread_t thread;
>  };
>  
>  static void *modprobe_task(void *arg)
> @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
>  
>  	data->err = modprobe(data->kmod, data->opts);
>  
> +	if (igt_debug_on(data->err)) {
> +		int err;
> +
> +		while (err = pthread_mutex_trylock(&data->lock),
> +		       err && !igt_debug_on(err != EBUSY))
> +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> +	} else {
> +		/* let main thread use mutex to detect modprobe completion */
> +		igt_debug_on(pthread_mutex_lock(&data->lock));
> +	}
> +
>  	return NULL;
>  }
>  
> +static void kunit_sigchld_handler(int signal)
> +{
> +	return;
--- ^^^^^^^
Why not removing this? checkpatch complains about return from void.

> +}
> +
> +static int kunit_kmsg_result_get(struct igt_list_head *results,
> +				 struct modprobe_data *modprobe,
> +				 int fd, struct igt_ktap_results *ktap)
> +{
> +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> +			 *saved;
> +	char record[BUF_LEN + 1], *buf;
> +	unsigned long taints;
> +	int ret;
> +
> +	do {
> +		int err;
> +
> +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> +			return -ENOTRECOVERABLE;
> +
> +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +		if (err == -1)
> +			return -errno;
> +		else if (unlikely(err))
> +			return err;
> +
> +		err = pthread_mutex_lock(&modprobe->lock);
> +		switch (err) {
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			__attribute__ ((fallthrough));
> +		case ENOTRECOVERABLE:
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			if (igt_debug_on(modprobe->err))
> +				return modprobe->err;

Why no 'return -err;' here?

> +			break;
> +		case 0:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			return -err;
> +		}
> +
> +		ret = read(fd, record, BUF_LEN);
> +
> +		if (!err) {
        ^^^^^^^^^^^
Looks strange here.

> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +		}
> +
> +		if (igt_debug_on(!ret))
> +			return -ENODATA;
> +		if (igt_debug_on(ret == -1))
> +			return -errno;
> +		if (unlikely(igt_debug_on(ret < 0)))
> +			break;
> +
> +		/* skip kmsg continuation lines */
> +		if (igt_debug_on(*record == ' '))
----------- ^^^^^^^^^^^^
Why debug here? imho it is not needed.

> +			continue;
> +
> +		/* NULL-terminate the record */
> +		record[ret] = '\0';
> +
> +		/* detect start of log message, continue if not found */
> +		buf = strchrnul(record, ';');
> +		if (igt_debug_on(*buf == '\0'))
----------- ^^^^^^^^^^^^
Same here, you just continue loop if no ';'?

> +			continue;
> +		buf++;
> +
> +		ret = igt_ktap_parse(buf, ktap);
> +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	} while (igt_list_empty(results));
> +
> +	return ret;
> +}
> +
> +static void kunit_result_free(struct igt_ktap_result **r,
> +			      char **suite_name, char **case_name)
> +{
> +	if (!*r)
> +		return;
> +
> +	igt_list_del(&(*r)->link);
> +
> +	if ((*r)->suite_name != *suite_name) {
> +		free(*suite_name);
> +		*suite_name = (*r)->suite_name;
> +	}
> +
> +	if ((*r)->case_name != *case_name) {
> +		free(*case_name);
> +		*case_name = (*r)->case_name;
> +	}
> +
> +	free((*r)->msg);
> +	free(*r);
> +	*r = NULL;
> +}
> +
>  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  {
> -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> -	struct kmod_module *kunit_kmod;
> -	bool is_builtin;
> -	struct ktap_test_results *results;
> -	pthread_t modprobe_thread;
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
>  	unsigned long taints;
>  	int flags, ret;
>  
> @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> -	kmod_module_unref(kunit_kmod);
> +	igt_skip_on(pthread_mutexattr_init(&attr));
> +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
>  
> -	results = ktap_parser_start(tst->kmsg, is_builtin);
> +	ktap = igt_ktap_alloc(&results);
> +	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
>  					modprobe_task, &modprobe))) {
> -		ktap_parser_cancel();
> -		igt_ignore_warn(ktap_parser_stop());
> +		igt_ktap_free(ktap);
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> -	{
> -		struct ktap_test_results_element *result;
> -
> -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> -			ktap_parser_cancel();
> +	do {
> +		ret = kunit_kmsg_result_get(&results, &modprobe,
> +					    tst->kmsg, ktap);
> +		if (igt_debug_on(ret && ret != -EINPROGRESS))
>  			break;
> -		}
>  
> -		if (igt_kernel_tainted(&taints)) {
> -			ktap_parser_cancel();
> -			pthread_cancel(modprobe_thread);
> +		if (igt_debug_on(igt_list_empty(&results)))
>  			break;
> -		}
>  
> -		pthread_mutex_lock(&results->mutex);
> -		if (igt_list_empty(&results->list)) {
> -			pthread_mutex_unlock(&results->mutex);
> -			continue;
> -		}
> +		r = igt_list_first_entry(&results, r, link);
>  
> -		result = igt_list_first_entry(&results->list, result, link);
> +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> +			if (r->code == IGT_EXIT_INVALID) {
> +				/* parametrized test case, get actual result */
> +				kunit_result_free(&r, &suite_name, &case_name);
>  
> -		igt_list_del(&result->link);
> -		pthread_mutex_unlock(&results->mutex);
> +				igt_assert(igt_list_empty(&results));
>  
> -		igt_dynamic(result->test_name) {
> -			igt_assert(READ_ONCE(result->passed));
> +				ret = kunit_kmsg_result_get(&results, &modprobe,
> +							    tst->kmsg, ktap);
> +				if (ret != -EINPROGRESS)
> +					igt_fail_on(ret);
> +
> +				igt_fail_on(igt_list_empty(&results));
> +
> +				r = igt_list_first_entry(&results, r, link);
> +
> +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      suite_name, r->suite_name);
> +				igt_fail_on_f(strcmp(r->case_name, case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      case_name, r->case_name);
> +			}
>  
> -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> +
> +			if (r->msg && *r->msg) {
> +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> +					      "%s\n", r->msg);
> +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> +					      "%s\n", r->msg);
> +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> +					      "%s\n", r->msg);
> +			} else {
> +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> +				if (r->code == IGT_EXIT_ABORT)
> +					igt_fail(r->code);
> +			}
> +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> +
> +			switch (pthread_mutex_lock(&modprobe.lock)) {
> +			case 0:
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				break;
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
>  				igt_assert_eq(modprobe.err, 0);
> +				break;

imho we should also break a do-while loop in case of error.

> +			default:
> +				igt_debug("pthread_mutex_lock() failed\n");
> +				break;
> +			}
>  
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		free(result);
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	} while (ret == -EINPROGRESS);
> +
> +	igt_list_for_each_entry_safe(r, rn, &results, link)
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	free(case_name);
> +	free(suite_name);
> +
> +	switch (pthread_mutex_lock(&modprobe.lock)) {
> +	case 0:
> +		igt_debug_on(pthread_cancel(modprobe.thread));
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
> +	case EOWNERDEAD:
> +		/* leave the mutex unrecoverable */
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		break;
> +	case ENOTRECOVERABLE:
> +		break;
> +	default:
> +		igt_debug("pthread_mutex_lock() failed\n");
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
>  	}
>  
> -	pthread_join(modprobe_thread, NULL);
> -
> -	ret = ktap_parser_stop();
> +	igt_ktap_free(ktap);
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 5eac102417..53a6c63288 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -4,17 +4,11 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> -#include <ctype.h>
> -#include <limits.h>
> -#include <libkmod.h>
> -#include <pthread.h>
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> -#include <unistd.h>
>  
> -#include "igt_aux.h"
>  #include "igt_core.h"
>  #include "igt_ktap.h"
>  #include "igt_list.h"
> @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
>  {
>  	free(ktap);
>  }
> -
> -#define DELIMITER "-"
> -
> -struct ktap_parser_args {
> -	int fd;
> -	bool is_builtin;
> -	int ret;
> -} ktap_args;
> -
> -static struct ktap_test_results results;
> -
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> -
> -/**
> - * log_to_end:
> - * @level: #igt_log_level
> - * @record: record to store the read data
> - * @format: format string
> - * @...: optional arguments used in the format string
> - *
> - * This is an altered version of the generic structured logging helper function
> - * igt_log capable of reading to the end of a given line.
> - *
> - * Returns: 0 for success, or -2 if there's an error reading from the file
> - */
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...)
> -{
> -	va_list args;
> -	const char *lend;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	char *cutoff = strchr(record, '\n');
> -	if (cutoff) {
> -		if (cutoff - record < BUF_LEN)
> -			cutoff[1] = '\0';
> -	}
> -
> -	va_start(args, format);
> -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> -	va_end(args);
> -
> -	lend = strchrnul(record, '\n');
> -	while (*lend == '\0') {
> -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -
> -		lend = strchrnul(record, '\n');
> -	}
> -	return 0;
> -}
> -
> -/**
> - * lookup_value:
> - * @haystack: the string to search in
> - * @needle: the string to search for
> - *
> - * Returns: the value of the needle in the haystack, or -1 if not found.
> - */
> -static long lookup_value(const char *haystack, const char *needle)
> -{
> -	const char *needle_rptr;
> -	char *needle_end;
> -	long num;
> -
> -	needle_rptr = strcasestr(haystack, needle);
> -
> -	if (needle_rptr == NULL)
> -		return -1;
> -
> -	/* Skip search string and whitespaces after it */
> -	needle_rptr += strlen(needle);
> -
> -	num = strtol(needle_rptr, &needle_end, 10);
> -
> -	if (needle_rptr == needle_end)
> -		return -1;
> -
> -	if (num == LONG_MIN || num == LONG_MAX)
> -		return 0;
> -
> -	return num > 0 ? num : 0;
> -}
> -
> -/**
> - * tap_version_present:
> - * @record: buffer with tap data
> - * @print_info: whether tap version should be printed or not
> - *
> - * Returns:
> - * 0 if not found
> - * 1 if found
> - */
> -static int tap_version_present(char* record, bool print_info)
> -{
> -	/*
> -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines

Could you save these two lines comment in some other place?

Regards,
Kamil

> -	 *
> -	 * but actually isn't, as it currently depends on the KUnit module
> -	 * being built-in, so we can't rely on it every time
> -	 */
> -	const char *version_rptr = strcasestr(record, "TAP version ");
> -	char *cutoff;
> -
> -	if (version_rptr == NULL)
> -		return 0;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	cutoff = strchr(version_rptr, '\n');
> -	if (cutoff)
> -		cutoff[0] = '\0';
> -
> -	if (print_info)
> -		igt_info("%s\n", version_rptr);
> -
> -	return 1;
> -}
> -
> -/**
> - * find_next_tap_subtest:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @is_builtin: whether KUnit is built-in or not
> - *
> - * Returns:
> - * 0 if there's missing information
> - * -1 if not found
> - * -2 if there are problems while reading the file.
> - * any other value corresponds to the amount of cases of the next (sub)test
> - */
> -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> -{
> -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> -	long test_count;
> -	char *cutoff;
> -
> -	test_name[0] = '\0';
> -	test_name[BUF_LEN] = '\0';
> -
> -	test_lookup_str = " subtest: ";
> -	subtest_lookup_str = " test: ";
> -
> -	if (!tap_version_present(record, true))
> -		return -1;
> -
> -	if (is_builtin) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -	}
> -
> -	name_rptr = strcasestr(record, test_lookup_str);
> -	if (name_rptr != NULL) {
> -		name_rptr += strlen(test_lookup_str);
> -	} else {
> -		name_rptr = strcasestr(record, subtest_lookup_str);
> -		if (name_rptr != NULL)
> -			name_rptr += strlen(subtest_lookup_str);
> -	}
> -
> -	if (name_rptr == NULL) {
> -		if (!is_builtin)
> -			/* We've probably found nothing */
> -			return -1;
> -		igt_info("Missing test name\n");
> -	} else {
> -		strncpy(test_name, name_rptr, BUF_LEN);
> -		/* Cutoff after newline character, in order to not display garbage */
> -		cutoff = strchr(test_name, '\n');
> -		if (cutoff)
> -			cutoff[0] = '\0';
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("unknown error reading kmsg (%m)\n");
> -
> -			return -2;
> -		}
> -
> -		/* Now we can be sure we found tests */
> -		if (!is_builtin)
> -			igt_info("KUnit is not built-in, skipping version check...\n");
> -	}
> -
> -	/*
> -	 * Total test count will almost always appear as 0..N at the beginning
> -	 * of a run, so we use it to reliably identify a new run
> -	 */
> -	test_count = lookup_value(record, "..");
> -
> -	if (test_count <= 0) {
> -		igt_info("Missing test count\n");
> -		if (test_name[0] == '\0')
> -			return 0;
> -		if (log_to_end(IGT_LOG_INFO, fd, record,
> -				"Running some tests in: %s\n",
> -				test_name) < 0)
> -			return -2;
> -		return 0;
> -	} else if (test_name[0] == '\0') {
> -		igt_info("Running %ld tests...\n", test_count);
> -		return 0;
> -	}
> -
> -	if (log_to_end(IGT_LOG_INFO, fd, record,
> -			"Executing %ld tests in: %s\n",
> -			test_count, test_name) < 0)
> -		return -2;
> -
> -	return test_count;
> -}
> -
> -/**
> - * parse_kmsg_for_tap:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @test_name: buffer to store the test name
> - *
> - * Returns:
> - * 1 if no results were found
> - * 0 if a test succeded
> - * -1 if a test failed
> - * -2 if there are problems reading the file
> - */
> -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> -{
> -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> -	char *test_name_end;
> -
> -	ok_lookup_str = "ok ";
> -	nok_lookup_str = "not ok ";
> -
> -	lstart = strchrnul(record, ';');
> -
> -	if (*lstart == '\0') {
> -		igt_warn("kmsg truncated: output malformed (%m)\n");
> -		return -2;
> -	}
> -
> -	lstart++;
> -	while (isspace(*lstart))
> -		lstart++;
> -
> -	nok_rptr = strstr(lstart, nok_lookup_str);
> -	if (nok_rptr != NULL) {
> -		nok_rptr += strlen(nok_lookup_str);
> -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> -			nok_rptr++;
> -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		if (log_to_end(IGT_LOG_WARN, fd, record,
> -			       "%s", lstart) < 0)
> -			return -2;
> -		return -1;
> -	}
> -
> -	comment_start = strchrnul(lstart, '#');
> -
> -	/* Check if we're still in a subtest */
> -	if (*comment_start != '\0') {
> -		comment_start++;
> -		value_parse_start = comment_start;
> -
> -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> -			if (log_to_end(IGT_LOG_WARN, fd, record,
> -				       "%s", lstart) < 0)
> -				return -2;
> -			return -1;
> -		}
> -	}
> -
> -	ok_rptr = strstr(lstart, ok_lookup_str);
> -	if (ok_rptr != NULL) {
> -		ok_rptr += strlen(ok_lookup_str);
> -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> -			ok_rptr++;
> -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		return 0;
> -	}
> -
> -	return 1;
> -}
> -
> -/**
> - * parse_tap_level:
> - * @fd: file descriptor
> - * @base_test_name: test_name from upper recursion level
> - * @test_count: test_count of this level
> - * @failed_tests: top level failed_tests pointer
> - * @found_tests: top level found_tests pointer
> - * @is_builtin: whether the KUnit module is built-in or not
> - *
> - * Returns:
> - * 0 if succeded
> - * -1 if error occurred
> - */
> -__maybe_unused
> -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> -			   bool *found_tests, bool is_builtin)
> -{
> -	char record[BUF_LEN + 1];
> -	struct ktap_test_results_element *r;
> -	int internal_test_count;
> -	char test_name[BUF_LEN + 1];
> -	char base_test_name_for_next_level[BUF_LEN + 1];
> -
> -	for (int i = 0; i < test_count; i++) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("error reading kmsg (%m)\n");
> -
> -			return -1;
> -		}
> -
> -		/* Sublevel found */
> -		if (tap_version_present(record, false))
> -		{
> -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> -								    is_builtin);
> -			switch (internal_test_count) {
> -			case -2:
> -				/* No more data to read */
> -				return -1;
> -			case -1:
> -				/* No test found */
> -				return -1;
> -			case 0:
> -				/* Tests found, but they're missing info */
> -				*found_tests = true;
> -				return -1;
> -			default:
> -				*found_tests = true;
> -
> -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> -				    base_test_name_for_next_level[0])
> -					strncat(base_test_name_for_next_level, DELIMITER,
> -						BUF_LEN - strlen(base_test_name_for_next_level));
> -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> -
> -				if (parse_tap_level(fd, base_test_name_for_next_level,
> -						    internal_test_count, failed_tests, found_tests,
> -						    is_builtin) == -1)
> -					return -1;
> -				break;
> -			}
> -		}
> -
> -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> -		case -2:
> -			return -1;
> -		case -1:
> -			*failed_tests = true;
> -
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = false;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		case 0:
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = true;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		default:
> -			break;
> -		}
> -	}
> -	return 0;
> -}
> -
> -/**
> - * igt_ktap_parser:
> - *
> - * This function parses the output of a ktap script and passes it to main thread.
> - */
> -void *igt_ktap_parser(void *unused)
> -{
> -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_results *ktap = NULL;
> -	int fd = ktap_args.fd;
> -	IGT_LIST_HEAD(list);
> -	int err;
> -
> -	ktap = igt_ktap_alloc(&list);
> -	if (igt_debug_on(!ktap))
> -		goto igt_ktap_parser_end;
> -
> -	while (err = read(fd, record, BUF_LEN), err > 0) {
> -		struct igt_ktap_result *r, *rn;
> -
> -		/* skip kmsg continuation lines */
> -		if (igt_debug_on(*record == ' '))
> -			continue;
> -
> -		/* NULL-terminate the record */
> -		record[err] = '\0';
> -
> -		/* detect start of log message, continue if not found */
> -		buf = strchrnul(record, ';');
> -		if (igt_debug_on(*buf == '\0'))
> -			continue;
> -		buf++;
> -
> -		err = igt_ktap_parse(buf, ktap);
> -
> -		/* parsing error */
> -		if (err && err != -EINPROGRESS)
> -			goto igt_ktap_parser_end;
> -
> -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> -			struct ktap_test_results_element *result = NULL;
> -			int code = r->code;
> -
> -			if (code != IGT_EXIT_INVALID)
> -				result = calloc(1, sizeof(*result));
> -
> -			if (result) {
> -				snprintf(result->test_name, sizeof(result->test_name),
> -					 "%s-%s", r->suite_name, r->case_name);
> -
> -				if (code == IGT_EXIT_SUCCESS)
> -					result->passed = true;
> -			}
> -
> -			igt_list_del(&r->link);
> -			if (r->suite_name != suite_name) {
> -				free(suite_name);
> -				suite_name = r->suite_name;
> -			}
> -			if (r->case_name != case_name) {
> -				free(case_name);
> -				case_name = r->case_name;
> -			}
> -			free(r->msg);
> -			free(r);
> -
> -			/*
> -			 * no extra result record expected on start
> -			 * of parametrized test case -- skip it
> -			 */
> -			if (code == IGT_EXIT_INVALID)
> -				continue;
> -
> -			if (!result) {
> -				err = -ENOMEM;
> -				goto igt_ktap_parser_end;
> -			}
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&result->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -		}
> -
> -		/* end of KTAP report */
> -		if (!err)
> -			goto igt_ktap_parser_end;
> -	}
> -
> -	if (err < 0) {
> -		if (errno == EPIPE)
> -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -		else
> -			igt_warn("error reading kmsg (%m)\n");
> -	}
> -
> -igt_ktap_parser_end:
> -	free(suite_name);
> -	free(case_name);
> -
> -	if (!err)
> -		ktap_args.ret = IGT_EXIT_SUCCESS;
> -
> -	results.still_running = false;
> -
> -	if (ktap)
> -		igt_ktap_free(ktap);
> -
> -	return NULL;
> -}
> -
> -static pthread_t ktap_parser_thread;
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> -{
> -	IGT_INIT_LIST_HEAD(&results.list);
> -	pthread_mutex_init(&results.mutex, NULL);
> -	results.still_running = true;
> -
> -	ktap_args.fd = fd;
> -	ktap_args.is_builtin = is_builtin;
> -	ktap_args.ret = IGT_EXIT_FAILURE;
> -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> -
> -	return &results;
> -}
> -
> -void ktap_parser_cancel(void)
> -{
> -	pthread_cancel(ktap_parser_thread);
> -}
> -
> -int ktap_parser_stop(void)
> -{
> -	pthread_join(ktap_parser_thread, NULL);
> -	return ktap_args.ret;
> -}
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index 6f8da3eab6..c422636bfc 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -27,8 +27,6 @@
>  
>  #define BUF_LEN 4096
>  
> -#include <pthread.h>
> -
>  #include "igt_list.h"
>  
>  struct igt_ktap_result {
> @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
>  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
>  void igt_ktap_free(struct igt_ktap_results *ktap);
>  
> -void *igt_ktap_parser(void *unused);
> -
> -typedef struct ktap_test_results_element {
> -	char test_name[BUF_LEN + 1];
> -	bool passed;
> -	struct igt_list_head link;
> -} ktap_test_results_element;
> -
> -struct ktap_test_results {
> -	struct igt_list_head list;
> -	pthread_mutex_t mutex;
> -	bool still_running;
> -};
> -
> -
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> -void ktap_parser_cancel(void);
> -int ktap_parser_stop(void);
> -
>  #endif /* IGT_KTAP_H */
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-10 15:59     ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-10 15:59 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Hi Janusz,
On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> There was an attempt to parse KTAP reports in the background while a kunit
> test module is loading.  However, since dynamic sub-subtests can be
> executed only from the main thread, that attempt was not quite successful,
> as IGT results from all executed kunit test cases were generated only
> after loading of kunit test module completed.
> 
> Now that the parser maintains its state and we can call it separately for
> each input line of a KTAP report, it is perfectly possible to call the
> parser from the main thread while the module is loading in the background,
> and convert results from kunit test cases immediately to results of IGT
> dynamic sub-subtests by running an igt_dynamic() section for each result
> as soon as returned by the parser.
> 
> Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> result obtained from igt_ktap_parse() called from the main thread.
> 
> Also, drop no longer needed functions from igt_ktap soruces.
> 
> v3: Fix ktap structure not freed on lseek error,
>   - fix initial SIGCHLD handler not restored,
>   - fix missing handling of potential errors returned by sigaction,
>   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
>     synchronization with modprobe thread,
>   - fix potentially illegal use of igt_assert() called outside of
>     dynamic sub-subtest section,
>   - fix unsupported exit code potentially passed to igt_fail(),
>   - no need to fail a dynamic sub-subtest on potential KTAP parser error
>     after a valid result from the parser has been processed,
>   - fix trailing newlines missing from error messages,
>   - add more debug statements,
>   - integrate common code around kunit_result_free() into it.
> v2: Interrupt blocking read() on modprobe failure.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> ---
>  lib/igt_kmod.c | 261 +++++++++++++++++++----
>  lib/igt_ktap.c | 568 -------------------------------------------------
>  lib/igt_ktap.h |  22 --
>  3 files changed, 222 insertions(+), 629 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index 426ae5b26f..7bca4cdaab 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright © 2016 Intel Corporation
> + * Copyright © 2016-2023 Intel Corporation
>   *
>   * Permission is hereby granted, free of charge, to any person obtaining a
>   * copy of this software and associated documentation files (the "Software"),
> @@ -26,7 +26,12 @@
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <pthread.h>
> +#include <stdlib.h>
> +#include <string.h>
>  #include <sys/utsname.h>
> +#include <unistd.h>
> +
> +#include "assembler/brw_compat.h"	/* [un]likely() */
------------ ^^^^^^^^^
Do we really need this?

>  
>  #include "igt_aux.h"
>  #include "igt_core.h"
> @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
>  }
>  
>  struct modprobe_data {
> +	pthread_t parent;
--- ^^^^^^^^^^^^^^^^
Please move it below to other related thread data.
Also consider a comment why(or for what purpose)
did you put this here.

>  	struct kmod_module *kmod;
>  	const char *opts;
>  	int err;
> +	pthread_mutex_t lock;
> +	pthread_t thread;
>  };
>  
>  static void *modprobe_task(void *arg)
> @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
>  
>  	data->err = modprobe(data->kmod, data->opts);
>  
> +	if (igt_debug_on(data->err)) {
> +		int err;
> +
> +		while (err = pthread_mutex_trylock(&data->lock),
> +		       err && !igt_debug_on(err != EBUSY))
> +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> +	} else {
> +		/* let main thread use mutex to detect modprobe completion */
> +		igt_debug_on(pthread_mutex_lock(&data->lock));
> +	}
> +
>  	return NULL;
>  }
>  
> +static void kunit_sigchld_handler(int signal)
> +{
> +	return;
--- ^^^^^^^
Why not removing this? checkpatch complains about return from void.

> +}
> +
> +static int kunit_kmsg_result_get(struct igt_list_head *results,
> +				 struct modprobe_data *modprobe,
> +				 int fd, struct igt_ktap_results *ktap)
> +{
> +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> +			 *saved;
> +	char record[BUF_LEN + 1], *buf;
> +	unsigned long taints;
> +	int ret;
> +
> +	do {
> +		int err;
> +
> +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> +			return -ENOTRECOVERABLE;
> +
> +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> +		if (err == -1)
> +			return -errno;
> +		else if (unlikely(err))
> +			return err;
> +
> +		err = pthread_mutex_lock(&modprobe->lock);
> +		switch (err) {
> +		case EOWNERDEAD:
> +			/* leave the mutex unrecoverable */
> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			__attribute__ ((fallthrough));
> +		case ENOTRECOVERABLE:
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			if (igt_debug_on(modprobe->err))
> +				return modprobe->err;

Why no 'return -err;' here?

> +			break;
> +		case 0:
> +			break;
> +		default:
> +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +			return -err;
> +		}
> +
> +		ret = read(fd, record, BUF_LEN);
> +
> +		if (!err) {
        ^^^^^^^^^^^
Looks strange here.

> +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> +		}
> +
> +		if (igt_debug_on(!ret))
> +			return -ENODATA;
> +		if (igt_debug_on(ret == -1))
> +			return -errno;
> +		if (unlikely(igt_debug_on(ret < 0)))
> +			break;
> +
> +		/* skip kmsg continuation lines */
> +		if (igt_debug_on(*record == ' '))
----------- ^^^^^^^^^^^^
Why debug here? imho it is not needed.

> +			continue;
> +
> +		/* NULL-terminate the record */
> +		record[ret] = '\0';
> +
> +		/* detect start of log message, continue if not found */
> +		buf = strchrnul(record, ';');
> +		if (igt_debug_on(*buf == '\0'))
----------- ^^^^^^^^^^^^
Same here, you just continue loop if no ';'?

> +			continue;
> +		buf++;
> +
> +		ret = igt_ktap_parse(buf, ktap);
> +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> +			break;
> +	} while (igt_list_empty(results));
> +
> +	return ret;
> +}
> +
> +static void kunit_result_free(struct igt_ktap_result **r,
> +			      char **suite_name, char **case_name)
> +{
> +	if (!*r)
> +		return;
> +
> +	igt_list_del(&(*r)->link);
> +
> +	if ((*r)->suite_name != *suite_name) {
> +		free(*suite_name);
> +		*suite_name = (*r)->suite_name;
> +	}
> +
> +	if ((*r)->case_name != *case_name) {
> +		free(*case_name);
> +		*case_name = (*r)->case_name;
> +	}
> +
> +	free((*r)->msg);
> +	free(*r);
> +	*r = NULL;
> +}
> +
>  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  {
> -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> -	struct kmod_module *kunit_kmod;
> -	bool is_builtin;
> -	struct ktap_test_results *results;
> -	pthread_t modprobe_thread;
> +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> +	char *suite_name = NULL, *case_name = NULL;
> +	struct igt_ktap_result *r, *rn;
> +	struct igt_ktap_results *ktap;
> +	pthread_mutexattr_t attr;
> +	IGT_LIST_HEAD(results);
>  	unsigned long taints;
>  	int flags, ret;
>  
> @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
>  
>  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
>  
> -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> -	kmod_module_unref(kunit_kmod);
> +	igt_skip_on(pthread_mutexattr_init(&attr));
> +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
>  
> -	results = ktap_parser_start(tst->kmsg, is_builtin);
> +	ktap = igt_ktap_alloc(&results);
> +	igt_require(ktap);
>  
> -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
>  					modprobe_task, &modprobe))) {
> -		ktap_parser_cancel();
> -		igt_ignore_warn(ktap_parser_stop());
> +		igt_ktap_free(ktap);
>  		igt_skip("Failed to create a modprobe thread\n");
>  	}
>  
> -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> -	{
> -		struct ktap_test_results_element *result;
> -
> -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> -			ktap_parser_cancel();
> +	do {
> +		ret = kunit_kmsg_result_get(&results, &modprobe,
> +					    tst->kmsg, ktap);
> +		if (igt_debug_on(ret && ret != -EINPROGRESS))
>  			break;
> -		}
>  
> -		if (igt_kernel_tainted(&taints)) {
> -			ktap_parser_cancel();
> -			pthread_cancel(modprobe_thread);
> +		if (igt_debug_on(igt_list_empty(&results)))
>  			break;
> -		}
>  
> -		pthread_mutex_lock(&results->mutex);
> -		if (igt_list_empty(&results->list)) {
> -			pthread_mutex_unlock(&results->mutex);
> -			continue;
> -		}
> +		r = igt_list_first_entry(&results, r, link);
>  
> -		result = igt_list_first_entry(&results->list, result, link);
> +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> +			if (r->code == IGT_EXIT_INVALID) {
> +				/* parametrized test case, get actual result */
> +				kunit_result_free(&r, &suite_name, &case_name);
>  
> -		igt_list_del(&result->link);
> -		pthread_mutex_unlock(&results->mutex);
> +				igt_assert(igt_list_empty(&results));
>  
> -		igt_dynamic(result->test_name) {
> -			igt_assert(READ_ONCE(result->passed));
> +				ret = kunit_kmsg_result_get(&results, &modprobe,
> +							    tst->kmsg, ktap);
> +				if (ret != -EINPROGRESS)
> +					igt_fail_on(ret);
> +
> +				igt_fail_on(igt_list_empty(&results));
> +
> +				r = igt_list_first_entry(&results, r, link);
> +
> +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> +					      "suite_name expected: %s, got: %s\n",
> +					      suite_name, r->suite_name);
> +				igt_fail_on_f(strcmp(r->case_name, case_name),
> +					      "case_name expected: %s, got: %s\n",
> +					      case_name, r->case_name);
> +			}
>  
> -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> +
> +			if (r->msg && *r->msg) {
> +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> +					      "%s\n", r->msg);
> +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> +					      "%s\n", r->msg);
> +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> +					      "%s\n", r->msg);
> +			} else {
> +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> +				if (r->code == IGT_EXIT_ABORT)
> +					igt_fail(r->code);
> +			}
> +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> +
> +			switch (pthread_mutex_lock(&modprobe.lock)) {
> +			case 0:
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				break;
> +			case EOWNERDEAD:
> +				/* leave the mutex unrecoverable */
> +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +				__attribute__ ((fallthrough));
> +			case ENOTRECOVERABLE:
>  				igt_assert_eq(modprobe.err, 0);
> +				break;

imho we should also break a do-while loop in case of error.

> +			default:
> +				igt_debug("pthread_mutex_lock() failed\n");
> +				break;
> +			}
>  
>  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
>  		}
>  
> -		free(result);
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	} while (ret == -EINPROGRESS);
> +
> +	igt_list_for_each_entry_safe(r, rn, &results, link)
> +		kunit_result_free(&r, &suite_name, &case_name);
> +
> +	free(case_name);
> +	free(suite_name);
> +
> +	switch (pthread_mutex_lock(&modprobe.lock)) {
> +	case 0:
> +		igt_debug_on(pthread_cancel(modprobe.thread));
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
> +	case EOWNERDEAD:
> +		/* leave the mutex unrecoverable */
> +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> +		break;
> +	case ENOTRECOVERABLE:
> +		break;
> +	default:
> +		igt_debug("pthread_mutex_lock() failed\n");
> +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> +		break;
>  	}
>  
> -	pthread_join(modprobe_thread, NULL);
> -
> -	ret = ktap_parser_stop();
> +	igt_ktap_free(ktap);
>  
>  	igt_skip_on(modprobe.err);
>  	igt_skip_on(igt_kernel_tainted(&taints));
> diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> index 5eac102417..53a6c63288 100644
> --- a/lib/igt_ktap.c
> +++ b/lib/igt_ktap.c
> @@ -4,17 +4,11 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> -#include <ctype.h>
> -#include <limits.h>
> -#include <libkmod.h>
> -#include <pthread.h>
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> -#include <unistd.h>
>  
> -#include "igt_aux.h"
>  #include "igt_core.h"
>  #include "igt_ktap.h"
>  #include "igt_list.h"
> @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
>  {
>  	free(ktap);
>  }
> -
> -#define DELIMITER "-"
> -
> -struct ktap_parser_args {
> -	int fd;
> -	bool is_builtin;
> -	int ret;
> -} ktap_args;
> -
> -static struct ktap_test_results results;
> -
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> -
> -/**
> - * log_to_end:
> - * @level: #igt_log_level
> - * @record: record to store the read data
> - * @format: format string
> - * @...: optional arguments used in the format string
> - *
> - * This is an altered version of the generic structured logging helper function
> - * igt_log capable of reading to the end of a given line.
> - *
> - * Returns: 0 for success, or -2 if there's an error reading from the file
> - */
> -static int log_to_end(enum igt_log_level level, int fd,
> -		      char *record, const char *format, ...)
> -{
> -	va_list args;
> -	const char *lend;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	char *cutoff = strchr(record, '\n');
> -	if (cutoff) {
> -		if (cutoff - record < BUF_LEN)
> -			cutoff[1] = '\0';
> -	}
> -
> -	va_start(args, format);
> -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> -	va_end(args);
> -
> -	lend = strchrnul(record, '\n');
> -	while (*lend == '\0') {
> -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -
> -		lend = strchrnul(record, '\n');
> -	}
> -	return 0;
> -}
> -
> -/**
> - * lookup_value:
> - * @haystack: the string to search in
> - * @needle: the string to search for
> - *
> - * Returns: the value of the needle in the haystack, or -1 if not found.
> - */
> -static long lookup_value(const char *haystack, const char *needle)
> -{
> -	const char *needle_rptr;
> -	char *needle_end;
> -	long num;
> -
> -	needle_rptr = strcasestr(haystack, needle);
> -
> -	if (needle_rptr == NULL)
> -		return -1;
> -
> -	/* Skip search string and whitespaces after it */
> -	needle_rptr += strlen(needle);
> -
> -	num = strtol(needle_rptr, &needle_end, 10);
> -
> -	if (needle_rptr == needle_end)
> -		return -1;
> -
> -	if (num == LONG_MIN || num == LONG_MAX)
> -		return 0;
> -
> -	return num > 0 ? num : 0;
> -}
> -
> -/**
> - * tap_version_present:
> - * @record: buffer with tap data
> - * @print_info: whether tap version should be printed or not
> - *
> - * Returns:
> - * 0 if not found
> - * 1 if found
> - */
> -static int tap_version_present(char* record, bool print_info)
> -{
> -	/*
> -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines

Could you save these two lines comment in some other place?

Regards,
Kamil

> -	 *
> -	 * but actually isn't, as it currently depends on the KUnit module
> -	 * being built-in, so we can't rely on it every time
> -	 */
> -	const char *version_rptr = strcasestr(record, "TAP version ");
> -	char *cutoff;
> -
> -	if (version_rptr == NULL)
> -		return 0;
> -
> -	/* Cutoff after newline character, in order to not display garbage */
> -	cutoff = strchr(version_rptr, '\n');
> -	if (cutoff)
> -		cutoff[0] = '\0';
> -
> -	if (print_info)
> -		igt_info("%s\n", version_rptr);
> -
> -	return 1;
> -}
> -
> -/**
> - * find_next_tap_subtest:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @is_builtin: whether KUnit is built-in or not
> - *
> - * Returns:
> - * 0 if there's missing information
> - * -1 if not found
> - * -2 if there are problems while reading the file.
> - * any other value corresponds to the amount of cases of the next (sub)test
> - */
> -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> -{
> -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> -	long test_count;
> -	char *cutoff;
> -
> -	test_name[0] = '\0';
> -	test_name[BUF_LEN] = '\0';
> -
> -	test_lookup_str = " subtest: ";
> -	subtest_lookup_str = " test: ";
> -
> -	if (!tap_version_present(record, true))
> -		return -1;
> -
> -	if (is_builtin) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("an error occurred while reading kmsg: %m\n");
> -
> -			return -2;
> -		}
> -	}
> -
> -	name_rptr = strcasestr(record, test_lookup_str);
> -	if (name_rptr != NULL) {
> -		name_rptr += strlen(test_lookup_str);
> -	} else {
> -		name_rptr = strcasestr(record, subtest_lookup_str);
> -		if (name_rptr != NULL)
> -			name_rptr += strlen(subtest_lookup_str);
> -	}
> -
> -	if (name_rptr == NULL) {
> -		if (!is_builtin)
> -			/* We've probably found nothing */
> -			return -1;
> -		igt_info("Missing test name\n");
> -	} else {
> -		strncpy(test_name, name_rptr, BUF_LEN);
> -		/* Cutoff after newline character, in order to not display garbage */
> -		cutoff = strchr(test_name, '\n');
> -		if (cutoff)
> -			cutoff[0] = '\0';
> -
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("unknown error reading kmsg (%m)\n");
> -
> -			return -2;
> -		}
> -
> -		/* Now we can be sure we found tests */
> -		if (!is_builtin)
> -			igt_info("KUnit is not built-in, skipping version check...\n");
> -	}
> -
> -	/*
> -	 * Total test count will almost always appear as 0..N at the beginning
> -	 * of a run, so we use it to reliably identify a new run
> -	 */
> -	test_count = lookup_value(record, "..");
> -
> -	if (test_count <= 0) {
> -		igt_info("Missing test count\n");
> -		if (test_name[0] == '\0')
> -			return 0;
> -		if (log_to_end(IGT_LOG_INFO, fd, record,
> -				"Running some tests in: %s\n",
> -				test_name) < 0)
> -			return -2;
> -		return 0;
> -	} else if (test_name[0] == '\0') {
> -		igt_info("Running %ld tests...\n", test_count);
> -		return 0;
> -	}
> -
> -	if (log_to_end(IGT_LOG_INFO, fd, record,
> -			"Executing %ld tests in: %s\n",
> -			test_count, test_name) < 0)
> -		return -2;
> -
> -	return test_count;
> -}
> -
> -/**
> - * parse_kmsg_for_tap:
> - * @fd: file descriptor
> - * @record: buffer used to read fd
> - * @test_name: buffer to store the test name
> - *
> - * Returns:
> - * 1 if no results were found
> - * 0 if a test succeded
> - * -1 if a test failed
> - * -2 if there are problems reading the file
> - */
> -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> -{
> -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> -	char *test_name_end;
> -
> -	ok_lookup_str = "ok ";
> -	nok_lookup_str = "not ok ";
> -
> -	lstart = strchrnul(record, ';');
> -
> -	if (*lstart == '\0') {
> -		igt_warn("kmsg truncated: output malformed (%m)\n");
> -		return -2;
> -	}
> -
> -	lstart++;
> -	while (isspace(*lstart))
> -		lstart++;
> -
> -	nok_rptr = strstr(lstart, nok_lookup_str);
> -	if (nok_rptr != NULL) {
> -		nok_rptr += strlen(nok_lookup_str);
> -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> -			nok_rptr++;
> -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		if (log_to_end(IGT_LOG_WARN, fd, record,
> -			       "%s", lstart) < 0)
> -			return -2;
> -		return -1;
> -	}
> -
> -	comment_start = strchrnul(lstart, '#');
> -
> -	/* Check if we're still in a subtest */
> -	if (*comment_start != '\0') {
> -		comment_start++;
> -		value_parse_start = comment_start;
> -
> -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> -			if (log_to_end(IGT_LOG_WARN, fd, record,
> -				       "%s", lstart) < 0)
> -				return -2;
> -			return -1;
> -		}
> -	}
> -
> -	ok_rptr = strstr(lstart, ok_lookup_str);
> -	if (ok_rptr != NULL) {
> -		ok_rptr += strlen(ok_lookup_str);
> -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> -			ok_rptr++;
> -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> -		while (!isspace(*test_name_end))
> -			test_name_end++;
> -		*test_name_end = '\0';
> -		return 0;
> -	}
> -
> -	return 1;
> -}
> -
> -/**
> - * parse_tap_level:
> - * @fd: file descriptor
> - * @base_test_name: test_name from upper recursion level
> - * @test_count: test_count of this level
> - * @failed_tests: top level failed_tests pointer
> - * @found_tests: top level found_tests pointer
> - * @is_builtin: whether the KUnit module is built-in or not
> - *
> - * Returns:
> - * 0 if succeded
> - * -1 if error occurred
> - */
> -__maybe_unused
> -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> -			   bool *found_tests, bool is_builtin)
> -{
> -	char record[BUF_LEN + 1];
> -	struct ktap_test_results_element *r;
> -	int internal_test_count;
> -	char test_name[BUF_LEN + 1];
> -	char base_test_name_for_next_level[BUF_LEN + 1];
> -
> -	for (int i = 0; i < test_count; i++) {
> -		if (read(fd, record, BUF_LEN) < 0) {
> -			if (errno == EPIPE)
> -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -			else
> -				igt_warn("error reading kmsg (%m)\n");
> -
> -			return -1;
> -		}
> -
> -		/* Sublevel found */
> -		if (tap_version_present(record, false))
> -		{
> -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> -								    is_builtin);
> -			switch (internal_test_count) {
> -			case -2:
> -				/* No more data to read */
> -				return -1;
> -			case -1:
> -				/* No test found */
> -				return -1;
> -			case 0:
> -				/* Tests found, but they're missing info */
> -				*found_tests = true;
> -				return -1;
> -			default:
> -				*found_tests = true;
> -
> -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> -				    base_test_name_for_next_level[0])
> -					strncat(base_test_name_for_next_level, DELIMITER,
> -						BUF_LEN - strlen(base_test_name_for_next_level));
> -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> -
> -				if (parse_tap_level(fd, base_test_name_for_next_level,
> -						    internal_test_count, failed_tests, found_tests,
> -						    is_builtin) == -1)
> -					return -1;
> -				break;
> -			}
> -		}
> -
> -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> -		case -2:
> -			return -1;
> -		case -1:
> -			*failed_tests = true;
> -
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = false;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		case 0:
> -			r = malloc(sizeof(*r));
> -
> -			memcpy(r->test_name, base_test_name, BUF_LEN);
> -			if (strlen(r->test_name) < BUF_LEN - 1)
> -				if (r->test_name[0])
> -					strncat(r->test_name, DELIMITER,
> -						BUF_LEN - strlen(r->test_name));
> -			memcpy(r->test_name + strlen(r->test_name), test_name,
> -			       BUF_LEN - strlen(r->test_name));
> -			r->test_name[BUF_LEN] = '\0';
> -
> -			r->passed = true;
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&r->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -
> -			test_name[0] = '\0';
> -			break;
> -		default:
> -			break;
> -		}
> -	}
> -	return 0;
> -}
> -
> -/**
> - * igt_ktap_parser:
> - *
> - * This function parses the output of a ktap script and passes it to main thread.
> - */
> -void *igt_ktap_parser(void *unused)
> -{
> -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> -	struct igt_ktap_results *ktap = NULL;
> -	int fd = ktap_args.fd;
> -	IGT_LIST_HEAD(list);
> -	int err;
> -
> -	ktap = igt_ktap_alloc(&list);
> -	if (igt_debug_on(!ktap))
> -		goto igt_ktap_parser_end;
> -
> -	while (err = read(fd, record, BUF_LEN), err > 0) {
> -		struct igt_ktap_result *r, *rn;
> -
> -		/* skip kmsg continuation lines */
> -		if (igt_debug_on(*record == ' '))
> -			continue;
> -
> -		/* NULL-terminate the record */
> -		record[err] = '\0';
> -
> -		/* detect start of log message, continue if not found */
> -		buf = strchrnul(record, ';');
> -		if (igt_debug_on(*buf == '\0'))
> -			continue;
> -		buf++;
> -
> -		err = igt_ktap_parse(buf, ktap);
> -
> -		/* parsing error */
> -		if (err && err != -EINPROGRESS)
> -			goto igt_ktap_parser_end;
> -
> -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> -			struct ktap_test_results_element *result = NULL;
> -			int code = r->code;
> -
> -			if (code != IGT_EXIT_INVALID)
> -				result = calloc(1, sizeof(*result));
> -
> -			if (result) {
> -				snprintf(result->test_name, sizeof(result->test_name),
> -					 "%s-%s", r->suite_name, r->case_name);
> -
> -				if (code == IGT_EXIT_SUCCESS)
> -					result->passed = true;
> -			}
> -
> -			igt_list_del(&r->link);
> -			if (r->suite_name != suite_name) {
> -				free(suite_name);
> -				suite_name = r->suite_name;
> -			}
> -			if (r->case_name != case_name) {
> -				free(case_name);
> -				case_name = r->case_name;
> -			}
> -			free(r->msg);
> -			free(r);
> -
> -			/*
> -			 * no extra result record expected on start
> -			 * of parametrized test case -- skip it
> -			 */
> -			if (code == IGT_EXIT_INVALID)
> -				continue;
> -
> -			if (!result) {
> -				err = -ENOMEM;
> -				goto igt_ktap_parser_end;
> -			}
> -
> -			pthread_mutex_lock(&results.mutex);
> -			igt_list_add_tail(&result->link, &results.list);
> -			pthread_mutex_unlock(&results.mutex);
> -		}
> -
> -		/* end of KTAP report */
> -		if (!err)
> -			goto igt_ktap_parser_end;
> -	}
> -
> -	if (err < 0) {
> -		if (errno == EPIPE)
> -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> -		else
> -			igt_warn("error reading kmsg (%m)\n");
> -	}
> -
> -igt_ktap_parser_end:
> -	free(suite_name);
> -	free(case_name);
> -
> -	if (!err)
> -		ktap_args.ret = IGT_EXIT_SUCCESS;
> -
> -	results.still_running = false;
> -
> -	if (ktap)
> -		igt_ktap_free(ktap);
> -
> -	return NULL;
> -}
> -
> -static pthread_t ktap_parser_thread;
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> -{
> -	IGT_INIT_LIST_HEAD(&results.list);
> -	pthread_mutex_init(&results.mutex, NULL);
> -	results.still_running = true;
> -
> -	ktap_args.fd = fd;
> -	ktap_args.is_builtin = is_builtin;
> -	ktap_args.ret = IGT_EXIT_FAILURE;
> -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> -
> -	return &results;
> -}
> -
> -void ktap_parser_cancel(void)
> -{
> -	pthread_cancel(ktap_parser_thread);
> -}
> -
> -int ktap_parser_stop(void)
> -{
> -	pthread_join(ktap_parser_thread, NULL);
> -	return ktap_args.ret;
> -}
> diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> index 6f8da3eab6..c422636bfc 100644
> --- a/lib/igt_ktap.h
> +++ b/lib/igt_ktap.h
> @@ -27,8 +27,6 @@
>  
>  #define BUF_LEN 4096
>  
> -#include <pthread.h>
> -
>  #include "igt_list.h"
>  
>  struct igt_ktap_result {
> @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
>  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
>  void igt_ktap_free(struct igt_ktap_results *ktap);
>  
> -void *igt_ktap_parser(void *unused);
> -
> -typedef struct ktap_test_results_element {
> -	char test_name[BUF_LEN + 1];
> -	bool passed;
> -	struct igt_list_head link;
> -} ktap_test_results_element;
> -
> -struct ktap_test_results {
> -	struct igt_list_head list;
> -	pthread_mutex_t mutex;
> -	bool still_running;
> -};
> -
> -
> -
> -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> -void ktap_parser_cancel(void);
> -int ktap_parser_stop(void);
> -
>  #endif /* IGT_KTAP_H */
> -- 
> 2.42.0
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
  2023-10-10 15:59     ` [Intel-xe] " Kamil Konieczny
@ 2023-10-10 17:49       ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 17:49 UTC (permalink / raw)
  To: Kamil Konieczny, igt-dev, Janusz Krzysztofik, intel-gfx,
	intel-xe, Mauro Carvalho Chehab

Hi Kamil,

Thanks for review.

On Tuesday, 10 October 2023 17:59:56 CEST Kamil Konieczny wrote:
> Hi Janusz,
> On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> > There was an attempt to parse KTAP reports in the background while a kunit
> > test module is loading.  However, since dynamic sub-subtests can be
> > executed only from the main thread, that attempt was not quite successful,
> > as IGT results from all executed kunit test cases were generated only
> > after loading of kunit test module completed.
> > 
> > Now that the parser maintains its state and we can call it separately for
> > each input line of a KTAP report, it is perfectly possible to call the
> > parser from the main thread while the module is loading in the background,
> > and convert results from kunit test cases immediately to results of IGT
> > dynamic sub-subtests by running an igt_dynamic() section for each result
> > as soon as returned by the parser.
> > 
> > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > result obtained from igt_ktap_parse() called from the main thread.
> > 
> > Also, drop no longer needed functions from igt_ktap soruces.
> > 
> > v3: Fix ktap structure not freed on lseek error,
> >   - fix initial SIGCHLD handler not restored,
> >   - fix missing handling of potential errors returned by sigaction,
> >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> >     synchronization with modprobe thread,
> >   - fix potentially illegal use of igt_assert() called outside of
> >     dynamic sub-subtest section,
> >   - fix unsupported exit code potentially passed to igt_fail(),
> >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> >     after a valid result from the parser has been processed,
> >   - fix trailing newlines missing from error messages,
> >   - add more debug statements,
> >   - integrate common code around kunit_result_free() into it.
> > v2: Interrupt blocking read() on modprobe failure.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > ---
> >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> >  lib/igt_ktap.c | 568 -------------------------------------------------
> >  lib/igt_ktap.h |  22 --
> >  3 files changed, 222 insertions(+), 629 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index 426ae5b26f..7bca4cdaab 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright © 2016 Intel Corporation
> > + * Copyright © 2016-2023 Intel Corporation
> >   *
> >   * Permission is hereby granted, free of charge, to any person obtaining a
> >   * copy of this software and associated documentation files (the "Software"),
> > @@ -26,7 +26,12 @@
> >  #include <errno.h>
> >  #include <fcntl.h>
> >  #include <pthread.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> >  #include <sys/utsname.h>
> > +#include <unistd.h>
> > +
> > +#include "assembler/brw_compat.h"	/* [un]likely() */
> ------------ ^^^^^^^^^
> Do we really need this?

I think the correct question is if wee really need [un]likely().  I'm using it 
to document unlikely cases, which is a widely accepted method of documenting 
cases like that, I believe.  Having that clarified, I hope you just tell me if 
you think we need those cases documented, and how, if not that way.

> 
> >  
> >  #include "igt_aux.h"
> >  #include "igt_core.h"
> > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> >  }
> >  
> >  struct modprobe_data {
> > +	pthread_t parent;
> --- ^^^^^^^^^^^^^^^^
> Please move it below to other related thread data.
> Also consider a comment why(or for what purpose)
> did you put this here.

No problem to move it to the bottom, if that's important to you, but regarding 
the comment, do you think that the purpose of this field of the structure, 
compared to other fields, is so unclear form review of the code, despite its 
name, that it requires a comment, unlike the other fields?

> 
> >  	struct kmod_module *kmod;
> >  	const char *opts;
> >  	int err;
> > +	pthread_mutex_t lock;
> > +	pthread_t thread;
> >  };
> >  
> >  static void *modprobe_task(void *arg)
> > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> >  
> >  	data->err = modprobe(data->kmod, data->opts);
> >  
> > +	if (igt_debug_on(data->err)) {
> > +		int err;
> > +
> > +		while (err = pthread_mutex_trylock(&data->lock),
> > +		       err && !igt_debug_on(err != EBUSY))
> > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> > +	} else {
> > +		/* let main thread use mutex to detect modprobe completion */
> > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > +	}
> > +
> >  	return NULL;
> >  }
> >  
> > +static void kunit_sigchld_handler(int signal)
> > +{
> > +	return;
> --- ^^^^^^^
> Why not removing this? checkpatch complains about return from void.

OK.

> 
> > +}
> > +
> > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > +				 struct modprobe_data *modprobe,
> > +				 int fd, struct igt_ktap_results *ktap)
> > +{
> > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > +			 *saved;
> > +	char record[BUF_LEN + 1], *buf;
> > +	unsigned long taints;
> > +	int ret;
> > +
> > +	do {
> > +		int err;
> > +
> > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > +			return -ENOTRECOVERABLE;
> > +
> > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > +		if (err == -1)
> > +			return -errno;
> > +		else if (unlikely(err))
> > +			return err;
> > +
> > +		err = pthread_mutex_lock(&modprobe->lock);
> > +		switch (err) {
> > +		case EOWNERDEAD:
> > +			/* leave the mutex unrecoverable */
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			__attribute__ ((fallthrough));
> > +		case ENOTRECOVERABLE:
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			if (igt_debug_on(modprobe->err))
> > +				return modprobe->err;
> 
> Why no 'return -err;' here?

Because modprobe->err, which can tell us more about what has gone bad in the 
modprobe thread, is more important here than err which only informs us about 
completion of the modprobe thread, also if successful, isn't it?

> 
> > +			break;
> > +		case 0:
> > +			break;
> > +		default:
> > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			return -err;
> > +		}
> > +
> > +		ret = read(fd, record, BUF_LEN);
> > +
> > +		if (!err) {
>         ^^^^^^^^^^^
> Looks strange here.

Here err still carries a return code from pthread_mutex_lock(), then !err 
means that the modprobe thread was still running and we have 1) acquired 
the mutex which now we have to release, 2) installed a non-default 
SIGCHLD handler that we should now reset.  Have you got a better idea 
how to encode that so it looks less strange?  Would a comment that reminds 
a reader where that err comes from be sufficient?

> 
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +		}
> > +
> > +		if (igt_debug_on(!ret))
> > +			return -ENODATA;
> > +		if (igt_debug_on(ret == -1))
> > +			return -errno;
> > +		if (unlikely(igt_debug_on(ret < 0)))
> > +			break;
> > +
> > +		/* skip kmsg continuation lines */
> > +		if (igt_debug_on(*record == ' '))
> ----------- ^^^^^^^^^^^^
> Why debug here? imho it is not needed.

Because we don't expect continuation lines while reading a KTAP report. Since 
the kernel log may still contain them, we want to know about them when 
something wrong happens to our KTAP parser.  That's the purpose of all those 
debug_on() calls: provide as much information on unexpected conditions as 
possible when the test fails, isn't it?

> 
> > +			continue;
> > +
> > +		/* NULL-terminate the record */
> > +		record[ret] = '\0';
> > +
> > +		/* detect start of log message, continue if not found */
> > +		buf = strchrnul(record, ';');
> > +		if (igt_debug_on(*buf == '\0'))
> ----------- ^^^^^^^^^^^^
> Same here, you just continue loop if no ';'?

That means we are getting incorrectly formatted records from /dev/kmsg, and 
I'm sure we would like to be informed about that if something goes wrong with 
the test, wouldn't we?

> 
> > +			continue;
> > +		buf++;
> > +
> > +		ret = igt_ktap_parse(buf, ktap);
> > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > +			break;
> > +	} while (igt_list_empty(results));
> > +
> > +	return ret;
> > +}
> > +
> > +static void kunit_result_free(struct igt_ktap_result **r,
> > +			      char **suite_name, char **case_name)
> > +{
> > +	if (!*r)
> > +		return;
> > +
> > +	igt_list_del(&(*r)->link);
> > +
> > +	if ((*r)->suite_name != *suite_name) {
> > +		free(*suite_name);
> > +		*suite_name = (*r)->suite_name;
> > +	}
> > +
> > +	if ((*r)->case_name != *case_name) {
> > +		free(*case_name);
> > +		*case_name = (*r)->case_name;
> > +	}
> > +
> > +	free((*r)->msg);
> > +	free(*r);
> > +	*r = NULL;
> > +}
> > +
> >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  {
> > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > -	struct kmod_module *kunit_kmod;
> > -	bool is_builtin;
> > -	struct ktap_test_results *results;
> > -	pthread_t modprobe_thread;
> > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > +	char *suite_name = NULL, *case_name = NULL;
> > +	struct igt_ktap_result *r, *rn;
> > +	struct igt_ktap_results *ktap;
> > +	pthread_mutexattr_t attr;
> > +	IGT_LIST_HEAD(results);
> >  	unsigned long taints;
> >  	int flags, ret;
> >  
> > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  
> >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> >  
> > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > -	kmod_module_unref(kunit_kmod);
> > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> >  
> > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > +	ktap = igt_ktap_alloc(&results);
> > +	igt_require(ktap);
> >  
> > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> >  					modprobe_task, &modprobe))) {
> > -		ktap_parser_cancel();
> > -		igt_ignore_warn(ktap_parser_stop());
> > +		igt_ktap_free(ktap);
> >  		igt_skip("Failed to create a modprobe thread\n");
> >  	}
> >  
> > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > -	{
> > -		struct ktap_test_results_element *result;
> > -
> > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > -			ktap_parser_cancel();
> > +	do {
> > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > +					    tst->kmsg, ktap);
> > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> >  			break;
> > -		}
> >  
> > -		if (igt_kernel_tainted(&taints)) {
> > -			ktap_parser_cancel();
> > -			pthread_cancel(modprobe_thread);
> > +		if (igt_debug_on(igt_list_empty(&results)))
> >  			break;
> > -		}
> >  
> > -		pthread_mutex_lock(&results->mutex);
> > -		if (igt_list_empty(&results->list)) {
> > -			pthread_mutex_unlock(&results->mutex);
> > -			continue;
> > -		}
> > +		r = igt_list_first_entry(&results, r, link);
> >  
> > -		result = igt_list_first_entry(&results->list, result, link);
> > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > +			if (r->code == IGT_EXIT_INVALID) {
> > +				/* parametrized test case, get actual result */
> > +				kunit_result_free(&r, &suite_name, &case_name);
> >  
> > -		igt_list_del(&result->link);
> > -		pthread_mutex_unlock(&results->mutex);
> > +				igt_assert(igt_list_empty(&results));
> >  
> > -		igt_dynamic(result->test_name) {
> > -			igt_assert(READ_ONCE(result->passed));
> > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > +							    tst->kmsg, ktap);
> > +				if (ret != -EINPROGRESS)
> > +					igt_fail_on(ret);
> > +
> > +				igt_fail_on(igt_list_empty(&results));
> > +
> > +				r = igt_list_first_entry(&results, r, link);
> > +
> > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > +					      "suite_name expected: %s, got: %s\n",
> > +					      suite_name, r->suite_name);
> > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > +					      "case_name expected: %s, got: %s\n",
> > +					      case_name, r->case_name);
> > +			}
> >  
> > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > +
> > +			if (r->msg && *r->msg) {
> > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > +					      "%s\n", r->msg);
> > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > +					      "%s\n", r->msg);
> > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > +					      "%s\n", r->msg);
> > +			} else {
> > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > +				if (r->code == IGT_EXIT_ABORT)
> > +					igt_fail(r->code);
> > +			}
> > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > +
> > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > +			case 0:
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				break;
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> >  				igt_assert_eq(modprobe.err, 0);
> > +				break;
> 
> imho we should also break a do-while loop in case of error.

We are inside igt_dynamic() body, then I don't think we can safely break the 
outer loop from here, only pass, skip, or fail.

If we are not able to check whether the modprobe thread hasn't returned an 
error then I don't think that's a good reason for failing or skipping the 
current dynamic sub-subtest since we have its KTAP results already collected 
and we should just report them in that case, I believe.

If the error persists then the loop will get broken on next attempt to lock 
the mutex before entering blocking read(), next dynamic sub-subtests won't be 
executed, and the whole subtest will inform the user about that incompleteness 
of results from dynamic sub-subtests by returning SKIP.  Isn't that 
sufficient?

> 
> > +			default:
> > +				igt_debug("pthread_mutex_lock() failed\n");
> > +				break;
> > +			}
> >  
> >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> >  		}
> >  
> > -		free(result);
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	} while (ret == -EINPROGRESS);
> > +
> > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	free(case_name);
> > +	free(suite_name);
> > +
> > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > +	case 0:
> > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> > +	case EOWNERDEAD:
> > +		/* leave the mutex unrecoverable */
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		break;
> > +	case ENOTRECOVERABLE:
> > +		break;
> > +	default:
> > +		igt_debug("pthread_mutex_lock() failed\n");
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> >  	}
> >  
> > -	pthread_join(modprobe_thread, NULL);
> > -
> > -	ret = ktap_parser_stop();
> > +	igt_ktap_free(ktap);
> >  
> >  	igt_skip_on(modprobe.err);
> >  	igt_skip_on(igt_kernel_tainted(&taints));
> > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > index 5eac102417..53a6c63288 100644
> > --- a/lib/igt_ktap.c
> > +++ b/lib/igt_ktap.c
> > @@ -4,17 +4,11 @@
> >   * Copyright © 2023 Intel Corporation
> >   */
> >  
> > -#include <ctype.h>
> > -#include <limits.h>
> > -#include <libkmod.h>
> > -#include <pthread.h>
> >  #include <errno.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >  #include <string.h>
> > -#include <unistd.h>
> >  
> > -#include "igt_aux.h"
> >  #include "igt_core.h"
> >  #include "igt_ktap.h"
> >  #include "igt_list.h"
> > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> >  {
> >  	free(ktap);
> >  }
> > -
> > -#define DELIMITER "-"
> > -
> > -struct ktap_parser_args {
> > -	int fd;
> > -	bool is_builtin;
> > -	int ret;
> > -} ktap_args;
> > -
> > -static struct ktap_test_results results;
> > -
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > -
> > -/**
> > - * log_to_end:
> > - * @level: #igt_log_level
> > - * @record: record to store the read data
> > - * @format: format string
> > - * @...: optional arguments used in the format string
> > - *
> > - * This is an altered version of the generic structured logging helper function
> > - * igt_log capable of reading to the end of a given line.
> > - *
> > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > - */
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...)
> > -{
> > -	va_list args;
> > -	const char *lend;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	char *cutoff = strchr(record, '\n');
> > -	if (cutoff) {
> > -		if (cutoff - record < BUF_LEN)
> > -			cutoff[1] = '\0';
> > -	}
> > -
> > -	va_start(args, format);
> > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > -	va_end(args);
> > -
> > -	lend = strchrnul(record, '\n');
> > -	while (*lend == '\0') {
> > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		lend = strchrnul(record, '\n');
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * lookup_value:
> > - * @haystack: the string to search in
> > - * @needle: the string to search for
> > - *
> > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > - */
> > -static long lookup_value(const char *haystack, const char *needle)
> > -{
> > -	const char *needle_rptr;
> > -	char *needle_end;
> > -	long num;
> > -
> > -	needle_rptr = strcasestr(haystack, needle);
> > -
> > -	if (needle_rptr == NULL)
> > -		return -1;
> > -
> > -	/* Skip search string and whitespaces after it */
> > -	needle_rptr += strlen(needle);
> > -
> > -	num = strtol(needle_rptr, &needle_end, 10);
> > -
> > -	if (needle_rptr == needle_end)
> > -		return -1;
> > -
> > -	if (num == LONG_MIN || num == LONG_MAX)
> > -		return 0;
> > -
> > -	return num > 0 ? num : 0;
> > -}
> > -
> > -/**
> > - * tap_version_present:
> > - * @record: buffer with tap data
> > - * @print_info: whether tap version should be printed or not
> > - *
> > - * Returns:
> > - * 0 if not found
> > - * 1 if found
> > - */
> > -static int tap_version_present(char* record, bool print_info)
> > -{
> > -	/*
> > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> 
> Could you save these two lines comment in some other place?

Why?  Do you think that just that information from a multi-paragraph 
description of KTAP format is of particular importance?

Instead, let me add just the reference to the KTAP standard to a comment above 
the current parser implementation, OK?

Thanks,
Janusz

> 
> Regards,
> Kamil
> 
> > -	 *
> > -	 * but actually isn't, as it currently depends on the KUnit module
> > -	 * being built-in, so we can't rely on it every time
> > -	 */
> > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > -	char *cutoff;
> > -
> > -	if (version_rptr == NULL)
> > -		return 0;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	cutoff = strchr(version_rptr, '\n');
> > -	if (cutoff)
> > -		cutoff[0] = '\0';
> > -
> > -	if (print_info)
> > -		igt_info("%s\n", version_rptr);
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * find_next_tap_subtest:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @is_builtin: whether KUnit is built-in or not
> > - *
> > - * Returns:
> > - * 0 if there's missing information
> > - * -1 if not found
> > - * -2 if there are problems while reading the file.
> > - * any other value corresponds to the amount of cases of the next (sub)test
> > - */
> > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > -{
> > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > -	long test_count;
> > -	char *cutoff;
> > -
> > -	test_name[0] = '\0';
> > -	test_name[BUF_LEN] = '\0';
> > -
> > -	test_lookup_str = " subtest: ";
> > -	subtest_lookup_str = " test: ";
> > -
> > -	if (!tap_version_present(record, true))
> > -		return -1;
> > -
> > -	if (is_builtin) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -	}
> > -
> > -	name_rptr = strcasestr(record, test_lookup_str);
> > -	if (name_rptr != NULL) {
> > -		name_rptr += strlen(test_lookup_str);
> > -	} else {
> > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > -		if (name_rptr != NULL)
> > -			name_rptr += strlen(subtest_lookup_str);
> > -	}
> > -
> > -	if (name_rptr == NULL) {
> > -		if (!is_builtin)
> > -			/* We've probably found nothing */
> > -			return -1;
> > -		igt_info("Missing test name\n");
> > -	} else {
> > -		strncpy(test_name, name_rptr, BUF_LEN);
> > -		/* Cutoff after newline character, in order to not display garbage */
> > -		cutoff = strchr(test_name, '\n');
> > -		if (cutoff)
> > -			cutoff[0] = '\0';
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("unknown error reading kmsg (%m)\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		/* Now we can be sure we found tests */
> > -		if (!is_builtin)
> > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > -	}
> > -
> > -	/*
> > -	 * Total test count will almost always appear as 0..N at the beginning
> > -	 * of a run, so we use it to reliably identify a new run
> > -	 */
> > -	test_count = lookup_value(record, "..");
> > -
> > -	if (test_count <= 0) {
> > -		igt_info("Missing test count\n");
> > -		if (test_name[0] == '\0')
> > -			return 0;
> > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > -				"Running some tests in: %s\n",
> > -				test_name) < 0)
> > -			return -2;
> > -		return 0;
> > -	} else if (test_name[0] == '\0') {
> > -		igt_info("Running %ld tests...\n", test_count);
> > -		return 0;
> > -	}
> > -
> > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > -			"Executing %ld tests in: %s\n",
> > -			test_count, test_name) < 0)
> > -		return -2;
> > -
> > -	return test_count;
> > -}
> > -
> > -/**
> > - * parse_kmsg_for_tap:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @test_name: buffer to store the test name
> > - *
> > - * Returns:
> > - * 1 if no results were found
> > - * 0 if a test succeded
> > - * -1 if a test failed
> > - * -2 if there are problems reading the file
> > - */
> > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > -{
> > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > -	char *test_name_end;
> > -
> > -	ok_lookup_str = "ok ";
> > -	nok_lookup_str = "not ok ";
> > -
> > -	lstart = strchrnul(record, ';');
> > -
> > -	if (*lstart == '\0') {
> > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > -		return -2;
> > -	}
> > -
> > -	lstart++;
> > -	while (isspace(*lstart))
> > -		lstart++;
> > -
> > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > -	if (nok_rptr != NULL) {
> > -		nok_rptr += strlen(nok_lookup_str);
> > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > -			nok_rptr++;
> > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > -			       "%s", lstart) < 0)
> > -			return -2;
> > -		return -1;
> > -	}
> > -
> > -	comment_start = strchrnul(lstart, '#');
> > -
> > -	/* Check if we're still in a subtest */
> > -	if (*comment_start != '\0') {
> > -		comment_start++;
> > -		value_parse_start = comment_start;
> > -
> > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > -				       "%s", lstart) < 0)
> > -				return -2;
> > -			return -1;
> > -		}
> > -	}
> > -
> > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > -	if (ok_rptr != NULL) {
> > -		ok_rptr += strlen(ok_lookup_str);
> > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > -			ok_rptr++;
> > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		return 0;
> > -	}
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * parse_tap_level:
> > - * @fd: file descriptor
> > - * @base_test_name: test_name from upper recursion level
> > - * @test_count: test_count of this level
> > - * @failed_tests: top level failed_tests pointer
> > - * @found_tests: top level found_tests pointer
> > - * @is_builtin: whether the KUnit module is built-in or not
> > - *
> > - * Returns:
> > - * 0 if succeded
> > - * -1 if error occurred
> > - */
> > -__maybe_unused
> > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > -			   bool *found_tests, bool is_builtin)
> > -{
> > -	char record[BUF_LEN + 1];
> > -	struct ktap_test_results_element *r;
> > -	int internal_test_count;
> > -	char test_name[BUF_LEN + 1];
> > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > -
> > -	for (int i = 0; i < test_count; i++) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("error reading kmsg (%m)\n");
> > -
> > -			return -1;
> > -		}
> > -
> > -		/* Sublevel found */
> > -		if (tap_version_present(record, false))
> > -		{
> > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > -								    is_builtin);
> > -			switch (internal_test_count) {
> > -			case -2:
> > -				/* No more data to read */
> > -				return -1;
> > -			case -1:
> > -				/* No test found */
> > -				return -1;
> > -			case 0:
> > -				/* Tests found, but they're missing info */
> > -				*found_tests = true;
> > -				return -1;
> > -			default:
> > -				*found_tests = true;
> > -
> > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > -				    base_test_name_for_next_level[0])
> > -					strncat(base_test_name_for_next_level, DELIMITER,
> > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > -
> > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > -						    internal_test_count, failed_tests, found_tests,
> > -						    is_builtin) == -1)
> > -					return -1;
> > -				break;
> > -			}
> > -		}
> > -
> > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > -		case -2:
> > -			return -1;
> > -		case -1:
> > -			*failed_tests = true;
> > -
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = false;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		case 0:
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = true;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		default:
> > -			break;
> > -		}
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * igt_ktap_parser:
> > - *
> > - * This function parses the output of a ktap script and passes it to main thread.
> > - */
> > -void *igt_ktap_parser(void *unused)
> > -{
> > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > -	struct igt_ktap_results *ktap = NULL;
> > -	int fd = ktap_args.fd;
> > -	IGT_LIST_HEAD(list);
> > -	int err;
> > -
> > -	ktap = igt_ktap_alloc(&list);
> > -	if (igt_debug_on(!ktap))
> > -		goto igt_ktap_parser_end;
> > -
> > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > -		struct igt_ktap_result *r, *rn;
> > -
> > -		/* skip kmsg continuation lines */
> > -		if (igt_debug_on(*record == ' '))
> > -			continue;
> > -
> > -		/* NULL-terminate the record */
> > -		record[err] = '\0';
> > -
> > -		/* detect start of log message, continue if not found */
> > -		buf = strchrnul(record, ';');
> > -		if (igt_debug_on(*buf == '\0'))
> > -			continue;
> > -		buf++;
> > -
> > -		err = igt_ktap_parse(buf, ktap);
> > -
> > -		/* parsing error */
> > -		if (err && err != -EINPROGRESS)
> > -			goto igt_ktap_parser_end;
> > -
> > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > -			struct ktap_test_results_element *result = NULL;
> > -			int code = r->code;
> > -
> > -			if (code != IGT_EXIT_INVALID)
> > -				result = calloc(1, sizeof(*result));
> > -
> > -			if (result) {
> > -				snprintf(result->test_name, sizeof(result->test_name),
> > -					 "%s-%s", r->suite_name, r->case_name);
> > -
> > -				if (code == IGT_EXIT_SUCCESS)
> > -					result->passed = true;
> > -			}
> > -
> > -			igt_list_del(&r->link);
> > -			if (r->suite_name != suite_name) {
> > -				free(suite_name);
> > -				suite_name = r->suite_name;
> > -			}
> > -			if (r->case_name != case_name) {
> > -				free(case_name);
> > -				case_name = r->case_name;
> > -			}
> > -			free(r->msg);
> > -			free(r);
> > -
> > -			/*
> > -			 * no extra result record expected on start
> > -			 * of parametrized test case -- skip it
> > -			 */
> > -			if (code == IGT_EXIT_INVALID)
> > -				continue;
> > -
> > -			if (!result) {
> > -				err = -ENOMEM;
> > -				goto igt_ktap_parser_end;
> > -			}
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&result->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -		}
> > -
> > -		/* end of KTAP report */
> > -		if (!err)
> > -			goto igt_ktap_parser_end;
> > -	}
> > -
> > -	if (err < 0) {
> > -		if (errno == EPIPE)
> > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -		else
> > -			igt_warn("error reading kmsg (%m)\n");
> > -	}
> > -
> > -igt_ktap_parser_end:
> > -	free(suite_name);
> > -	free(case_name);
> > -
> > -	if (!err)
> > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > -
> > -	results.still_running = false;
> > -
> > -	if (ktap)
> > -		igt_ktap_free(ktap);
> > -
> > -	return NULL;
> > -}
> > -
> > -static pthread_t ktap_parser_thread;
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > -{
> > -	IGT_INIT_LIST_HEAD(&results.list);
> > -	pthread_mutex_init(&results.mutex, NULL);
> > -	results.still_running = true;
> > -
> > -	ktap_args.fd = fd;
> > -	ktap_args.is_builtin = is_builtin;
> > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > -
> > -	return &results;
> > -}
> > -
> > -void ktap_parser_cancel(void)
> > -{
> > -	pthread_cancel(ktap_parser_thread);
> > -}
> > -
> > -int ktap_parser_stop(void)
> > -{
> > -	pthread_join(ktap_parser_thread, NULL);
> > -	return ktap_args.ret;
> > -}
> > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > index 6f8da3eab6..c422636bfc 100644
> > --- a/lib/igt_ktap.h
> > +++ b/lib/igt_ktap.h
> > @@ -27,8 +27,6 @@
> >  
> >  #define BUF_LEN 4096
> >  
> > -#include <pthread.h>
> > -
> >  #include "igt_list.h"
> >  
> >  struct igt_ktap_result {
> > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> >  void igt_ktap_free(struct igt_ktap_results *ktap);
> >  
> > -void *igt_ktap_parser(void *unused);
> > -
> > -typedef struct ktap_test_results_element {
> > -	char test_name[BUF_LEN + 1];
> > -	bool passed;
> > -	struct igt_list_head link;
> > -} ktap_test_results_element;
> > -
> > -struct ktap_test_results {
> > -	struct igt_list_head list;
> > -	pthread_mutex_t mutex;
> > -	bool still_running;
> > -};
> > -
> > -
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > -void ktap_parser_cancel(void);
> > -int ktap_parser_stop(void);
> > -
> >  #endif /* IGT_KTAP_H */
> 





^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-10 17:49       ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 17:49 UTC (permalink / raw)
  To: Kamil Konieczny, igt-dev, Janusz Krzysztofik, intel-gfx,
	intel-xe, Mauro Carvalho Chehab

Hi Kamil,

Thanks for review.

On Tuesday, 10 October 2023 17:59:56 CEST Kamil Konieczny wrote:
> Hi Janusz,
> On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> > There was an attempt to parse KTAP reports in the background while a kunit
> > test module is loading.  However, since dynamic sub-subtests can be
> > executed only from the main thread, that attempt was not quite successful,
> > as IGT results from all executed kunit test cases were generated only
> > after loading of kunit test module completed.
> > 
> > Now that the parser maintains its state and we can call it separately for
> > each input line of a KTAP report, it is perfectly possible to call the
> > parser from the main thread while the module is loading in the background,
> > and convert results from kunit test cases immediately to results of IGT
> > dynamic sub-subtests by running an igt_dynamic() section for each result
> > as soon as returned by the parser.
> > 
> > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > result obtained from igt_ktap_parse() called from the main thread.
> > 
> > Also, drop no longer needed functions from igt_ktap soruces.
> > 
> > v3: Fix ktap structure not freed on lseek error,
> >   - fix initial SIGCHLD handler not restored,
> >   - fix missing handling of potential errors returned by sigaction,
> >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> >     synchronization with modprobe thread,
> >   - fix potentially illegal use of igt_assert() called outside of
> >     dynamic sub-subtest section,
> >   - fix unsupported exit code potentially passed to igt_fail(),
> >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> >     after a valid result from the parser has been processed,
> >   - fix trailing newlines missing from error messages,
> >   - add more debug statements,
> >   - integrate common code around kunit_result_free() into it.
> > v2: Interrupt blocking read() on modprobe failure.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > ---
> >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> >  lib/igt_ktap.c | 568 -------------------------------------------------
> >  lib/igt_ktap.h |  22 --
> >  3 files changed, 222 insertions(+), 629 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index 426ae5b26f..7bca4cdaab 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright © 2016 Intel Corporation
> > + * Copyright © 2016-2023 Intel Corporation
> >   *
> >   * Permission is hereby granted, free of charge, to any person obtaining a
> >   * copy of this software and associated documentation files (the "Software"),
> > @@ -26,7 +26,12 @@
> >  #include <errno.h>
> >  #include <fcntl.h>
> >  #include <pthread.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> >  #include <sys/utsname.h>
> > +#include <unistd.h>
> > +
> > +#include "assembler/brw_compat.h"	/* [un]likely() */
> ------------ ^^^^^^^^^
> Do we really need this?

I think the correct question is if wee really need [un]likely().  I'm using it 
to document unlikely cases, which is a widely accepted method of documenting 
cases like that, I believe.  Having that clarified, I hope you just tell me if 
you think we need those cases documented, and how, if not that way.

> 
> >  
> >  #include "igt_aux.h"
> >  #include "igt_core.h"
> > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> >  }
> >  
> >  struct modprobe_data {
> > +	pthread_t parent;
> --- ^^^^^^^^^^^^^^^^
> Please move it below to other related thread data.
> Also consider a comment why(or for what purpose)
> did you put this here.

No problem to move it to the bottom, if that's important to you, but regarding 
the comment, do you think that the purpose of this field of the structure, 
compared to other fields, is so unclear form review of the code, despite its 
name, that it requires a comment, unlike the other fields?

> 
> >  	struct kmod_module *kmod;
> >  	const char *opts;
> >  	int err;
> > +	pthread_mutex_t lock;
> > +	pthread_t thread;
> >  };
> >  
> >  static void *modprobe_task(void *arg)
> > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> >  
> >  	data->err = modprobe(data->kmod, data->opts);
> >  
> > +	if (igt_debug_on(data->err)) {
> > +		int err;
> > +
> > +		while (err = pthread_mutex_trylock(&data->lock),
> > +		       err && !igt_debug_on(err != EBUSY))
> > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> > +	} else {
> > +		/* let main thread use mutex to detect modprobe completion */
> > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > +	}
> > +
> >  	return NULL;
> >  }
> >  
> > +static void kunit_sigchld_handler(int signal)
> > +{
> > +	return;
> --- ^^^^^^^
> Why not removing this? checkpatch complains about return from void.

OK.

> 
> > +}
> > +
> > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > +				 struct modprobe_data *modprobe,
> > +				 int fd, struct igt_ktap_results *ktap)
> > +{
> > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > +			 *saved;
> > +	char record[BUF_LEN + 1], *buf;
> > +	unsigned long taints;
> > +	int ret;
> > +
> > +	do {
> > +		int err;
> > +
> > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > +			return -ENOTRECOVERABLE;
> > +
> > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > +		if (err == -1)
> > +			return -errno;
> > +		else if (unlikely(err))
> > +			return err;
> > +
> > +		err = pthread_mutex_lock(&modprobe->lock);
> > +		switch (err) {
> > +		case EOWNERDEAD:
> > +			/* leave the mutex unrecoverable */
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			__attribute__ ((fallthrough));
> > +		case ENOTRECOVERABLE:
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			if (igt_debug_on(modprobe->err))
> > +				return modprobe->err;
> 
> Why no 'return -err;' here?

Because modprobe->err, which can tell us more about what has gone bad in the 
modprobe thread, is more important here than err which only informs us about 
completion of the modprobe thread, also if successful, isn't it?

> 
> > +			break;
> > +		case 0:
> > +			break;
> > +		default:
> > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			return -err;
> > +		}
> > +
> > +		ret = read(fd, record, BUF_LEN);
> > +
> > +		if (!err) {
>         ^^^^^^^^^^^
> Looks strange here.

Here err still carries a return code from pthread_mutex_lock(), then !err 
means that the modprobe thread was still running and we have 1) acquired 
the mutex which now we have to release, 2) installed a non-default 
SIGCHLD handler that we should now reset.  Have you got a better idea 
how to encode that so it looks less strange?  Would a comment that reminds 
a reader where that err comes from be sufficient?

> 
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +		}
> > +
> > +		if (igt_debug_on(!ret))
> > +			return -ENODATA;
> > +		if (igt_debug_on(ret == -1))
> > +			return -errno;
> > +		if (unlikely(igt_debug_on(ret < 0)))
> > +			break;
> > +
> > +		/* skip kmsg continuation lines */
> > +		if (igt_debug_on(*record == ' '))
> ----------- ^^^^^^^^^^^^
> Why debug here? imho it is not needed.

Because we don't expect continuation lines while reading a KTAP report. Since 
the kernel log may still contain them, we want to know about them when 
something wrong happens to our KTAP parser.  That's the purpose of all those 
debug_on() calls: provide as much information on unexpected conditions as 
possible when the test fails, isn't it?

> 
> > +			continue;
> > +
> > +		/* NULL-terminate the record */
> > +		record[ret] = '\0';
> > +
> > +		/* detect start of log message, continue if not found */
> > +		buf = strchrnul(record, ';');
> > +		if (igt_debug_on(*buf == '\0'))
> ----------- ^^^^^^^^^^^^
> Same here, you just continue loop if no ';'?

That means we are getting incorrectly formatted records from /dev/kmsg, and 
I'm sure we would like to be informed about that if something goes wrong with 
the test, wouldn't we?

> 
> > +			continue;
> > +		buf++;
> > +
> > +		ret = igt_ktap_parse(buf, ktap);
> > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > +			break;
> > +	} while (igt_list_empty(results));
> > +
> > +	return ret;
> > +}
> > +
> > +static void kunit_result_free(struct igt_ktap_result **r,
> > +			      char **suite_name, char **case_name)
> > +{
> > +	if (!*r)
> > +		return;
> > +
> > +	igt_list_del(&(*r)->link);
> > +
> > +	if ((*r)->suite_name != *suite_name) {
> > +		free(*suite_name);
> > +		*suite_name = (*r)->suite_name;
> > +	}
> > +
> > +	if ((*r)->case_name != *case_name) {
> > +		free(*case_name);
> > +		*case_name = (*r)->case_name;
> > +	}
> > +
> > +	free((*r)->msg);
> > +	free(*r);
> > +	*r = NULL;
> > +}
> > +
> >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  {
> > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > -	struct kmod_module *kunit_kmod;
> > -	bool is_builtin;
> > -	struct ktap_test_results *results;
> > -	pthread_t modprobe_thread;
> > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > +	char *suite_name = NULL, *case_name = NULL;
> > +	struct igt_ktap_result *r, *rn;
> > +	struct igt_ktap_results *ktap;
> > +	pthread_mutexattr_t attr;
> > +	IGT_LIST_HEAD(results);
> >  	unsigned long taints;
> >  	int flags, ret;
> >  
> > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  
> >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> >  
> > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > -	kmod_module_unref(kunit_kmod);
> > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> >  
> > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > +	ktap = igt_ktap_alloc(&results);
> > +	igt_require(ktap);
> >  
> > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> >  					modprobe_task, &modprobe))) {
> > -		ktap_parser_cancel();
> > -		igt_ignore_warn(ktap_parser_stop());
> > +		igt_ktap_free(ktap);
> >  		igt_skip("Failed to create a modprobe thread\n");
> >  	}
> >  
> > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > -	{
> > -		struct ktap_test_results_element *result;
> > -
> > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > -			ktap_parser_cancel();
> > +	do {
> > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > +					    tst->kmsg, ktap);
> > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> >  			break;
> > -		}
> >  
> > -		if (igt_kernel_tainted(&taints)) {
> > -			ktap_parser_cancel();
> > -			pthread_cancel(modprobe_thread);
> > +		if (igt_debug_on(igt_list_empty(&results)))
> >  			break;
> > -		}
> >  
> > -		pthread_mutex_lock(&results->mutex);
> > -		if (igt_list_empty(&results->list)) {
> > -			pthread_mutex_unlock(&results->mutex);
> > -			continue;
> > -		}
> > +		r = igt_list_first_entry(&results, r, link);
> >  
> > -		result = igt_list_first_entry(&results->list, result, link);
> > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > +			if (r->code == IGT_EXIT_INVALID) {
> > +				/* parametrized test case, get actual result */
> > +				kunit_result_free(&r, &suite_name, &case_name);
> >  
> > -		igt_list_del(&result->link);
> > -		pthread_mutex_unlock(&results->mutex);
> > +				igt_assert(igt_list_empty(&results));
> >  
> > -		igt_dynamic(result->test_name) {
> > -			igt_assert(READ_ONCE(result->passed));
> > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > +							    tst->kmsg, ktap);
> > +				if (ret != -EINPROGRESS)
> > +					igt_fail_on(ret);
> > +
> > +				igt_fail_on(igt_list_empty(&results));
> > +
> > +				r = igt_list_first_entry(&results, r, link);
> > +
> > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > +					      "suite_name expected: %s, got: %s\n",
> > +					      suite_name, r->suite_name);
> > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > +					      "case_name expected: %s, got: %s\n",
> > +					      case_name, r->case_name);
> > +			}
> >  
> > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > +
> > +			if (r->msg && *r->msg) {
> > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > +					      "%s\n", r->msg);
> > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > +					      "%s\n", r->msg);
> > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > +					      "%s\n", r->msg);
> > +			} else {
> > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > +				if (r->code == IGT_EXIT_ABORT)
> > +					igt_fail(r->code);
> > +			}
> > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > +
> > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > +			case 0:
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				break;
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> >  				igt_assert_eq(modprobe.err, 0);
> > +				break;
> 
> imho we should also break a do-while loop in case of error.

We are inside igt_dynamic() body, then I don't think we can safely break the 
outer loop from here, only pass, skip, or fail.

If we are not able to check whether the modprobe thread hasn't returned an 
error then I don't think that's a good reason for failing or skipping the 
current dynamic sub-subtest since we have its KTAP results already collected 
and we should just report them in that case, I believe.

If the error persists then the loop will get broken on next attempt to lock 
the mutex before entering blocking read(), next dynamic sub-subtests won't be 
executed, and the whole subtest will inform the user about that incompleteness 
of results from dynamic sub-subtests by returning SKIP.  Isn't that 
sufficient?

> 
> > +			default:
> > +				igt_debug("pthread_mutex_lock() failed\n");
> > +				break;
> > +			}
> >  
> >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> >  		}
> >  
> > -		free(result);
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	} while (ret == -EINPROGRESS);
> > +
> > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	free(case_name);
> > +	free(suite_name);
> > +
> > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > +	case 0:
> > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> > +	case EOWNERDEAD:
> > +		/* leave the mutex unrecoverable */
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		break;
> > +	case ENOTRECOVERABLE:
> > +		break;
> > +	default:
> > +		igt_debug("pthread_mutex_lock() failed\n");
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> >  	}
> >  
> > -	pthread_join(modprobe_thread, NULL);
> > -
> > -	ret = ktap_parser_stop();
> > +	igt_ktap_free(ktap);
> >  
> >  	igt_skip_on(modprobe.err);
> >  	igt_skip_on(igt_kernel_tainted(&taints));
> > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > index 5eac102417..53a6c63288 100644
> > --- a/lib/igt_ktap.c
> > +++ b/lib/igt_ktap.c
> > @@ -4,17 +4,11 @@
> >   * Copyright © 2023 Intel Corporation
> >   */
> >  
> > -#include <ctype.h>
> > -#include <limits.h>
> > -#include <libkmod.h>
> > -#include <pthread.h>
> >  #include <errno.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >  #include <string.h>
> > -#include <unistd.h>
> >  
> > -#include "igt_aux.h"
> >  #include "igt_core.h"
> >  #include "igt_ktap.h"
> >  #include "igt_list.h"
> > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> >  {
> >  	free(ktap);
> >  }
> > -
> > -#define DELIMITER "-"
> > -
> > -struct ktap_parser_args {
> > -	int fd;
> > -	bool is_builtin;
> > -	int ret;
> > -} ktap_args;
> > -
> > -static struct ktap_test_results results;
> > -
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > -
> > -/**
> > - * log_to_end:
> > - * @level: #igt_log_level
> > - * @record: record to store the read data
> > - * @format: format string
> > - * @...: optional arguments used in the format string
> > - *
> > - * This is an altered version of the generic structured logging helper function
> > - * igt_log capable of reading to the end of a given line.
> > - *
> > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > - */
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...)
> > -{
> > -	va_list args;
> > -	const char *lend;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	char *cutoff = strchr(record, '\n');
> > -	if (cutoff) {
> > -		if (cutoff - record < BUF_LEN)
> > -			cutoff[1] = '\0';
> > -	}
> > -
> > -	va_start(args, format);
> > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > -	va_end(args);
> > -
> > -	lend = strchrnul(record, '\n');
> > -	while (*lend == '\0') {
> > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		lend = strchrnul(record, '\n');
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * lookup_value:
> > - * @haystack: the string to search in
> > - * @needle: the string to search for
> > - *
> > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > - */
> > -static long lookup_value(const char *haystack, const char *needle)
> > -{
> > -	const char *needle_rptr;
> > -	char *needle_end;
> > -	long num;
> > -
> > -	needle_rptr = strcasestr(haystack, needle);
> > -
> > -	if (needle_rptr == NULL)
> > -		return -1;
> > -
> > -	/* Skip search string and whitespaces after it */
> > -	needle_rptr += strlen(needle);
> > -
> > -	num = strtol(needle_rptr, &needle_end, 10);
> > -
> > -	if (needle_rptr == needle_end)
> > -		return -1;
> > -
> > -	if (num == LONG_MIN || num == LONG_MAX)
> > -		return 0;
> > -
> > -	return num > 0 ? num : 0;
> > -}
> > -
> > -/**
> > - * tap_version_present:
> > - * @record: buffer with tap data
> > - * @print_info: whether tap version should be printed or not
> > - *
> > - * Returns:
> > - * 0 if not found
> > - * 1 if found
> > - */
> > -static int tap_version_present(char* record, bool print_info)
> > -{
> > -	/*
> > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> 
> Could you save these two lines comment in some other place?

Why?  Do you think that just that information from a multi-paragraph 
description of KTAP format is of particular importance?

Instead, let me add just the reference to the KTAP standard to a comment above 
the current parser implementation, OK?

Thanks,
Janusz

> 
> Regards,
> Kamil
> 
> > -	 *
> > -	 * but actually isn't, as it currently depends on the KUnit module
> > -	 * being built-in, so we can't rely on it every time
> > -	 */
> > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > -	char *cutoff;
> > -
> > -	if (version_rptr == NULL)
> > -		return 0;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	cutoff = strchr(version_rptr, '\n');
> > -	if (cutoff)
> > -		cutoff[0] = '\0';
> > -
> > -	if (print_info)
> > -		igt_info("%s\n", version_rptr);
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * find_next_tap_subtest:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @is_builtin: whether KUnit is built-in or not
> > - *
> > - * Returns:
> > - * 0 if there's missing information
> > - * -1 if not found
> > - * -2 if there are problems while reading the file.
> > - * any other value corresponds to the amount of cases of the next (sub)test
> > - */
> > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > -{
> > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > -	long test_count;
> > -	char *cutoff;
> > -
> > -	test_name[0] = '\0';
> > -	test_name[BUF_LEN] = '\0';
> > -
> > -	test_lookup_str = " subtest: ";
> > -	subtest_lookup_str = " test: ";
> > -
> > -	if (!tap_version_present(record, true))
> > -		return -1;
> > -
> > -	if (is_builtin) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -	}
> > -
> > -	name_rptr = strcasestr(record, test_lookup_str);
> > -	if (name_rptr != NULL) {
> > -		name_rptr += strlen(test_lookup_str);
> > -	} else {
> > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > -		if (name_rptr != NULL)
> > -			name_rptr += strlen(subtest_lookup_str);
> > -	}
> > -
> > -	if (name_rptr == NULL) {
> > -		if (!is_builtin)
> > -			/* We've probably found nothing */
> > -			return -1;
> > -		igt_info("Missing test name\n");
> > -	} else {
> > -		strncpy(test_name, name_rptr, BUF_LEN);
> > -		/* Cutoff after newline character, in order to not display garbage */
> > -		cutoff = strchr(test_name, '\n');
> > -		if (cutoff)
> > -			cutoff[0] = '\0';
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("unknown error reading kmsg (%m)\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		/* Now we can be sure we found tests */
> > -		if (!is_builtin)
> > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > -	}
> > -
> > -	/*
> > -	 * Total test count will almost always appear as 0..N at the beginning
> > -	 * of a run, so we use it to reliably identify a new run
> > -	 */
> > -	test_count = lookup_value(record, "..");
> > -
> > -	if (test_count <= 0) {
> > -		igt_info("Missing test count\n");
> > -		if (test_name[0] == '\0')
> > -			return 0;
> > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > -				"Running some tests in: %s\n",
> > -				test_name) < 0)
> > -			return -2;
> > -		return 0;
> > -	} else if (test_name[0] == '\0') {
> > -		igt_info("Running %ld tests...\n", test_count);
> > -		return 0;
> > -	}
> > -
> > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > -			"Executing %ld tests in: %s\n",
> > -			test_count, test_name) < 0)
> > -		return -2;
> > -
> > -	return test_count;
> > -}
> > -
> > -/**
> > - * parse_kmsg_for_tap:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @test_name: buffer to store the test name
> > - *
> > - * Returns:
> > - * 1 if no results were found
> > - * 0 if a test succeded
> > - * -1 if a test failed
> > - * -2 if there are problems reading the file
> > - */
> > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > -{
> > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > -	char *test_name_end;
> > -
> > -	ok_lookup_str = "ok ";
> > -	nok_lookup_str = "not ok ";
> > -
> > -	lstart = strchrnul(record, ';');
> > -
> > -	if (*lstart == '\0') {
> > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > -		return -2;
> > -	}
> > -
> > -	lstart++;
> > -	while (isspace(*lstart))
> > -		lstart++;
> > -
> > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > -	if (nok_rptr != NULL) {
> > -		nok_rptr += strlen(nok_lookup_str);
> > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > -			nok_rptr++;
> > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > -			       "%s", lstart) < 0)
> > -			return -2;
> > -		return -1;
> > -	}
> > -
> > -	comment_start = strchrnul(lstart, '#');
> > -
> > -	/* Check if we're still in a subtest */
> > -	if (*comment_start != '\0') {
> > -		comment_start++;
> > -		value_parse_start = comment_start;
> > -
> > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > -				       "%s", lstart) < 0)
> > -				return -2;
> > -			return -1;
> > -		}
> > -	}
> > -
> > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > -	if (ok_rptr != NULL) {
> > -		ok_rptr += strlen(ok_lookup_str);
> > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > -			ok_rptr++;
> > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		return 0;
> > -	}
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * parse_tap_level:
> > - * @fd: file descriptor
> > - * @base_test_name: test_name from upper recursion level
> > - * @test_count: test_count of this level
> > - * @failed_tests: top level failed_tests pointer
> > - * @found_tests: top level found_tests pointer
> > - * @is_builtin: whether the KUnit module is built-in or not
> > - *
> > - * Returns:
> > - * 0 if succeded
> > - * -1 if error occurred
> > - */
> > -__maybe_unused
> > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > -			   bool *found_tests, bool is_builtin)
> > -{
> > -	char record[BUF_LEN + 1];
> > -	struct ktap_test_results_element *r;
> > -	int internal_test_count;
> > -	char test_name[BUF_LEN + 1];
> > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > -
> > -	for (int i = 0; i < test_count; i++) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("error reading kmsg (%m)\n");
> > -
> > -			return -1;
> > -		}
> > -
> > -		/* Sublevel found */
> > -		if (tap_version_present(record, false))
> > -		{
> > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > -								    is_builtin);
> > -			switch (internal_test_count) {
> > -			case -2:
> > -				/* No more data to read */
> > -				return -1;
> > -			case -1:
> > -				/* No test found */
> > -				return -1;
> > -			case 0:
> > -				/* Tests found, but they're missing info */
> > -				*found_tests = true;
> > -				return -1;
> > -			default:
> > -				*found_tests = true;
> > -
> > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > -				    base_test_name_for_next_level[0])
> > -					strncat(base_test_name_for_next_level, DELIMITER,
> > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > -
> > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > -						    internal_test_count, failed_tests, found_tests,
> > -						    is_builtin) == -1)
> > -					return -1;
> > -				break;
> > -			}
> > -		}
> > -
> > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > -		case -2:
> > -			return -1;
> > -		case -1:
> > -			*failed_tests = true;
> > -
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = false;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		case 0:
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = true;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		default:
> > -			break;
> > -		}
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * igt_ktap_parser:
> > - *
> > - * This function parses the output of a ktap script and passes it to main thread.
> > - */
> > -void *igt_ktap_parser(void *unused)
> > -{
> > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > -	struct igt_ktap_results *ktap = NULL;
> > -	int fd = ktap_args.fd;
> > -	IGT_LIST_HEAD(list);
> > -	int err;
> > -
> > -	ktap = igt_ktap_alloc(&list);
> > -	if (igt_debug_on(!ktap))
> > -		goto igt_ktap_parser_end;
> > -
> > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > -		struct igt_ktap_result *r, *rn;
> > -
> > -		/* skip kmsg continuation lines */
> > -		if (igt_debug_on(*record == ' '))
> > -			continue;
> > -
> > -		/* NULL-terminate the record */
> > -		record[err] = '\0';
> > -
> > -		/* detect start of log message, continue if not found */
> > -		buf = strchrnul(record, ';');
> > -		if (igt_debug_on(*buf == '\0'))
> > -			continue;
> > -		buf++;
> > -
> > -		err = igt_ktap_parse(buf, ktap);
> > -
> > -		/* parsing error */
> > -		if (err && err != -EINPROGRESS)
> > -			goto igt_ktap_parser_end;
> > -
> > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > -			struct ktap_test_results_element *result = NULL;
> > -			int code = r->code;
> > -
> > -			if (code != IGT_EXIT_INVALID)
> > -				result = calloc(1, sizeof(*result));
> > -
> > -			if (result) {
> > -				snprintf(result->test_name, sizeof(result->test_name),
> > -					 "%s-%s", r->suite_name, r->case_name);
> > -
> > -				if (code == IGT_EXIT_SUCCESS)
> > -					result->passed = true;
> > -			}
> > -
> > -			igt_list_del(&r->link);
> > -			if (r->suite_name != suite_name) {
> > -				free(suite_name);
> > -				suite_name = r->suite_name;
> > -			}
> > -			if (r->case_name != case_name) {
> > -				free(case_name);
> > -				case_name = r->case_name;
> > -			}
> > -			free(r->msg);
> > -			free(r);
> > -
> > -			/*
> > -			 * no extra result record expected on start
> > -			 * of parametrized test case -- skip it
> > -			 */
> > -			if (code == IGT_EXIT_INVALID)
> > -				continue;
> > -
> > -			if (!result) {
> > -				err = -ENOMEM;
> > -				goto igt_ktap_parser_end;
> > -			}
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&result->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -		}
> > -
> > -		/* end of KTAP report */
> > -		if (!err)
> > -			goto igt_ktap_parser_end;
> > -	}
> > -
> > -	if (err < 0) {
> > -		if (errno == EPIPE)
> > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -		else
> > -			igt_warn("error reading kmsg (%m)\n");
> > -	}
> > -
> > -igt_ktap_parser_end:
> > -	free(suite_name);
> > -	free(case_name);
> > -
> > -	if (!err)
> > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > -
> > -	results.still_running = false;
> > -
> > -	if (ktap)
> > -		igt_ktap_free(ktap);
> > -
> > -	return NULL;
> > -}
> > -
> > -static pthread_t ktap_parser_thread;
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > -{
> > -	IGT_INIT_LIST_HEAD(&results.list);
> > -	pthread_mutex_init(&results.mutex, NULL);
> > -	results.still_running = true;
> > -
> > -	ktap_args.fd = fd;
> > -	ktap_args.is_builtin = is_builtin;
> > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > -
> > -	return &results;
> > -}
> > -
> > -void ktap_parser_cancel(void)
> > -{
> > -	pthread_cancel(ktap_parser_thread);
> > -}
> > -
> > -int ktap_parser_stop(void)
> > -{
> > -	pthread_join(ktap_parser_thread, NULL);
> > -	return ktap_args.ret;
> > -}
> > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > index 6f8da3eab6..c422636bfc 100644
> > --- a/lib/igt_ktap.h
> > +++ b/lib/igt_ktap.h
> > @@ -27,8 +27,6 @@
> >  
> >  #define BUF_LEN 4096
> >  
> > -#include <pthread.h>
> > -
> >  #include "igt_list.h"
> >  
> >  struct igt_ktap_result {
> > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> >  void igt_ktap_free(struct igt_ktap_results *ktap);
> >  
> > -void *igt_ktap_parser(void *unused);
> > -
> > -typedef struct ktap_test_results_element {
> > -	char test_name[BUF_LEN + 1];
> > -	bool passed;
> > -	struct igt_list_head link;
> > -} ktap_test_results_element;
> > -
> > -struct ktap_test_results {
> > -	struct igt_list_head list;
> > -	pthread_mutex_t mutex;
> > -	bool still_running;
> > -};
> > -
> > -
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > -void ktap_parser_cancel(void);
> > -int ktap_parser_stop(void);
> > -
> >  #endif /* IGT_KTAP_H */
> 





^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
  2023-10-10 13:33     ` [Intel-xe] " Mauro Carvalho Chehab
  (?)
@ 2023-10-10 17:54       ` Janusz Krzysztofik
  -1 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 17:54 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: igt-dev, intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Mauro,

Thanks for review.

On Tuesday, 10 October 2023 15:33:57 CEST Mauro Carvalho Chehab wrote:
> On Mon,  9 Oct 2023 14:27:55 +0200
> Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com> wrote:
> 
> > There was an attempt to parse KTAP reports in the background while a kunit
> > test module is loading.  However, since dynamic sub-subtests can be
> > executed only from the main thread, that attempt was not quite successful,
> > as IGT results from all executed kunit test cases were generated only
> > after loading of kunit test module completed.
> > 
> > Now that the parser maintains its state and we can call it separately for
> > each input line of a KTAP report, it is perfectly possible to call the
> > parser from the main thread while the module is loading in the background,
> > and convert results from kunit test cases immediately to results of IGT
> > dynamic sub-subtests by running an igt_dynamic() section for each result
> > as soon as returned by the parser.
> > 
> > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > result obtained from igt_ktap_parse() called from the main thread.
> > 
> > Also, drop no longer needed functions from igt_ktap soruces.
> > 
> > v3: Fix ktap structure not freed on lseek error,
> >   - fix initial SIGCHLD handler not restored,
> >   - fix missing handling of potential errors returned by sigaction,
> >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> >     synchronization with modprobe thread,
> >   - fix potentially illegal use of igt_assert() called outside of
> >     dynamic sub-subtest section,
> >   - fix unsupported exit code potentially passed to igt_fail(),
> >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> >     after a valid result from the parser has been processed,
> >   - fix trailing newlines missing from error messages,
> >   - add more debug statements,
> >   - integrate common code around kunit_result_free() into it.
> > v2: Interrupt blocking read() on modprobe failure.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > ---
> >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> >  lib/igt_ktap.c | 568 -------------------------------------------------
> >  lib/igt_ktap.h |  22 --
> >  3 files changed, 222 insertions(+), 629 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index 426ae5b26f..7bca4cdaab 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright © 2016 Intel Corporation
> > + * Copyright © 2016-2023 Intel Corporation
> >   *
> >   * Permission is hereby granted, free of charge, to any person obtaining a
> >   * copy of this software and associated documentation files (the "Software"),
> > @@ -26,7 +26,12 @@
> >  #include <errno.h>
> >  #include <fcntl.h>
> >  #include <pthread.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> >  #include <sys/utsname.h>
> > +#include <unistd.h>
> > +
> > +#include "assembler/brw_compat.h"	/* [un]likely() */
> >  
> >  #include "igt_aux.h"
> >  #include "igt_core.h"
> > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> >  }
> >  
> >  struct modprobe_data {
> > +	pthread_t parent;
> >  	struct kmod_module *kmod;
> >  	const char *opts;
> >  	int err;
> > +	pthread_mutex_t lock;
> > +	pthread_t thread;
> >  };
> >  
> >  static void *modprobe_task(void *arg)
> > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> >  
> >  	data->err = modprobe(data->kmod, data->opts);
> >  
> > +	if (igt_debug_on(data->err)) {
> > +		int err;
> > +
> > +		while (err = pthread_mutex_trylock(&data->lock),
> > +		       err && !igt_debug_on(err != EBUSY))
> > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> 
> I guess you need here an "igt_debug_on_once", as it doesn't make
> sense to have a (potentially) endless loop here printing error
> messages.

Right.  Since we don't have igt_debug_on_once() implemented, I'll open code 
that improvement.

Thanks,
Janusz

> 
> the other changes LGTM.
> 
> > +	} else {
> > +		/* let main thread use mutex to detect modprobe completion */
> > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > +	}
> > +
> >  	return NULL;
> >  }
> >  
> > +static void kunit_sigchld_handler(int signal)
> > +{
> > +	return;
> > +}
> > +
> > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > +				 struct modprobe_data *modprobe,
> > +				 int fd, struct igt_ktap_results *ktap)
> > +{
> > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > +			 *saved;
> > +	char record[BUF_LEN + 1], *buf;
> > +	unsigned long taints;
> > +	int ret;
> > +
> > +	do {
> > +		int err;
> > +
> > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > +			return -ENOTRECOVERABLE;
> > +
> > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > +		if (err == -1)
> > +			return -errno;
> > +		else if (unlikely(err))
> > +			return err;
> > +
> > +		err = pthread_mutex_lock(&modprobe->lock);
> > +		switch (err) {
> > +		case EOWNERDEAD:
> > +			/* leave the mutex unrecoverable */
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			__attribute__ ((fallthrough));
> > +		case ENOTRECOVERABLE:
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			if (igt_debug_on(modprobe->err))
> > +				return modprobe->err;
> > +			break;
> > +		case 0:
> > +			break;
> > +		default:
> > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			return -err;
> > +		}
> > +
> > +		ret = read(fd, record, BUF_LEN);
> > +
> > +		if (!err) {
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +		}
> > +
> > +		if (igt_debug_on(!ret))
> > +			return -ENODATA;
> > +		if (igt_debug_on(ret == -1))
> > +			return -errno;
> > +		if (unlikely(igt_debug_on(ret < 0)))
> > +			break;
> > +
> > +		/* skip kmsg continuation lines */
> > +		if (igt_debug_on(*record == ' '))
> > +			continue;
> > +
> > +		/* NULL-terminate the record */
> > +		record[ret] = '\0';
> > +
> > +		/* detect start of log message, continue if not found */
> > +		buf = strchrnul(record, ';');
> > +		if (igt_debug_on(*buf == '\0'))
> > +			continue;
> > +		buf++;
> > +
> > +		ret = igt_ktap_parse(buf, ktap);
> > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > +			break;
> > +	} while (igt_list_empty(results));
> > +
> > +	return ret;
> > +}
> > +
> > +static void kunit_result_free(struct igt_ktap_result **r,
> > +			      char **suite_name, char **case_name)
> > +{
> > +	if (!*r)
> > +		return;
> > +
> > +	igt_list_del(&(*r)->link);
> > +
> > +	if ((*r)->suite_name != *suite_name) {
> > +		free(*suite_name);
> > +		*suite_name = (*r)->suite_name;
> > +	}
> > +
> > +	if ((*r)->case_name != *case_name) {
> > +		free(*case_name);
> > +		*case_name = (*r)->case_name;
> > +	}
> > +
> > +	free((*r)->msg);
> > +	free(*r);
> > +	*r = NULL;
> > +}
> > +
> >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  {
> > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > -	struct kmod_module *kunit_kmod;
> > -	bool is_builtin;
> > -	struct ktap_test_results *results;
> > -	pthread_t modprobe_thread;
> > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > +	char *suite_name = NULL, *case_name = NULL;
> > +	struct igt_ktap_result *r, *rn;
> > +	struct igt_ktap_results *ktap;
> > +	pthread_mutexattr_t attr;
> > +	IGT_LIST_HEAD(results);
> >  	unsigned long taints;
> >  	int flags, ret;
> >  
> > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  
> >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> >  
> > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > -	kmod_module_unref(kunit_kmod);
> > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> >  
> > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > +	ktap = igt_ktap_alloc(&results);
> > +	igt_require(ktap);
> >  
> > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> >  					modprobe_task, &modprobe))) {
> > -		ktap_parser_cancel();
> > -		igt_ignore_warn(ktap_parser_stop());
> > +		igt_ktap_free(ktap);
> >  		igt_skip("Failed to create a modprobe thread\n");
> >  	}
> >  
> > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > -	{
> > -		struct ktap_test_results_element *result;
> > -
> > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > -			ktap_parser_cancel();
> > +	do {
> > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > +					    tst->kmsg, ktap);
> > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> >  			break;
> > -		}
> >  
> > -		if (igt_kernel_tainted(&taints)) {
> > -			ktap_parser_cancel();
> > -			pthread_cancel(modprobe_thread);
> > +		if (igt_debug_on(igt_list_empty(&results)))
> >  			break;
> > -		}
> >  
> > -		pthread_mutex_lock(&results->mutex);
> > -		if (igt_list_empty(&results->list)) {
> > -			pthread_mutex_unlock(&results->mutex);
> > -			continue;
> > -		}
> > +		r = igt_list_first_entry(&results, r, link);
> >  
> > -		result = igt_list_first_entry(&results->list, result, link);
> > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > +			if (r->code == IGT_EXIT_INVALID) {
> > +				/* parametrized test case, get actual result */
> > +				kunit_result_free(&r, &suite_name, &case_name);
> >  
> > -		igt_list_del(&result->link);
> > -		pthread_mutex_unlock(&results->mutex);
> > +				igt_assert(igt_list_empty(&results));
> >  
> > -		igt_dynamic(result->test_name) {
> > -			igt_assert(READ_ONCE(result->passed));
> > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > +							    tst->kmsg, ktap);
> > +				if (ret != -EINPROGRESS)
> > +					igt_fail_on(ret);
> > +
> > +				igt_fail_on(igt_list_empty(&results));
> > +
> > +				r = igt_list_first_entry(&results, r, link);
> > +
> > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > +					      "suite_name expected: %s, got: %s\n",
> > +					      suite_name, r->suite_name);
> > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > +					      "case_name expected: %s, got: %s\n",
> > +					      case_name, r->case_name);
> > +			}
> >  
> > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > +
> > +			if (r->msg && *r->msg) {
> > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > +					      "%s\n", r->msg);
> > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > +					      "%s\n", r->msg);
> > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > +					      "%s\n", r->msg);
> > +			} else {
> > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > +				if (r->code == IGT_EXIT_ABORT)
> > +					igt_fail(r->code);
> > +			}
> > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > +
> > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > +			case 0:
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				break;
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> >  				igt_assert_eq(modprobe.err, 0);
> > +				break;
> > +			default:
> > +				igt_debug("pthread_mutex_lock() failed\n");
> > +				break;
> > +			}
> >  
> >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> >  		}
> >  
> > -		free(result);
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	} while (ret == -EINPROGRESS);
> > +
> > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	free(case_name);
> > +	free(suite_name);
> > +
> > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > +	case 0:
> > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> > +	case EOWNERDEAD:
> > +		/* leave the mutex unrecoverable */
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		break;
> > +	case ENOTRECOVERABLE:
> > +		break;
> > +	default:
> > +		igt_debug("pthread_mutex_lock() failed\n");
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> >  	}
> >  
> > -	pthread_join(modprobe_thread, NULL);
> > -
> > -	ret = ktap_parser_stop();
> > +	igt_ktap_free(ktap);
> >  
> >  	igt_skip_on(modprobe.err);
> >  	igt_skip_on(igt_kernel_tainted(&taints));
> > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > index 5eac102417..53a6c63288 100644
> > --- a/lib/igt_ktap.c
> > +++ b/lib/igt_ktap.c
> > @@ -4,17 +4,11 @@
> >   * Copyright © 2023 Intel Corporation
> >   */
> >  
> > -#include <ctype.h>
> > -#include <limits.h>
> > -#include <libkmod.h>
> > -#include <pthread.h>
> >  #include <errno.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >  #include <string.h>
> > -#include <unistd.h>
> >  
> > -#include "igt_aux.h"
> >  #include "igt_core.h"
> >  #include "igt_ktap.h"
> >  #include "igt_list.h"
> > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> >  {
> >  	free(ktap);
> >  }
> > -
> > -#define DELIMITER "-"
> > -
> > -struct ktap_parser_args {
> > -	int fd;
> > -	bool is_builtin;
> > -	int ret;
> > -} ktap_args;
> > -
> > -static struct ktap_test_results results;
> > -
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > -
> > -/**
> > - * log_to_end:
> > - * @level: #igt_log_level
> > - * @record: record to store the read data
> > - * @format: format string
> > - * @...: optional arguments used in the format string
> > - *
> > - * This is an altered version of the generic structured logging helper function
> > - * igt_log capable of reading to the end of a given line.
> > - *
> > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > - */
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...)
> > -{
> > -	va_list args;
> > -	const char *lend;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	char *cutoff = strchr(record, '\n');
> > -	if (cutoff) {
> > -		if (cutoff - record < BUF_LEN)
> > -			cutoff[1] = '\0';
> > -	}
> > -
> > -	va_start(args, format);
> > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > -	va_end(args);
> > -
> > -	lend = strchrnul(record, '\n');
> > -	while (*lend == '\0') {
> > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		lend = strchrnul(record, '\n');
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * lookup_value:
> > - * @haystack: the string to search in
> > - * @needle: the string to search for
> > - *
> > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > - */
> > -static long lookup_value(const char *haystack, const char *needle)
> > -{
> > -	const char *needle_rptr;
> > -	char *needle_end;
> > -	long num;
> > -
> > -	needle_rptr = strcasestr(haystack, needle);
> > -
> > -	if (needle_rptr == NULL)
> > -		return -1;
> > -
> > -	/* Skip search string and whitespaces after it */
> > -	needle_rptr += strlen(needle);
> > -
> > -	num = strtol(needle_rptr, &needle_end, 10);
> > -
> > -	if (needle_rptr == needle_end)
> > -		return -1;
> > -
> > -	if (num == LONG_MIN || num == LONG_MAX)
> > -		return 0;
> > -
> > -	return num > 0 ? num : 0;
> > -}
> > -
> > -/**
> > - * tap_version_present:
> > - * @record: buffer with tap data
> > - * @print_info: whether tap version should be printed or not
> > - *
> > - * Returns:
> > - * 0 if not found
> > - * 1 if found
> > - */
> > -static int tap_version_present(char* record, bool print_info)
> > -{
> > -	/*
> > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> > -	 *
> > -	 * but actually isn't, as it currently depends on the KUnit module
> > -	 * being built-in, so we can't rely on it every time
> > -	 */
> > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > -	char *cutoff;
> > -
> > -	if (version_rptr == NULL)
> > -		return 0;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	cutoff = strchr(version_rptr, '\n');
> > -	if (cutoff)
> > -		cutoff[0] = '\0';
> > -
> > -	if (print_info)
> > -		igt_info("%s\n", version_rptr);
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * find_next_tap_subtest:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @is_builtin: whether KUnit is built-in or not
> > - *
> > - * Returns:
> > - * 0 if there's missing information
> > - * -1 if not found
> > - * -2 if there are problems while reading the file.
> > - * any other value corresponds to the amount of cases of the next (sub)test
> > - */
> > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > -{
> > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > -	long test_count;
> > -	char *cutoff;
> > -
> > -	test_name[0] = '\0';
> > -	test_name[BUF_LEN] = '\0';
> > -
> > -	test_lookup_str = " subtest: ";
> > -	subtest_lookup_str = " test: ";
> > -
> > -	if (!tap_version_present(record, true))
> > -		return -1;
> > -
> > -	if (is_builtin) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -	}
> > -
> > -	name_rptr = strcasestr(record, test_lookup_str);
> > -	if (name_rptr != NULL) {
> > -		name_rptr += strlen(test_lookup_str);
> > -	} else {
> > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > -		if (name_rptr != NULL)
> > -			name_rptr += strlen(subtest_lookup_str);
> > -	}
> > -
> > -	if (name_rptr == NULL) {
> > -		if (!is_builtin)
> > -			/* We've probably found nothing */
> > -			return -1;
> > -		igt_info("Missing test name\n");
> > -	} else {
> > -		strncpy(test_name, name_rptr, BUF_LEN);
> > -		/* Cutoff after newline character, in order to not display garbage */
> > -		cutoff = strchr(test_name, '\n');
> > -		if (cutoff)
> > -			cutoff[0] = '\0';
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("unknown error reading kmsg (%m)\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		/* Now we can be sure we found tests */
> > -		if (!is_builtin)
> > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > -	}
> > -
> > -	/*
> > -	 * Total test count will almost always appear as 0..N at the beginning
> > -	 * of a run, so we use it to reliably identify a new run
> > -	 */
> > -	test_count = lookup_value(record, "..");
> > -
> > -	if (test_count <= 0) {
> > -		igt_info("Missing test count\n");
> > -		if (test_name[0] == '\0')
> > -			return 0;
> > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > -				"Running some tests in: %s\n",
> > -				test_name) < 0)
> > -			return -2;
> > -		return 0;
> > -	} else if (test_name[0] == '\0') {
> > -		igt_info("Running %ld tests...\n", test_count);
> > -		return 0;
> > -	}
> > -
> > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > -			"Executing %ld tests in: %s\n",
> > -			test_count, test_name) < 0)
> > -		return -2;
> > -
> > -	return test_count;
> > -}
> > -
> > -/**
> > - * parse_kmsg_for_tap:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @test_name: buffer to store the test name
> > - *
> > - * Returns:
> > - * 1 if no results were found
> > - * 0 if a test succeded
> > - * -1 if a test failed
> > - * -2 if there are problems reading the file
> > - */
> > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > -{
> > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > -	char *test_name_end;
> > -
> > -	ok_lookup_str = "ok ";
> > -	nok_lookup_str = "not ok ";
> > -
> > -	lstart = strchrnul(record, ';');
> > -
> > -	if (*lstart == '\0') {
> > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > -		return -2;
> > -	}
> > -
> > -	lstart++;
> > -	while (isspace(*lstart))
> > -		lstart++;
> > -
> > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > -	if (nok_rptr != NULL) {
> > -		nok_rptr += strlen(nok_lookup_str);
> > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > -			nok_rptr++;
> > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > -			       "%s", lstart) < 0)
> > -			return -2;
> > -		return -1;
> > -	}
> > -
> > -	comment_start = strchrnul(lstart, '#');
> > -
> > -	/* Check if we're still in a subtest */
> > -	if (*comment_start != '\0') {
> > -		comment_start++;
> > -		value_parse_start = comment_start;
> > -
> > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > -				       "%s", lstart) < 0)
> > -				return -2;
> > -			return -1;
> > -		}
> > -	}
> > -
> > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > -	if (ok_rptr != NULL) {
> > -		ok_rptr += strlen(ok_lookup_str);
> > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > -			ok_rptr++;
> > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		return 0;
> > -	}
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * parse_tap_level:
> > - * @fd: file descriptor
> > - * @base_test_name: test_name from upper recursion level
> > - * @test_count: test_count of this level
> > - * @failed_tests: top level failed_tests pointer
> > - * @found_tests: top level found_tests pointer
> > - * @is_builtin: whether the KUnit module is built-in or not
> > - *
> > - * Returns:
> > - * 0 if succeded
> > - * -1 if error occurred
> > - */
> > -__maybe_unused
> > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > -			   bool *found_tests, bool is_builtin)
> > -{
> > -	char record[BUF_LEN + 1];
> > -	struct ktap_test_results_element *r;
> > -	int internal_test_count;
> > -	char test_name[BUF_LEN + 1];
> > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > -
> > -	for (int i = 0; i < test_count; i++) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("error reading kmsg (%m)\n");
> > -
> > -			return -1;
> > -		}
> > -
> > -		/* Sublevel found */
> > -		if (tap_version_present(record, false))
> > -		{
> > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > -								    is_builtin);
> > -			switch (internal_test_count) {
> > -			case -2:
> > -				/* No more data to read */
> > -				return -1;
> > -			case -1:
> > -				/* No test found */
> > -				return -1;
> > -			case 0:
> > -				/* Tests found, but they're missing info */
> > -				*found_tests = true;
> > -				return -1;
> > -			default:
> > -				*found_tests = true;
> > -
> > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > -				    base_test_name_for_next_level[0])
> > -					strncat(base_test_name_for_next_level, DELIMITER,
> > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > -
> > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > -						    internal_test_count, failed_tests, found_tests,
> > -						    is_builtin) == -1)
> > -					return -1;
> > -				break;
> > -			}
> > -		}
> > -
> > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > -		case -2:
> > -			return -1;
> > -		case -1:
> > -			*failed_tests = true;
> > -
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = false;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		case 0:
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = true;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		default:
> > -			break;
> > -		}
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * igt_ktap_parser:
> > - *
> > - * This function parses the output of a ktap script and passes it to main thread.
> > - */
> > -void *igt_ktap_parser(void *unused)
> > -{
> > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > -	struct igt_ktap_results *ktap = NULL;
> > -	int fd = ktap_args.fd;
> > -	IGT_LIST_HEAD(list);
> > -	int err;
> > -
> > -	ktap = igt_ktap_alloc(&list);
> > -	if (igt_debug_on(!ktap))
> > -		goto igt_ktap_parser_end;
> > -
> > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > -		struct igt_ktap_result *r, *rn;
> > -
> > -		/* skip kmsg continuation lines */
> > -		if (igt_debug_on(*record == ' '))
> > -			continue;
> > -
> > -		/* NULL-terminate the record */
> > -		record[err] = '\0';
> > -
> > -		/* detect start of log message, continue if not found */
> > -		buf = strchrnul(record, ';');
> > -		if (igt_debug_on(*buf == '\0'))
> > -			continue;
> > -		buf++;
> > -
> > -		err = igt_ktap_parse(buf, ktap);
> > -
> > -		/* parsing error */
> > -		if (err && err != -EINPROGRESS)
> > -			goto igt_ktap_parser_end;
> > -
> > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > -			struct ktap_test_results_element *result = NULL;
> > -			int code = r->code;
> > -
> > -			if (code != IGT_EXIT_INVALID)
> > -				result = calloc(1, sizeof(*result));
> > -
> > -			if (result) {
> > -				snprintf(result->test_name, sizeof(result->test_name),
> > -					 "%s-%s", r->suite_name, r->case_name);
> > -
> > -				if (code == IGT_EXIT_SUCCESS)
> > -					result->passed = true;
> > -			}
> > -
> > -			igt_list_del(&r->link);
> > -			if (r->suite_name != suite_name) {
> > -				free(suite_name);
> > -				suite_name = r->suite_name;
> > -			}
> > -			if (r->case_name != case_name) {
> > -				free(case_name);
> > -				case_name = r->case_name;
> > -			}
> > -			free(r->msg);
> > -			free(r);
> > -
> > -			/*
> > -			 * no extra result record expected on start
> > -			 * of parametrized test case -- skip it
> > -			 */
> > -			if (code == IGT_EXIT_INVALID)
> > -				continue;
> > -
> > -			if (!result) {
> > -				err = -ENOMEM;
> > -				goto igt_ktap_parser_end;
> > -			}
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&result->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -		}
> > -
> > -		/* end of KTAP report */
> > -		if (!err)
> > -			goto igt_ktap_parser_end;
> > -	}
> > -
> > -	if (err < 0) {
> > -		if (errno == EPIPE)
> > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -		else
> > -			igt_warn("error reading kmsg (%m)\n");
> > -	}
> > -
> > -igt_ktap_parser_end:
> > -	free(suite_name);
> > -	free(case_name);
> > -
> > -	if (!err)
> > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > -
> > -	results.still_running = false;
> > -
> > -	if (ktap)
> > -		igt_ktap_free(ktap);
> > -
> > -	return NULL;
> > -}
> > -
> > -static pthread_t ktap_parser_thread;
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > -{
> > -	IGT_INIT_LIST_HEAD(&results.list);
> > -	pthread_mutex_init(&results.mutex, NULL);
> > -	results.still_running = true;
> > -
> > -	ktap_args.fd = fd;
> > -	ktap_args.is_builtin = is_builtin;
> > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > -
> > -	return &results;
> > -}
> > -
> > -void ktap_parser_cancel(void)
> > -{
> > -	pthread_cancel(ktap_parser_thread);
> > -}
> > -
> > -int ktap_parser_stop(void)
> > -{
> > -	pthread_join(ktap_parser_thread, NULL);
> > -	return ktap_args.ret;
> > -}
> > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > index 6f8da3eab6..c422636bfc 100644
> > --- a/lib/igt_ktap.h
> > +++ b/lib/igt_ktap.h
> > @@ -27,8 +27,6 @@
> >  
> >  #define BUF_LEN 4096
> >  
> > -#include <pthread.h>
> > -
> >  #include "igt_list.h"
> >  
> >  struct igt_ktap_result {
> > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> >  void igt_ktap_free(struct igt_ktap_results *ktap);
> >  
> > -void *igt_ktap_parser(void *unused);
> > -
> > -typedef struct ktap_test_results_element {
> > -	char test_name[BUF_LEN + 1];
> > -	bool passed;
> > -	struct igt_list_head link;
> > -} ktap_test_results_element;
> > -
> > -struct ktap_test_results {
> > -	struct igt_list_head list;
> > -	pthread_mutex_t mutex;
> > -	bool still_running;
> > -};
> > -
> > -
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > -void ktap_parser_cancel(void);
> > -int ktap_parser_stop(void);
> > -
> >  #endif /* IGT_KTAP_H */
> 





^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-10 17:54       ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 17:54 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: igt-dev, Janusz Krzysztofik, intel-gfx, intel-xe

Hi Mauro,

Thanks for review.

On Tuesday, 10 October 2023 15:33:57 CEST Mauro Carvalho Chehab wrote:
> On Mon,  9 Oct 2023 14:27:55 +0200
> Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com> wrote:
> 
> > There was an attempt to parse KTAP reports in the background while a kunit
> > test module is loading.  However, since dynamic sub-subtests can be
> > executed only from the main thread, that attempt was not quite successful,
> > as IGT results from all executed kunit test cases were generated only
> > after loading of kunit test module completed.
> > 
> > Now that the parser maintains its state and we can call it separately for
> > each input line of a KTAP report, it is perfectly possible to call the
> > parser from the main thread while the module is loading in the background,
> > and convert results from kunit test cases immediately to results of IGT
> > dynamic sub-subtests by running an igt_dynamic() section for each result
> > as soon as returned by the parser.
> > 
> > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > result obtained from igt_ktap_parse() called from the main thread.
> > 
> > Also, drop no longer needed functions from igt_ktap soruces.
> > 
> > v3: Fix ktap structure not freed on lseek error,
> >   - fix initial SIGCHLD handler not restored,
> >   - fix missing handling of potential errors returned by sigaction,
> >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> >     synchronization with modprobe thread,
> >   - fix potentially illegal use of igt_assert() called outside of
> >     dynamic sub-subtest section,
> >   - fix unsupported exit code potentially passed to igt_fail(),
> >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> >     after a valid result from the parser has been processed,
> >   - fix trailing newlines missing from error messages,
> >   - add more debug statements,
> >   - integrate common code around kunit_result_free() into it.
> > v2: Interrupt blocking read() on modprobe failure.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > ---
> >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> >  lib/igt_ktap.c | 568 -------------------------------------------------
> >  lib/igt_ktap.h |  22 --
> >  3 files changed, 222 insertions(+), 629 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index 426ae5b26f..7bca4cdaab 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright © 2016 Intel Corporation
> > + * Copyright © 2016-2023 Intel Corporation
> >   *
> >   * Permission is hereby granted, free of charge, to any person obtaining a
> >   * copy of this software and associated documentation files (the "Software"),
> > @@ -26,7 +26,12 @@
> >  #include <errno.h>
> >  #include <fcntl.h>
> >  #include <pthread.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> >  #include <sys/utsname.h>
> > +#include <unistd.h>
> > +
> > +#include "assembler/brw_compat.h"	/* [un]likely() */
> >  
> >  #include "igt_aux.h"
> >  #include "igt_core.h"
> > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> >  }
> >  
> >  struct modprobe_data {
> > +	pthread_t parent;
> >  	struct kmod_module *kmod;
> >  	const char *opts;
> >  	int err;
> > +	pthread_mutex_t lock;
> > +	pthread_t thread;
> >  };
> >  
> >  static void *modprobe_task(void *arg)
> > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> >  
> >  	data->err = modprobe(data->kmod, data->opts);
> >  
> > +	if (igt_debug_on(data->err)) {
> > +		int err;
> > +
> > +		while (err = pthread_mutex_trylock(&data->lock),
> > +		       err && !igt_debug_on(err != EBUSY))
> > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> 
> I guess you need here an "igt_debug_on_once", as it doesn't make
> sense to have a (potentially) endless loop here printing error
> messages.

Right.  Since we don't have igt_debug_on_once() implemented, I'll open code 
that improvement.

Thanks,
Janusz

> 
> the other changes LGTM.
> 
> > +	} else {
> > +		/* let main thread use mutex to detect modprobe completion */
> > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > +	}
> > +
> >  	return NULL;
> >  }
> >  
> > +static void kunit_sigchld_handler(int signal)
> > +{
> > +	return;
> > +}
> > +
> > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > +				 struct modprobe_data *modprobe,
> > +				 int fd, struct igt_ktap_results *ktap)
> > +{
> > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > +			 *saved;
> > +	char record[BUF_LEN + 1], *buf;
> > +	unsigned long taints;
> > +	int ret;
> > +
> > +	do {
> > +		int err;
> > +
> > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > +			return -ENOTRECOVERABLE;
> > +
> > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > +		if (err == -1)
> > +			return -errno;
> > +		else if (unlikely(err))
> > +			return err;
> > +
> > +		err = pthread_mutex_lock(&modprobe->lock);
> > +		switch (err) {
> > +		case EOWNERDEAD:
> > +			/* leave the mutex unrecoverable */
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			__attribute__ ((fallthrough));
> > +		case ENOTRECOVERABLE:
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			if (igt_debug_on(modprobe->err))
> > +				return modprobe->err;
> > +			break;
> > +		case 0:
> > +			break;
> > +		default:
> > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			return -err;
> > +		}
> > +
> > +		ret = read(fd, record, BUF_LEN);
> > +
> > +		if (!err) {
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +		}
> > +
> > +		if (igt_debug_on(!ret))
> > +			return -ENODATA;
> > +		if (igt_debug_on(ret == -1))
> > +			return -errno;
> > +		if (unlikely(igt_debug_on(ret < 0)))
> > +			break;
> > +
> > +		/* skip kmsg continuation lines */
> > +		if (igt_debug_on(*record == ' '))
> > +			continue;
> > +
> > +		/* NULL-terminate the record */
> > +		record[ret] = '\0';
> > +
> > +		/* detect start of log message, continue if not found */
> > +		buf = strchrnul(record, ';');
> > +		if (igt_debug_on(*buf == '\0'))
> > +			continue;
> > +		buf++;
> > +
> > +		ret = igt_ktap_parse(buf, ktap);
> > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > +			break;
> > +	} while (igt_list_empty(results));
> > +
> > +	return ret;
> > +}
> > +
> > +static void kunit_result_free(struct igt_ktap_result **r,
> > +			      char **suite_name, char **case_name)
> > +{
> > +	if (!*r)
> > +		return;
> > +
> > +	igt_list_del(&(*r)->link);
> > +
> > +	if ((*r)->suite_name != *suite_name) {
> > +		free(*suite_name);
> > +		*suite_name = (*r)->suite_name;
> > +	}
> > +
> > +	if ((*r)->case_name != *case_name) {
> > +		free(*case_name);
> > +		*case_name = (*r)->case_name;
> > +	}
> > +
> > +	free((*r)->msg);
> > +	free(*r);
> > +	*r = NULL;
> > +}
> > +
> >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  {
> > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > -	struct kmod_module *kunit_kmod;
> > -	bool is_builtin;
> > -	struct ktap_test_results *results;
> > -	pthread_t modprobe_thread;
> > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > +	char *suite_name = NULL, *case_name = NULL;
> > +	struct igt_ktap_result *r, *rn;
> > +	struct igt_ktap_results *ktap;
> > +	pthread_mutexattr_t attr;
> > +	IGT_LIST_HEAD(results);
> >  	unsigned long taints;
> >  	int flags, ret;
> >  
> > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  
> >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> >  
> > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > -	kmod_module_unref(kunit_kmod);
> > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> >  
> > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > +	ktap = igt_ktap_alloc(&results);
> > +	igt_require(ktap);
> >  
> > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> >  					modprobe_task, &modprobe))) {
> > -		ktap_parser_cancel();
> > -		igt_ignore_warn(ktap_parser_stop());
> > +		igt_ktap_free(ktap);
> >  		igt_skip("Failed to create a modprobe thread\n");
> >  	}
> >  
> > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > -	{
> > -		struct ktap_test_results_element *result;
> > -
> > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > -			ktap_parser_cancel();
> > +	do {
> > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > +					    tst->kmsg, ktap);
> > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> >  			break;
> > -		}
> >  
> > -		if (igt_kernel_tainted(&taints)) {
> > -			ktap_parser_cancel();
> > -			pthread_cancel(modprobe_thread);
> > +		if (igt_debug_on(igt_list_empty(&results)))
> >  			break;
> > -		}
> >  
> > -		pthread_mutex_lock(&results->mutex);
> > -		if (igt_list_empty(&results->list)) {
> > -			pthread_mutex_unlock(&results->mutex);
> > -			continue;
> > -		}
> > +		r = igt_list_first_entry(&results, r, link);
> >  
> > -		result = igt_list_first_entry(&results->list, result, link);
> > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > +			if (r->code == IGT_EXIT_INVALID) {
> > +				/* parametrized test case, get actual result */
> > +				kunit_result_free(&r, &suite_name, &case_name);
> >  
> > -		igt_list_del(&result->link);
> > -		pthread_mutex_unlock(&results->mutex);
> > +				igt_assert(igt_list_empty(&results));
> >  
> > -		igt_dynamic(result->test_name) {
> > -			igt_assert(READ_ONCE(result->passed));
> > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > +							    tst->kmsg, ktap);
> > +				if (ret != -EINPROGRESS)
> > +					igt_fail_on(ret);
> > +
> > +				igt_fail_on(igt_list_empty(&results));
> > +
> > +				r = igt_list_first_entry(&results, r, link);
> > +
> > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > +					      "suite_name expected: %s, got: %s\n",
> > +					      suite_name, r->suite_name);
> > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > +					      "case_name expected: %s, got: %s\n",
> > +					      case_name, r->case_name);
> > +			}
> >  
> > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > +
> > +			if (r->msg && *r->msg) {
> > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > +					      "%s\n", r->msg);
> > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > +					      "%s\n", r->msg);
> > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > +					      "%s\n", r->msg);
> > +			} else {
> > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > +				if (r->code == IGT_EXIT_ABORT)
> > +					igt_fail(r->code);
> > +			}
> > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > +
> > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > +			case 0:
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				break;
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> >  				igt_assert_eq(modprobe.err, 0);
> > +				break;
> > +			default:
> > +				igt_debug("pthread_mutex_lock() failed\n");
> > +				break;
> > +			}
> >  
> >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> >  		}
> >  
> > -		free(result);
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	} while (ret == -EINPROGRESS);
> > +
> > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	free(case_name);
> > +	free(suite_name);
> > +
> > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > +	case 0:
> > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> > +	case EOWNERDEAD:
> > +		/* leave the mutex unrecoverable */
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		break;
> > +	case ENOTRECOVERABLE:
> > +		break;
> > +	default:
> > +		igt_debug("pthread_mutex_lock() failed\n");
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> >  	}
> >  
> > -	pthread_join(modprobe_thread, NULL);
> > -
> > -	ret = ktap_parser_stop();
> > +	igt_ktap_free(ktap);
> >  
> >  	igt_skip_on(modprobe.err);
> >  	igt_skip_on(igt_kernel_tainted(&taints));
> > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > index 5eac102417..53a6c63288 100644
> > --- a/lib/igt_ktap.c
> > +++ b/lib/igt_ktap.c
> > @@ -4,17 +4,11 @@
> >   * Copyright © 2023 Intel Corporation
> >   */
> >  
> > -#include <ctype.h>
> > -#include <limits.h>
> > -#include <libkmod.h>
> > -#include <pthread.h>
> >  #include <errno.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >  #include <string.h>
> > -#include <unistd.h>
> >  
> > -#include "igt_aux.h"
> >  #include "igt_core.h"
> >  #include "igt_ktap.h"
> >  #include "igt_list.h"
> > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> >  {
> >  	free(ktap);
> >  }
> > -
> > -#define DELIMITER "-"
> > -
> > -struct ktap_parser_args {
> > -	int fd;
> > -	bool is_builtin;
> > -	int ret;
> > -} ktap_args;
> > -
> > -static struct ktap_test_results results;
> > -
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > -
> > -/**
> > - * log_to_end:
> > - * @level: #igt_log_level
> > - * @record: record to store the read data
> > - * @format: format string
> > - * @...: optional arguments used in the format string
> > - *
> > - * This is an altered version of the generic structured logging helper function
> > - * igt_log capable of reading to the end of a given line.
> > - *
> > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > - */
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...)
> > -{
> > -	va_list args;
> > -	const char *lend;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	char *cutoff = strchr(record, '\n');
> > -	if (cutoff) {
> > -		if (cutoff - record < BUF_LEN)
> > -			cutoff[1] = '\0';
> > -	}
> > -
> > -	va_start(args, format);
> > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > -	va_end(args);
> > -
> > -	lend = strchrnul(record, '\n');
> > -	while (*lend == '\0') {
> > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		lend = strchrnul(record, '\n');
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * lookup_value:
> > - * @haystack: the string to search in
> > - * @needle: the string to search for
> > - *
> > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > - */
> > -static long lookup_value(const char *haystack, const char *needle)
> > -{
> > -	const char *needle_rptr;
> > -	char *needle_end;
> > -	long num;
> > -
> > -	needle_rptr = strcasestr(haystack, needle);
> > -
> > -	if (needle_rptr == NULL)
> > -		return -1;
> > -
> > -	/* Skip search string and whitespaces after it */
> > -	needle_rptr += strlen(needle);
> > -
> > -	num = strtol(needle_rptr, &needle_end, 10);
> > -
> > -	if (needle_rptr == needle_end)
> > -		return -1;
> > -
> > -	if (num == LONG_MIN || num == LONG_MAX)
> > -		return 0;
> > -
> > -	return num > 0 ? num : 0;
> > -}
> > -
> > -/**
> > - * tap_version_present:
> > - * @record: buffer with tap data
> > - * @print_info: whether tap version should be printed or not
> > - *
> > - * Returns:
> > - * 0 if not found
> > - * 1 if found
> > - */
> > -static int tap_version_present(char* record, bool print_info)
> > -{
> > -	/*
> > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> > -	 *
> > -	 * but actually isn't, as it currently depends on the KUnit module
> > -	 * being built-in, so we can't rely on it every time
> > -	 */
> > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > -	char *cutoff;
> > -
> > -	if (version_rptr == NULL)
> > -		return 0;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	cutoff = strchr(version_rptr, '\n');
> > -	if (cutoff)
> > -		cutoff[0] = '\0';
> > -
> > -	if (print_info)
> > -		igt_info("%s\n", version_rptr);
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * find_next_tap_subtest:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @is_builtin: whether KUnit is built-in or not
> > - *
> > - * Returns:
> > - * 0 if there's missing information
> > - * -1 if not found
> > - * -2 if there are problems while reading the file.
> > - * any other value corresponds to the amount of cases of the next (sub)test
> > - */
> > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > -{
> > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > -	long test_count;
> > -	char *cutoff;
> > -
> > -	test_name[0] = '\0';
> > -	test_name[BUF_LEN] = '\0';
> > -
> > -	test_lookup_str = " subtest: ";
> > -	subtest_lookup_str = " test: ";
> > -
> > -	if (!tap_version_present(record, true))
> > -		return -1;
> > -
> > -	if (is_builtin) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -	}
> > -
> > -	name_rptr = strcasestr(record, test_lookup_str);
> > -	if (name_rptr != NULL) {
> > -		name_rptr += strlen(test_lookup_str);
> > -	} else {
> > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > -		if (name_rptr != NULL)
> > -			name_rptr += strlen(subtest_lookup_str);
> > -	}
> > -
> > -	if (name_rptr == NULL) {
> > -		if (!is_builtin)
> > -			/* We've probably found nothing */
> > -			return -1;
> > -		igt_info("Missing test name\n");
> > -	} else {
> > -		strncpy(test_name, name_rptr, BUF_LEN);
> > -		/* Cutoff after newline character, in order to not display garbage */
> > -		cutoff = strchr(test_name, '\n');
> > -		if (cutoff)
> > -			cutoff[0] = '\0';
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("unknown error reading kmsg (%m)\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		/* Now we can be sure we found tests */
> > -		if (!is_builtin)
> > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > -	}
> > -
> > -	/*
> > -	 * Total test count will almost always appear as 0..N at the beginning
> > -	 * of a run, so we use it to reliably identify a new run
> > -	 */
> > -	test_count = lookup_value(record, "..");
> > -
> > -	if (test_count <= 0) {
> > -		igt_info("Missing test count\n");
> > -		if (test_name[0] == '\0')
> > -			return 0;
> > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > -				"Running some tests in: %s\n",
> > -				test_name) < 0)
> > -			return -2;
> > -		return 0;
> > -	} else if (test_name[0] == '\0') {
> > -		igt_info("Running %ld tests...\n", test_count);
> > -		return 0;
> > -	}
> > -
> > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > -			"Executing %ld tests in: %s\n",
> > -			test_count, test_name) < 0)
> > -		return -2;
> > -
> > -	return test_count;
> > -}
> > -
> > -/**
> > - * parse_kmsg_for_tap:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @test_name: buffer to store the test name
> > - *
> > - * Returns:
> > - * 1 if no results were found
> > - * 0 if a test succeded
> > - * -1 if a test failed
> > - * -2 if there are problems reading the file
> > - */
> > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > -{
> > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > -	char *test_name_end;
> > -
> > -	ok_lookup_str = "ok ";
> > -	nok_lookup_str = "not ok ";
> > -
> > -	lstart = strchrnul(record, ';');
> > -
> > -	if (*lstart == '\0') {
> > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > -		return -2;
> > -	}
> > -
> > -	lstart++;
> > -	while (isspace(*lstart))
> > -		lstart++;
> > -
> > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > -	if (nok_rptr != NULL) {
> > -		nok_rptr += strlen(nok_lookup_str);
> > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > -			nok_rptr++;
> > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > -			       "%s", lstart) < 0)
> > -			return -2;
> > -		return -1;
> > -	}
> > -
> > -	comment_start = strchrnul(lstart, '#');
> > -
> > -	/* Check if we're still in a subtest */
> > -	if (*comment_start != '\0') {
> > -		comment_start++;
> > -		value_parse_start = comment_start;
> > -
> > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > -				       "%s", lstart) < 0)
> > -				return -2;
> > -			return -1;
> > -		}
> > -	}
> > -
> > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > -	if (ok_rptr != NULL) {
> > -		ok_rptr += strlen(ok_lookup_str);
> > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > -			ok_rptr++;
> > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		return 0;
> > -	}
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * parse_tap_level:
> > - * @fd: file descriptor
> > - * @base_test_name: test_name from upper recursion level
> > - * @test_count: test_count of this level
> > - * @failed_tests: top level failed_tests pointer
> > - * @found_tests: top level found_tests pointer
> > - * @is_builtin: whether the KUnit module is built-in or not
> > - *
> > - * Returns:
> > - * 0 if succeded
> > - * -1 if error occurred
> > - */
> > -__maybe_unused
> > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > -			   bool *found_tests, bool is_builtin)
> > -{
> > -	char record[BUF_LEN + 1];
> > -	struct ktap_test_results_element *r;
> > -	int internal_test_count;
> > -	char test_name[BUF_LEN + 1];
> > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > -
> > -	for (int i = 0; i < test_count; i++) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("error reading kmsg (%m)\n");
> > -
> > -			return -1;
> > -		}
> > -
> > -		/* Sublevel found */
> > -		if (tap_version_present(record, false))
> > -		{
> > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > -								    is_builtin);
> > -			switch (internal_test_count) {
> > -			case -2:
> > -				/* No more data to read */
> > -				return -1;
> > -			case -1:
> > -				/* No test found */
> > -				return -1;
> > -			case 0:
> > -				/* Tests found, but they're missing info */
> > -				*found_tests = true;
> > -				return -1;
> > -			default:
> > -				*found_tests = true;
> > -
> > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > -				    base_test_name_for_next_level[0])
> > -					strncat(base_test_name_for_next_level, DELIMITER,
> > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > -
> > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > -						    internal_test_count, failed_tests, found_tests,
> > -						    is_builtin) == -1)
> > -					return -1;
> > -				break;
> > -			}
> > -		}
> > -
> > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > -		case -2:
> > -			return -1;
> > -		case -1:
> > -			*failed_tests = true;
> > -
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = false;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		case 0:
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = true;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		default:
> > -			break;
> > -		}
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * igt_ktap_parser:
> > - *
> > - * This function parses the output of a ktap script and passes it to main thread.
> > - */
> > -void *igt_ktap_parser(void *unused)
> > -{
> > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > -	struct igt_ktap_results *ktap = NULL;
> > -	int fd = ktap_args.fd;
> > -	IGT_LIST_HEAD(list);
> > -	int err;
> > -
> > -	ktap = igt_ktap_alloc(&list);
> > -	if (igt_debug_on(!ktap))
> > -		goto igt_ktap_parser_end;
> > -
> > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > -		struct igt_ktap_result *r, *rn;
> > -
> > -		/* skip kmsg continuation lines */
> > -		if (igt_debug_on(*record == ' '))
> > -			continue;
> > -
> > -		/* NULL-terminate the record */
> > -		record[err] = '\0';
> > -
> > -		/* detect start of log message, continue if not found */
> > -		buf = strchrnul(record, ';');
> > -		if (igt_debug_on(*buf == '\0'))
> > -			continue;
> > -		buf++;
> > -
> > -		err = igt_ktap_parse(buf, ktap);
> > -
> > -		/* parsing error */
> > -		if (err && err != -EINPROGRESS)
> > -			goto igt_ktap_parser_end;
> > -
> > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > -			struct ktap_test_results_element *result = NULL;
> > -			int code = r->code;
> > -
> > -			if (code != IGT_EXIT_INVALID)
> > -				result = calloc(1, sizeof(*result));
> > -
> > -			if (result) {
> > -				snprintf(result->test_name, sizeof(result->test_name),
> > -					 "%s-%s", r->suite_name, r->case_name);
> > -
> > -				if (code == IGT_EXIT_SUCCESS)
> > -					result->passed = true;
> > -			}
> > -
> > -			igt_list_del(&r->link);
> > -			if (r->suite_name != suite_name) {
> > -				free(suite_name);
> > -				suite_name = r->suite_name;
> > -			}
> > -			if (r->case_name != case_name) {
> > -				free(case_name);
> > -				case_name = r->case_name;
> > -			}
> > -			free(r->msg);
> > -			free(r);
> > -
> > -			/*
> > -			 * no extra result record expected on start
> > -			 * of parametrized test case -- skip it
> > -			 */
> > -			if (code == IGT_EXIT_INVALID)
> > -				continue;
> > -
> > -			if (!result) {
> > -				err = -ENOMEM;
> > -				goto igt_ktap_parser_end;
> > -			}
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&result->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -		}
> > -
> > -		/* end of KTAP report */
> > -		if (!err)
> > -			goto igt_ktap_parser_end;
> > -	}
> > -
> > -	if (err < 0) {
> > -		if (errno == EPIPE)
> > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -		else
> > -			igt_warn("error reading kmsg (%m)\n");
> > -	}
> > -
> > -igt_ktap_parser_end:
> > -	free(suite_name);
> > -	free(case_name);
> > -
> > -	if (!err)
> > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > -
> > -	results.still_running = false;
> > -
> > -	if (ktap)
> > -		igt_ktap_free(ktap);
> > -
> > -	return NULL;
> > -}
> > -
> > -static pthread_t ktap_parser_thread;
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > -{
> > -	IGT_INIT_LIST_HEAD(&results.list);
> > -	pthread_mutex_init(&results.mutex, NULL);
> > -	results.still_running = true;
> > -
> > -	ktap_args.fd = fd;
> > -	ktap_args.is_builtin = is_builtin;
> > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > -
> > -	return &results;
> > -}
> > -
> > -void ktap_parser_cancel(void)
> > -{
> > -	pthread_cancel(ktap_parser_thread);
> > -}
> > -
> > -int ktap_parser_stop(void)
> > -{
> > -	pthread_join(ktap_parser_thread, NULL);
> > -	return ktap_args.ret;
> > -}
> > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > index 6f8da3eab6..c422636bfc 100644
> > --- a/lib/igt_ktap.h
> > +++ b/lib/igt_ktap.h
> > @@ -27,8 +27,6 @@
> >  
> >  #define BUF_LEN 4096
> >  
> > -#include <pthread.h>
> > -
> >  #include "igt_list.h"
> >  
> >  struct igt_ktap_result {
> > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> >  void igt_ktap_free(struct igt_ktap_results *ktap);
> >  
> > -void *igt_ktap_parser(void *unused);
> > -
> > -typedef struct ktap_test_results_element {
> > -	char test_name[BUF_LEN + 1];
> > -	bool passed;
> > -	struct igt_list_head link;
> > -} ktap_test_results_element;
> > -
> > -struct ktap_test_results {
> > -	struct igt_list_head list;
> > -	pthread_mutex_t mutex;
> > -	bool still_running;
> > -};
> > -
> > -
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > -void ktap_parser_cancel(void);
> > -int ktap_parser_stop(void);
> > -
> >  #endif /* IGT_KTAP_H */
> 





^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-10 17:54       ` Janusz Krzysztofik
  0 siblings, 0 replies; 77+ messages in thread
From: Janusz Krzysztofik @ 2023-10-10 17:54 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: igt-dev, intel-gfx, intel-xe

Hi Mauro,

Thanks for review.

On Tuesday, 10 October 2023 15:33:57 CEST Mauro Carvalho Chehab wrote:
> On Mon,  9 Oct 2023 14:27:55 +0200
> Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com> wrote:
> 
> > There was an attempt to parse KTAP reports in the background while a kunit
> > test module is loading.  However, since dynamic sub-subtests can be
> > executed only from the main thread, that attempt was not quite successful,
> > as IGT results from all executed kunit test cases were generated only
> > after loading of kunit test module completed.
> > 
> > Now that the parser maintains its state and we can call it separately for
> > each input line of a KTAP report, it is perfectly possible to call the
> > parser from the main thread while the module is loading in the background,
> > and convert results from kunit test cases immediately to results of IGT
> > dynamic sub-subtests by running an igt_dynamic() section for each result
> > as soon as returned by the parser.
> > 
> > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > result obtained from igt_ktap_parse() called from the main thread.
> > 
> > Also, drop no longer needed functions from igt_ktap soruces.
> > 
> > v3: Fix ktap structure not freed on lseek error,
> >   - fix initial SIGCHLD handler not restored,
> >   - fix missing handling of potential errors returned by sigaction,
> >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> >     synchronization with modprobe thread,
> >   - fix potentially illegal use of igt_assert() called outside of
> >     dynamic sub-subtest section,
> >   - fix unsupported exit code potentially passed to igt_fail(),
> >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> >     after a valid result from the parser has been processed,
> >   - fix trailing newlines missing from error messages,
> >   - add more debug statements,
> >   - integrate common code around kunit_result_free() into it.
> > v2: Interrupt blocking read() on modprobe failure.
> > 
> > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > ---
> >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> >  lib/igt_ktap.c | 568 -------------------------------------------------
> >  lib/igt_ktap.h |  22 --
> >  3 files changed, 222 insertions(+), 629 deletions(-)
> > 
> > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > index 426ae5b26f..7bca4cdaab 100644
> > --- a/lib/igt_kmod.c
> > +++ b/lib/igt_kmod.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright © 2016 Intel Corporation
> > + * Copyright © 2016-2023 Intel Corporation
> >   *
> >   * Permission is hereby granted, free of charge, to any person obtaining a
> >   * copy of this software and associated documentation files (the "Software"),
> > @@ -26,7 +26,12 @@
> >  #include <errno.h>
> >  #include <fcntl.h>
> >  #include <pthread.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> >  #include <sys/utsname.h>
> > +#include <unistd.h>
> > +
> > +#include "assembler/brw_compat.h"	/* [un]likely() */
> >  
> >  #include "igt_aux.h"
> >  #include "igt_core.h"
> > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> >  }
> >  
> >  struct modprobe_data {
> > +	pthread_t parent;
> >  	struct kmod_module *kmod;
> >  	const char *opts;
> >  	int err;
> > +	pthread_mutex_t lock;
> > +	pthread_t thread;
> >  };
> >  
> >  static void *modprobe_task(void *arg)
> > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> >  
> >  	data->err = modprobe(data->kmod, data->opts);
> >  
> > +	if (igt_debug_on(data->err)) {
> > +		int err;
> > +
> > +		while (err = pthread_mutex_trylock(&data->lock),
> > +		       err && !igt_debug_on(err != EBUSY))
> > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> 
> I guess you need here an "igt_debug_on_once", as it doesn't make
> sense to have a (potentially) endless loop here printing error
> messages.

Right.  Since we don't have igt_debug_on_once() implemented, I'll open code 
that improvement.

Thanks,
Janusz

> 
> the other changes LGTM.
> 
> > +	} else {
> > +		/* let main thread use mutex to detect modprobe completion */
> > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > +	}
> > +
> >  	return NULL;
> >  }
> >  
> > +static void kunit_sigchld_handler(int signal)
> > +{
> > +	return;
> > +}
> > +
> > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > +				 struct modprobe_data *modprobe,
> > +				 int fd, struct igt_ktap_results *ktap)
> > +{
> > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > +			 *saved;
> > +	char record[BUF_LEN + 1], *buf;
> > +	unsigned long taints;
> > +	int ret;
> > +
> > +	do {
> > +		int err;
> > +
> > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > +			return -ENOTRECOVERABLE;
> > +
> > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > +		if (err == -1)
> > +			return -errno;
> > +		else if (unlikely(err))
> > +			return err;
> > +
> > +		err = pthread_mutex_lock(&modprobe->lock);
> > +		switch (err) {
> > +		case EOWNERDEAD:
> > +			/* leave the mutex unrecoverable */
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			__attribute__ ((fallthrough));
> > +		case ENOTRECOVERABLE:
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			if (igt_debug_on(modprobe->err))
> > +				return modprobe->err;
> > +			break;
> > +		case 0:
> > +			break;
> > +		default:
> > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +			return -err;
> > +		}
> > +
> > +		ret = read(fd, record, BUF_LEN);
> > +
> > +		if (!err) {
> > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > +		}
> > +
> > +		if (igt_debug_on(!ret))
> > +			return -ENODATA;
> > +		if (igt_debug_on(ret == -1))
> > +			return -errno;
> > +		if (unlikely(igt_debug_on(ret < 0)))
> > +			break;
> > +
> > +		/* skip kmsg continuation lines */
> > +		if (igt_debug_on(*record == ' '))
> > +			continue;
> > +
> > +		/* NULL-terminate the record */
> > +		record[ret] = '\0';
> > +
> > +		/* detect start of log message, continue if not found */
> > +		buf = strchrnul(record, ';');
> > +		if (igt_debug_on(*buf == '\0'))
> > +			continue;
> > +		buf++;
> > +
> > +		ret = igt_ktap_parse(buf, ktap);
> > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > +			break;
> > +	} while (igt_list_empty(results));
> > +
> > +	return ret;
> > +}
> > +
> > +static void kunit_result_free(struct igt_ktap_result **r,
> > +			      char **suite_name, char **case_name)
> > +{
> > +	if (!*r)
> > +		return;
> > +
> > +	igt_list_del(&(*r)->link);
> > +
> > +	if ((*r)->suite_name != *suite_name) {
> > +		free(*suite_name);
> > +		*suite_name = (*r)->suite_name;
> > +	}
> > +
> > +	if ((*r)->case_name != *case_name) {
> > +		free(*case_name);
> > +		*case_name = (*r)->case_name;
> > +	}
> > +
> > +	free((*r)->msg);
> > +	free(*r);
> > +	*r = NULL;
> > +}
> > +
> >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  {
> > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > -	struct kmod_module *kunit_kmod;
> > -	bool is_builtin;
> > -	struct ktap_test_results *results;
> > -	pthread_t modprobe_thread;
> > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > +	char *suite_name = NULL, *case_name = NULL;
> > +	struct igt_ktap_result *r, *rn;
> > +	struct igt_ktap_results *ktap;
> > +	pthread_mutexattr_t attr;
> > +	IGT_LIST_HEAD(results);
> >  	unsigned long taints;
> >  	int flags, ret;
> >  
> > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> >  
> >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> >  
> > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > -	kmod_module_unref(kunit_kmod);
> > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> >  
> > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > +	ktap = igt_ktap_alloc(&results);
> > +	igt_require(ktap);
> >  
> > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> >  					modprobe_task, &modprobe))) {
> > -		ktap_parser_cancel();
> > -		igt_ignore_warn(ktap_parser_stop());
> > +		igt_ktap_free(ktap);
> >  		igt_skip("Failed to create a modprobe thread\n");
> >  	}
> >  
> > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > -	{
> > -		struct ktap_test_results_element *result;
> > -
> > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > -			ktap_parser_cancel();
> > +	do {
> > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > +					    tst->kmsg, ktap);
> > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> >  			break;
> > -		}
> >  
> > -		if (igt_kernel_tainted(&taints)) {
> > -			ktap_parser_cancel();
> > -			pthread_cancel(modprobe_thread);
> > +		if (igt_debug_on(igt_list_empty(&results)))
> >  			break;
> > -		}
> >  
> > -		pthread_mutex_lock(&results->mutex);
> > -		if (igt_list_empty(&results->list)) {
> > -			pthread_mutex_unlock(&results->mutex);
> > -			continue;
> > -		}
> > +		r = igt_list_first_entry(&results, r, link);
> >  
> > -		result = igt_list_first_entry(&results->list, result, link);
> > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > +			if (r->code == IGT_EXIT_INVALID) {
> > +				/* parametrized test case, get actual result */
> > +				kunit_result_free(&r, &suite_name, &case_name);
> >  
> > -		igt_list_del(&result->link);
> > -		pthread_mutex_unlock(&results->mutex);
> > +				igt_assert(igt_list_empty(&results));
> >  
> > -		igt_dynamic(result->test_name) {
> > -			igt_assert(READ_ONCE(result->passed));
> > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > +							    tst->kmsg, ktap);
> > +				if (ret != -EINPROGRESS)
> > +					igt_fail_on(ret);
> > +
> > +				igt_fail_on(igt_list_empty(&results));
> > +
> > +				r = igt_list_first_entry(&results, r, link);
> > +
> > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > +					      "suite_name expected: %s, got: %s\n",
> > +					      suite_name, r->suite_name);
> > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > +					      "case_name expected: %s, got: %s\n",
> > +					      case_name, r->case_name);
> > +			}
> >  
> > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > +
> > +			if (r->msg && *r->msg) {
> > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > +					      "%s\n", r->msg);
> > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > +					      "%s\n", r->msg);
> > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > +					      "%s\n", r->msg);
> > +			} else {
> > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > +				if (r->code == IGT_EXIT_ABORT)
> > +					igt_fail(r->code);
> > +			}
> > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > +
> > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > +			case 0:
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				break;
> > +			case EOWNERDEAD:
> > +				/* leave the mutex unrecoverable */
> > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +				__attribute__ ((fallthrough));
> > +			case ENOTRECOVERABLE:
> >  				igt_assert_eq(modprobe.err, 0);
> > +				break;
> > +			default:
> > +				igt_debug("pthread_mutex_lock() failed\n");
> > +				break;
> > +			}
> >  
> >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> >  		}
> >  
> > -		free(result);
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	} while (ret == -EINPROGRESS);
> > +
> > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > +		kunit_result_free(&r, &suite_name, &case_name);
> > +
> > +	free(case_name);
> > +	free(suite_name);
> > +
> > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > +	case 0:
> > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> > +	case EOWNERDEAD:
> > +		/* leave the mutex unrecoverable */
> > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > +		break;
> > +	case ENOTRECOVERABLE:
> > +		break;
> > +	default:
> > +		igt_debug("pthread_mutex_lock() failed\n");
> > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > +		break;
> >  	}
> >  
> > -	pthread_join(modprobe_thread, NULL);
> > -
> > -	ret = ktap_parser_stop();
> > +	igt_ktap_free(ktap);
> >  
> >  	igt_skip_on(modprobe.err);
> >  	igt_skip_on(igt_kernel_tainted(&taints));
> > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > index 5eac102417..53a6c63288 100644
> > --- a/lib/igt_ktap.c
> > +++ b/lib/igt_ktap.c
> > @@ -4,17 +4,11 @@
> >   * Copyright © 2023 Intel Corporation
> >   */
> >  
> > -#include <ctype.h>
> > -#include <limits.h>
> > -#include <libkmod.h>
> > -#include <pthread.h>
> >  #include <errno.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >  #include <string.h>
> > -#include <unistd.h>
> >  
> > -#include "igt_aux.h"
> >  #include "igt_core.h"
> >  #include "igt_ktap.h"
> >  #include "igt_list.h"
> > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> >  {
> >  	free(ktap);
> >  }
> > -
> > -#define DELIMITER "-"
> > -
> > -struct ktap_parser_args {
> > -	int fd;
> > -	bool is_builtin;
> > -	int ret;
> > -} ktap_args;
> > -
> > -static struct ktap_test_results results;
> > -
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > -
> > -/**
> > - * log_to_end:
> > - * @level: #igt_log_level
> > - * @record: record to store the read data
> > - * @format: format string
> > - * @...: optional arguments used in the format string
> > - *
> > - * This is an altered version of the generic structured logging helper function
> > - * igt_log capable of reading to the end of a given line.
> > - *
> > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > - */
> > -static int log_to_end(enum igt_log_level level, int fd,
> > -		      char *record, const char *format, ...)
> > -{
> > -	va_list args;
> > -	const char *lend;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	char *cutoff = strchr(record, '\n');
> > -	if (cutoff) {
> > -		if (cutoff - record < BUF_LEN)
> > -			cutoff[1] = '\0';
> > -	}
> > -
> > -	va_start(args, format);
> > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > -	va_end(args);
> > -
> > -	lend = strchrnul(record, '\n');
> > -	while (*lend == '\0') {
> > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		lend = strchrnul(record, '\n');
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * lookup_value:
> > - * @haystack: the string to search in
> > - * @needle: the string to search for
> > - *
> > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > - */
> > -static long lookup_value(const char *haystack, const char *needle)
> > -{
> > -	const char *needle_rptr;
> > -	char *needle_end;
> > -	long num;
> > -
> > -	needle_rptr = strcasestr(haystack, needle);
> > -
> > -	if (needle_rptr == NULL)
> > -		return -1;
> > -
> > -	/* Skip search string and whitespaces after it */
> > -	needle_rptr += strlen(needle);
> > -
> > -	num = strtol(needle_rptr, &needle_end, 10);
> > -
> > -	if (needle_rptr == needle_end)
> > -		return -1;
> > -
> > -	if (num == LONG_MIN || num == LONG_MAX)
> > -		return 0;
> > -
> > -	return num > 0 ? num : 0;
> > -}
> > -
> > -/**
> > - * tap_version_present:
> > - * @record: buffer with tap data
> > - * @print_info: whether tap version should be printed or not
> > - *
> > - * Returns:
> > - * 0 if not found
> > - * 1 if found
> > - */
> > -static int tap_version_present(char* record, bool print_info)
> > -{
> > -	/*
> > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> > -	 *
> > -	 * but actually isn't, as it currently depends on the KUnit module
> > -	 * being built-in, so we can't rely on it every time
> > -	 */
> > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > -	char *cutoff;
> > -
> > -	if (version_rptr == NULL)
> > -		return 0;
> > -
> > -	/* Cutoff after newline character, in order to not display garbage */
> > -	cutoff = strchr(version_rptr, '\n');
> > -	if (cutoff)
> > -		cutoff[0] = '\0';
> > -
> > -	if (print_info)
> > -		igt_info("%s\n", version_rptr);
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * find_next_tap_subtest:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @is_builtin: whether KUnit is built-in or not
> > - *
> > - * Returns:
> > - * 0 if there's missing information
> > - * -1 if not found
> > - * -2 if there are problems while reading the file.
> > - * any other value corresponds to the amount of cases of the next (sub)test
> > - */
> > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > -{
> > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > -	long test_count;
> > -	char *cutoff;
> > -
> > -	test_name[0] = '\0';
> > -	test_name[BUF_LEN] = '\0';
> > -
> > -	test_lookup_str = " subtest: ";
> > -	subtest_lookup_str = " test: ";
> > -
> > -	if (!tap_version_present(record, true))
> > -		return -1;
> > -
> > -	if (is_builtin) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > -
> > -			return -2;
> > -		}
> > -	}
> > -
> > -	name_rptr = strcasestr(record, test_lookup_str);
> > -	if (name_rptr != NULL) {
> > -		name_rptr += strlen(test_lookup_str);
> > -	} else {
> > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > -		if (name_rptr != NULL)
> > -			name_rptr += strlen(subtest_lookup_str);
> > -	}
> > -
> > -	if (name_rptr == NULL) {
> > -		if (!is_builtin)
> > -			/* We've probably found nothing */
> > -			return -1;
> > -		igt_info("Missing test name\n");
> > -	} else {
> > -		strncpy(test_name, name_rptr, BUF_LEN);
> > -		/* Cutoff after newline character, in order to not display garbage */
> > -		cutoff = strchr(test_name, '\n');
> > -		if (cutoff)
> > -			cutoff[0] = '\0';
> > -
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("unknown error reading kmsg (%m)\n");
> > -
> > -			return -2;
> > -		}
> > -
> > -		/* Now we can be sure we found tests */
> > -		if (!is_builtin)
> > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > -	}
> > -
> > -	/*
> > -	 * Total test count will almost always appear as 0..N at the beginning
> > -	 * of a run, so we use it to reliably identify a new run
> > -	 */
> > -	test_count = lookup_value(record, "..");
> > -
> > -	if (test_count <= 0) {
> > -		igt_info("Missing test count\n");
> > -		if (test_name[0] == '\0')
> > -			return 0;
> > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > -				"Running some tests in: %s\n",
> > -				test_name) < 0)
> > -			return -2;
> > -		return 0;
> > -	} else if (test_name[0] == '\0') {
> > -		igt_info("Running %ld tests...\n", test_count);
> > -		return 0;
> > -	}
> > -
> > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > -			"Executing %ld tests in: %s\n",
> > -			test_count, test_name) < 0)
> > -		return -2;
> > -
> > -	return test_count;
> > -}
> > -
> > -/**
> > - * parse_kmsg_for_tap:
> > - * @fd: file descriptor
> > - * @record: buffer used to read fd
> > - * @test_name: buffer to store the test name
> > - *
> > - * Returns:
> > - * 1 if no results were found
> > - * 0 if a test succeded
> > - * -1 if a test failed
> > - * -2 if there are problems reading the file
> > - */
> > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > -{
> > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > -	char *test_name_end;
> > -
> > -	ok_lookup_str = "ok ";
> > -	nok_lookup_str = "not ok ";
> > -
> > -	lstart = strchrnul(record, ';');
> > -
> > -	if (*lstart == '\0') {
> > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > -		return -2;
> > -	}
> > -
> > -	lstart++;
> > -	while (isspace(*lstart))
> > -		lstart++;
> > -
> > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > -	if (nok_rptr != NULL) {
> > -		nok_rptr += strlen(nok_lookup_str);
> > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > -			nok_rptr++;
> > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > -			       "%s", lstart) < 0)
> > -			return -2;
> > -		return -1;
> > -	}
> > -
> > -	comment_start = strchrnul(lstart, '#');
> > -
> > -	/* Check if we're still in a subtest */
> > -	if (*comment_start != '\0') {
> > -		comment_start++;
> > -		value_parse_start = comment_start;
> > -
> > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > -				       "%s", lstart) < 0)
> > -				return -2;
> > -			return -1;
> > -		}
> > -	}
> > -
> > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > -	if (ok_rptr != NULL) {
> > -		ok_rptr += strlen(ok_lookup_str);
> > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > -			ok_rptr++;
> > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > -		while (!isspace(*test_name_end))
> > -			test_name_end++;
> > -		*test_name_end = '\0';
> > -		return 0;
> > -	}
> > -
> > -	return 1;
> > -}
> > -
> > -/**
> > - * parse_tap_level:
> > - * @fd: file descriptor
> > - * @base_test_name: test_name from upper recursion level
> > - * @test_count: test_count of this level
> > - * @failed_tests: top level failed_tests pointer
> > - * @found_tests: top level found_tests pointer
> > - * @is_builtin: whether the KUnit module is built-in or not
> > - *
> > - * Returns:
> > - * 0 if succeded
> > - * -1 if error occurred
> > - */
> > -__maybe_unused
> > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > -			   bool *found_tests, bool is_builtin)
> > -{
> > -	char record[BUF_LEN + 1];
> > -	struct ktap_test_results_element *r;
> > -	int internal_test_count;
> > -	char test_name[BUF_LEN + 1];
> > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > -
> > -	for (int i = 0; i < test_count; i++) {
> > -		if (read(fd, record, BUF_LEN) < 0) {
> > -			if (errno == EPIPE)
> > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -			else
> > -				igt_warn("error reading kmsg (%m)\n");
> > -
> > -			return -1;
> > -		}
> > -
> > -		/* Sublevel found */
> > -		if (tap_version_present(record, false))
> > -		{
> > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > -								    is_builtin);
> > -			switch (internal_test_count) {
> > -			case -2:
> > -				/* No more data to read */
> > -				return -1;
> > -			case -1:
> > -				/* No test found */
> > -				return -1;
> > -			case 0:
> > -				/* Tests found, but they're missing info */
> > -				*found_tests = true;
> > -				return -1;
> > -			default:
> > -				*found_tests = true;
> > -
> > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > -				    base_test_name_for_next_level[0])
> > -					strncat(base_test_name_for_next_level, DELIMITER,
> > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > -
> > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > -						    internal_test_count, failed_tests, found_tests,
> > -						    is_builtin) == -1)
> > -					return -1;
> > -				break;
> > -			}
> > -		}
> > -
> > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > -		case -2:
> > -			return -1;
> > -		case -1:
> > -			*failed_tests = true;
> > -
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = false;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		case 0:
> > -			r = malloc(sizeof(*r));
> > -
> > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > -				if (r->test_name[0])
> > -					strncat(r->test_name, DELIMITER,
> > -						BUF_LEN - strlen(r->test_name));
> > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > -			       BUF_LEN - strlen(r->test_name));
> > -			r->test_name[BUF_LEN] = '\0';
> > -
> > -			r->passed = true;
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&r->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -
> > -			test_name[0] = '\0';
> > -			break;
> > -		default:
> > -			break;
> > -		}
> > -	}
> > -	return 0;
> > -}
> > -
> > -/**
> > - * igt_ktap_parser:
> > - *
> > - * This function parses the output of a ktap script and passes it to main thread.
> > - */
> > -void *igt_ktap_parser(void *unused)
> > -{
> > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > -	struct igt_ktap_results *ktap = NULL;
> > -	int fd = ktap_args.fd;
> > -	IGT_LIST_HEAD(list);
> > -	int err;
> > -
> > -	ktap = igt_ktap_alloc(&list);
> > -	if (igt_debug_on(!ktap))
> > -		goto igt_ktap_parser_end;
> > -
> > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > -		struct igt_ktap_result *r, *rn;
> > -
> > -		/* skip kmsg continuation lines */
> > -		if (igt_debug_on(*record == ' '))
> > -			continue;
> > -
> > -		/* NULL-terminate the record */
> > -		record[err] = '\0';
> > -
> > -		/* detect start of log message, continue if not found */
> > -		buf = strchrnul(record, ';');
> > -		if (igt_debug_on(*buf == '\0'))
> > -			continue;
> > -		buf++;
> > -
> > -		err = igt_ktap_parse(buf, ktap);
> > -
> > -		/* parsing error */
> > -		if (err && err != -EINPROGRESS)
> > -			goto igt_ktap_parser_end;
> > -
> > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > -			struct ktap_test_results_element *result = NULL;
> > -			int code = r->code;
> > -
> > -			if (code != IGT_EXIT_INVALID)
> > -				result = calloc(1, sizeof(*result));
> > -
> > -			if (result) {
> > -				snprintf(result->test_name, sizeof(result->test_name),
> > -					 "%s-%s", r->suite_name, r->case_name);
> > -
> > -				if (code == IGT_EXIT_SUCCESS)
> > -					result->passed = true;
> > -			}
> > -
> > -			igt_list_del(&r->link);
> > -			if (r->suite_name != suite_name) {
> > -				free(suite_name);
> > -				suite_name = r->suite_name;
> > -			}
> > -			if (r->case_name != case_name) {
> > -				free(case_name);
> > -				case_name = r->case_name;
> > -			}
> > -			free(r->msg);
> > -			free(r);
> > -
> > -			/*
> > -			 * no extra result record expected on start
> > -			 * of parametrized test case -- skip it
> > -			 */
> > -			if (code == IGT_EXIT_INVALID)
> > -				continue;
> > -
> > -			if (!result) {
> > -				err = -ENOMEM;
> > -				goto igt_ktap_parser_end;
> > -			}
> > -
> > -			pthread_mutex_lock(&results.mutex);
> > -			igt_list_add_tail(&result->link, &results.list);
> > -			pthread_mutex_unlock(&results.mutex);
> > -		}
> > -
> > -		/* end of KTAP report */
> > -		if (!err)
> > -			goto igt_ktap_parser_end;
> > -	}
> > -
> > -	if (err < 0) {
> > -		if (errno == EPIPE)
> > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > -		else
> > -			igt_warn("error reading kmsg (%m)\n");
> > -	}
> > -
> > -igt_ktap_parser_end:
> > -	free(suite_name);
> > -	free(case_name);
> > -
> > -	if (!err)
> > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > -
> > -	results.still_running = false;
> > -
> > -	if (ktap)
> > -		igt_ktap_free(ktap);
> > -
> > -	return NULL;
> > -}
> > -
> > -static pthread_t ktap_parser_thread;
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > -{
> > -	IGT_INIT_LIST_HEAD(&results.list);
> > -	pthread_mutex_init(&results.mutex, NULL);
> > -	results.still_running = true;
> > -
> > -	ktap_args.fd = fd;
> > -	ktap_args.is_builtin = is_builtin;
> > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > -
> > -	return &results;
> > -}
> > -
> > -void ktap_parser_cancel(void)
> > -{
> > -	pthread_cancel(ktap_parser_thread);
> > -}
> > -
> > -int ktap_parser_stop(void)
> > -{
> > -	pthread_join(ktap_parser_thread, NULL);
> > -	return ktap_args.ret;
> > -}
> > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > index 6f8da3eab6..c422636bfc 100644
> > --- a/lib/igt_ktap.h
> > +++ b/lib/igt_ktap.h
> > @@ -27,8 +27,6 @@
> >  
> >  #define BUF_LEN 4096
> >  
> > -#include <pthread.h>
> > -
> >  #include "igt_list.h"
> >  
> >  struct igt_ktap_result {
> > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> >  void igt_ktap_free(struct igt_ktap_results *ktap);
> >  
> > -void *igt_ktap_parser(void *unused);
> > -
> > -typedef struct ktap_test_results_element {
> > -	char test_name[BUF_LEN + 1];
> > -	bool passed;
> > -	struct igt_list_head link;
> > -} ktap_test_results_element;
> > -
> > -struct ktap_test_results {
> > -	struct igt_list_head list;
> > -	pthread_mutex_t mutex;
> > -	bool still_running;
> > -};
> > -
> > -
> > -
> > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > -void ktap_parser_cancel(void);
> > -int ktap_parser_stop(void);
> > -
> >  #endif /* IGT_KTAP_H */
> 






^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✓ Fi.CI.BAT: success for Kunit fixes and improvements (rev3)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (16 preceding siblings ...)
  (?)
@ 2023-10-10 18:32 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-10 18:32 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 7492 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev3)
URL   : https://patchwork.freedesktop.org/series/124554/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_13735 -> IGTPW_9965
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html

Participating hosts (37 -> 35)
------------------------------

  Additional (1): bat-dg2-11 
  Missing    (3): fi-kbl-soraka fi-hsw-4770 fi-bsw-n3050 

Known issues
------------

  Here are the changes found in IGTPW_9965 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_mmap@basic:
    - bat-dg2-11:         NOTRUN -> [SKIP][1] ([i915#4083])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@gem_mmap@basic.html

  * igt@gem_tiled_fence_blits@basic:
    - bat-dg2-11:         NOTRUN -> [SKIP][2] ([i915#4077]) +2 other tests skip
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@gem_tiled_fence_blits@basic.html

  * igt@gem_tiled_pread_basic:
    - bat-dg2-11:         NOTRUN -> [SKIP][3] ([i915#4079]) +1 other test skip
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@gem_tiled_pread_basic.html

  * igt@i915_pm_rps@basic-api:
    - bat-dg2-11:         NOTRUN -> [SKIP][4] ([i915#6621])
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@i915_pm_rps@basic-api.html

  * igt@kms_addfb_basic@addfb25-x-tiled-mismatch-legacy:
    - bat-dg2-11:         NOTRUN -> [SKIP][5] ([i915#4212]) +6 other tests skip
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_addfb_basic@addfb25-x-tiled-mismatch-legacy.html

  * igt@kms_addfb_basic@addfb25-y-tiled-small-legacy:
    - bat-dg2-11:         NOTRUN -> [SKIP][6] ([i915#5190])
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_addfb_basic@addfb25-y-tiled-small-legacy.html

  * igt@kms_addfb_basic@basic-y-tiled-legacy:
    - bat-dg2-11:         NOTRUN -> [SKIP][7] ([i915#4215] / [i915#5190])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_addfb_basic@basic-y-tiled-legacy.html

  * igt@kms_addfb_basic@tile-pitch-mismatch:
    - bat-dg2-11:         NOTRUN -> [SKIP][8] ([i915#4212] / [i915#5608])
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_addfb_basic@tile-pitch-mismatch.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic:
    - bat-dg2-11:         NOTRUN -> [SKIP][9] ([i915#4103] / [i915#4213] / [i915#5608]) +1 other test skip
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic.html

  * igt@kms_dsc@dsc-basic:
    - bat-dg2-11:         NOTRUN -> [SKIP][10] ([i915#3555] / [i915#3840])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_dsc@dsc-basic.html

  * igt@kms_force_connector_basic@force-load-detect:
    - bat-dg2-11:         NOTRUN -> [SKIP][11] ([fdo#109285])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_force_connector_basic@force-load-detect.html

  * igt@kms_force_connector_basic@prune-stale-modes:
    - bat-dg2-11:         NOTRUN -> [SKIP][12] ([i915#5274])
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_force_connector_basic@prune-stale-modes.html

  * igt@kms_pipe_crc_basic@nonblocking-crc-frame-sequence:
    - bat-dg2-11:         NOTRUN -> [SKIP][13] ([i915#1845]) +2 other tests skip
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_pipe_crc_basic@nonblocking-crc-frame-sequence.html

  * igt@kms_psr@sprite_plane_onoff:
    - bat-dg2-11:         NOTRUN -> [SKIP][14] ([i915#1072]) +3 other tests skip
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_psr@sprite_plane_onoff.html

  * igt@kms_setmode@basic-clone-single-crtc:
    - bat-dg2-11:         NOTRUN -> [SKIP][15] ([i915#3555] / [i915#4098])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@kms_setmode@basic-clone-single-crtc.html

  * igt@prime_vgem@basic-fence-flip:
    - bat-dg2-11:         NOTRUN -> [SKIP][16] ([i915#3708])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@prime_vgem@basic-fence-flip.html

  * igt@prime_vgem@basic-fence-mmap:
    - bat-dg2-11:         NOTRUN -> [SKIP][17] ([i915#3708] / [i915#4077]) +1 other test skip
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@prime_vgem@basic-fence-mmap.html

  * igt@prime_vgem@basic-read:
    - bat-dg2-11:         NOTRUN -> [SKIP][18] ([i915#3291] / [i915#3708]) +2 other tests skip
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/bat-dg2-11/igt@prime_vgem@basic-read.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
  [i915#1845]: https://gitlab.freedesktop.org/drm/intel/issues/1845
  [i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
  [i915#3840]: https://gitlab.freedesktop.org/drm/intel/issues/3840
  [i915#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077
  [i915#4079]: https://gitlab.freedesktop.org/drm/intel/issues/4079
  [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083
  [i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4212]: https://gitlab.freedesktop.org/drm/intel/issues/4212
  [i915#4213]: https://gitlab.freedesktop.org/drm/intel/issues/4213
  [i915#4215]: https://gitlab.freedesktop.org/drm/intel/issues/4215
  [i915#5190]: https://gitlab.freedesktop.org/drm/intel/issues/5190
  [i915#5274]: https://gitlab.freedesktop.org/drm/intel/issues/5274
  [i915#5354]: https://gitlab.freedesktop.org/drm/intel/issues/5354
  [i915#5608]: https://gitlab.freedesktop.org/drm/intel/issues/5608
  [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621
  [i915#7359]: https://gitlab.freedesktop.org/drm/intel/issues/7359
  [i915#8981]: https://gitlab.freedesktop.org/drm/intel/issues/8981


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7527 -> IGTPW_9965

  CI-20190529: 20190529
  CI_DRM_13735: 78e2c2b4839d929de3f7393d8b93d4e1faae3cd8 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_9965: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html
  IGT_7527: 46f98a3041f73a1d6ee7ec3ace6eba79b15369c4 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git


Testlist changes
----------------

+igt@kms_plane@invalid-pixel-format-settings
+igt@kms_selftest@drm_cmdline_parser
+igt@kms_selftest@drm_damage_helper
+igt@kms_selftest@drm_dp_mst_helper
+igt@kms_selftest@drm_framebuffer
+igt@kms_selftest@drm_plane_helper
-igt@kms_plane@planar-pixel-format-settings
-igt@kms_selftest@drm_cmdline
-igt@kms_selftest@drm_damage
-igt@kms_selftest@drm_dp_mst
-igt@kms_selftest@drm_plane
-igt@kms_selftest@framebuffer

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html

[-- Attachment #2: Type: text/html, Size: 8869 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✓ CI.xeBAT: success for Kunit fixes and improvements (rev3)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (17 preceding siblings ...)
  (?)
@ 2023-10-10 19:09 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-10 19:09 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 5056 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev3)
URL   : https://patchwork.freedesktop.org/series/124554/
State : success

== Summary ==

CI Bug Log - changes from XEIGT_7527_BAT -> XEIGTPW_9965_BAT
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  

Participating hosts (4 -> 4)
------------------------------

  No changes in participating hosts

Known issues
------------

  Here are the changes found in XEIGTPW_9965_BAT that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@kms_pipe_crc_basic@hang-read-crc:
    - bat-dg2-oem2:       [PASS][1] -> [INCOMPLETE][2] ([Intel XE#282] / [Intel XE#749])
   [1]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-dg2-oem2/igt@kms_pipe_crc_basic@hang-read-crc.html
   [2]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-dg2-oem2/igt@kms_pipe_crc_basic@hang-read-crc.html

  * igt@kms_pipe_crc_basic@hang-read-crc@pipe-a-dp-3:
    - bat-dg2-oem2:       [PASS][3] -> [INCOMPLETE][4] ([Intel XE#282])
   [3]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-dg2-oem2/igt@kms_pipe_crc_basic@hang-read-crc@pipe-a-dp-3.html
   [4]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-dg2-oem2/igt@kms_pipe_crc_basic@hang-read-crc@pipe-a-dp-3.html

  * igt@xe_live_ktest@bo:
    - bat-pvc-2:          [PASS][5] -> [SKIP][6] ([Intel XE#762]) +2 other tests skip
   [5]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-pvc-2/igt@xe_live_ktest@bo.html
   [6]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-pvc-2/igt@xe_live_ktest@bo.html

  * igt@xe_live_ktest@dmabuf:
    - bat-atsm-2:         [PASS][7] -> [SKIP][8] ([Intel XE#762]) +2 other tests skip
   [7]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-atsm-2/igt@xe_live_ktest@dmabuf.html
   [8]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-atsm-2/igt@xe_live_ktest@dmabuf.html

  
#### Possible fixes ####

  * igt@kms_flip@basic-flip-vs-wf_vblank@c-edp1:
    - bat-adlp-7:         [FAIL][9] ([Intel XE#480]) -> [PASS][10] +1 other test pass
   [9]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-adlp-7/igt@kms_flip@basic-flip-vs-wf_vblank@c-edp1.html
   [10]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-adlp-7/igt@kms_flip@basic-flip-vs-wf_vblank@c-edp1.html

  * {igt@xe_create@create-execqueues-noleak}:
    - bat-adlp-7:         [FAIL][11] ([Intel XE#524]) -> [PASS][12]
   [11]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-adlp-7/igt@xe_create@create-execqueues-noleak.html
   [12]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-adlp-7/igt@xe_create@create-execqueues-noleak.html
    - bat-pvc-2:          [FAIL][13] -> [PASS][14]
   [13]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-pvc-2/igt@xe_create@create-execqueues-noleak.html
   [14]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-pvc-2/igt@xe_create@create-execqueues-noleak.html

  
#### Warnings ####

  * igt@kms_pipe_crc_basic@compare-crc-sanitycheck-nv12:
    - bat-dg2-oem2:       [FAIL][15] ([Intel XE#400]) -> [TIMEOUT][16] ([Intel XE#430] / [Intel XE#530])
   [15]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-dg2-oem2/igt@kms_pipe_crc_basic@compare-crc-sanitycheck-nv12.html
   [16]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-dg2-oem2/igt@kms_pipe_crc_basic@compare-crc-sanitycheck-nv12.html

  * igt@kms_pipe_crc_basic@compare-crc-sanitycheck-nv12@pipe-b-dp-3:
    - bat-dg2-oem2:       [FAIL][17] ([Intel XE#400]) -> [TIMEOUT][18] ([Intel XE#530])
   [17]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_7527/bat-dg2-oem2/igt@kms_pipe_crc_basic@compare-crc-sanitycheck-nv12@pipe-b-dp-3.html
   [18]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/bat-dg2-oem2/igt@kms_pipe_crc_basic@compare-crc-sanitycheck-nv12@pipe-b-dp-3.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [Intel XE#282]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/282
  [Intel XE#400]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/400
  [Intel XE#430]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/430
  [Intel XE#480]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/480
  [Intel XE#524]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/524
  [Intel XE#530]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/530
  [Intel XE#749]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/749
  [Intel XE#762]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/762


Build changes
-------------

  * IGT: IGT_7527 -> IGTPW_9965

  IGTPW_9965: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html
  IGT_7527: 46f98a3041f73a1d6ee7ec3ace6eba79b15369c4 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  xe-423-37b2d042c23ae6a10055ea92101a15083d64c718: 37b2d042c23ae6a10055ea92101a15083d64c718

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_9965/index.html

[-- Attachment #2: Type: text/html, Size: 6111 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✓ Fi.CI.IGT: success for Kunit fixes and improvements (rev2)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (18 preceding siblings ...)
  (?)
@ 2023-10-10 19:11 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-10 19:11 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 85463 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev2)
URL   : https://patchwork.freedesktop.org/series/124554/
State : success

== Summary ==

CI Bug Log - changes from IGT_7522_full -> IGTPW_9951_full
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html

Participating hosts (9 -> 9)
------------------------------

  No changes in participating hosts

New tests
---------

  New tests have been introduced between IGT_7522_full and IGTPW_9951_full:

### New IGT tests (114) ###

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_limit:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_optimistic:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_pathological:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_pessimistic:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_range:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_smoke:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align32:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align64:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_bottomup:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color_evict:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color_evict_range:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_debug:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_evict:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_evict_range:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_frag:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_highest:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_init:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_insert:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_insert_range:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_lowest:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_replace:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_reserve:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_topdown:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_bpp_extra_and_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_extra_and_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_dvi:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_hdmi:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_not_digital:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_e_only:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_freestanding_force_e_and_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_freestanding_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_hmirror:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_margin_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_multiple_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_bpp_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_panel_orientation:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_off:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on_analog:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on_digital:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_interlaced:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_interlaced_margins_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_margins:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_margins_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_rblank:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_refresh:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa_margins:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa_rblank:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_0:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_180:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_270:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_90:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_tv_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_vmirror:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_not_visible:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_one_intersect:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_one_outside:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_fractional_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_no_crtc:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_no_fb:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_not_visible:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_simple_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_fractional_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_intersect_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_intersect_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_outside_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_outside_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_dp_mst_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_dp_mst_helper@drm_test_dp_mst_calc_pbn_mode:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_dp_mst_helper@drm_test_dp_mst_sideband_msg_req_decode:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_one_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_three_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_one_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_three_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_16bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_24bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_32bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_8bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_three_plane_8bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_framebuffer:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_framebuffer@drm_test_framebuffer_create:
    - Statuses : 5 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_plane_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_plane_helper@drm_test_check_invalid_plane_state:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_plane_helper@drm_test_check_plane_state:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  

Known issues
------------

  Here are the changes found in IGTPW_9951_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@api_intel_bb@object-reloc-keep-cache:
    - shard-dg2:          NOTRUN -> [SKIP][1] ([i915#8411]) +1 other test skip
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@api_intel_bb@object-reloc-keep-cache.html

  * igt@drm_fdinfo@most-busy-check-all@bcs0:
    - shard-dg2:          NOTRUN -> [SKIP][2] ([i915#8414]) +11 other tests skip
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@drm_fdinfo@most-busy-check-all@bcs0.html

  * igt@drm_fdinfo@most-busy-idle-check-all@rcs0:
    - shard-rkl:          [PASS][3] -> [FAIL][4] ([i915#7742])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-4/igt@drm_fdinfo@most-busy-idle-check-all@rcs0.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@drm_fdinfo@most-busy-idle-check-all@rcs0.html

  * igt@gem_ccs@ctrl-surf-copy-new-ctx:
    - shard-mtlp:         NOTRUN -> [SKIP][5] ([i915#9323])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@gem_ccs@ctrl-surf-copy-new-ctx.html

  * igt@gem_ccs@suspend-resume@linear-compressed-compfmt0-lmem0-lmem0:
    - shard-dg2:          [PASS][6] -> [INCOMPLETE][7] ([i915#7297])
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg2-3/igt@gem_ccs@suspend-resume@linear-compressed-compfmt0-lmem0-lmem0.html
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@gem_ccs@suspend-resume@linear-compressed-compfmt0-lmem0-lmem0.html

  * igt@gem_close_race@multigpu-basic-process:
    - shard-dg1:          NOTRUN -> [SKIP][8] ([i915#7697])
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-18/igt@gem_close_race@multigpu-basic-process.html

  * igt@gem_create@create-ext-set-pat:
    - shard-dg2:          NOTRUN -> [SKIP][9] ([i915#8562])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@gem_create@create-ext-set-pat.html

  * igt@gem_ctx_param@set-priority-not-supported:
    - shard-dg2:          NOTRUN -> [SKIP][10] ([fdo#109314])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-5/igt@gem_ctx_param@set-priority-not-supported.html

  * igt@gem_ctx_persistence@engines-hang@vcs0:
    - shard-mtlp:         [PASS][11] -> [FAIL][12] ([i915#2410]) +1 other test fail
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-2/igt@gem_ctx_persistence@engines-hang@vcs0.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-8/igt@gem_ctx_persistence@engines-hang@vcs0.html

  * igt@gem_ctx_persistence@heartbeat-many:
    - shard-dg2:          NOTRUN -> [SKIP][13] ([i915#8555])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@gem_ctx_persistence@heartbeat-many.html

  * igt@gem_ctx_sseu@engines:
    - shard-rkl:          NOTRUN -> [SKIP][14] ([i915#280])
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@gem_ctx_sseu@engines.html

  * igt@gem_ctx_sseu@invalid-args:
    - shard-dg2:          NOTRUN -> [SKIP][15] ([i915#280])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@gem_ctx_sseu@invalid-args.html

  * igt@gem_eio@suspend:
    - shard-mtlp:         [PASS][16] -> [FAIL][17] ([i915#7052])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-3/igt@gem_eio@suspend.html
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-8/igt@gem_eio@suspend.html

  * igt@gem_eio@unwedge-stress:
    - shard-dg1:          [PASS][18] -> [FAIL][19] ([i915#5784]) +1 other test fail
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg1-18/igt@gem_eio@unwedge-stress.html
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-16/igt@gem_eio@unwedge-stress.html

  * igt@gem_exec_balancer@parallel:
    - shard-rkl:          NOTRUN -> [SKIP][20] ([i915#4525])
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@gem_exec_balancer@parallel.html

  * igt@gem_exec_fair@basic-none-rrul:
    - shard-dg2:          NOTRUN -> [SKIP][21] ([i915#3539] / [i915#4852]) +1 other test skip
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@gem_exec_fair@basic-none-rrul.html

  * igt@gem_exec_fair@basic-none-solo:
    - shard-dg1:          NOTRUN -> [SKIP][22] ([i915#3539] / [i915#4852])
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-15/igt@gem_exec_fair@basic-none-solo.html

  * igt@gem_exec_fair@basic-pace:
    - shard-dg2:          NOTRUN -> [SKIP][23] ([i915#3539]) +1 other test skip
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@gem_exec_fair@basic-pace.html

  * igt@gem_exec_fair@basic-pace-share@rcs0:
    - shard-tglu:         [PASS][24] -> [FAIL][25] ([i915#2842])
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-tglu-5/igt@gem_exec_fair@basic-pace-share@rcs0.html
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-5/igt@gem_exec_fair@basic-pace-share@rcs0.html

  * igt@gem_exec_reloc@basic-gtt-cpu-active:
    - shard-dg2:          NOTRUN -> [SKIP][26] ([i915#3281]) +8 other tests skip
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@gem_exec_reloc@basic-gtt-cpu-active.html

  * igt@gem_exec_reloc@basic-gtt-wc:
    - shard-mtlp:         NOTRUN -> [SKIP][27] ([i915#3281]) +2 other tests skip
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-6/igt@gem_exec_reloc@basic-gtt-wc.html

  * igt@gem_exec_reloc@basic-wc-read:
    - shard-rkl:          NOTRUN -> [SKIP][28] ([i915#3281]) +4 other tests skip
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@gem_exec_reloc@basic-wc-read.html

  * igt@gem_exec_schedule@preempt-queue-contexts:
    - shard-dg2:          NOTRUN -> [SKIP][29] ([i915#4537] / [i915#4812]) +1 other test skip
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@gem_exec_schedule@preempt-queue-contexts.html

  * igt@gem_exec_suspend@basic-s0@lmem0:
    - shard-dg2:          [PASS][30] -> [INCOMPLETE][31] ([i915#9275])
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg2-11/igt@gem_exec_suspend@basic-s0@lmem0.html
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@gem_exec_suspend@basic-s0@lmem0.html

  * igt@gem_exec_suspend@basic-s4-devices@lmem0:
    - shard-dg2:          NOTRUN -> [ABORT][32] ([i915#7975] / [i915#8213])
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-7/igt@gem_exec_suspend@basic-s4-devices@lmem0.html
    - shard-dg1:          [PASS][33] -> [ABORT][34] ([i915#7975] / [i915#8213])
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg1-17/igt@gem_exec_suspend@basic-s4-devices@lmem0.html
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-14/igt@gem_exec_suspend@basic-s4-devices@lmem0.html

  * igt@gem_fenced_exec_thrash@2-spare-fences:
    - shard-dg2:          NOTRUN -> [SKIP][35] ([i915#4860]) +1 other test skip
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@gem_fenced_exec_thrash@2-spare-fences.html

  * igt@gem_gtt_cpu_tlb:
    - shard-mtlp:         NOTRUN -> [SKIP][36] ([i915#4077]) +3 other tests skip
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@gem_gtt_cpu_tlb.html

  * igt@gem_lmem_swapping@parallel-random:
    - shard-mtlp:         NOTRUN -> [SKIP][37] ([i915#4613])
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@gem_lmem_swapping@parallel-random.html

  * igt@gem_lmem_swapping@parallel-random-verify-ccs:
    - shard-tglu:         NOTRUN -> [SKIP][38] ([i915#4613]) +1 other test skip
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-3/igt@gem_lmem_swapping@parallel-random-verify-ccs.html

  * igt@gem_madvise@dontneed-before-exec:
    - shard-dg1:          NOTRUN -> [SKIP][39] ([i915#3282])
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-17/igt@gem_madvise@dontneed-before-exec.html

  * igt@gem_media_fill@media-fill:
    - shard-mtlp:         NOTRUN -> [SKIP][40] ([i915#8289])
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-6/igt@gem_media_fill@media-fill.html

  * igt@gem_mmap_gtt@fault-concurrent:
    - shard-dg1:          NOTRUN -> [SKIP][41] ([i915#4077]) +4 other tests skip
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-15/igt@gem_mmap_gtt@fault-concurrent.html

  * igt@gem_mmap_gtt@fault-concurrent-x:
    - shard-snb:          [PASS][42] -> [INCOMPLETE][43] ([i915#5161])
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-snb7/igt@gem_mmap_gtt@fault-concurrent-x.html
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-snb4/igt@gem_mmap_gtt@fault-concurrent-x.html

  * igt@gem_mmap_wc@write-gtt-read-wc:
    - shard-mtlp:         NOTRUN -> [SKIP][44] ([i915#4083]) +1 other test skip
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-2/igt@gem_mmap_wc@write-gtt-read-wc.html

  * igt@gem_mmap_wc@write-wc-read-gtt:
    - shard-dg2:          NOTRUN -> [SKIP][45] ([i915#4083]) +3 other tests skip
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@gem_mmap_wc@write-wc-read-gtt.html

  * igt@gem_partial_pwrite_pread@write-uncached:
    - shard-mtlp:         NOTRUN -> [SKIP][46] ([i915#3282])
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-7/igt@gem_partial_pwrite_pread@write-uncached.html

  * igt@gem_pread@exhaustion:
    - shard-tglu:         NOTRUN -> [WARN][47] ([i915#2658])
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-2/igt@gem_pread@exhaustion.html

  * igt@gem_pread@uncached:
    - shard-rkl:          NOTRUN -> [SKIP][48] ([i915#3282])
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@gem_pread@uncached.html

  * igt@gem_pwrite_snooped:
    - shard-dg2:          NOTRUN -> [SKIP][49] ([i915#3282]) +2 other tests skip
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@gem_pwrite_snooped.html

  * igt@gem_pxp@create-protected-buffer:
    - shard-dg2:          NOTRUN -> [SKIP][50] ([i915#4270]) +2 other tests skip
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-11/igt@gem_pxp@create-protected-buffer.html

  * igt@gem_pxp@create-regular-context-2:
    - shard-rkl:          NOTRUN -> [SKIP][51] ([i915#4270]) +1 other test skip
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@gem_pxp@create-regular-context-2.html

  * igt@gem_pxp@fail-invalid-protected-context:
    - shard-mtlp:         NOTRUN -> [SKIP][52] ([i915#4270])
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-7/igt@gem_pxp@fail-invalid-protected-context.html

  * igt@gem_pxp@reject-modify-context-protection-off-3:
    - shard-dg1:          NOTRUN -> [SKIP][53] ([i915#4270]) +1 other test skip
   [53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-19/igt@gem_pxp@reject-modify-context-protection-off-3.html

  * igt@gem_render_copy@mixed-tiled-to-yf-tiled-ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][54] ([i915#8428])
   [54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-8/igt@gem_render_copy@mixed-tiled-to-yf-tiled-ccs.html

  * igt@gem_tiled_blits@basic:
    - shard-dg2:          NOTRUN -> [SKIP][55] ([i915#4077]) +6 other tests skip
   [55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-7/igt@gem_tiled_blits@basic.html

  * igt@gem_userptr_blits@dmabuf-sync:
    - shard-rkl:          NOTRUN -> [SKIP][56] ([i915#3323])
   [56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@gem_userptr_blits@dmabuf-sync.html

  * igt@gem_userptr_blits@dmabuf-unsync:
    - shard-dg2:          NOTRUN -> [SKIP][57] ([i915#3297]) +1 other test skip
   [57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@gem_userptr_blits@dmabuf-unsync.html

  * igt@gem_userptr_blits@huge-split:
    - shard-rkl:          [PASS][58] -> [FAIL][59] ([i915#3318])
   [58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-2/igt@gem_userptr_blits@huge-split.html
   [59]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@gem_userptr_blits@huge-split.html

  * igt@gem_userptr_blits@vma-merge:
    - shard-dg2:          NOTRUN -> [FAIL][60] ([i915#3318])
   [60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@gem_userptr_blits@vma-merge.html

  * igt@gen3_render_mixed_blits:
    - shard-dg2:          NOTRUN -> [SKIP][61] ([fdo#109289]) +1 other test skip
   [61]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@gen3_render_mixed_blits.html

  * igt@gen7_exec_parse@basic-allowed:
    - shard-dg1:          NOTRUN -> [SKIP][62] ([fdo#109289])
   [62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-16/igt@gen7_exec_parse@basic-allowed.html

  * igt@gen9_exec_parse@bb-start-cmd:
    - shard-dg1:          NOTRUN -> [SKIP][63] ([i915#2527])
   [63]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-18/igt@gen9_exec_parse@bb-start-cmd.html

  * igt@gen9_exec_parse@cmd-crossing-page:
    - shard-mtlp:         NOTRUN -> [SKIP][64] ([i915#2856]) +1 other test skip
   [64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-6/igt@gen9_exec_parse@cmd-crossing-page.html

  * igt@gen9_exec_parse@secure-batches:
    - shard-dg2:          NOTRUN -> [SKIP][65] ([i915#2856]) +1 other test skip
   [65]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@gen9_exec_parse@secure-batches.html
    - shard-rkl:          NOTRUN -> [SKIP][66] ([i915#2527])
   [66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@gen9_exec_parse@secure-batches.html

  * igt@i915_hangman@detector@vcs0:
    - shard-mtlp:         [PASS][67] -> [FAIL][68] ([i915#8456]) +2 other tests fail
   [67]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-8/igt@i915_hangman@detector@vcs0.html
   [68]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-1/igt@i915_hangman@detector@vcs0.html

  * igt@i915_pm_freq_api@freq-reset-multiple:
    - shard-rkl:          NOTRUN -> [SKIP][69] ([i915#8399])
   [69]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@i915_pm_freq_api@freq-reset-multiple.html

  * igt@i915_pm_rpm@dpms-lpsp:
    - shard-dg2:          NOTRUN -> [SKIP][70] ([i915#1397])
   [70]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@i915_pm_rpm@dpms-lpsp.html

  * igt@i915_pm_rpm@dpms-mode-unset-lpsp:
    - shard-dg1:          [PASS][71] -> [SKIP][72] ([i915#1397])
   [71]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg1-19/igt@i915_pm_rpm@dpms-mode-unset-lpsp.html
   [72]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@i915_pm_rpm@dpms-mode-unset-lpsp.html

  * igt@i915_pm_rpm@modeset-lpsp-stress:
    - shard-rkl:          [PASS][73] -> [SKIP][74] ([i915#1397])
   [73]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-7/igt@i915_pm_rpm@modeset-lpsp-stress.html
   [74]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@i915_pm_rpm@modeset-lpsp-stress.html

  * igt@i915_query@query-topology-coherent-slice-mask:
    - shard-dg2:          NOTRUN -> [SKIP][75] ([i915#6188])
   [75]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@i915_query@query-topology-coherent-slice-mask.html

  * igt@i915_selftest@live@gt_heartbeat:
    - shard-apl:          [PASS][76] -> [DMESG-FAIL][77] ([i915#5334])
   [76]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-apl7/igt@i915_selftest@live@gt_heartbeat.html
   [77]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-apl7/igt@i915_selftest@live@gt_heartbeat.html

  * igt@i915_suspend@basic-s3-without-i915:
    - shard-rkl:          [PASS][78] -> [FAIL][79] ([fdo#103375])
   [78]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-1/igt@i915_suspend@basic-s3-without-i915.html
   [79]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@i915_suspend@basic-s3-without-i915.html

  * igt@i915_suspend@forcewake:
    - shard-dg2:          [PASS][80] -> [FAIL][81] ([fdo#103375]) +2 other tests fail
   [80]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg2-6/igt@i915_suspend@forcewake.html
   [81]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-5/igt@i915_suspend@forcewake.html

  * igt@kms_addfb_basic@framebuffer-vs-set-tiling:
    - shard-mtlp:         NOTRUN -> [SKIP][82] ([i915#4212])
   [82]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-6/igt@kms_addfb_basic@framebuffer-vs-set-tiling.html

  * igt@kms_async_flips@crc@pipe-a-hdmi-a-2:
    - shard-dg2:          NOTRUN -> [FAIL][83] ([i915#8247]) +3 other tests fail
   [83]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@kms_async_flips@crc@pipe-a-hdmi-a-2.html

  * igt@kms_atomic@plane-primary-overlay-mutable-zpos:
    - shard-dg2:          NOTRUN -> [SKIP][84] ([i915#404])
   [84]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@kms_atomic@plane-primary-overlay-mutable-zpos.html

  * igt@kms_big_fb@4-tiled-16bpp-rotate-90:
    - shard-mtlp:         NOTRUN -> [SKIP][85] ([fdo#111614])
   [85]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@kms_big_fb@4-tiled-16bpp-rotate-90.html

  * igt@kms_big_fb@4-tiled-32bpp-rotate-180:
    - shard-rkl:          NOTRUN -> [SKIP][86] ([i915#5286]) +1 other test skip
   [86]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@kms_big_fb@4-tiled-32bpp-rotate-180.html

  * igt@kms_big_fb@4-tiled-addfb:
    - shard-tglu:         NOTRUN -> [SKIP][87] ([i915#5286])
   [87]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-5/igt@kms_big_fb@4-tiled-addfb.html

  * igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-hflip:
    - shard-mtlp:         [PASS][88] -> [FAIL][89] ([i915#5138]) +2 other tests fail
   [88]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-7/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-hflip.html
   [89]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-hflip.html

  * igt@kms_big_fb@linear-16bpp-rotate-90:
    - shard-rkl:          NOTRUN -> [SKIP][90] ([fdo#111614] / [i915#3638])
   [90]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@kms_big_fb@linear-16bpp-rotate-90.html

  * igt@kms_big_fb@linear-32bpp-rotate-270:
    - shard-tglu:         NOTRUN -> [SKIP][91] ([fdo#111614]) +1 other test skip
   [91]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-9/igt@kms_big_fb@linear-32bpp-rotate-270.html

  * igt@kms_big_fb@x-tiled-64bpp-rotate-270:
    - shard-dg2:          NOTRUN -> [SKIP][92] ([fdo#111614]) +1 other test skip
   [92]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@kms_big_fb@x-tiled-64bpp-rotate-270.html

  * igt@kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip:
    - shard-tglu:         [PASS][93] -> [FAIL][94] ([i915#3743])
   [93]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-tglu-8/igt@kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html
   [94]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-7/igt@kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html

  * igt@kms_big_fb@y-tiled-8bpp-rotate-180:
    - shard-dg2:          NOTRUN -> [SKIP][95] ([i915#5190]) +11 other tests skip
   [95]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-7/igt@kms_big_fb@y-tiled-8bpp-rotate-180.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-async-flip:
    - shard-mtlp:         NOTRUN -> [SKIP][96] ([fdo#111615]) +3 other tests skip
   [96]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-7/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-async-flip.html

  * igt@kms_big_fb@yf-tiled-32bpp-rotate-180:
    - shard-dg1:          NOTRUN -> [SKIP][97] ([i915#4538])
   [97]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-18/igt@kms_big_fb@yf-tiled-32bpp-rotate-180.html

  * igt@kms_big_fb@yf-tiled-8bpp-rotate-180:
    - shard-dg2:          NOTRUN -> [SKIP][98] ([i915#4538] / [i915#5190]) +2 other tests skip
   [98]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@kms_big_fb@yf-tiled-8bpp-rotate-180.html

  * igt@kms_big_fb@yf-tiled-8bpp-rotate-270:
    - shard-rkl:          NOTRUN -> [SKIP][99] ([fdo#110723])
   [99]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@kms_big_fb@yf-tiled-8bpp-rotate-270.html

  * igt@kms_big_fb@yf-tiled-addfb-size-overflow:
    - shard-mtlp:         NOTRUN -> [SKIP][100] ([i915#6187])
   [100]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-6/igt@kms_big_fb@yf-tiled-addfb-size-overflow.html

  * igt@kms_big_joiner@invalid-modeset:
    - shard-dg2:          NOTRUN -> [SKIP][101] ([i915#2705])
   [101]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@kms_big_joiner@invalid-modeset.html

  * igt@kms_ccs@pipe-a-bad-pixel-format-4_tiled_mtl_mc_ccs:
    - shard-rkl:          NOTRUN -> [SKIP][102] ([i915#5354] / [i915#6095]) +4 other tests skip
   [102]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@kms_ccs@pipe-a-bad-pixel-format-4_tiled_mtl_mc_ccs.html

  * igt@kms_ccs@pipe-a-ccs-on-another-bo-y_tiled_ccs:
    - shard-rkl:          NOTRUN -> [SKIP][103] ([i915#3734] / [i915#5354] / [i915#6095])
   [103]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@kms_ccs@pipe-a-ccs-on-another-bo-y_tiled_ccs.html

  * igt@kms_ccs@pipe-a-crc-primary-basic-4_tiled_dg2_rc_ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][104] ([i915#5354] / [i915#6095]) +7 other tests skip
   [104]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@kms_ccs@pipe-a-crc-primary-basic-4_tiled_dg2_rc_ccs.html

  * igt@kms_ccs@pipe-a-crc-primary-basic-yf_tiled_ccs:
    - shard-dg2:          NOTRUN -> [SKIP][105] ([i915#3689] / [i915#5354]) +16 other tests skip
   [105]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@kms_ccs@pipe-a-crc-primary-basic-yf_tiled_ccs.html

  * igt@kms_ccs@pipe-a-missing-ccs-buffer-4_tiled_mtl_mc_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][106] ([i915#5354] / [i915#6095]) +5 other tests skip
   [106]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-19/igt@kms_ccs@pipe-a-missing-ccs-buffer-4_tiled_mtl_mc_ccs.html

  * igt@kms_ccs@pipe-b-ccs-on-another-bo-yf_tiled_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][107] ([i915#3689] / [i915#5354] / [i915#6095]) +4 other tests skip
   [107]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-15/igt@kms_ccs@pipe-b-ccs-on-another-bo-yf_tiled_ccs.html

  * igt@kms_ccs@pipe-b-crc-primary-basic-y_tiled_gen12_rc_ccs_cc:
    - shard-dg2:          NOTRUN -> [SKIP][108] ([i915#3689] / [i915#3886] / [i915#5354]) +6 other tests skip
   [108]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@kms_ccs@pipe-b-crc-primary-basic-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-c-bad-aux-stride-y_tiled_gen12_mc_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][109] ([i915#3689] / [i915#3886] / [i915#5354] / [i915#6095])
   [109]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@kms_ccs@pipe-c-bad-aux-stride-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-c-bad-pixel-format-4_tiled_mtl_rc_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][110] ([i915#5354] / [i915#6095]) +3 other tests skip
   [110]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-7/igt@kms_ccs@pipe-c-bad-pixel-format-4_tiled_mtl_rc_ccs.html

  * igt@kms_ccs@pipe-c-bad-pixel-format-y_tiled_gen12_rc_ccs_cc:
    - shard-mtlp:         NOTRUN -> [SKIP][111] ([i915#3886] / [i915#5354] / [i915#6095]) +2 other tests skip
   [111]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-2/igt@kms_ccs@pipe-c-bad-pixel-format-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-d-bad-aux-stride-yf_tiled_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][112] ([fdo#111615] / [i915#3689] / [i915#5354] / [i915#6095]) +1 other test skip
   [112]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-9/igt@kms_ccs@pipe-d-bad-aux-stride-yf_tiled_ccs.html

  * igt@kms_ccs@pipe-d-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs:
    - shard-rkl:          NOTRUN -> [SKIP][113] ([i915#5354]) +10 other tests skip
   [113]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@kms_ccs@pipe-d-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs.html

  * igt@kms_cdclk@mode-transition:
    - shard-tglu:         NOTRUN -> [SKIP][114] ([i915#3742])
   [114]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-10/igt@kms_cdclk@mode-transition.html

  * igt@kms_chamelium_color@ctm-0-50:
    - shard-mtlp:         NOTRUN -> [SKIP][115] ([fdo#111827]) +1 other test skip
   [115]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-7/igt@kms_chamelium_color@ctm-0-50.html

  * igt@kms_chamelium_color@gamma:
    - shard-tglu:         NOTRUN -> [SKIP][116] ([fdo#111827])
   [116]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-7/igt@kms_chamelium_color@gamma.html

  * igt@kms_chamelium_edid@hdmi-edid-stress-resolution-non-4k:
    - shard-dg2:          NOTRUN -> [SKIP][117] ([i915#7828]) +5 other tests skip
   [117]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-11/igt@kms_chamelium_edid@hdmi-edid-stress-resolution-non-4k.html

  * igt@kms_chamelium_hpd@dp-hpd-enable-disable-mode:
    - shard-rkl:          NOTRUN -> [SKIP][118] ([i915#7828]) +2 other tests skip
   [118]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@kms_chamelium_hpd@dp-hpd-enable-disable-mode.html

  * igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe:
    - shard-dg1:          NOTRUN -> [SKIP][119] ([i915#7828]) +1 other test skip
   [119]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe.html

  * igt@kms_chamelium_hpd@vga-hpd-without-ddc:
    - shard-mtlp:         NOTRUN -> [SKIP][120] ([i915#7828]) +2 other tests skip
   [120]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-1/igt@kms_chamelium_hpd@vga-hpd-without-ddc.html

  * igt@kms_content_protection@atomic-dpms:
    - shard-tglu:         NOTRUN -> [SKIP][121] ([i915#6944] / [i915#7116] / [i915#7118])
   [121]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-10/igt@kms_content_protection@atomic-dpms.html

  * igt@kms_content_protection@atomic@pipe-a-dp-4:
    - shard-dg2:          NOTRUN -> [TIMEOUT][122] ([i915#7173])
   [122]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-11/igt@kms_content_protection@atomic@pipe-a-dp-4.html

  * igt@kms_content_protection@dp-mst-lic-type-0:
    - shard-dg2:          NOTRUN -> [SKIP][123] ([i915#3299])
   [123]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-7/igt@kms_content_protection@dp-mst-lic-type-0.html

  * igt@kms_content_protection@dp-mst-type-0:
    - shard-mtlp:         NOTRUN -> [SKIP][124] ([i915#3299])
   [124]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@kms_content_protection@dp-mst-type-0.html

  * igt@kms_content_protection@dp-mst-type-1:
    - shard-tglu:         NOTRUN -> [SKIP][125] ([i915#3116] / [i915#3299])
   [125]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-4/igt@kms_content_protection@dp-mst-type-1.html

  * igt@kms_content_protection@uevent:
    - shard-dg2:          NOTRUN -> [SKIP][126] ([i915#7118])
   [126]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@kms_content_protection@uevent.html

  * igt@kms_cursor_crc@cursor-offscreen-512x170:
    - shard-rkl:          NOTRUN -> [SKIP][127] ([fdo#109279] / [i915#3359])
   [127]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@kms_cursor_crc@cursor-offscreen-512x170.html

  * igt@kms_cursor_crc@cursor-offscreen-max-size:
    - shard-mtlp:         NOTRUN -> [SKIP][128] ([i915#3555] / [i915#8814])
   [128]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@kms_cursor_crc@cursor-offscreen-max-size.html

  * igt@kms_cursor_crc@cursor-random-512x170:
    - shard-dg2:          NOTRUN -> [SKIP][129] ([i915#3359])
   [129]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-11/igt@kms_cursor_crc@cursor-random-512x170.html
    - shard-rkl:          NOTRUN -> [SKIP][130] ([i915#3359])
   [130]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@kms_cursor_crc@cursor-random-512x170.html

  * igt@kms_cursor_crc@cursor-rapid-movement-32x10:
    - shard-dg1:          NOTRUN -> [SKIP][131] ([i915#3555])
   [131]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@kms_cursor_crc@cursor-rapid-movement-32x10.html

  * igt@kms_cursor_crc@cursor-rapid-movement-512x170:
    - shard-mtlp:         NOTRUN -> [SKIP][132] ([i915#3359])
   [132]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-8/igt@kms_cursor_crc@cursor-rapid-movement-512x170.html

  * igt@kms_cursor_crc@cursor-sliding-max-size:
    - shard-rkl:          NOTRUN -> [SKIP][133] ([i915#3555])
   [133]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@kms_cursor_crc@cursor-sliding-max-size.html

  * igt@kms_cursor_crc@cursor-suspend@pipe-a-hdmi-a-3:
    - shard-dg2:          NOTRUN -> [FAIL][134] ([fdo#103375])
   [134]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@kms_cursor_crc@cursor-suspend@pipe-a-hdmi-a-3.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic:
    - shard-dg2:          NOTRUN -> [SKIP][135] ([i915#4103] / [i915#4213] / [i915#5608])
   [135]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-11/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic.html

  * igt@kms_cursor_legacy@cursorb-vs-flipa-legacy:
    - shard-rkl:          NOTRUN -> [SKIP][136] ([fdo#111825]) +2 other tests skip
   [136]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@kms_cursor_legacy@cursorb-vs-flipa-legacy.html

  * igt@kms_cursor_legacy@cursorb-vs-flipa-varying-size:
    - shard-mtlp:         NOTRUN -> [SKIP][137] ([i915#3546])
   [137]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-2/igt@kms_cursor_legacy@cursorb-vs-flipa-varying-size.html

  * igt@kms_cursor_legacy@cursorb-vs-flipb-atomic:
    - shard-dg2:          NOTRUN -> [SKIP][138] ([fdo#109274] / [i915#5354]) +1 other test skip
   [138]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@kms_cursor_legacy@cursorb-vs-flipb-atomic.html

  * igt@kms_cursor_legacy@forked-move@all-pipes:
    - shard-mtlp:         [PASS][139] -> [DMESG-WARN][140] ([i915#2017])
   [139]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-5/igt@kms_cursor_legacy@forked-move@all-pipes.html
   [140]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@kms_cursor_legacy@forked-move@all-pipes.html

  * igt@kms_display_modes@mst-extended-mode-negative:
    - shard-dg2:          NOTRUN -> [SKIP][141] ([i915#8588])
   [141]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-11/igt@kms_display_modes@mst-extended-mode-negative.html

  * igt@kms_dither@fb-8bpc-vs-panel-8bpc:
    - shard-dg2:          NOTRUN -> [SKIP][142] ([i915#3555]) +3 other tests skip
   [142]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@kms_dither@fb-8bpc-vs-panel-8bpc.html

  * igt@kms_draw_crc@draw-method-mmap-wc:
    - shard-dg2:          NOTRUN -> [SKIP][143] ([i915#8812])
   [143]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@kms_draw_crc@draw-method-mmap-wc.html

  * igt@kms_dsc@dsc-with-bpc-formats:
    - shard-dg1:          NOTRUN -> [SKIP][144] ([i915#3555] / [i915#3840])
   [144]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-19/igt@kms_dsc@dsc-with-bpc-formats.html

  * igt@kms_fbcon_fbt@fbc-suspend:
    - shard-dg2:          NOTRUN -> [INCOMPLETE][145] ([i915#8912])
   [145]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-5/igt@kms_fbcon_fbt@fbc-suspend.html

  * igt@kms_fence_pin_leak:
    - shard-dg2:          NOTRUN -> [SKIP][146] ([i915#4881])
   [146]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@kms_fence_pin_leak.html

  * igt@kms_flip@2x-blocking-wf_vblank:
    - shard-dg2:          NOTRUN -> [SKIP][147] ([fdo#109274]) +3 other tests skip
   [147]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-3/igt@kms_flip@2x-blocking-wf_vblank.html

  * igt@kms_flip@2x-flip-vs-expired-vblank-interruptible:
    - shard-snb:          NOTRUN -> [SKIP][148] ([fdo#109271] / [fdo#111767])
   [148]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-snb6/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible.html

  * igt@kms_flip@2x-plain-flip:
    - shard-snb:          NOTRUN -> [SKIP][149] ([fdo#109271]) +17 other tests skip
   [149]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-snb2/igt@kms_flip@2x-plain-flip.html

  * igt@kms_flip_scaled_crc@flip-32bpp-yftile-to-32bpp-yftileccs-downscaling@pipe-a-valid-mode:
    - shard-rkl:          NOTRUN -> [SKIP][150] ([i915#2672]) +1 other test skip
   [150]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@kms_flip_scaled_crc@flip-32bpp-yftile-to-32bpp-yftileccs-downscaling@pipe-a-valid-mode.html

  * igt@kms_flip_scaled_crc@flip-32bpp-yftileccs-to-64bpp-yftile-upscaling@pipe-a-valid-mode:
    - shard-dg2:          NOTRUN -> [SKIP][151] ([i915#2672]) +2 other tests skip
   [151]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@kms_flip_scaled_crc@flip-32bpp-yftileccs-to-64bpp-yftile-upscaling@pipe-a-valid-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling@pipe-a-default-mode:
    - shard-mtlp:         NOTRUN -> [SKIP][152] ([i915#2672] / [i915#3555])
   [152]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling@pipe-a-default-mode.html

  * igt@kms_force_connector_basic@force-load-detect:
    - shard-dg2:          NOTRUN -> [SKIP][153] ([fdo#109285])
   [153]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-11/igt@kms_force_connector_basic@force-load-detect.html

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-mmap-wc:
    - shard-dg2:          NOTRUN -> [SKIP][154] ([i915#8708]) +12 other tests skip
   [154]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-mmap-wc.html

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-shrfb-pgflip-blt:
    - shard-dg2:          [PASS][155] -> [FAIL][156] ([i915#6880])
   [155]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg2-6/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-shrfb-pgflip-blt.html
   [156]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-shrfb-pgflip-blt.html

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-gtt:
    - shard-dg1:          NOTRUN -> [SKIP][157] ([i915#8708]) +2 other tests skip
   [157]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-17/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-indfb-plflip-blt:
    - shard-dg2:          NOTRUN -> [SKIP][158] ([i915#5354]) +27 other tests skip
   [158]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-indfb-plflip-blt.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-blt:
    - shard-mtlp:         NOTRUN -> [SKIP][159] ([i915#1825]) +12 other tests skip
   [159]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-8/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-blt.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-pri-indfb-multidraw:
    - shard-dg1:          NOTRUN -> [SKIP][160] ([i915#3458]) +5 other tests skip
   [160]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-18/igt@kms_frontbuffer_tracking@fbcpsr-1p-pri-indfb-multidraw.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-move:
    - shard-rkl:          NOTRUN -> [SKIP][161] ([i915#3023]) +5 other tests skip
   [161]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-move.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-gtt:
    - shard-tglu:         NOTRUN -> [SKIP][162] ([fdo#110189])
   [162]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-9/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-mmap-gtt:
    - shard-mtlp:         NOTRUN -> [SKIP][163] ([i915#8708]) +3 other tests skip
   [163]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-2/igt@kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@psr-1p-rte:
    - shard-dg2:          NOTRUN -> [SKIP][164] ([i915#3458]) +11 other tests skip
   [164]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@kms_frontbuffer_tracking@psr-1p-rte.html

  * igt@kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-pwrite:
    - shard-rkl:          NOTRUN -> [SKIP][165] ([fdo#111825] / [i915#1825]) +9 other tests skip
   [165]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-pwrite.html

  * igt@kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-move:
    - shard-dg1:          NOTRUN -> [SKIP][166] ([fdo#111825]) +7 other tests skip
   [166]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-18/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-move.html

  * igt@kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-mmap-cpu:
    - shard-tglu:         NOTRUN -> [SKIP][167] ([fdo#109280]) +5 other tests skip
   [167]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-7/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-mmap-cpu.html

  * igt@kms_hdr@static-swap:
    - shard-dg1:          NOTRUN -> [SKIP][168] ([i915#3555] / [i915#8228])
   [168]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-18/igt@kms_hdr@static-swap.html

  * igt@kms_hdr@static-toggle-dpms:
    - shard-rkl:          NOTRUN -> [SKIP][169] ([i915#3555] / [i915#8228])
   [169]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@kms_hdr@static-toggle-dpms.html

  * igt@kms_hdr@static-toggle-suspend:
    - shard-dg2:          NOTRUN -> [SKIP][170] ([i915#3555] / [i915#8228]) +2 other tests skip
   [170]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@kms_hdr@static-toggle-suspend.html

  * igt@kms_panel_fitting@atomic-fastset:
    - shard-tglu:         NOTRUN -> [SKIP][171] ([i915#6301])
   [171]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-7/igt@kms_panel_fitting@atomic-fastset.html

  * igt@kms_pipe_b_c_ivb@pipe-b-double-modeset-then-modeset-pipe-c:
    - shard-mtlp:         NOTRUN -> [SKIP][172] ([fdo#109289]) +2 other tests skip
   [172]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@kms_pipe_b_c_ivb@pipe-b-double-modeset-then-modeset-pipe-c.html

  * igt@kms_pipe_crc_basic@suspend-read-crc@pipe-a-hdmi-a-1:
    - shard-snb:          NOTRUN -> [DMESG-WARN][173] ([i915#8841]) +1 other test dmesg-warn
   [173]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-snb1/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-a-hdmi-a-1.html

  * igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1:
    - shard-apl:          [PASS][174] -> [INCOMPLETE][175] ([i915#180] / [i915#9392])
   [174]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-apl2/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html
   [175]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-apl3/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html

  * igt@kms_plane_multiple@tiling-4:
    - shard-tglu:         NOTRUN -> [SKIP][176] ([i915#3555])
   [176]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-4/igt@kms_plane_multiple@tiling-4.html

  * igt@kms_plane_scaling@intel-max-src-size@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [FAIL][177] ([i915#8292])
   [177]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@kms_plane_scaling@intel-max-src-size@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-1:
    - shard-rkl:          NOTRUN -> [SKIP][178] ([i915#5176] / [i915#9423]) +1 other test skip
   [178]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-7/igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-1.html

  * igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [SKIP][179] ([i915#5176] / [i915#9423]) +3 other tests skip
   [179]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25-unity-scaling@pipe-b-hdmi-a-2:
    - shard-rkl:          NOTRUN -> [SKIP][180] ([i915#5235]) +1 other test skip
   [180]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@kms_plane_scaling@planes-downscale-factor-0-25-unity-scaling@pipe-b-hdmi-a-2.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-d-hdmi-a-3:
    - shard-dg2:          NOTRUN -> [SKIP][181] ([i915#5235]) +15 other tests skip
   [181]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-d-hdmi-a-3.html

  * igt@kms_plane_scaling@planes-unity-scaling-downscale-factor-0-25@pipe-d-edp-1:
    - shard-mtlp:         NOTRUN -> [SKIP][182] ([i915#5235]) +3 other tests skip
   [182]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@kms_plane_scaling@planes-unity-scaling-downscale-factor-0-25@pipe-d-edp-1.html

  * igt@kms_plane_scaling@planes-unity-scaling-downscale-factor-0-25@pipe-d-hdmi-a-4:
    - shard-dg1:          NOTRUN -> [SKIP][183] ([i915#5235]) +19 other tests skip
   [183]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-15/igt@kms_plane_scaling@planes-unity-scaling-downscale-factor-0-25@pipe-d-hdmi-a-4.html

  * igt@kms_psr2_sf@cursor-plane-move-continuous-sf:
    - shard-dg2:          NOTRUN -> [SKIP][184] ([i915#658]) +1 other test skip
   [184]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-5/igt@kms_psr2_sf@cursor-plane-move-continuous-sf.html

  * igt@kms_psr2_sf@overlay-plane-move-continuous-sf:
    - shard-rkl:          NOTRUN -> [SKIP][185] ([i915#658])
   [185]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@kms_psr2_sf@overlay-plane-move-continuous-sf.html

  * igt@kms_psr@cursor_blt:
    - shard-rkl:          NOTRUN -> [SKIP][186] ([i915#1072]) +2 other tests skip
   [186]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@kms_psr@cursor_blt.html

  * igt@kms_psr@psr2_no_drrs:
    - shard-dg1:          NOTRUN -> [SKIP][187] ([i915#1072] / [i915#4078])
   [187]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-14/igt@kms_psr@psr2_no_drrs.html

  * igt@kms_psr@psr2_sprite_plane_move:
    - shard-dg2:          NOTRUN -> [SKIP][188] ([i915#1072]) +6 other tests skip
   [188]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@kms_psr@psr2_sprite_plane_move.html

  * igt@kms_rotation_crc@bad-tiling:
    - shard-mtlp:         NOTRUN -> [SKIP][189] ([i915#4235])
   [189]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@kms_rotation_crc@bad-tiling.html

  * igt@kms_rotation_crc@primary-yf-tiled-reflect-x-0:
    - shard-rkl:          NOTRUN -> [SKIP][190] ([fdo#111615] / [i915#5289])
   [190]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@kms_rotation_crc@primary-yf-tiled-reflect-x-0.html

  * igt@kms_rotation_crc@primary-yf-tiled-reflect-x-90:
    - shard-dg1:          NOTRUN -> [SKIP][191] ([fdo#111615] / [i915#5289])
   [191]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-17/igt@kms_rotation_crc@primary-yf-tiled-reflect-x-90.html

  * igt@kms_setmode@clone-exclusive-crtc:
    - shard-rkl:          NOTRUN -> [SKIP][192] ([i915#3555] / [i915#4098])
   [192]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@kms_setmode@clone-exclusive-crtc.html

  * igt@kms_setmode@invalid-clone-exclusive-crtc:
    - shard-dg2:          NOTRUN -> [SKIP][193] ([i915#3555] / [i915#4098])
   [193]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@kms_setmode@invalid-clone-exclusive-crtc.html

  * igt@kms_tiled_display@basic-test-pattern:
    - shard-mtlp:         NOTRUN -> [SKIP][194] ([i915#8623])
   [194]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-7/igt@kms_tiled_display@basic-test-pattern.html

  * igt@kms_universal_plane@cursor-fb-leak@pipe-c-dp-1:
    - shard-apl:          [PASS][195] -> [FAIL][196] ([i915#9196])
   [195]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-apl7/igt@kms_universal_plane@cursor-fb-leak@pipe-c-dp-1.html
   [196]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-apl2/igt@kms_universal_plane@cursor-fb-leak@pipe-c-dp-1.html

  * igt@kms_universal_plane@cursor-fb-leak@pipe-d-hdmi-a-1:
    - shard-dg2:          NOTRUN -> [FAIL][197] ([i915#9196])
   [197]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-10/igt@kms_universal_plane@cursor-fb-leak@pipe-d-hdmi-a-1.html

  * igt@kms_vblank@pipe-c-wait-forked-busy-hang:
    - shard-rkl:          NOTRUN -> [SKIP][198] ([i915#4070] / [i915#6768])
   [198]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-1/igt@kms_vblank@pipe-c-wait-forked-busy-hang.html

  * igt@kms_vblank@pipe-d-ts-continuation-modeset-rpm:
    - shard-rkl:          NOTRUN -> [SKIP][199] ([i915#4070] / [i915#533] / [i915#6768])
   [199]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@kms_vblank@pipe-d-ts-continuation-modeset-rpm.html

  * igt@kms_vrr@flip-dpms:
    - shard-mtlp:         NOTRUN -> [SKIP][200] ([i915#3555] / [i915#8808])
   [200]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@kms_vrr@flip-dpms.html

  * igt@kms_writeback@writeback-invalid-parameters:
    - shard-dg1:          NOTRUN -> [SKIP][201] ([i915#2437])
   [201]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@kms_writeback@writeback-invalid-parameters.html

  * igt@perf@global-sseu-config:
    - shard-dg2:          NOTRUN -> [SKIP][202] ([i915#7387])
   [202]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@perf@global-sseu-config.html

  * igt@perf_pmu@faulting-read@gtt:
    - shard-mtlp:         NOTRUN -> [SKIP][203] ([i915#8440])
   [203]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-4/igt@perf_pmu@faulting-read@gtt.html

  * igt@prime_vgem@basic-fence-flip:
    - shard-dg1:          NOTRUN -> [SKIP][204] ([i915#3708])
   [204]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@prime_vgem@basic-fence-flip.html

  * igt@prime_vgem@basic-fence-mmap:
    - shard-dg2:          NOTRUN -> [SKIP][205] ([i915#3708] / [i915#4077])
   [205]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@prime_vgem@basic-fence-mmap.html

  * igt@prime_vgem@basic-gtt:
    - shard-dg1:          NOTRUN -> [SKIP][206] ([i915#3708] / [i915#4077])
   [206]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@prime_vgem@basic-gtt.html

  * igt@prime_vgem@fence-flip-hang:
    - shard-dg2:          NOTRUN -> [SKIP][207] ([i915#3708])
   [207]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-7/igt@prime_vgem@fence-flip-hang.html
    - shard-rkl:          NOTRUN -> [SKIP][208] ([fdo#109295] / [i915#3708])
   [208]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@prime_vgem@fence-flip-hang.html

  * igt@tools_test@sysfs_l3_parity:
    - shard-dg2:          NOTRUN -> [SKIP][209] ([i915#4818])
   [209]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@tools_test@sysfs_l3_parity.html

  * igt@v3d/v3d_perfmon@create-perfmon-exceed:
    - shard-dg2:          NOTRUN -> [SKIP][210] ([i915#2575]) +7 other tests skip
   [210]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@v3d/v3d_perfmon@create-perfmon-exceed.html

  * igt@v3d/v3d_perfmon@create-single-perfmon:
    - shard-rkl:          NOTRUN -> [SKIP][211] ([fdo#109315])
   [211]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@v3d/v3d_perfmon@create-single-perfmon.html

  * igt@v3d/v3d_submit_csd@bad-multisync-extension:
    - shard-dg1:          NOTRUN -> [SKIP][212] ([i915#2575]) +2 other tests skip
   [212]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-16/igt@v3d/v3d_submit_csd@bad-multisync-extension.html

  * igt@v3d/v3d_submit_csd@valid-multisync-submission:
    - shard-mtlp:         NOTRUN -> [SKIP][213] ([i915#2575]) +4 other tests skip
   [213]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-6/igt@v3d/v3d_submit_csd@valid-multisync-submission.html

  * igt@vc4/vc4_mmap@mmap-bo:
    - shard-dg2:          NOTRUN -> [SKIP][214] ([i915#7711]) +6 other tests skip
   [214]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-2/igt@vc4/vc4_mmap@mmap-bo.html

  * igt@vc4/vc4_tiling@get-after-free:
    - shard-tglu:         NOTRUN -> [SKIP][215] ([i915#2575])
   [215]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-2/igt@vc4/vc4_tiling@get-after-free.html

  * igt@vc4/vc4_tiling@get-bad-handle:
    - shard-mtlp:         NOTRUN -> [SKIP][216] ([i915#7711])
   [216]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-2/igt@vc4/vc4_tiling@get-bad-handle.html

  * igt@vc4/vc4_wait_bo@used-bo-0ns:
    - shard-dg1:          NOTRUN -> [SKIP][217] ([i915#7711]) +2 other tests skip
   [217]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-12/igt@vc4/vc4_wait_bo@used-bo-0ns.html

  * igt@vc4/vc4_wait_seqno@bad-seqno-1ns:
    - shard-rkl:          NOTRUN -> [SKIP][218] ([i915#7711]) +1 other test skip
   [218]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@vc4/vc4_wait_seqno@bad-seqno-1ns.html

  
#### Possible fixes ####

  * igt@drm_fdinfo@most-busy-check-all@rcs0:
    - shard-rkl:          [FAIL][219] ([i915#7742]) -> [PASS][220] +1 other test pass
   [219]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-1/igt@drm_fdinfo@most-busy-check-all@rcs0.html
   [220]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-2/igt@drm_fdinfo@most-busy-check-all@rcs0.html

  * igt@gem_ctx_persistence@engines-hang@ccs0:
    - shard-mtlp:         [ABORT][221] ([i915#9414]) -> [PASS][222]
   [221]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-2/igt@gem_ctx_persistence@engines-hang@ccs0.html
   [222]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-8/igt@gem_ctx_persistence@engines-hang@ccs0.html

  * igt@gem_eio@hibernate:
    - shard-tglu:         [ABORT][223] ([i915#7975] / [i915#8213] / [i915#8398]) -> [PASS][224]
   [223]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-tglu-10/igt@gem_eio@hibernate.html
   [224]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-2/igt@gem_eio@hibernate.html

  * igt@gem_exec_fair@basic-deadline:
    - shard-rkl:          [FAIL][225] ([i915#2846]) -> [PASS][226]
   [225]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-1/igt@gem_exec_fair@basic-deadline.html
   [226]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-4/igt@gem_exec_fair@basic-deadline.html

  * igt@gem_exec_fair@basic-none-share@rcs0:
    - shard-glk:          [FAIL][227] ([i915#2842]) -> [PASS][228] +1 other test pass
   [227]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-glk2/igt@gem_exec_fair@basic-none-share@rcs0.html
   [228]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-glk5/igt@gem_exec_fair@basic-none-share@rcs0.html

  * igt@gem_exec_fair@basic-pace-solo@rcs0:
    - shard-rkl:          [FAIL][229] ([i915#2842]) -> [PASS][230] +1 other test pass
   [229]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-1/igt@gem_exec_fair@basic-pace-solo@rcs0.html
   [230]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-7/igt@gem_exec_fair@basic-pace-solo@rcs0.html

  * igt@gem_exec_fence@invalid-fence-array:
    - shard-mtlp:         [ABORT][231] ([i915#9262]) -> [PASS][232]
   [231]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-4/igt@gem_exec_fence@invalid-fence-array.html
   [232]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@gem_exec_fence@invalid-fence-array.html

  * igt@gem_exec_fence@parallel@bcs0:
    - shard-mtlp:         [TIMEOUT][233] ([i915#9137]) -> [PASS][234]
   [233]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-4/igt@gem_exec_fence@parallel@bcs0.html
   [234]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-2/igt@gem_exec_fence@parallel@bcs0.html

  * igt@gem_exec_fence@parallel@rcs0:
    - shard-mtlp:         [DMESG-FAIL][235] ([i915#8962]) -> [PASS][236]
   [235]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-4/igt@gem_exec_fence@parallel@rcs0.html
   [236]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-2/igt@gem_exec_fence@parallel@rcs0.html

  * igt@gem_lmem_swapping@smem-oom@lmem0:
    - shard-dg1:          [TIMEOUT][237] ([i915#5493]) -> [PASS][238]
   [237]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg1-16/igt@gem_lmem_swapping@smem-oom@lmem0.html
   [238]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-17/igt@gem_lmem_swapping@smem-oom@lmem0.html

  * igt@i915_hangman@engine-engine-hang@vcs0:
    - shard-mtlp:         [FAIL][239] ([i915#7069]) -> [PASS][240]
   [239]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-7/igt@i915_hangman@engine-engine-hang@vcs0.html
   [240]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@i915_hangman@engine-engine-hang@vcs0.html

  * igt@i915_module_load@reload-with-fault-injection:
    - shard-mtlp:         [ABORT][241] ([i915#8489] / [i915#8668]) -> [PASS][242]
   [241]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-1/igt@i915_module_load@reload-with-fault-injection.html
   [242]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-5/igt@i915_module_load@reload-with-fault-injection.html

  * igt@i915_pm_rpm@dpms-mode-unset-lpsp:
    - shard-rkl:          [SKIP][243] ([i915#1397]) -> [PASS][244]
   [243]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-6/igt@i915_pm_rpm@dpms-mode-unset-lpsp.html
   [244]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-7/igt@i915_pm_rpm@dpms-mode-unset-lpsp.html

  * igt@i915_pm_rpm@modeset-lpsp-stress-no-wait:
    - shard-dg1:          [SKIP][245] ([i915#1397]) -> [PASS][246]
   [245]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg1-18/igt@i915_pm_rpm@modeset-lpsp-stress-no-wait.html
   [246]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-19/igt@i915_pm_rpm@modeset-lpsp-stress-no-wait.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-0-async-flip:
    - shard-tglu:         [FAIL][247] ([i915#3743]) -> [PASS][248]
   [247]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-tglu-10/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-0-async-flip.html
   [248]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-tglu-4/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-0-async-flip.html

  * igt@kms_flip@flip-vs-expired-vblank@c-hdmi-a1:
    - shard-glk:          [FAIL][249] ([i915#79]) -> [PASS][250]
   [249]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-glk4/igt@kms_flip@flip-vs-expired-vblank@c-hdmi-a1.html
   [250]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-glk8/igt@kms_flip@flip-vs-expired-vblank@c-hdmi-a1.html

  * igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-mmap-cpu:
    - shard-dg2:          [FAIL][251] ([i915#6880]) -> [PASS][252]
   [251]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg2-3/igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-mmap-cpu.html
   [252]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-6/igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-mmap-cpu.html

  * igt@kms_plane@pixel-format-source-clamping@pipe-a-planes:
    - shard-mtlp:         [DMESG-WARN][253] ([i915#1982]) -> [PASS][254]
   [253]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-3/igt@kms_plane@pixel-format-source-clamping@pipe-a-planes.html
   [254]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-7/igt@kms_plane@pixel-format-source-clamping@pipe-a-planes.html

  * igt@kms_plane@pixel-format-source-clamping@pipe-b-planes:
    - shard-glk:          [DMESG-FAIL][255] ([i915#118]) -> [PASS][256]
   [255]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-glk7/igt@kms_plane@pixel-format-source-clamping@pipe-b-planes.html
   [256]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-glk4/igt@kms_plane@pixel-format-source-clamping@pipe-b-planes.html

  * {igt@kms_plane_scaling@plane-upscale-factor-0-25-with-modifiers@pipe-b-hdmi-a-2}:
    - shard-rkl:          [INCOMPLETE][257] -> [PASS][258]
   [257]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-2/igt@kms_plane_scaling@plane-upscale-factor-0-25-with-modifiers@pipe-b-hdmi-a-2.html
   [258]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@kms_plane_scaling@plane-upscale-factor-0-25-with-modifiers@pipe-b-hdmi-a-2.html

  * igt@kms_universal_plane@cursor-fb-leak@pipe-d-edp-1:
    - shard-mtlp:         [FAIL][259] ([i915#9196]) -> [PASS][260]
   [259]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-mtlp-7/igt@kms_universal_plane@cursor-fb-leak@pipe-d-edp-1.html
   [260]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-mtlp-8/igt@kms_universal_plane@cursor-fb-leak@pipe-d-edp-1.html

  * igt@kms_vblank@pipe-c-ts-continuation-suspend:
    - shard-dg2:          [FAIL][261] ([fdo#103375]) -> [PASS][262]
   [261]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg2-5/igt@kms_vblank@pipe-c-ts-continuation-suspend.html
   [262]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg2-1/igt@kms_vblank@pipe-c-ts-continuation-suspend.html

  
#### Warnings ####

  * igt@kms_fbcon_fbt@psr-suspend:
    - shard-rkl:          [SKIP][263] ([fdo#110189] / [i915#3955]) -> [SKIP][264] ([i915#3955]) +1 other test skip
   [263]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-rkl-2/igt@kms_fbcon_fbt@psr-suspend.html
   [264]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-rkl-6/igt@kms_fbcon_fbt@psr-suspend.html

  * igt@kms_frontbuffer_tracking@psr-2p-primscrn-shrfb-pgflip-blt:
    - shard-dg1:          [SKIP][265] ([fdo#111825] / [i915#4423]) -> [SKIP][266] ([fdo#111825])
   [265]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_7522/shard-dg1-14/igt@kms_frontbuffer_tracking@psr-2p-primscrn-shrfb-pgflip-blt.html
   [266]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/shard-dg1-16/igt@kms_frontbuffer_tracking@psr-2p-primscrn-shrfb-pgflip-blt.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#103375]: https://bugs.freedesktop.org/show_bug.cgi?id=103375
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109274]: https://bugs.freedesktop.org/show_bug.cgi?id=109274
  [fdo#109279]: https://bugs.freedesktop.org/show_bug.cgi?id=109279
  [fdo#109280]: https://bugs.freedesktop.org/show_bug.cgi?id=109280
  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [fdo#109289]: https://bugs.freedesktop.org/show_bug.cgi?id=109289
  [fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295
  [fdo#109314]: https://bugs.freedesktop.org/show_bug.cgi?id=109314
  [fdo#109315]: https://bugs.freedesktop.org/show_bug.cgi?id=109315
  [fdo#110189]: https://bugs.freedesktop.org/show_bug.cgi?id=110189
  [fdo#110723]: https://bugs.freedesktop.org/show_bug.cgi?id=110723
  [fdo#111614]: https://bugs.freedesktop.org/show_bug.cgi?id=111614
  [fdo#111615]: https://bugs.freedesktop.org/show_bug.cgi?id=111615
  [fdo#111767]: https://bugs.freedesktop.org/show_bug.cgi?id=111767
  [fdo#111825]: https://bugs.freedesktop.org/show_bug.cgi?id=111825
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
  [i915#118]: https://gitlab.freedesktop.org/drm/intel/issues/118
  [i915#1397]: https://gitlab.freedesktop.org/drm/intel/issues/1397
  [i915#180]: https://gitlab.freedesktop.org/drm/intel/issues/180
  [i915#1825]: https://gitlab.freedesktop.org/drm/intel/issues/1825
  [i915#1839]: https://gitlab.freedesktop.org/drm/intel/issues/1839
  [i915#1982]: https://gitlab.freedesktop.org/drm/intel/issues/1982
  [i915#2017]: https://gitlab.freedesktop.org/drm/intel/issues/2017
  [i915#2410]: https://gitlab.freedesktop.org/drm/intel/issues/2410
  [i915#2437]: https://gitlab.freedesktop.org/drm/intel/issues/2437
  [i915#2527]: https://gitlab.freedesktop.org/drm/intel/issues/2527
  [i915#2575]: https://gitlab.freedesktop.org/drm/intel/issues/2575
  [i915#2658]: https://gitlab.freedesktop.org/drm/intel/issues/2658
  [i915#2672]: https://gitlab.freedesktop.org/drm/intel/issues/2672
  [i915#2705]: https://gitlab.freedesktop.org/drm/intel/issues/2705
  [i915#280]: https://gitlab.freedesktop.org/drm/intel/issues/280
  [i915#2842]: https://gitlab.freedesktop.org/drm/intel/issues/2842
  [i915#2846]: https://gitlab.freedesktop.org/drm/intel/issues/2846
  [i915#2856]: https://gitlab.freedesktop.org/drm/intel/issues/2856
  [i915#3023]: https://gitlab.freedesktop.org/drm/intel/issues/3023
  [i915#3116]: https://gitlab.freedesktop.org/drm/intel/issues/3116
  [i915#3281]: https://gitlab.freedesktop.org/drm/intel/issues/3281
  [i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
  [i915#3297]: https://gitlab.freedesktop.org/drm/intel/issues/3297
  [i915#3299]: https://gitlab.freedesktop.org/drm/intel/issues/3299
  [i915#3318]: https://gitlab.freedesktop.org/drm/intel/issues/3318
  [i915#3323]: https://gitlab.freedesktop.org/drm/intel/issues/3323
  [i915#3359]: https://gitlab.freedesktop.org/drm/intel/issues/3359
  [i915#3458]: https://gitlab.freedesktop.org/drm/intel/issues/3458
  [i915#3539]: https://gitlab.freedesktop.org/drm/intel/issues/3539
  [i915#3546]: https://gitlab.freedesktop.org/drm/intel/issues/3546
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3638]: https://gitlab.freedesktop.org/drm/intel/issues/3638
  [i915#3689]: https://gitlab.freedesktop.org/drm/intel/issues/3689
  [i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
  [i915#3734]: https://gitlab.freedesktop.org/drm/intel/issues/3734
  [i915#3742]: https://gitlab.freedesktop.org/drm/intel/issues/3742
  [i915#3743]: https://gitlab.freedesktop.org/drm/intel/issues/3743
  [i915#3840]: https://gitlab.freedesktop.org/drm/intel/issues/3840
  [i915#3886]: https://gitlab.freedesktop.org/drm/intel/issues/3886
  [i915#3955]: https://gitlab.freedesktop.org/drm/intel/issues/3955
  [i915#404]: https://gitlab.freedesktop.org/drm/intel/issues/404
  [i915#4070]: https://gitlab.freedesktop.org/drm/intel/issues/4070
  [i915#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077
  [i915#4078]: https://gitlab.freedesktop.org/drm/intel/issues/4078
  [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083
  [i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4212]: https://gitlab.freedesktop.org/drm/intel/issues/4212
  [i915#4213]: https://gitlab.freedesktop.org/drm/intel/issues/4213
  [i915#4235]: https://gitlab.freedesktop.org/drm/intel/issues/4235
  [i915#4270]: https://gitlab.freedesktop.org/drm/intel/issues/4270
  [i915#4423]: https://gitlab.freedesktop.org/drm/intel/issues/4423
  [i915#4525]: https://gitlab.freedesktop.org/drm/intel/issues/4525
  [i915#4537]: https://gitlab.freedesktop.org/drm/intel/issues/4537
  [i915#4538]: https://gitlab.freedesktop.org/drm/intel/issues/4538
  [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
  [i915#4812]: https://gitlab.freedesktop.org/drm/intel/issues/4812
  [i915#4818]: https://gitlab.freedesktop.org/drm/intel/issues/4818
  [i915#4852]: https://gitlab.freedesktop.org/drm/intel/issues/4852
  [i915#4860]: https://gitlab.freedesktop.org/drm/intel/issues/4860
  [i915#4881]: https://gitlab.freedesktop.org/drm/intel/issues/4881
  [i915#5138]: https://gitlab.freedesktop.org/drm/intel/issues/5138
  [i915#5161]: https://gitlab.freedesktop.org/drm/intel/issues/5161
  [i915#5176]: https://gitlab.freedesktop.org/drm/intel/issues/5176
  [i915#5190]: https://gitlab.freedesktop.org/drm/intel/issues/5190
  [i915#5235]: https://gitlab.freedesktop.org/drm/intel/issues/5235
  [i915#5286]: https://gitlab.freedesktop.org/drm/intel/issues/5286
  [i915#5289]: https://gitlab.freedesktop.org/drm/intel/issues/5289
  [i915#533]: https://gitlab.freedesktop.org/drm/intel/issues/533
  [i915#5334]: https://gitlab.freedesktop.org/drm/intel/issues/5334
  [i915#5354]: https://gitlab.freedesktop.org/drm/intel/issues/5354
  [i915#5493]: https://gitlab.freedesktop.org/drm/intel/issues/5493
  [i915#5608]: https://gitlab.freedesktop.org/drm/intel/issues/5608
  [i915#5784]: https://gitlab.freedesktop.org/drm/intel/issues/5784
  [i915#6095]: https://gitlab.freedesktop.org/drm/intel/issues/6095
  [i915#6187]: https://gitlab.freedesktop.org/drm/intel/issues/6187
  [i915#6188]: https://gitlab.freedesktop.org/drm/intel/issues/6188
  [i915#6301]: https://gitlab.freedesktop.org/drm/intel/issues/6301
  [i915#658]: https://gitlab.freedesktop.org/drm/intel/issues/658
  [i915#6768]: https://gitlab.freedesktop.org/drm/intel/issues/6768
  [i915#6880]: https://gitlab.freedesktop.org/drm/intel/issues/6880
  [i915#6944]: https://gitlab.freedesktop.org/drm/intel/issues/6944
  [i915#7052]: https://gitlab.freedesktop.org/drm/intel/issues/7052
  [i915#7069]: https://gitlab.freedesktop.org/drm/intel/issues/7069
  [i915#7116]: https://gitlab.freedesktop.org/drm/intel/issues/7116
  [i915#7118]: https://gitlab.freedesktop.org/drm/intel/issues/7118
  [i915#7173]: https://gitlab.freedesktop.org/drm/intel/issues/7173
  [i915#7297]: https://gitlab.freedesktop.org/drm/intel/issues/7297
  [i915#7387]: https://gitlab.freedesktop.org/drm/intel/issues/7387
  [i915#7697]: https://gitlab.freedesktop.org/drm/intel/issues/7697
  [i915#7707]: https://gitlab.freedesktop.org/drm/intel/issues/7707
  [i915#7711]: https://gitlab.freedesktop.org/drm/intel/issues/7711
  [i915#7742]: https://gitlab.freedesktop.org/drm/intel/issues/7742
  [i915#7828]: https://gitlab.freedesktop.org/drm/intel/issues/7828
  [i915#79]: https://gitlab.freedesktop.org/drm/intel/issues/79
  [i915#7975]: https://gitlab.freedesktop.org/drm/intel/issues/7975
  [i915#8213]: https://gitlab.freedesktop.org/drm/intel/issues/8213
  [i915#8228]: https://gitlab.freedesktop.org/drm/intel/issues/8228
  [i915#8247]: https://gitlab.freedesktop.org/drm/intel/issues/8247
  [i915#8289]: https://gitlab.freedesktop.org/drm/intel/issues/8289
  [i915#8292]: https://gitlab.freedesktop.org/drm/intel/issues/8292
  [i915#8398]: https://gitlab.freedesktop.org/drm/intel/issues/8398
  [i915#8399]: https://gitlab.freedesktop.org/drm/intel/issues/8399
  [i915#8411]: https://gitlab.freedesktop.org/drm/intel/issues/8411
  [i915#8414]: https://gitlab.freedesktop.org/drm/intel/issues/8414
  [i915#8428]: https://gitlab.freedesktop.org/drm/intel/issues/8428
  [i915#8440]: https://gitlab.freedesktop.org/drm/intel/issues/8440
  [i915#8456]: https://gitlab.freedesktop.org/drm/intel/issues/8456
  [i915#8489]: https://gitlab.freedesktop.org/drm/intel/issues/8489
  [i915#8555]: https://gitlab.freedesktop.org/drm/intel/issues/8555
  [i915#8562]: https://gitlab.freedesktop.org/drm/intel/issues/8562
  [i915#8588]: https://gitlab.freedesktop.org/drm/intel/issues/8588
  [i915#8623]: https://gitlab.freedesktop.org/drm/intel/issues/8623
  [i915#8668]: https://gitlab.freedesktop.org/drm/intel/issues/8668
  [i915#8708]: https://gitlab.freedesktop.org/drm/intel/issues/8708
  [i915#8808]: https://gitlab.freedesktop.org/drm/intel/issues/8808
  [i915#8812]: https://gitlab.freedesktop.org/drm/intel/issues/8812
  [i915#8814]: https://gitlab.freedesktop.org/drm/intel/issues/8814
  [i915#8841]: https://gitlab.freedesktop.org/drm/intel/issues/8841
  [i915#8912]: https://gitlab.freedesktop.org/drm/intel/issues/8912
  [i915#8962]: https://gitlab.freedesktop.org/drm/intel/issues/8962
  [i915#9137]: https://gitlab.freedesktop.org/drm/intel/issues/9137
  [i915#9196]: https://gitlab.freedesktop.org/drm/intel/issues/9196
  [i915#9226]: https://gitlab.freedesktop.org/drm/intel/issues/9226
  [i915#9227]: https://gitlab.freedesktop.org/drm/intel/issues/9227
  [i915#9261]: https://gitlab.freedesktop.org/drm/intel/issues/9261
  [i915#9262]: https://gitlab.freedesktop.org/drm/intel/issues/9262
  [i915#9275]: https://gitlab.freedesktop.org/drm/intel/issues/9275
  [i915#9323]: https://gitlab.freedesktop.org/drm/intel/issues/9323
  [i915#9392]: https://gitlab.freedesktop.org/drm/intel/issues/9392
  [i915#9414]: https://gitlab.freedesktop.org/drm/intel/issues/9414
  [i915#9423]: https://gitlab.freedesktop.org/drm/intel/issues/9423
  [i915#9424]: https://gitlab.freedesktop.org/drm/intel/issues/9424


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7522 -> IGTPW_9951

  CI-20190529: 20190529
  CI_DRM_13729: fe7c35e5be39964f7c0b83899b9d5d9355848544 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_9951: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html
  IGT_7522: 334a1178a36a1327dc7fbba43ab16d1f3a7d887b @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9951/index.html

[-- Attachment #2: Type: text/html, Size: 103506 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✗ Fi.CI.IGT: failure for Kunit fixes and improvements (rev3)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (19 preceding siblings ...)
  (?)
@ 2023-10-11  4:43 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-11  4:43 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 97168 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev3)
URL   : https://patchwork.freedesktop.org/series/124554/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_13735_full -> IGTPW_9965_full
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with IGTPW_9965_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_9965_full, please notify your bug team (lgci.bug.filing@intel.com) to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html

Participating hosts (9 -> 9)
------------------------------

  No changes in participating hosts

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in IGTPW_9965_full:

### IGT changes ###

#### Possible regressions ####

  * igt@sysfs_timeslice_duration@timeout@vecs0:
    - shard-mtlp:         NOTRUN -> [TIMEOUT][1]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@sysfs_timeslice_duration@timeout@vecs0.html

  
New tests
---------

  New tests have been introduced between CI_DRM_13735_full and IGTPW_9965_full:

### New IGT tests (114) ###

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_limit:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_optimistic:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_pathological:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_pessimistic:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_range:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_smoke:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align32:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align64:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_bottomup:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color_evict:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color_evict_range:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_debug:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_evict:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_evict_range:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_frag:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_highest:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_init:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_insert:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_insert_range:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_lowest:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_replace:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_reserve:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_topdown:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_bpp_extra_and_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_extra_and_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_dvi:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_hdmi:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_not_digital:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_e_only:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_freestanding_force_e_and_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_freestanding_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_hmirror:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_margin_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_multiple_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_bpp_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_panel_orientation:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_off:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on_analog:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on_digital:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_interlaced:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_interlaced_margins_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_margins:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_margins_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_rblank:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_refresh:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa_margins:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa_rblank:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_0:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_180:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_270:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_90:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_tv_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_vmirror:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_not_visible:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_one_intersect:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_one_outside:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_fractional_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_no_crtc:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_no_fb:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_not_visible:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_simple_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_fractional_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_intersect_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_intersect_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_outside_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_outside_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_dp_mst_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_dp_mst_helper@drm_test_dp_mst_calc_pbn_mode:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_dp_mst_helper@drm_test_dp_mst_sideband_msg_req_decode:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_one_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_three_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_one_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_three_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_16bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_24bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_32bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_8bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_three_plane_8bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_framebuffer:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_framebuffer@drm_test_framebuffer_create:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_plane_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_plane_helper@drm_test_check_invalid_plane_state:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_plane_helper@drm_test_check_plane_state:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  

Known issues
------------

  Here are the changes found in IGTPW_9965_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@api_intel_bb@blit-reloc-keep-cache:
    - shard-mtlp:         NOTRUN -> [SKIP][2] ([i915#8411])
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@api_intel_bb@blit-reloc-keep-cache.html

  * igt@api_intel_bb@render-ccs:
    - shard-dg2:          NOTRUN -> [FAIL][3] ([i915#6122])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@api_intel_bb@render-ccs.html

  * igt@device_reset@cold-reset-bound:
    - shard-mtlp:         NOTRUN -> [SKIP][4] ([i915#7701])
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@device_reset@cold-reset-bound.html

  * igt@drm_fdinfo@busy-hang@rcs0:
    - shard-mtlp:         NOTRUN -> [SKIP][5] ([i915#8414]) +6 other tests skip
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@drm_fdinfo@busy-hang@rcs0.html

  * igt@drm_fdinfo@most-busy-idle-check-all@vecs1:
    - shard-dg2:          NOTRUN -> [SKIP][6] ([i915#8414]) +21 other tests skip
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@drm_fdinfo@most-busy-idle-check-all@vecs1.html

  * igt@gem_ccs@block-multicopy-inplace:
    - shard-mtlp:         NOTRUN -> [SKIP][7] ([i915#3555])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@gem_ccs@block-multicopy-inplace.html

  * igt@gem_ccs@suspend-resume@tile64-compressed-compfmt0-lmem0-lmem0:
    - shard-dg2:          NOTRUN -> [INCOMPLETE][8] ([i915#7297])
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_ccs@suspend-resume@tile64-compressed-compfmt0-lmem0-lmem0.html

  * igt@gem_close_race@multigpu-basic-process:
    - shard-mtlp:         NOTRUN -> [SKIP][9] ([i915#7697])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@gem_close_race@multigpu-basic-process.html

  * igt@gem_create@create-ext-cpu-access-big:
    - shard-dg2:          NOTRUN -> [ABORT][10] ([i915#7461])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_create@create-ext-cpu-access-big.html
    - shard-rkl:          NOTRUN -> [SKIP][11] ([i915#6335])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-2/igt@gem_create@create-ext-cpu-access-big.html

  * igt@gem_ctx_isolation@preservation-s3@vcs1:
    - shard-dg2:          NOTRUN -> [FAIL][12] ([fdo#103375])
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@gem_ctx_isolation@preservation-s3@vcs1.html

  * igt@gem_ctx_persistence@engines-hang@vcs0:
    - shard-mtlp:         NOTRUN -> [FAIL][13] ([i915#2410])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@gem_ctx_persistence@engines-hang@vcs0.html

  * igt@gem_ctx_persistence@heartbeat-stop:
    - shard-dg2:          NOTRUN -> [SKIP][14] ([i915#8555])
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_ctx_persistence@heartbeat-stop.html

  * igt@gem_ctx_persistence@legacy-engines-cleanup:
    - shard-snb:          NOTRUN -> [SKIP][15] ([fdo#109271] / [i915#1099])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-snb4/igt@gem_ctx_persistence@legacy-engines-cleanup.html

  * igt@gem_exec_balancer@bonded-semaphore:
    - shard-mtlp:         NOTRUN -> [SKIP][16] ([i915#4812])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_exec_balancer@bonded-semaphore.html

  * igt@gem_exec_balancer@invalid-bonds:
    - shard-dg2:          NOTRUN -> [SKIP][17] ([i915#4036])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_exec_balancer@invalid-bonds.html

  * igt@gem_exec_capture@capture-invisible@smem0:
    - shard-glk:          NOTRUN -> [SKIP][18] ([fdo#109271] / [i915#6334])
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk5/igt@gem_exec_capture@capture-invisible@smem0.html

  * igt@gem_exec_fair@basic-none-share@rcs0:
    - shard-tglu:         NOTRUN -> [FAIL][19] ([i915#2842])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@gem_exec_fair@basic-none-share@rcs0.html

  * igt@gem_exec_fair@basic-none-vip:
    - shard-mtlp:         NOTRUN -> [SKIP][20] ([i915#4473] / [i915#4771])
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@gem_exec_fair@basic-none-vip.html

  * igt@gem_exec_fair@basic-pace@rcs0:
    - shard-rkl:          [PASS][21] -> [FAIL][22] ([i915#2842]) +2 other tests fail
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-6/igt@gem_exec_fair@basic-pace@rcs0.html
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@gem_exec_fair@basic-pace@rcs0.html

  * igt@gem_exec_fair@basic-throttle:
    - shard-dg2:          NOTRUN -> [SKIP][23] ([i915#3539]) +1 other test skip
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_fair@basic-throttle.html

  * igt@gem_exec_flush@basic-batch-kernel-default-uc:
    - shard-mtlp:         [PASS][24] -> [DMESG-FAIL][25] ([i915#8962])
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-2/igt@gem_exec_flush@basic-batch-kernel-default-uc.html
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@gem_exec_flush@basic-batch-kernel-default-uc.html

  * igt@gem_exec_flush@basic-uc-rw-default:
    - shard-dg2:          NOTRUN -> [SKIP][26] ([i915#3539] / [i915#4852]) +1 other test skip
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@gem_exec_flush@basic-uc-rw-default.html

  * igt@gem_exec_gttfill@multigpu-basic:
    - shard-dg2:          NOTRUN -> [SKIP][27] ([i915#7697])
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_exec_gttfill@multigpu-basic.html
    - shard-rkl:          NOTRUN -> [SKIP][28] ([i915#7697])
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@gem_exec_gttfill@multigpu-basic.html
    - shard-dg1:          NOTRUN -> [SKIP][29] ([i915#7697])
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@gem_exec_gttfill@multigpu-basic.html

  * igt@gem_exec_reloc@basic-cpu-active:
    - shard-mtlp:         NOTRUN -> [SKIP][30] ([i915#3281]) +3 other tests skip
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@gem_exec_reloc@basic-cpu-active.html

  * igt@gem_exec_reloc@basic-gtt-cpu:
    - shard-dg1:          NOTRUN -> [SKIP][31] ([i915#3281]) +2 other tests skip
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@gem_exec_reloc@basic-gtt-cpu.html

  * igt@gem_exec_reloc@basic-gtt-wc-active:
    - shard-rkl:          NOTRUN -> [SKIP][32] ([i915#3281]) +2 other tests skip
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@gem_exec_reloc@basic-gtt-wc-active.html

  * igt@gem_exec_reloc@basic-write-read-active:
    - shard-dg2:          NOTRUN -> [SKIP][33] ([i915#3281]) +11 other tests skip
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@gem_exec_reloc@basic-write-read-active.html

  * igt@gem_exec_schedule@preempt-queue-contexts:
    - shard-dg2:          NOTRUN -> [SKIP][34] ([i915#4537] / [i915#4812])
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_schedule@preempt-queue-contexts.html

  * igt@gem_exec_schedule@semaphore-power:
    - shard-mtlp:         NOTRUN -> [SKIP][35] ([i915#4537] / [i915#4812]) +1 other test skip
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_exec_schedule@semaphore-power.html

  * igt@gem_exec_suspend@basic-s4-devices@lmem0:
    - shard-dg2:          NOTRUN -> [ABORT][36] ([i915#7975] / [i915#8213])
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_suspend@basic-s4-devices@lmem0.html

  * igt@gem_fence_thrash@bo-write-verify-threaded-none:
    - shard-mtlp:         NOTRUN -> [SKIP][37] ([i915#4860])
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_fence_thrash@bo-write-verify-threaded-none.html

  * igt@gem_fence_thrash@bo-write-verify-y:
    - shard-dg2:          NOTRUN -> [SKIP][38] ([i915#4860])
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@gem_fence_thrash@bo-write-verify-y.html
    - shard-dg1:          NOTRUN -> [SKIP][39] ([i915#4860])
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@gem_fence_thrash@bo-write-verify-y.html

  * igt@gem_lmem_swapping@basic:
    - shard-apl:          NOTRUN -> [SKIP][40] ([fdo#109271] / [i915#4613])
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl6/igt@gem_lmem_swapping@basic.html

  * igt@gem_lmem_swapping@parallel-random:
    - shard-glk:          NOTRUN -> [SKIP][41] ([fdo#109271] / [i915#4613])
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk9/igt@gem_lmem_swapping@parallel-random.html

  * igt@gem_lmem_swapping@parallel-random-verify-ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][42] ([i915#4613]) +2 other tests skip
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@gem_lmem_swapping@parallel-random-verify-ccs.html

  * igt@gem_lmem_swapping@smem-oom@lmem0:
    - shard-dg2:          [PASS][43] -> [DMESG-WARN][44] ([i915#4936] / [i915#5493])
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-2/igt@gem_lmem_swapping@smem-oom@lmem0.html
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_lmem_swapping@smem-oom@lmem0.html

  * igt@gem_media_fill@media-fill:
    - shard-dg2:          NOTRUN -> [SKIP][45] ([i915#8289])
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_media_fill@media-fill.html

  * igt@gem_mmap@short-mmap:
    - shard-dg2:          NOTRUN -> [SKIP][46] ([i915#4083]) +5 other tests skip
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_mmap@short-mmap.html

  * igt@gem_mmap_gtt@cpuset-medium-copy-xy:
    - shard-dg2:          NOTRUN -> [SKIP][47] ([i915#4077]) +11 other tests skip
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@gem_mmap_gtt@cpuset-medium-copy-xy.html

  * igt@gem_mmap_wc@bad-offset:
    - shard-mtlp:         NOTRUN -> [SKIP][48] ([i915#4083])
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_mmap_wc@bad-offset.html

  * igt@gem_partial_pwrite_pread@reads:
    - shard-dg2:          NOTRUN -> [SKIP][49] ([i915#3282]) +7 other tests skip
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_partial_pwrite_pread@reads.html

  * igt@gem_partial_pwrite_pread@write-snoop:
    - shard-rkl:          NOTRUN -> [SKIP][50] ([i915#3282]) +1 other test skip
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-2/igt@gem_partial_pwrite_pread@write-snoop.html

  * igt@gem_pxp@create-regular-buffer:
    - shard-mtlp:         NOTRUN -> [SKIP][51] ([i915#4270])
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@gem_pxp@create-regular-buffer.html

  * igt@gem_pxp@create-regular-context-1:
    - shard-dg1:          NOTRUN -> [SKIP][52] ([i915#4270])
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-15/igt@gem_pxp@create-regular-context-1.html

  * igt@gem_pxp@dmabuf-shared-protected-dst-is-context-refcounted:
    - shard-dg2:          NOTRUN -> [SKIP][53] ([i915#4270]) +2 other tests skip
   [53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_pxp@dmabuf-shared-protected-dst-is-context-refcounted.html

  * igt@gem_pxp@reject-modify-context-protection-off-2:
    - shard-rkl:          NOTRUN -> [SKIP][54] ([i915#4270]) +1 other test skip
   [54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@gem_pxp@reject-modify-context-protection-off-2.html

  * igt@gem_render_copy@x-tiled-to-vebox-yf-tiled:
    - shard-mtlp:         NOTRUN -> [SKIP][55] ([i915#8428]) +2 other tests skip
   [55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@gem_render_copy@x-tiled-to-vebox-yf-tiled.html

  * igt@gem_set_tiling_vs_blt@untiled-to-tiled:
    - shard-rkl:          NOTRUN -> [SKIP][56] ([i915#8411])
   [56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@gem_set_tiling_vs_blt@untiled-to-tiled.html

  * igt@gem_softpin@evict-snoop:
    - shard-dg2:          NOTRUN -> [SKIP][57] ([i915#4885])
   [57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_softpin@evict-snoop.html

  * igt@gem_softpin@evict-snoop-interruptible:
    - shard-mtlp:         NOTRUN -> [SKIP][58] ([i915#4885])
   [58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@gem_softpin@evict-snoop-interruptible.html

  * igt@gem_spin_batch@spin-all-new:
    - shard-dg2:          NOTRUN -> [FAIL][59] ([i915#5889])
   [59]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_spin_batch@spin-all-new.html

  * igt@gem_tiled_pread_pwrite:
    - shard-dg2:          NOTRUN -> [SKIP][60] ([i915#4079]) +1 other test skip
   [60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@gem_tiled_pread_pwrite.html

  * igt@gem_userptr_blits@access-control:
    - shard-mtlp:         NOTRUN -> [SKIP][61] ([i915#3297])
   [61]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@gem_userptr_blits@access-control.html

  * igt@gem_userptr_blits@dmabuf-unsync:
    - shard-dg2:          NOTRUN -> [SKIP][62] ([i915#3297]) +2 other tests skip
   [62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@gem_userptr_blits@dmabuf-unsync.html

  * igt@gem_userptr_blits@nohangcheck:
    - shard-mtlp:         [PASS][63] -> [FAIL][64] ([i915#9353])
   [63]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-6/igt@gem_userptr_blits@nohangcheck.html
   [64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@gem_userptr_blits@nohangcheck.html

  * igt@gem_userptr_blits@readonly-unsync:
    - shard-rkl:          NOTRUN -> [SKIP][65] ([i915#3297])
   [65]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@gem_userptr_blits@readonly-unsync.html

  * igt@gem_workarounds@suspend-resume-context:
    - shard-dg2:          [PASS][66] -> [FAIL][67] ([fdo#103375]) +1 other test fail
   [66]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-6/igt@gem_workarounds@suspend-resume-context.html
   [67]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@gem_workarounds@suspend-resume-context.html

  * igt@gen3_render_mixed_blits:
    - shard-rkl:          NOTRUN -> [SKIP][68] ([fdo#109289]) +2 other tests skip
   [68]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@gen3_render_mixed_blits.html

  * igt@gen7_exec_parse@basic-allocation:
    - shard-dg1:          NOTRUN -> [SKIP][69] ([fdo#109289])
   [69]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-15/igt@gen7_exec_parse@basic-allocation.html

  * igt@gen9_exec_parse@batch-invalid-length:
    - shard-rkl:          NOTRUN -> [SKIP][70] ([i915#2527])
   [70]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@gen9_exec_parse@batch-invalid-length.html
    - shard-dg1:          NOTRUN -> [SKIP][71] ([i915#2527])
   [71]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@gen9_exec_parse@batch-invalid-length.html

  * igt@gen9_exec_parse@bb-large:
    - shard-mtlp:         NOTRUN -> [SKIP][72] ([i915#2856]) +1 other test skip
   [72]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gen9_exec_parse@bb-large.html

  * igt@gen9_exec_parse@valid-registers:
    - shard-dg2:          NOTRUN -> [SKIP][73] ([i915#2856]) +3 other tests skip
   [73]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gen9_exec_parse@valid-registers.html

  * igt@i915_fb_tiling:
    - shard-dg2:          NOTRUN -> [SKIP][74] ([i915#4881])
   [74]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@i915_fb_tiling.html

  * igt@i915_hangman@detector@vcs0:
    - shard-mtlp:         [PASS][75] -> [FAIL][76] ([i915#8456]) +2 other tests fail
   [75]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-3/igt@i915_hangman@detector@vcs0.html
   [76]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@i915_hangman@detector@vcs0.html

  * igt@i915_hangman@engine-engine-error@vcs0:
    - shard-mtlp:         [PASS][77] -> [FAIL][78] ([i915#7069])
   [77]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-6/igt@i915_hangman@engine-engine-error@vcs0.html
   [78]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@i915_hangman@engine-engine-error@vcs0.html

  * igt@i915_module_load@load:
    - shard-dg2:          NOTRUN -> [SKIP][79] ([i915#6227])
   [79]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@i915_module_load@load.html

  * igt@i915_pm_rc6_residency@media-rc6-accuracy:
    - shard-tglu:         NOTRUN -> [SKIP][80] ([fdo#109289])
   [80]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@i915_pm_rc6_residency@media-rc6-accuracy.html

  * igt@i915_pm_rpm@dpms-non-lpsp:
    - shard-rkl:          [PASS][81] -> [SKIP][82] ([i915#1397]) +1 other test skip
   [81]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-3/igt@i915_pm_rpm@dpms-non-lpsp.html
   [82]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@i915_pm_rpm@dpms-non-lpsp.html

  * igt@i915_pm_rpm@gem-execbuf-stress-pc8:
    - shard-dg2:          NOTRUN -> [SKIP][83] ([fdo#109506])
   [83]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@i915_pm_rpm@gem-execbuf-stress-pc8.html

  * igt@i915_pm_rpm@modeset-lpsp-stress:
    - shard-dg2:          NOTRUN -> [SKIP][84] ([i915#1397])
   [84]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@i915_pm_rpm@modeset-lpsp-stress.html

  * igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait:
    - shard-dg1:          [PASS][85] -> [SKIP][86] ([i915#1397])
   [85]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-15/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html
   [86]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html

  * igt@i915_pm_rps@min-max-config-idle:
    - shard-dg2:          NOTRUN -> [SKIP][87] ([i915#6621])
   [87]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@i915_pm_rps@min-max-config-idle.html

  * igt@i915_pm_rps@thresholds-idle@gt0:
    - shard-dg2:          NOTRUN -> [SKIP][88] ([i915#8925])
   [88]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@i915_pm_rps@thresholds-idle@gt0.html
    - shard-dg1:          NOTRUN -> [SKIP][89] ([i915#8925])
   [89]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@i915_pm_rps@thresholds-idle@gt0.html

  * igt@i915_pm_rps@thresholds@gt1:
    - shard-mtlp:         NOTRUN -> [SKIP][90] ([i915#8925]) +1 other test skip
   [90]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-8/igt@i915_pm_rps@thresholds@gt1.html

  * igt@i915_selftest@live@requests:
    - shard-mtlp:         [PASS][91] -> [ABORT][92] ([i915#9414])
   [91]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-7/igt@i915_selftest@live@requests.html
   [92]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@i915_selftest@live@requests.html

  * igt@i915_suspend@fence-restore-tiled2untiled:
    - shard-mtlp:         NOTRUN -> [SKIP][93] ([i915#4077]) +4 other tests skip
   [93]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@i915_suspend@fence-restore-tiled2untiled.html

  * igt@kms_addfb_basic@basic-x-tiled-legacy:
    - shard-dg2:          NOTRUN -> [SKIP][94] ([i915#4212])
   [94]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_addfb_basic@basic-x-tiled-legacy.html

  * igt@kms_addfb_basic@clobberred-modifier:
    - shard-mtlp:         NOTRUN -> [SKIP][95] ([i915#4212])
   [95]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@kms_addfb_basic@clobberred-modifier.html

  * igt@kms_addfb_basic@invalid-smem-bo-on-discrete:
    - shard-rkl:          NOTRUN -> [SKIP][96] ([i915#3826])
   [96]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_addfb_basic@invalid-smem-bo-on-discrete.html

  * igt@kms_addfb_basic@tile-pitch-mismatch:
    - shard-dg2:          NOTRUN -> [SKIP][97] ([i915#4212] / [i915#5608])
   [97]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_addfb_basic@tile-pitch-mismatch.html

  * igt@kms_async_flips@async-flip-with-page-flip-events@pipe-a-hdmi-a-3-4-mc_ccs:
    - shard-dg2:          NOTRUN -> [SKIP][98] ([i915#8502] / [i915#8709]) +11 other tests skip
   [98]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_async_flips@async-flip-with-page-flip-events@pipe-a-hdmi-a-3-4-mc_ccs.html

  * igt@kms_async_flips@crc@pipe-b-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [FAIL][99] ([i915#8247]) +3 other tests fail
   [99]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_async_flips@crc@pipe-b-hdmi-a-3.html

  * igt@kms_atomic@plane-primary-overlay-mutable-zpos:
    - shard-dg2:          NOTRUN -> [SKIP][100] ([i915#404])
   [100]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_atomic@plane-primary-overlay-mutable-zpos.html

  * igt@kms_atomic_transition@plane-all-modeset-transition:
    - shard-mtlp:         NOTRUN -> [SKIP][101] ([i915#1769] / [i915#3555])
   [101]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@kms_atomic_transition@plane-all-modeset-transition.html

  * igt@kms_atomic_transition@plane-all-modeset-transition-internal-panels:
    - shard-dg2:          NOTRUN -> [SKIP][102] ([i915#1769] / [i915#3555])
   [102]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_atomic_transition@plane-all-modeset-transition-internal-panels.html

  * igt@kms_big_fb@4-tiled-16bpp-rotate-90:
    - shard-dg1:          NOTRUN -> [SKIP][103] ([i915#4538] / [i915#5286])
   [103]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@kms_big_fb@4-tiled-16bpp-rotate-90.html

  * igt@kms_big_fb@4-tiled-32bpp-rotate-0:
    - shard-rkl:          NOTRUN -> [SKIP][104] ([i915#5286]) +1 other test skip
   [104]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_big_fb@4-tiled-32bpp-rotate-0.html

  * igt@kms_big_fb@4-tiled-64bpp-rotate-0:
    - shard-tglu:         NOTRUN -> [SKIP][105] ([fdo#111615] / [i915#5286])
   [105]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-7/igt@kms_big_fb@4-tiled-64bpp-rotate-0.html

  * igt@kms_big_fb@linear-16bpp-rotate-90:
    - shard-dg1:          NOTRUN -> [SKIP][106] ([i915#3638])
   [106]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@kms_big_fb@linear-16bpp-rotate-90.html

  * igt@kms_big_fb@linear-32bpp-rotate-270:
    - shard-rkl:          NOTRUN -> [SKIP][107] ([fdo#111614] / [i915#3638])
   [107]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_big_fb@linear-32bpp-rotate-270.html

  * igt@kms_big_fb@x-tiled-16bpp-rotate-90:
    - shard-dg2:          NOTRUN -> [SKIP][108] ([fdo#111614]) +2 other tests skip
   [108]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_big_fb@x-tiled-16bpp-rotate-90.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip:
    - shard-mtlp:         NOTRUN -> [SKIP][109] ([fdo#111615]) +5 other tests skip
   [109]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip:
    - shard-tglu:         [PASS][110] -> [FAIL][111] ([i915#3743])
   [110]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-tglu-9/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html
   [111]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-9/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-async-flip:
    - shard-dg2:          NOTRUN -> [SKIP][112] ([i915#5190]) +18 other tests skip
   [112]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-async-flip.html

  * igt@kms_big_fb@yf-tiled-8bpp-rotate-90:
    - shard-rkl:          NOTRUN -> [SKIP][113] ([fdo#110723])
   [113]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_big_fb@yf-tiled-8bpp-rotate-90.html
    - shard-dg1:          NOTRUN -> [SKIP][114] ([i915#4538])
   [114]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@kms_big_fb@yf-tiled-8bpp-rotate-90.html

  * igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0:
    - shard-dg2:          NOTRUN -> [SKIP][115] ([i915#4538] / [i915#5190]) +7 other tests skip
   [115]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0.html

  * igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180-async-flip:
    - shard-tglu:         NOTRUN -> [SKIP][116] ([fdo#111615])
   [116]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180-async-flip.html

  * igt@kms_ccs@pipe-a-ccs-on-another-bo-y_tiled_gen12_mc_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][117] ([i915#3689] / [i915#3886] / [i915#5354] / [i915#6095])
   [117]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-4/igt@kms_ccs@pipe-a-ccs-on-another-bo-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-b-bad-pixel-format-y_tiled_gen12_mc_ccs:
    - shard-rkl:          NOTRUN -> [SKIP][118] ([i915#3886] / [i915#5354] / [i915#6095])
   [118]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@kms_ccs@pipe-b-bad-pixel-format-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-b-ccs-on-another-bo-y_tiled_gen12_rc_ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][119] ([i915#5354] / [i915#6095]) +16 other tests skip
   [119]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_ccs@pipe-b-ccs-on-another-bo-y_tiled_gen12_rc_ccs.html

  * igt@kms_ccs@pipe-b-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc:
    - shard-glk:          NOTRUN -> [SKIP][120] ([fdo#109271] / [i915#3886])
   [120]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk2/igt@kms_ccs@pipe-b-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc:
    - shard-rkl:          NOTRUN -> [SKIP][121] ([i915#5354] / [i915#6095]) +4 other tests skip
   [121]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_ccs@pipe-b-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][122] ([i915#3886] / [i915#5354] / [i915#6095]) +5 other tests skip
   [122]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc:
    - shard-dg2:          NOTRUN -> [SKIP][123] ([i915#3689] / [i915#3886] / [i915#5354]) +12 other tests skip
   [123]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-missing-ccs-buffer-4_tiled_mtl_rc_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][124] ([i915#5354] / [i915#6095])
   [124]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@kms_ccs@pipe-b-missing-ccs-buffer-4_tiled_mtl_rc_ccs.html

  * igt@kms_ccs@pipe-b-random-ccs-data-4_tiled_dg2_rc_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][125] ([i915#3689] / [i915#5354] / [i915#6095]) +1 other test skip
   [125]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-10/igt@kms_ccs@pipe-b-random-ccs-data-4_tiled_dg2_rc_ccs.html

  * igt@kms_ccs@pipe-c-bad-rotation-90-y_tiled_gen12_mc_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][126] ([i915#3689] / [i915#3886] / [i915#5354] / [i915#6095])
   [126]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_ccs@pipe-c-bad-rotation-90-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-c-crc-primary-basic-4_tiled_mtl_rc_ccs:
    - shard-rkl:          NOTRUN -> [SKIP][127] ([i915#5354]) +7 other tests skip
   [127]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_ccs@pipe-c-crc-primary-basic-4_tiled_mtl_rc_ccs.html

  * igt@kms_ccs@pipe-c-crc-primary-basic-y_tiled_gen12_mc_ccs:
    - shard-apl:          NOTRUN -> [SKIP][128] ([fdo#109271] / [i915#3886]) +3 other tests skip
   [128]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl1/igt@kms_ccs@pipe-c-crc-primary-basic-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-c-crc-sprite-planes-basic-y_tiled_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][129] ([i915#3689] / [i915#5354] / [i915#6095]) +1 other test skip
   [129]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_ccs@pipe-c-crc-sprite-planes-basic-y_tiled_ccs.html

  * igt@kms_ccs@pipe-d-bad-pixel-format-yf_tiled_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][130] ([fdo#111615] / [i915#3689] / [i915#5354] / [i915#6095])
   [130]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-4/igt@kms_ccs@pipe-d-bad-pixel-format-yf_tiled_ccs.html

  * igt@kms_ccs@pipe-d-crc-primary-basic-y_tiled_gen12_rc_ccs:
    - shard-dg2:          NOTRUN -> [SKIP][131] ([i915#3689] / [i915#5354]) +25 other tests skip
   [131]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_ccs@pipe-d-crc-primary-basic-y_tiled_gen12_rc_ccs.html

  * igt@kms_ccs@pipe-d-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][132] ([i915#5354] / [i915#6095]) +4 other tests skip
   [132]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@kms_ccs@pipe-d-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs.html

  * igt@kms_cdclk@plane-scaling@pipe-c-hdmi-a-3:
    - shard-dg2:          NOTRUN -> [SKIP][133] ([i915#4087]) +3 other tests skip
   [133]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_cdclk@plane-scaling@pipe-c-hdmi-a-3.html

  * igt@kms_chamelium_audio@hdmi-audio-edid:
    - shard-tglu:         NOTRUN -> [SKIP][134] ([i915#7828]) +1 other test skip
   [134]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@kms_chamelium_audio@hdmi-audio-edid.html

  * igt@kms_chamelium_color@ctm-0-75:
    - shard-mtlp:         NOTRUN -> [SKIP][135] ([fdo#111827])
   [135]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@kms_chamelium_color@ctm-0-75.html

  * igt@kms_chamelium_color@gamma:
    - shard-dg2:          NOTRUN -> [SKIP][136] ([fdo#111827]) +1 other test skip
   [136]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_chamelium_color@gamma.html

  * igt@kms_chamelium_edid@hdmi-edid-stress-resolution-non-4k:
    - shard-dg2:          NOTRUN -> [SKIP][137] ([i915#7828]) +13 other tests skip
   [137]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@kms_chamelium_edid@hdmi-edid-stress-resolution-non-4k.html

  * igt@kms_chamelium_hpd@dp-hpd-for-each-pipe:
    - shard-mtlp:         NOTRUN -> [SKIP][138] ([i915#7828]) +3 other tests skip
   [138]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_chamelium_hpd@dp-hpd-for-each-pipe.html

  * igt@kms_chamelium_hpd@vga-hpd-fast:
    - shard-rkl:          NOTRUN -> [SKIP][139] ([i915#7828]) +3 other tests skip
   [139]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_chamelium_hpd@vga-hpd-fast.html
    - shard-dg1:          NOTRUN -> [SKIP][140] ([i915#7828]) +3 other tests skip
   [140]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@kms_chamelium_hpd@vga-hpd-fast.html

  * igt@kms_content_protection@dp-mst-lic-type-0:
    - shard-dg2:          NOTRUN -> [SKIP][141] ([i915#3299])
   [141]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_content_protection@dp-mst-lic-type-0.html

  * igt@kms_content_protection@legacy:
    - shard-mtlp:         NOTRUN -> [SKIP][142] ([i915#6944])
   [142]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-8/igt@kms_content_protection@legacy.html

  * igt@kms_content_protection@lic:
    - shard-rkl:          NOTRUN -> [SKIP][143] ([i915#7118])
   [143]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_content_protection@lic.html

  * igt@kms_content_protection@srm:
    - shard-dg2:          NOTRUN -> [SKIP][144] ([i915#7118])
   [144]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_content_protection@srm.html

  * igt@kms_content_protection@srm@pipe-a-dp-1:
    - shard-apl:          NOTRUN -> [TIMEOUT][145] ([i915#7173])
   [145]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl6/igt@kms_content_protection@srm@pipe-a-dp-1.html

  * igt@kms_cursor_crc@cursor-offscreen-512x512:
    - shard-mtlp:         NOTRUN -> [SKIP][146] ([i915#3359])
   [146]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@kms_cursor_crc@cursor-offscreen-512x512.html

  * igt@kms_cursor_crc@cursor-onscreen-512x170:
    - shard-dg2:          NOTRUN -> [SKIP][147] ([i915#3359])
   [147]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_cursor_crc@cursor-onscreen-512x170.html

  * igt@kms_cursor_crc@cursor-sliding-32x10:
    - shard-dg2:          NOTRUN -> [SKIP][148] ([i915#3555]) +8 other tests skip
   [148]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_cursor_crc@cursor-sliding-32x10.html
    - shard-rkl:          NOTRUN -> [SKIP][149] ([i915#3555]) +4 other tests skip
   [149]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_cursor_crc@cursor-sliding-32x10.html
    - shard-dg1:          NOTRUN -> [SKIP][150] ([i915#3555]) +2 other tests skip
   [150]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@kms_cursor_crc@cursor-sliding-32x10.html

  * igt@kms_cursor_legacy@2x-flip-vs-cursor-atomic:
    - shard-dg2:          NOTRUN -> [SKIP][151] ([fdo#109274] / [fdo#111767] / [i915#5354]) +1 other test skip
   [151]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_cursor_legacy@2x-flip-vs-cursor-atomic.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic:
    - shard-mtlp:         NOTRUN -> [SKIP][152] ([i915#4213])
   [152]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-8/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic.html

  * igt@kms_cursor_legacy@cursora-vs-flipb-varying-size:
    - shard-mtlp:         NOTRUN -> [SKIP][153] ([i915#3546]) +2 other tests skip
   [153]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_cursor_legacy@cursora-vs-flipb-varying-size.html

  * igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions:
    - shard-dg2:          NOTRUN -> [SKIP][154] ([fdo#109274] / [i915#5354]) +7 other tests skip
   [154]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions.html

  * igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions-varying-size:
    - shard-apl:          NOTRUN -> [SKIP][155] ([fdo#109271]) +84 other tests skip
   [155]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl2/igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions-varying-size.html

  * igt@kms_cursor_legacy@cursorb-vs-flipb-atomic-transitions:
    - shard-mtlp:         NOTRUN -> [SKIP][156] ([fdo#111767] / [i915#3546])
   [156]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_cursor_legacy@cursorb-vs-flipb-atomic-transitions.html

  * igt@kms_cursor_legacy@cursorb-vs-flipb-varying-size:
    - shard-tglu:         NOTRUN -> [SKIP][157] ([fdo#109274])
   [157]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@kms_cursor_legacy@cursorb-vs-flipb-varying-size.html

  * igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions:
    - shard-glk:          [PASS][158] -> [FAIL][159] ([i915#2346])
   [158]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk3/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions.html
   [159]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk4/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions.html

  * igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions:
    - shard-dg2:          NOTRUN -> [SKIP][160] ([i915#4103] / [i915#4213]) +1 other test skip
   [160]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions.html
    - shard-rkl:          NOTRUN -> [SKIP][161] ([i915#4103])
   [161]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions.html
    - shard-dg1:          NOTRUN -> [SKIP][162] ([i915#4103] / [i915#4213])
   [162]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions.html

  * igt@kms_dirtyfb@dirtyfb-ioctl@drrs-hdmi-a-2:
    - shard-dg2:          NOTRUN -> [SKIP][163] ([i915#9226] / [i915#9261]) +1 other test skip
   [163]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_dirtyfb@dirtyfb-ioctl@drrs-hdmi-a-2.html

  * igt@kms_dirtyfb@dirtyfb-ioctl@fbc-hdmi-a-2:
    - shard-dg2:          NOTRUN -> [SKIP][164] ([i915#9227])
   [164]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_dirtyfb@dirtyfb-ioctl@fbc-hdmi-a-2.html

  * igt@kms_dither@fb-8bpc-vs-panel-6bpc@pipe-a-hdmi-a-1:
    - shard-rkl:          NOTRUN -> [SKIP][165] ([i915#3804])
   [165]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_dither@fb-8bpc-vs-panel-6bpc@pipe-a-hdmi-a-1.html

  * igt@kms_draw_crc@draw-method-mmap-wc:
    - shard-dg2:          NOTRUN -> [SKIP][166] ([i915#8812]) +1 other test skip
   [166]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_draw_crc@draw-method-mmap-wc.html

  * igt@kms_dsc@dsc-with-output-formats:
    - shard-dg2:          NOTRUN -> [SKIP][167] ([i915#3555] / [i915#3840]) +1 other test skip
   [167]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_dsc@dsc-with-output-formats.html

  * igt@kms_fbcon_fbt@fbc-suspend:
    - shard-dg2:          [PASS][168] -> [FAIL][169] ([i915#4767])
   [168]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-3/igt@kms_fbcon_fbt@fbc-suspend.html
   [169]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_fbcon_fbt@fbc-suspend.html

  * igt@kms_fbcon_fbt@psr-suspend:
    - shard-tglu:         NOTRUN -> [SKIP][170] ([i915#3469])
   [170]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@kms_fbcon_fbt@psr-suspend.html

  * igt@kms_fence_pin_leak:
    - shard-mtlp:         NOTRUN -> [SKIP][171] ([i915#4881])
   [171]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_fence_pin_leak.html

  * igt@kms_flip@2x-blocking-absolute-wf_vblank:
    - shard-mtlp:         NOTRUN -> [SKIP][172] ([i915#3637]) +3 other tests skip
   [172]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_flip@2x-blocking-absolute-wf_vblank.html

  * igt@kms_flip@2x-flip-vs-blocking-wf-vblank:
    - shard-mtlp:         NOTRUN -> [SKIP][173] ([fdo#111767] / [i915#3637])
   [173]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_flip@2x-flip-vs-blocking-wf-vblank.html

  * igt@kms_flip@2x-flip-vs-expired-vblank-interruptible:
    - shard-rkl:          NOTRUN -> [SKIP][174] ([fdo#111767] / [fdo#111825])
   [174]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible.html

  * igt@kms_flip@2x-flip-vs-fences:
    - shard-dg2:          NOTRUN -> [SKIP][175] ([i915#8381]) +1 other test skip
   [175]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_flip@2x-flip-vs-fences.html

  * igt@kms_flip@2x-plain-flip:
    - shard-tglu:         NOTRUN -> [SKIP][176] ([fdo#109274] / [i915#3637])
   [176]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-7/igt@kms_flip@2x-plain-flip.html

  * igt@kms_flip@2x-plain-flip-fb-recreate-interruptible:
    - shard-dg1:          NOTRUN -> [SKIP][177] ([fdo#111825]) +4 other tests skip
   [177]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@kms_flip@2x-plain-flip-fb-recreate-interruptible.html

  * igt@kms_flip@2x-wf_vblank-ts-check:
    - shard-dg2:          NOTRUN -> [SKIP][178] ([fdo#109274]) +9 other tests skip
   [178]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_flip@2x-wf_vblank-ts-check.html

  * igt@kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytileccs-downscaling@pipe-a-default-mode:
    - shard-mtlp:         NOTRUN -> [SKIP][179] ([i915#2672] / [i915#3555])
   [179]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytileccs-downscaling@pipe-a-default-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tile-downscaling@pipe-a-default-mode:
    - shard-mtlp:         NOTRUN -> [SKIP][180] ([i915#8810])
   [180]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tile-downscaling@pipe-a-default-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode:
    - shard-rkl:          NOTRUN -> [SKIP][181] ([i915#2672])
   [181]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode.html
    - shard-dg1:          NOTRUN -> [SKIP][182] ([i915#2587] / [i915#2672])
   [182]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-15/igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling@pipe-a-valid-mode:
    - shard-dg2:          NOTRUN -> [SKIP][183] ([i915#2672]) +3 other tests skip
   [183]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling@pipe-a-valid-mode.html

  * igt@kms_force_connector_basic@force-load-detect:
    - shard-mtlp:         NOTRUN -> [SKIP][184] ([fdo#109285])
   [184]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@kms_force_connector_basic@force-load-detect.html

  * igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-mmap-gtt:
    - shard-dg2:          NOTRUN -> [SKIP][185] ([i915#8708]) +29 other tests skip
   [185]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-pwrite:
    - shard-mtlp:         NOTRUN -> [SKIP][186] ([i915#1825]) +21 other tests skip
   [186]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-pwrite.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-gtt:
    - shard-rkl:          NOTRUN -> [SKIP][187] ([fdo#111825] / [i915#1825]) +7 other tests skip
   [187]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-gtt.html
    - shard-dg1:          NOTRUN -> [SKIP][188] ([i915#8708]) +3 other tests skip
   [188]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@fbc-2p-shrfb-fliptrack-mmap-gtt:
    - shard-rkl:          NOTRUN -> [SKIP][189] ([fdo#111825]) +4 other tests skip
   [189]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_frontbuffer_tracking@fbc-2p-shrfb-fliptrack-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@fbc-rgb565-draw-pwrite:
    - shard-dg2:          [PASS][190] -> [FAIL][191] ([i915#6880])
   [190]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-3/igt@kms_frontbuffer_tracking@fbc-rgb565-draw-pwrite.html
   [191]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_frontbuffer_tracking@fbc-rgb565-draw-pwrite.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-cpu:
    - shard-dg2:          NOTRUN -> [SKIP][192] ([i915#3458]) +16 other tests skip
   [192]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-cpu.html

  * igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-render:
    - shard-dg2:          NOTRUN -> [SKIP][193] ([i915#5354]) +56 other tests skip
   [193]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-render.html

  * igt@kms_frontbuffer_tracking@fbcpsr-tiling-y:
    - shard-tglu:         NOTRUN -> [SKIP][194] ([fdo#110189]) +2 other tests skip
   [194]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-7/igt@kms_frontbuffer_tracking@fbcpsr-tiling-y.html

  * igt@kms_frontbuffer_tracking@psr-1p-primscrn-shrfb-msflip-blt:
    - shard-dg1:          NOTRUN -> [SKIP][195] ([i915#3458]) +4 other tests skip
   [195]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@kms_frontbuffer_tracking@psr-1p-primscrn-shrfb-msflip-blt.html

  * igt@kms_frontbuffer_tracking@psr-2p-scndscrn-indfb-msflip-blt:
    - shard-tglu:         NOTRUN -> [SKIP][196] ([fdo#109280]) +5 other tests skip
   [196]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-6/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-indfb-msflip-blt.html

  * igt@kms_frontbuffer_tracking@psr-rgb565-draw-mmap-gtt:
    - shard-mtlp:         NOTRUN -> [SKIP][197] ([i915#8708]) +3 other tests skip
   [197]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_frontbuffer_tracking@psr-rgb565-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@psr-suspend:
    - shard-rkl:          NOTRUN -> [SKIP][198] ([i915#3023]) +5 other tests skip
   [198]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_frontbuffer_tracking@psr-suspend.html

  * igt@kms_hdr@bpc-switch:
    - shard-rkl:          NOTRUN -> [SKIP][199] ([i915#3555] / [i915#8228]) +1 other test skip
   [199]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_hdr@bpc-switch.html

  * igt@kms_hdr@static-toggle:
    - shard-dg2:          NOTRUN -> [SKIP][200] ([i915#3555] / [i915#8228]) +1 other test skip
   [200]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_hdr@static-toggle.html

  * igt@kms_multipipe_modeset@basic-max-pipe-crc-check:
    - shard-dg2:          NOTRUN -> [SKIP][201] ([i915#4816])
   [201]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_multipipe_modeset@basic-max-pipe-crc-check.html

  * igt@kms_pipe_b_c_ivb@from-pipe-c-to-b-with-3-lanes:
    - shard-dg2:          NOTRUN -> [SKIP][202] ([fdo#109289]) +5 other tests skip
   [202]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_pipe_b_c_ivb@from-pipe-c-to-b-with-3-lanes.html

  * igt@kms_pipe_b_c_ivb@pipe-b-double-modeset-then-modeset-pipe-c:
    - shard-mtlp:         NOTRUN -> [SKIP][203] ([fdo#109289])
   [203]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_pipe_b_c_ivb@pipe-b-double-modeset-then-modeset-pipe-c.html

  * igt@kms_plane_alpha_blend@constant-alpha-max@pipe-c-hdmi-a-1:
    - shard-glk:          NOTRUN -> [FAIL][204] ([i915#4573]) +1 other test fail
   [204]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk2/igt@kms_plane_alpha_blend@constant-alpha-max@pipe-c-hdmi-a-1.html

  * igt@kms_plane_lowres@tiling-yf:
    - shard-mtlp:         NOTRUN -> [SKIP][205] ([i915#3555] / [i915#8821])
   [205]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@kms_plane_lowres@tiling-yf.html

  * igt@kms_plane_scaling@intel-max-src-size:
    - shard-mtlp:         NOTRUN -> [SKIP][206] ([i915#6953])
   [206]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_plane_scaling@intel-max-src-size.html

  * igt@kms_plane_scaling@intel-max-src-size@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [FAIL][207] ([i915#8292])
   [207]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_plane_scaling@intel-max-src-size@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [SKIP][208] ([i915#5176] / [i915#9423]) +3 other tests skip
   [208]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-b-hdmi-a-1:
    - shard-tglu:         NOTRUN -> [SKIP][209] ([i915#5176] / [i915#9423]) +3 other tests skip
   [209]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-9/igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-b-hdmi-a-1.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-a-vga-1:
    - shard-snb:          NOTRUN -> [SKIP][210] ([fdo#109271]) +32 other tests skip
   [210]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-snb4/igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-a-vga-1.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-d-hdmi-a-3:
    - shard-dg2:          NOTRUN -> [SKIP][211] ([i915#5235]) +11 other tests skip
   [211]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-d-hdmi-a-3.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25@pipe-c-hdmi-a-1:
    - shard-tglu:         NOTRUN -> [SKIP][212] ([i915#5235]) +3 other tests skip
   [212]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-6/igt@kms_plane_scaling@planes-downscale-factor-0-25@pipe-c-hdmi-a-1.html

  * igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [SKIP][213] ([i915#5235]) +7 other tests skip
   [213]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-b-edp-1:
    - shard-mtlp:         NOTRUN -> [SKIP][214] ([i915#5235]) +3 other tests skip
   [214]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-b-edp-1.html

  * igt@kms_prime@basic-modeset-hybrid:
    - shard-tglu:         NOTRUN -> [SKIP][215] ([i915#6524])
   [215]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-5/igt@kms_prime@basic-modeset-hybrid.html

  * igt@kms_psr2_sf@overlay-plane-move-continuous-sf:
    - shard-apl:          NOTRUN -> [SKIP][216] ([fdo#109271] / [i915#658])
   [216]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl1/igt@kms_psr2_sf@overlay-plane-move-continuous-sf.html

  * igt@kms_psr2_sf@overlay-plane-update-sf-dmg-area:
    - shard-tglu:         NOTRUN -> [SKIP][217] ([fdo#111068] / [i915#658])
   [217]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-5/igt@kms_psr2_sf@overlay-plane-update-sf-dmg-area.html

  * igt@kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb:
    - shard-dg2:          NOTRUN -> [SKIP][218] ([i915#658]) +2 other tests skip
   [218]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb.html
    - shard-dg1:          NOTRUN -> [SKIP][219] ([i915#658])
   [219]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb.html

  * igt@kms_psr2_su@frontbuffer-xrgb8888:
    - shard-mtlp:         NOTRUN -> [SKIP][220] ([i915#4348]) +1 other test skip
   [220]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_psr2_su@frontbuffer-xrgb8888.html

  * igt@kms_psr@psr2_sprite_mmap_gtt:
    - shard-rkl:          NOTRUN -> [SKIP][221] ([i915#1072]) +4 other tests skip
   [221]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_psr@psr2_sprite_mmap_gtt.html

  * igt@kms_psr@sprite_blt:
    - shard-dg2:          NOTRUN -> [SKIP][222] ([i915#1072]) +8 other tests skip
   [222]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_psr@sprite_blt.html

  * igt@kms_psr@sprite_plane_move:
    - shard-dg1:          NOTRUN -> [SKIP][223] ([i915#1072] / [i915#4078]) +1 other test skip
   [223]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_psr@sprite_plane_move.html

  * igt@kms_psr_stress_test@flip-primary-invalidate-overlay:
    - shard-dg2:          NOTRUN -> [SKIP][224] ([i915#5461] / [i915#658])
   [224]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_psr_stress_test@flip-primary-invalidate-overlay.html

  * igt@kms_rotation_crc@bad-pixel-format:
    - shard-dg2:          NOTRUN -> [SKIP][225] ([i915#4235])
   [225]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_rotation_crc@bad-pixel-format.html

  * igt@kms_rotation_crc@primary-y-tiled-reflect-x-0:
    - shard-rkl:          [PASS][226] -> [INCOMPLETE][227] ([i915#8875])
   [226]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-2/igt@kms_rotation_crc@primary-y-tiled-reflect-x-0.html
   [227]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_rotation_crc@primary-y-tiled-reflect-x-0.html

  * igt@kms_rotation_crc@primary-y-tiled-reflect-x-270:
    - shard-rkl:          [PASS][228] -> [INCOMPLETE][229] ([i915#8875] / [i915#9475])
   [228]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-2/igt@kms_rotation_crc@primary-y-tiled-reflect-x-270.html
   [229]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_rotation_crc@primary-y-tiled-reflect-x-270.html

  * igt@kms_rotation_crc@primary-yf-tiled-reflect-x-90:
    - shard-dg2:          NOTRUN -> [SKIP][230] ([i915#4235] / [i915#5190]) +1 other test skip
   [230]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_rotation_crc@primary-yf-tiled-reflect-x-90.html

  * igt@kms_setmode@invalid-clone-exclusive-crtc:
    - shard-mtlp:         NOTRUN -> [SKIP][231] ([i915#3555] / [i915#8823])
   [231]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_setmode@invalid-clone-exclusive-crtc.html

  * igt@kms_setmode@invalid-clone-single-crtc:
    - shard-mtlp:         NOTRUN -> [SKIP][232] ([i915#3555] / [i915#8809])
   [232]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_setmode@invalid-clone-single-crtc.html

  * igt@kms_vblank@pipe-d-ts-continuation-modeset-rpm:
    - shard-rkl:          NOTRUN -> [SKIP][233] ([i915#4070] / [i915#533] / [i915#6768]) +1 other test skip
   [233]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_vblank@pipe-d-ts-continuation-modeset-rpm.html

  * igt@kms_writeback@writeback-invalid-parameters:
    - shard-mtlp:         NOTRUN -> [SKIP][234] ([i915#2437])
   [234]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_writeback@writeback-invalid-parameters.html

  * igt@kms_writeback@writeback-pixel-formats:
    - shard-dg2:          NOTRUN -> [SKIP][235] ([i915#2437]) +1 other test skip
   [235]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_writeback@writeback-pixel-formats.html

  * igt@perf@global-sseu-config-invalid:
    - shard-dg2:          NOTRUN -> [SKIP][236] ([i915#7387])
   [236]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@perf@global-sseu-config-invalid.html

  * igt@perf_pmu@cpu-hotplug:
    - shard-glk:          NOTRUN -> [SKIP][237] ([fdo#109271]) +52 other tests skip
   [237]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk2/igt@perf_pmu@cpu-hotplug.html

  * igt@perf_pmu@event-wait@rcs0:
    - shard-dg2:          NOTRUN -> [SKIP][238] ([fdo#112283]) +1 other test skip
   [238]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@perf_pmu@event-wait@rcs0.html

  * igt@perf_pmu@module-unload:
    - shard-dg2:          NOTRUN -> [FAIL][239] ([i915#5793])
   [239]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@perf_pmu@module-unload.html

  * igt@prime_mmap@test_aperture_limit@test_aperture_limit-smem:
    - shard-dg2:          NOTRUN -> [CRASH][240] ([i915#9351])
   [240]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@prime_mmap@test_aperture_limit@test_aperture_limit-smem.html

  * igt@prime_vgem@basic-read:
    - shard-dg2:          NOTRUN -> [SKIP][241] ([i915#3291] / [i915#3708])
   [241]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@prime_vgem@basic-read.html

  * igt@prime_vgem@fence-write-hang:
    - shard-dg2:          NOTRUN -> [SKIP][242] ([i915#3708]) +1 other test skip
   [242]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@prime_vgem@fence-write-hang.html

  * igt@tools_test@sysfs_l3_parity:
    - shard-dg2:          NOTRUN -> [SKIP][243] ([i915#4818])
   [243]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@tools_test@sysfs_l3_parity.html

  * igt@v3d/v3d_job_submission@array-job-submission:
    - shard-dg2:          NOTRUN -> [SKIP][244] ([i915#2575]) +18 other tests skip
   [244]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@v3d/v3d_job_submission@array-job-submission.html

  * igt@v3d/v3d_submit_cl@bad-multisync-out-sync:
    - shard-tglu:         NOTRUN -> [SKIP][245] ([fdo#109315] / [i915#2575])
   [245]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@v3d/v3d_submit_cl@bad-multisync-out-sync.html

  * igt@v3d/v3d_submit_csd@bad-multisync-extension:
    - shard-dg1:          NOTRUN -> [SKIP][246] ([i915#2575]) +2 other tests skip
   [246]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@v3d/v3d_submit_csd@bad-multisync-extension.html

  * igt@v3d/v3d_wait_bo@bad-bo:
    - shard-rkl:          NOTRUN -> [SKIP][247] ([fdo#109315]) +5 other tests skip
   [247]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@v3d/v3d_wait_bo@bad-bo.html

  * igt@v3d/v3d_wait_bo@used-bo:
    - shard-mtlp:         NOTRUN -> [SKIP][248] ([i915#2575]) +7 other tests skip
   [248]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@v3d/v3d_wait_bo@used-bo.html

  * igt@vc4/vc4_lookup_fail@bad-color-write:
    - shard-rkl:          NOTRUN -> [SKIP][249] ([i915#7711]) +2 other tests skip
   [249]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@vc4/vc4_lookup_fail@bad-color-write.html
    - shard-dg1:          NOTRUN -> [SKIP][250] ([i915#7711]) +1 other test skip
   [250]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@vc4/vc4_lookup_fail@bad-color-write.html

  * igt@vc4/vc4_tiling@get-bad-modifier:
    - shard-dg2:          NOTRUN -> [SKIP][251] ([i915#7711]) +9 other tests skip
   [251]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@vc4/vc4_tiling@get-bad-modifier.html

  * igt@vc4/vc4_wait_bo@used-bo-0ns:
    - shard-mtlp:         NOTRUN -> [SKIP][252] ([i915#7711]) +3 other tests skip
   [252]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@vc4/vc4_wait_bo@used-bo-0ns.html

  
#### Possible fixes ####

  * igt@gem_ctx_exec@basic-nohangcheck:
    - shard-rkl:          [FAIL][253] ([i915#6268]) -> [PASS][254]
   [253]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-7/igt@gem_ctx_exec@basic-nohangcheck.html
   [254]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@gem_ctx_exec@basic-nohangcheck.html

  * igt@gem_ctx_isolation@preservation-s3@vcs0:
    - shard-dg2:          [INCOMPLETE][255] ([i915#9162]) -> [PASS][256]
   [255]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-11/igt@gem_ctx_isolation@preservation-s3@vcs0.html
   [256]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@gem_ctx_isolation@preservation-s3@vcs0.html

  * igt@gem_eio@reset-stress:
    - shard-dg1:          [FAIL][257] ([i915#5784]) -> [PASS][258]
   [257]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-16/igt@gem_eio@reset-stress.html
   [258]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@gem_eio@reset-stress.html

  * igt@gem_exec_fair@basic-deadline:
    - shard-rkl:          [FAIL][259] ([i915#2846]) -> [PASS][260]
   [259]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-6/igt@gem_exec_fair@basic-deadline.html
   [260]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@gem_exec_fair@basic-deadline.html

  * igt@gem_exec_fair@basic-pace-share@rcs0:
    - shard-glk:          [FAIL][261] ([i915#2876]) -> [PASS][262]
   [261]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk3/igt@gem_exec_fair@basic-pace-share@rcs0.html
   [262]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk4/igt@gem_exec_fair@basic-pace-share@rcs0.html
    - shard-tglu:         [FAIL][263] ([i915#2842]) -> [PASS][264]
   [263]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-tglu-4/igt@gem_exec_fair@basic-pace-share@rcs0.html
   [264]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@gem_exec_fair@basic-pace-share@rcs0.html

  * igt@gem_exec_fair@basic-pace-solo@rcs0:
    - shard-rkl:          [FAIL][265] ([i915#2842]) -> [PASS][266]
   [265]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-4/igt@gem_exec_fair@basic-pace-solo@rcs0.html
   [266]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@gem_exec_fair@basic-pace-solo@rcs0.html

  * igt@gem_exec_fair@basic-pace@rcs0:
    - shard-glk:          [FAIL][267] ([i915#2842]) -> [PASS][268]
   [267]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk8/igt@gem_exec_fair@basic-pace@rcs0.html
   [268]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk9/igt@gem_exec_fair@basic-pace@rcs0.html

  * igt@gem_exec_suspend@basic-s0@smem:
    - shard-dg2:          [INCOMPLETE][269] -> [PASS][270]
   [269]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-1/igt@gem_exec_suspend@basic-s0@smem.html
   [270]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_suspend@basic-s0@smem.html

  * igt@gen9_exec_parse@allowed-single:
    - shard-apl:          [ABORT][271] ([i915#5566]) -> [PASS][272]
   [271]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl7/igt@gen9_exec_parse@allowed-single.html
   [272]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl1/igt@gen9_exec_parse@allowed-single.html

  * igt@i915_hangman@gt-engine-error@vcs0:
    - shard-mtlp:         [FAIL][273] ([i915#7069]) -> [PASS][274]
   [273]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-3/igt@i915_hangman@gt-engine-error@vcs0.html
   [274]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@i915_hangman@gt-engine-error@vcs0.html

  * igt@i915_pm_rc6_residency@rc6-idle@rcs0:
    - shard-dg1:          [FAIL][275] ([i915#3591]) -> [PASS][276] +1 other test pass
   [275]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-19/igt@i915_pm_rc6_residency@rc6-idle@rcs0.html
   [276]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@i915_pm_rc6_residency@rc6-idle@rcs0.html

  * igt@i915_pm_rpm@dpms-mode-unset-non-lpsp:
    - shard-dg1:          [SKIP][277] ([i915#1397]) -> [PASS][278] +2 other tests pass
   [277]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-19/igt@i915_pm_rpm@dpms-mode-unset-non-lpsp.html
   [278]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-18/igt@i915_pm_rpm@dpms-mode-unset-non-lpsp.html

  * igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait:
    - shard-rkl:          [SKIP][279] ([i915#1397]) -> [PASS][280]
   [279]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-7/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html
   [280]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html

  * igt@i915_pm_rps@reset:
    - shard-snb:          [INCOMPLETE][281] ([i915#7790]) -> [PASS][282]
   [281]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-snb2/igt@i915_pm_rps@reset.html
   [282]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-snb5/igt@i915_pm_rps@reset.html

  * igt@i915_suspend@basic-s3-without-i915:
    - shard-rkl:          [FAIL][283] ([fdo#103375]) -> [PASS][284] +1 other test pass
   [283]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-6/igt@i915_suspend@basic-s3-without-i915.html
   [284]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@i915_suspend@basic-s3-without-i915.html

  * igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0:
    - shard-mtlp:         [FAIL][285] ([i915#5138]) -> [PASS][286]
   [285]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-2/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0.html
   [286]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0.html

  * igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions-varying-size:
    - shard-glk:          [FAIL][287] ([i915#2346]) -> [PASS][288]
   [287]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk2/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions-varying-size.html
   [288]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk9/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions-varying-size.html

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-pgflip-blt:
    - shard-dg2:          [FAIL][289] ([i915#6880]) -> [PASS][290] +1 other test pass
   [289]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-6/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-pgflip-blt.html
   [290]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-pgflip-blt.html

  * {igt@kms_lease@page-flip-implicit-plane@pipe-d-edp-1}:
    - shard-mtlp:         [ABORT][291] ([i915#9414]) -> [PASS][292] +3 other tests pass
   [291]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-2/igt@kms_lease@page-flip-implicit-plane@pipe-d-edp-1.html
   [292]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@kms_lease@page-flip-implicit-plane@pipe-d-edp-1.html

  * igt@kms_pipe_crc_basic@suspend-read-crc@pipe-b-dp-1:
    - shard-apl:          [INCOMPLETE][293] ([i915#180] / [i915#9392]) -> [PASS][294]
   [293]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl2/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-b-dp-1.html
   [294]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl4/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-b-dp-1.html

  * {igt@kms_pm_dc@dc9-dpms}:
    - shard-apl:          [SKIP][295] ([fdo#109271]) -> [PASS][296]
   [295]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl1/igt@kms_pm_dc@dc9-dpms.html
   [296]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl4/igt@kms_pm_dc@dc9-dpms.html

  * igt@kms_rotation_crc@sprite-rotation-270:
    - shard-rkl:          [INCOMPLETE][297] ([i915#8875]) -> [PASS][298]
   [297]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-4/igt@kms_rotation_crc@sprite-rotation-270.html
   [298]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_rotation_crc@sprite-rotation-270.html

  * igt@kms_universal_plane@cursor-fb-leak@pipe-a-hdmi-a-1:
    - shard-tglu:         [FAIL][299] ([i915#9196]) -> [PASS][300] +1 other test pass
   [299]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-tglu-2/igt@kms_universal_plane@cursor-fb-leak@pipe-a-hdmi-a-1.html
   [300]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-6/igt@kms_universal_plane@cursor-fb-leak@pipe-a-hdmi-a-1.html

  * igt@kms_vblank@pipe-a-ts-continuation-dpms-suspend:
    - shard-apl:          [INCOMPLETE][301] ([i915#9392]) -> [PASS][302]
   [301]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl6/igt@kms_vblank@pipe-a-ts-continuation-dpms-suspend.html
   [302]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl2/igt@kms_vblank@pipe-a-ts-continuation-dpms-suspend.html

  * igt@perf_pmu@busy-double-start@rcs0:
    - shard-mtlp:         [FAIL][303] ([i915#4349]) -> [PASS][304] +1 other test pass
   [303]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-8/igt@perf_pmu@busy-double-start@rcs0.html
   [304]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@perf_pmu@busy-double-start@rcs0.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#103375]: https://bugs.freedesktop.org/show_bug.cgi?id=103375
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109274]: https://bugs.freedesktop.org/show_bug.cgi?id=109274
  [fdo#109280]: https://bugs.freedesktop.org/show_bug.cgi?id=109280
  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [fdo#109289]: https://bugs.freedesktop.org/show_bug.cgi?id=109289
  [fdo#109315]: https://bugs.freedesktop.org/show_bug.cgi?id=109315
  [fdo#109506]: https://bugs.freedesktop.org/show_bug.cgi?id=109506
  [fdo#110189]: https://bugs.freedesktop.org/show_bug.cgi?id=110189
  [fdo#110723]: https://bugs.freedesktop.org/show_bug.cgi?id=110723
  [fdo#111068]: https://bugs.freedesktop.org/show_bug.cgi?id=111068
  [fdo#111614]: https://bugs.freedesktop.org/show_bug.cgi?id=111614
  [fdo#111615]: https://bugs.freedesktop.org/show_bug.cgi?id=111615
  [fdo#111767]: https://bugs.freedesktop.org/show_bug.cgi?id=111767
  [fdo#111825]: https://bugs.freedesktop.org/show_bug.cgi?id=111825
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [fdo#112283]: https://bugs.freedesktop.org/show_bug.cgi?id=112283
  [i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
  [i915#1099]: https://gitlab.freedesktop.org/drm/intel/issues/1099
  [i915#1397]: https://gitlab.freedesktop.org/drm/intel/issues/1397
  [i915#1769]: https://gitlab.freedesktop.org/drm/intel/issues/1769
  [i915#180]: https://gitlab.freedesktop.org/drm/intel/issues/180
  [i915#1825]: https://gitlab.freedesktop.org/drm/intel/issues/1825
  [i915#1937]: https://gitlab.freedesktop.org/drm/intel/issues/1937
  [i915#2346]: https://gitlab.freedesktop.org/drm/intel/issues/2346
  [i915#2410]: https://gitlab.freedesktop.org/drm/intel/issues/2410
  [i915#2437]: https://gitlab.freedesktop.org/drm/intel/issues/2437
  [i915#2527]: https://gitlab.freedesktop.org/drm/intel/issues/2527
  [i915#2575]: https://gitlab.freedesktop.org/drm/intel/issues/2575
  [i915#2587]: https://gitlab.freedesktop.org/drm/intel/issues/2587
  [i915#2672]: https://gitlab.freedesktop.org/drm/intel/issues/2672
  [i915#2842]: https://gitlab.freedesktop.org/drm/intel/issues/2842
  [i915#2846]: https://gitlab.freedesktop.org/drm/intel/issues/2846
  [i915#2856]: https://gitlab.freedesktop.org/drm/intel/issues/2856
  [i915#2876]: https://gitlab.freedesktop.org/drm/intel/issues/2876
  [i915#3023]: https://gitlab.freedesktop.org/drm/intel/issues/3023
  [i915#3281]: https://gitlab.freedesktop.org/drm/intel/issues/3281
  [i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
  [i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
  [i915#3297]: https://gitlab.freedesktop.org/drm/intel/issues/3297
  [i915#3299]: https://gitlab.freedesktop.org/drm/intel/issues/3299
  [i915#3359]: https://gitlab.freedesktop.org/drm/intel/issues/3359
  [i915#3458]: https://gitlab.freedesktop.org/drm/intel/issues/3458
  [i915#3469]: https://gitlab.freedesktop.org/drm/intel/issues/3469
  [i915#3539]: https://gitlab.freedesktop.org/drm/intel/issues/3539
  [i915#3546]: https://gitlab.freedesktop.org/drm/intel/issues/3546
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3591]: https://gitlab.freedesktop.org/drm/intel/issues/3591
  [i915#3637]: https://gitlab.freedesktop.org/drm/intel/issues/3637
  [i915#3638]: https://gitlab.freedesktop.org/drm/intel/issues/3638
  [i915#3689]: https://gitlab.freedesktop.org/drm/intel/issues/3689
  [i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
  [i915#3743]: https://gitlab.freedesktop.org/drm/intel/issues/3743
  [i915#3804]: https://gitlab.freedesktop.org/drm/intel/issues/3804
  [i915#3826]: https://gitlab.freedesktop.org/drm/intel/issues/3826
  [i915#3840]: https://gitlab.freedesktop.org/drm/intel/issues/3840
  [i915#3886]: https://gitlab.freedesktop.org/drm/intel/issues/3886
  [i915#4036]: https://gitlab.freedesktop.org/drm/intel/issues/4036
  [i915#404]: https://gitlab.freedesktop.org/drm/intel/issues/404
  [i915#4070]: https://gitlab.freedesktop.org/drm/intel/issues/4070
  [i915#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077
  [i915#4078]: https://gitlab.freedesktop.org/drm/intel/issues/4078
  [i915#4079]: https://gitlab.freedesktop.org/drm/intel/issues/4079
  [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083
  [i915#4087]: https://gitlab.freedesktop.org/drm/intel/issues/4087
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4212]: https://gitlab.freedesktop.org/drm/intel/issues/4212
  [i915#4213]: https://gitlab.freedesktop.org/drm/intel/issues/4213
  [i915#4235]: https://gitlab.freedesktop.org/drm/intel/issues/4235
  [i915#4270]: https://gitlab.freedesktop.org/drm/intel/issues/4270
  [i915#4348]: https://gitlab.freedesktop.org/drm/intel/issues/4348
  [i915#4349]: https://gitlab.freedesktop.org/drm/intel/issues/4349
  [i915#4473]: https://gitlab.freedesktop.org/drm/intel/issues/4473
  [i915#4537]: https://gitlab.freedesktop.org/drm/intel/issues/4537
  [i915#4538]: https://gitlab.freedesktop.org/drm/intel/issues/4538
  [i915#4573]: https://gitlab.freedesktop.org/drm/intel/issues/4573
  [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
  [i915#4767]: https://gitlab.freedesktop.org/drm/intel/issues/4767
  [i915#4771]: https://gitlab.freedesktop.org/drm/intel/issues/4771
  [i915#4812]: https://gitlab.freedesktop.org/drm/intel/issues/4812
  [i915#4816]: https://gitlab.freedesktop.org/drm/intel/issues/4816
  [i915#4818]: https://gitlab.freedesktop.org/drm/intel/issues/4818
  [i915#4852]: https://gitlab.freedesktop.org/drm/intel/issues/4852
  [i915#4860]: https://gitlab.freedesktop.org/drm/intel/issues/4860
  [i915#4881]: https://gitlab.freedesktop.org/drm/intel/issues/4881
  [i915#4885]: https://gitlab.freedesktop.org/drm/intel/issues/4885
  [i915#4936]: https://gitlab.freedesktop.org/drm/intel/issues/4936
  [i915#5138]: https://gitlab.freedesktop.org/drm/intel/issues/5138
  [i915#5176]: https://gitlab.freedesktop.org/drm/intel/issues/5176
  [i915#5190]: https://gitlab.freedesktop.org/drm/intel/issues/5190
  [i915#5235]: https://gitlab.freedesktop.org/drm/intel/issues/5235
  [i915#5286]: https://gitlab.freedesktop.org/drm/intel/issues/5286
  [i915#533]: https://gitlab.freedesktop.org/drm/intel/issues/533
  [i915#5354]: https://gitlab.freedesktop.org/drm/intel/issues/5354
  [i915#5461]: https://gitlab.freedesktop.org/drm/intel/issues/5461
  [i915#5493]: https://gitlab.freedesktop.org/drm/intel/issues/5493
  [i915#5566]: https://gitlab.freedesktop.org/drm/intel/issues/5566
  [i915#5608]: https://gitlab.freedesktop.org/drm/intel/issues/5608
  [i915#5784]: https://gitlab.freedesktop.org/drm/intel/issues/5784
  [i915#5793]: https://gitlab.freedesktop.org/drm/intel/issues/5793
  [i915#5889]: https://gitlab.freedesktop.org/drm/intel/issues/5889
  [i915#6095]: https://gitlab.freedesktop.org/drm/intel/issues/6095
  [i915#6122]: https://gitlab.freedesktop.org/drm/intel/issues/6122
  [i915#6227]: https://gitlab.freedesktop.org/drm/intel/issues/6227
  [i915#6268]: https://gitlab.freedesktop.org/drm/intel/issues/6268
  [i915#6334]: https://gitlab.freedesktop.org/drm/intel/issues/6334
  [i915#6335]: https://gitlab.freedesktop.org/drm/intel/issues/6335
  [i915#6524]: https://gitlab.freedesktop.org/drm/intel/issues/6524
  [i915#658]: https://gitlab.freedesktop.org/drm/intel/issues/658
  [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621
  [i915#6768]: https://gitlab.freedesktop.org/drm/intel/issues/6768
  [i915#6880]: https://gitlab.freedesktop.org/drm/intel/issues/6880
  [i915#6944]: https://gitlab.freedesktop.org/drm/intel/issues/6944
  [i915#6953]: https://gitlab.freedesktop.org/drm/intel/issues/6953
  [i915#7069]: https://gitlab.freedesktop.org/drm/intel/issues/7069
  [i915#7118]: https://gitlab.freedesktop.org/drm/intel/issues/7118
  [i915#7173]: https://gitlab.freedesktop.org/drm/intel/issues/7173
  [i915#7297]: https://gitlab.freedesktop.org/drm/intel/issues/7297
  [i915#7387]: https://gitlab.freedesktop.org/drm/intel/issues/7387
  [i915#7461]: https://gitlab.freedesktop.org/drm/intel/issues/7461
  [i915#7697]: https://gitlab.freedesktop.org/drm/intel/issues/7697
  [i915#7701]: https://gitlab.freedesktop.org/drm/intel/issues/7701
  [i915#7707]: https://gitlab.freedesktop.org/drm/intel/issues/7707
  [i915#7711]: https://gitlab.freedesktop.org/drm/intel/issues/7711
  [i915#7790]: https://gitlab.freedesktop.org/drm/intel/issues/7790
  [i915#7828]: https://gitlab.freedesktop.org/drm/intel/issues/7828
  [i915#7975]: https://gitlab.freedesktop.org/drm/intel/issues/7975
  [i915#8213]: https://gitlab.freedesktop.org/drm/intel/issues/8213
  [i915#8228]: https://gitlab.freedesktop.org/drm/intel/issues/8228
  [i915#8247]: https://gitlab.freedesktop.org/drm/intel/issues/8247
  [i915#8289]: https://gitlab.freedesktop.org/drm/intel/issues/8289
  [i915#8292]: https://gitlab.freedesktop.org/drm/intel/issues/8292
  [i915#8381]: https://gitlab.freedesktop.org/drm/intel/issues/8381
  [i915#8411]: https://gitlab.freedesktop.org/drm/intel/issues/8411
  [i915#8414]: https://gitlab.freedesktop.org/drm/intel/issues/8414
  [i915#8428]: https://gitlab.freedesktop.org/drm/intel/issues/8428
  [i915#8430]: https://gitlab.freedesktop.org/drm/intel/issues/8430
  [i915#8456]: https://gitlab.freedesktop.org/drm/intel/issues/8456
  [i915#8502]: https://gitlab.freedesktop.org/drm/intel/issues/8502
  [i915#8555]: https://gitlab.freedesktop.org/drm/intel/issues/8555
  [i915#8708]: https://gitlab.freedesktop.org/drm/intel/issues/8708
  [i915#8709]: https://gitlab.freedesktop.org/drm/intel/issues/8709
  [i915#8809]: https://gitlab.freedesktop.org/drm/intel/issues/8809
  [i915#8810]: https://gitlab.freedesktop.org/drm/intel/issues/8810
  [i915#8812]: https://gitlab.freedesktop.org/drm/intel/issues/8812
  [i915#8821]: https://gitlab.freedesktop.org/drm/intel/issues/8821
  [i915#8823]: https://gitlab.freedesktop.org/drm/intel/issues/8823
  [i915#8875]: https://gitlab.freedesktop.org/drm/intel/issues/8875
  [i915#8925]: https://gitlab.freedesktop.org/drm/intel/issues/8925
  [i915#8962]: https://gitlab.freedesktop.org/drm/intel/issues/8962
  [i915#9067]: https://gitlab.freedesktop.org/drm/intel/issues/9067
  [i915#9162]: https://gitlab.freedesktop.org/drm/intel/issues/9162
  [i915#9196]: https://gitlab.freedesktop.org/drm/intel/issues/9196
  [i915#9226]: https://gitlab.freedesktop.org/drm/intel/issues/9226
  [i915#9227]: https://gitlab.freedesktop.org/drm/intel/issues/9227
  [i915#9261]: https://gitlab.freedesktop.org/drm/intel/issues/9261
  [i915#9351]: https://gitlab.freedesktop.org/drm/intel/issues/9351
  [i915#9353]: https://gitlab.freedesktop.org/drm/intel/issues/9353
  [i915#9392]: https://gitlab.freedesktop.org/drm/intel/issues/9392
  [i915#9412]: https://gitlab.freedesktop.org/drm/intel/issues/9412
  [i915#9414]: https://gitlab.freedesktop.org/drm/intel/issues/9414
  [i915#9423]: https://gitlab.freedesktop.org/drm/intel/issues/9423
  [i915#9475]: https://gitlab.freedesktop.org/drm/intel/issues/9475


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7527 -> IGTPW_9965
  * Piglit: piglit_4509 -> None

  CI-20190529: 20190529
  CI_DRM_13735: 78e2c2b4839d929de3f7393d8b93d4e1faae3cd8 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_9965: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html
  IGT_7527: 46f98a3041f73a1d6ee7ec3ace6eba79b15369c4 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html

[-- Attachment #2: Type: text/html, Size: 118944 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
  2023-10-10 17:49       ` [Intel-xe] " Janusz Krzysztofik
  (?)
@ 2023-10-11 13:00         ` Kamil Konieczny
  -1 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-11 13:00 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, Mauro Carvalho Chehab, intel-xe

Hi Janusz,
On 2023-10-10 at 19:49:35 +0200, Janusz Krzysztofik wrote:
> Hi Kamil,
> 
> Thanks for review.
> 
> On Tuesday, 10 October 2023 17:59:56 CEST Kamil Konieczny wrote:
> > Hi Janusz,
> > On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> > > There was an attempt to parse KTAP reports in the background while a kunit
> > > test module is loading.  However, since dynamic sub-subtests can be
> > > executed only from the main thread, that attempt was not quite successful,
> > > as IGT results from all executed kunit test cases were generated only
> > > after loading of kunit test module completed.
> > > 
> > > Now that the parser maintains its state and we can call it separately for
> > > each input line of a KTAP report, it is perfectly possible to call the
> > > parser from the main thread while the module is loading in the background,
> > > and convert results from kunit test cases immediately to results of IGT
> > > dynamic sub-subtests by running an igt_dynamic() section for each result
> > > as soon as returned by the parser.
> > > 
> > > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > > result obtained from igt_ktap_parse() called from the main thread.
> > > 
> > > Also, drop no longer needed functions from igt_ktap soruces.
> > > 
> > > v3: Fix ktap structure not freed on lseek error,
> > >   - fix initial SIGCHLD handler not restored,
> > >   - fix missing handling of potential errors returned by sigaction,
> > >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> > >     synchronization with modprobe thread,
> > >   - fix potentially illegal use of igt_assert() called outside of
> > >     dynamic sub-subtest section,
> > >   - fix unsupported exit code potentially passed to igt_fail(),
> > >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> > >     after a valid result from the parser has been processed,
> > >   - fix trailing newlines missing from error messages,
> > >   - add more debug statements,
> > >   - integrate common code around kunit_result_free() into it.
> > > v2: Interrupt blocking read() on modprobe failure.
> > > 
> > > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > > ---
> > >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> > >  lib/igt_ktap.c | 568 -------------------------------------------------
> > >  lib/igt_ktap.h |  22 --
> > >  3 files changed, 222 insertions(+), 629 deletions(-)
> > > 
> > > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > > index 426ae5b26f..7bca4cdaab 100644
> > > --- a/lib/igt_kmod.c
> > > +++ b/lib/igt_kmod.c
> > > @@ -1,5 +1,5 @@
> > >  /*
> > > - * Copyright © 2016 Intel Corporation
> > > + * Copyright © 2016-2023 Intel Corporation
> > >   *
> > >   * Permission is hereby granted, free of charge, to any person obtaining a
> > >   * copy of this software and associated documentation files (the "Software"),
> > > @@ -26,7 +26,12 @@
> > >  #include <errno.h>
> > >  #include <fcntl.h>
> > >  #include <pthread.h>
> > > +#include <stdlib.h>
> > > +#include <string.h>
> > >  #include <sys/utsname.h>
> > > +#include <unistd.h>
> > > +
> > > +#include "assembler/brw_compat.h"	/* [un]likely() */
> > ------------ ^^^^^^^^^
> > Do we really need this?
> 
> I think the correct question is if wee really need [un]likely().  I'm using it 
> to document unlikely cases, which is a widely accepted method of documenting 
> cases like that, I believe.  Having that clarified, I hope you just tell me if 
> you think we need those cases documented, and how, if not that way.
> 

Now I see, it is for unlikely() - ok it can stay.

> > 
> > >  
> > >  #include "igt_aux.h"
> > >  #include "igt_core.h"
> > > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> > >  }
> > >  
> > >  struct modprobe_data {
> > > +	pthread_t parent;
> > --- ^^^^^^^^^^^^^^^^
> > Please move it below to other related thread data.
> > Also consider a comment why(or for what purpose)
> > did you put this here.
> 
> No problem to move it to the bottom, if that's important to you, but regarding 
> the comment, do you think that the purpose of this field of the structure, 
> compared to other fields, is so unclear form review of the code, despite its 
> name, that it requires a comment, unlike the other fields?
> 

I am ok with only move.

> > 
> > >  	struct kmod_module *kmod;
> > >  	const char *opts;
> > >  	int err;
> > > +	pthread_mutex_t lock;
> > > +	pthread_t thread;
> > >  };
> > >  
> > >  static void *modprobe_task(void *arg)
> > > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> > >  
> > >  	data->err = modprobe(data->kmod, data->opts);
> > >  
> > > +	if (igt_debug_on(data->err)) {
> > > +		int err;
> > > +
> > > +		while (err = pthread_mutex_trylock(&data->lock),
> > > +		       err && !igt_debug_on(err != EBUSY))
> > > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> > > +	} else {
> > > +		/* let main thread use mutex to detect modprobe completion */
> > > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > > +	}
> > > +
> > >  	return NULL;
> > >  }
> > >  
> > > +static void kunit_sigchld_handler(int signal)
> > > +{
> > > +	return;
> > --- ^^^^^^^
> > Why not removing this? checkpatch complains about return from void.
> 
> OK.
> 
> > 
> > > +}
> > > +
> > > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > > +				 struct modprobe_data *modprobe,
> > > +				 int fd, struct igt_ktap_results *ktap)
> > > +{
> > > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > > +			 *saved;
> > > +	char record[BUF_LEN + 1], *buf;
> > > +	unsigned long taints;
> > > +	int ret;
> > > +
> > > +	do {
> > > +		int err;
> > > +
> > > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > > +			return -ENOTRECOVERABLE;
> > > +
> > > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > > +		if (err == -1)
> > > +			return -errno;
> > > +		else if (unlikely(err))
> > > +			return err;
> > > +
> > > +		err = pthread_mutex_lock(&modprobe->lock);
> > > +		switch (err) {
> > > +		case EOWNERDEAD:
> > > +			/* leave the mutex unrecoverable */
> > > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > > +			__attribute__ ((fallthrough));
> > > +		case ENOTRECOVERABLE:
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +			if (igt_debug_on(modprobe->err))
> > > +				return modprobe->err;
> > 
> > Why no 'return -err;' here?
> 
> Because modprobe->err, which can tell us more about what has gone bad in the 
> modprobe thread, is more important here than err which only informs us about 
> completion of the modprobe thread, also if successful, isn't it?
> 

Thank you for explanation.

> > 
> > > +			break;
> > > +		case 0:
> > > +			break;
> > > +		default:
> > > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +			return -err;
> > > +		}
> > > +
> > > +		ret = read(fd, record, BUF_LEN);
> > > +
> > > +		if (!err) {
> >         ^^^^^^^^^^^
> > Looks strange here.
> 
> Here err still carries a return code from pthread_mutex_lock(), then !err 
> means that the modprobe thread was still running and we have 1) acquired 
> the mutex which now we have to release, 2) installed a non-default 
> SIGCHLD handler that we should now reset.  Have you got a better idea 
> how to encode that so it looks less strange?  Would a comment that reminds 
> a reader where that err comes from be sufficient?
> 

Yes please add comment here.

> > 
> > > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +		}
> > > +
> > > +		if (igt_debug_on(!ret))
> > > +			return -ENODATA;
> > > +		if (igt_debug_on(ret == -1))
> > > +			return -errno;
> > > +		if (unlikely(igt_debug_on(ret < 0)))
> > > +			break;
> > > +
> > > +		/* skip kmsg continuation lines */
> > > +		if (igt_debug_on(*record == ' '))
> > ----------- ^^^^^^^^^^^^
> > Why debug here? imho it is not needed.
> 
> Because we don't expect continuation lines while reading a KTAP report. Since 
> the kernel log may still contain them, we want to know about them when 
> something wrong happens to our KTAP parser.  That's the purpose of all those 
> debug_on() calls: provide as much information on unexpected conditions as 
> possible when the test fails, isn't it?
> 

Ok.

> > 
> > > +			continue;
> > > +
> > > +		/* NULL-terminate the record */
> > > +		record[ret] = '\0';
> > > +
> > > +		/* detect start of log message, continue if not found */
> > > +		buf = strchrnul(record, ';');
> > > +		if (igt_debug_on(*buf == '\0'))
> > ----------- ^^^^^^^^^^^^
> > Same here, you just continue loop if no ';'?
> 
> That means we are getting incorrectly formatted records from /dev/kmsg, and 
> I'm sure we would like to be informed about that if something goes wrong with 
> the test, wouldn't we?
> 

Ok.

> > 
> > > +			continue;
> > > +		buf++;
> > > +
> > > +		ret = igt_ktap_parse(buf, ktap);
> > > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > > +			break;
> > > +	} while (igt_list_empty(results));
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void kunit_result_free(struct igt_ktap_result **r,
> > > +			      char **suite_name, char **case_name)
> > > +{
> > > +	if (!*r)
> > > +		return;
> > > +
> > > +	igt_list_del(&(*r)->link);
> > > +
> > > +	if ((*r)->suite_name != *suite_name) {
> > > +		free(*suite_name);
> > > +		*suite_name = (*r)->suite_name;
> > > +	}
> > > +
> > > +	if ((*r)->case_name != *case_name) {
> > > +		free(*case_name);
> > > +		*case_name = (*r)->case_name;
> > > +	}
> > > +
> > > +	free((*r)->msg);
> > > +	free(*r);
> > > +	*r = NULL;
> > > +}
> > > +
> > >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> > >  {
> > > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > > -	struct kmod_module *kunit_kmod;
> > > -	bool is_builtin;
> > > -	struct ktap_test_results *results;
> > > -	pthread_t modprobe_thread;
> > > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > > +	char *suite_name = NULL, *case_name = NULL;
> > > +	struct igt_ktap_result *r, *rn;
> > > +	struct igt_ktap_results *ktap;
> > > +	pthread_mutexattr_t attr;
> > > +	IGT_LIST_HEAD(results);
> > >  	unsigned long taints;
> > >  	int flags, ret;
> > >  
> > > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> > >  
> > >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> > >  
> > > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > > -	kmod_module_unref(kunit_kmod);
> > > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> > >  
> > > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > > +	ktap = igt_ktap_alloc(&results);
> > > +	igt_require(ktap);
> > >  
> > > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> > >  					modprobe_task, &modprobe))) {
> > > -		ktap_parser_cancel();
> > > -		igt_ignore_warn(ktap_parser_stop());
> > > +		igt_ktap_free(ktap);
> > >  		igt_skip("Failed to create a modprobe thread\n");
> > >  	}
> > >  
> > > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > > -	{
> > > -		struct ktap_test_results_element *result;
> > > -
> > > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > > -			ktap_parser_cancel();
> > > +	do {
> > > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > > +					    tst->kmsg, ktap);
> > > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> > >  			break;
> > > -		}
> > >  
> > > -		if (igt_kernel_tainted(&taints)) {
> > > -			ktap_parser_cancel();
> > > -			pthread_cancel(modprobe_thread);
> > > +		if (igt_debug_on(igt_list_empty(&results)))
> > >  			break;
> > > -		}
> > >  
> > > -		pthread_mutex_lock(&results->mutex);
> > > -		if (igt_list_empty(&results->list)) {
> > > -			pthread_mutex_unlock(&results->mutex);
> > > -			continue;
> > > -		}
> > > +		r = igt_list_first_entry(&results, r, link);
> > >  
> > > -		result = igt_list_first_entry(&results->list, result, link);
> > > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > > +			if (r->code == IGT_EXIT_INVALID) {
> > > +				/* parametrized test case, get actual result */
> > > +				kunit_result_free(&r, &suite_name, &case_name);
> > >  
> > > -		igt_list_del(&result->link);
> > > -		pthread_mutex_unlock(&results->mutex);
> > > +				igt_assert(igt_list_empty(&results));
> > >  
> > > -		igt_dynamic(result->test_name) {
> > > -			igt_assert(READ_ONCE(result->passed));
> > > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > > +							    tst->kmsg, ktap);
> > > +				if (ret != -EINPROGRESS)
> > > +					igt_fail_on(ret);
> > > +
> > > +				igt_fail_on(igt_list_empty(&results));
> > > +
> > > +				r = igt_list_first_entry(&results, r, link);
> > > +
> > > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > > +					      "suite_name expected: %s, got: %s\n",
> > > +					      suite_name, r->suite_name);
> > > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > > +					      "case_name expected: %s, got: %s\n",
> > > +					      case_name, r->case_name);
> > > +			}
> > >  
> > > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > > +
> > > +			if (r->msg && *r->msg) {
> > > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > > +					      "%s\n", r->msg);
> > > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > > +					      "%s\n", r->msg);
> > > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > > +					      "%s\n", r->msg);
> > > +			} else {
> > > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > > +				if (r->code == IGT_EXIT_ABORT)
> > > +					igt_fail(r->code);
> > > +			}
> > > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > > +
> > > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > > +			case 0:
> > > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +				break;
> > > +			case EOWNERDEAD:
> > > +				/* leave the mutex unrecoverable */
> > > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +				__attribute__ ((fallthrough));
> > > +			case ENOTRECOVERABLE:
> > >  				igt_assert_eq(modprobe.err, 0);
> > > +				break;
> > 
> > imho we should also break a do-while loop in case of error.
> 
> We are inside igt_dynamic() body, then I don't think we can safely break the 
> outer loop from here, only pass, skip, or fail.
> 
> If we are not able to check whether the modprobe thread hasn't returned an 
> error then I don't think that's a good reason for failing or skipping the 
> current dynamic sub-subtest since we have its KTAP results already collected 
> and we should just report them in that case, I believe.
> 
> If the error persists then the loop will get broken on next attempt to lock 
> the mutex before entering blocking read(), next dynamic sub-subtests won't be 
> executed, and the whole subtest will inform the user about that incompleteness 
> of results from dynamic sub-subtests by returning SKIP.  Isn't that 
> sufficient?
> 

Yes it is, thank you for explaining it.

> > 
> > > +			default:
> > > +				igt_debug("pthread_mutex_lock() failed\n");
> > > +				break;
> > > +			}
> > >  
> > >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> > >  		}
> > >  
> > > -		free(result);
> > > +		kunit_result_free(&r, &suite_name, &case_name);
> > > +
> > > +	} while (ret == -EINPROGRESS);
> > > +
> > > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > > +		kunit_result_free(&r, &suite_name, &case_name);
> > > +
> > > +	free(case_name);
> > > +	free(suite_name);
> > > +
> > > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > > +	case 0:
> > > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > > +		break;
> > > +	case EOWNERDEAD:
> > > +		/* leave the mutex unrecoverable */
> > > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +		break;
> > > +	case ENOTRECOVERABLE:
> > > +		break;
> > > +	default:
> > > +		igt_debug("pthread_mutex_lock() failed\n");
> > > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > > +		break;
> > >  	}
> > >  
> > > -	pthread_join(modprobe_thread, NULL);
> > > -
> > > -	ret = ktap_parser_stop();
> > > +	igt_ktap_free(ktap);
> > >  
> > >  	igt_skip_on(modprobe.err);
> > >  	igt_skip_on(igt_kernel_tainted(&taints));
> > > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > > index 5eac102417..53a6c63288 100644
> > > --- a/lib/igt_ktap.c
> > > +++ b/lib/igt_ktap.c
> > > @@ -4,17 +4,11 @@
> > >   * Copyright © 2023 Intel Corporation
> > >   */
> > >  
> > > -#include <ctype.h>
> > > -#include <limits.h>
> > > -#include <libkmod.h>
> > > -#include <pthread.h>
> > >  #include <errno.h>
> > >  #include <stdio.h>
> > >  #include <stdlib.h>
> > >  #include <string.h>
> > > -#include <unistd.h>
> > >  
> > > -#include "igt_aux.h"
> > >  #include "igt_core.h"
> > >  #include "igt_ktap.h"
> > >  #include "igt_list.h"
> > > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> > >  {
> > >  	free(ktap);
> > >  }
> > > -
> > > -#define DELIMITER "-"
> > > -
> > > -struct ktap_parser_args {
> > > -	int fd;
> > > -	bool is_builtin;
> > > -	int ret;
> > > -} ktap_args;
> > > -
> > > -static struct ktap_test_results results;
> > > -
> > > -static int log_to_end(enum igt_log_level level, int fd,
> > > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > > -
> > > -/**
> > > - * log_to_end:
> > > - * @level: #igt_log_level
> > > - * @record: record to store the read data
> > > - * @format: format string
> > > - * @...: optional arguments used in the format string
> > > - *
> > > - * This is an altered version of the generic structured logging helper function
> > > - * igt_log capable of reading to the end of a given line.
> > > - *
> > > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > > - */
> > > -static int log_to_end(enum igt_log_level level, int fd,
> > > -		      char *record, const char *format, ...)
> > > -{
> > > -	va_list args;
> > > -	const char *lend;
> > > -
> > > -	/* Cutoff after newline character, in order to not display garbage */
> > > -	char *cutoff = strchr(record, '\n');
> > > -	if (cutoff) {
> > > -		if (cutoff - record < BUF_LEN)
> > > -			cutoff[1] = '\0';
> > > -	}
> > > -
> > > -	va_start(args, format);
> > > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > > -	va_end(args);
> > > -
> > > -	lend = strchrnul(record, '\n');
> > > -	while (*lend == '\0') {
> > > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > > -
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -
> > > -		lend = strchrnul(record, '\n');
> > > -	}
> > > -	return 0;
> > > -}
> > > -
> > > -/**
> > > - * lookup_value:
> > > - * @haystack: the string to search in
> > > - * @needle: the string to search for
> > > - *
> > > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > > - */
> > > -static long lookup_value(const char *haystack, const char *needle)
> > > -{
> > > -	const char *needle_rptr;
> > > -	char *needle_end;
> > > -	long num;
> > > -
> > > -	needle_rptr = strcasestr(haystack, needle);
> > > -
> > > -	if (needle_rptr == NULL)
> > > -		return -1;
> > > -
> > > -	/* Skip search string and whitespaces after it */
> > > -	needle_rptr += strlen(needle);
> > > -
> > > -	num = strtol(needle_rptr, &needle_end, 10);
> > > -
> > > -	if (needle_rptr == needle_end)
> > > -		return -1;
> > > -
> > > -	if (num == LONG_MIN || num == LONG_MAX)
> > > -		return 0;
> > > -
> > > -	return num > 0 ? num : 0;
> > > -}
> > > -
> > > -/**
> > > - * tap_version_present:
> > > - * @record: buffer with tap data
> > > - * @print_info: whether tap version should be printed or not
> > > - *
> > > - * Returns:
> > > - * 0 if not found
> > > - * 1 if found
> > > - */
> > > -static int tap_version_present(char* record, bool print_info)
> > > -{
> > > -	/*
> > > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> > 
> > Could you save these two lines comment in some other place?
> 
> Why?  Do you think that just that information from a multi-paragraph 
> description of KTAP format is of particular importance?
> 
> Instead, let me add just the reference to the KTAP standard to a comment above 
> the current parser implementation, OK?

Seems good, please add the reference.

Regards,
Kamil

> 
> Thanks,
> Janusz
> 
> > 
> > Regards,
> > Kamil
> > 
> > > -	 *
> > > -	 * but actually isn't, as it currently depends on the KUnit module
> > > -	 * being built-in, so we can't rely on it every time
> > > -	 */
> > > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > > -	char *cutoff;
> > > -
> > > -	if (version_rptr == NULL)
> > > -		return 0;
> > > -
> > > -	/* Cutoff after newline character, in order to not display garbage */
> > > -	cutoff = strchr(version_rptr, '\n');
> > > -	if (cutoff)
> > > -		cutoff[0] = '\0';
> > > -
> > > -	if (print_info)
> > > -		igt_info("%s\n", version_rptr);
> > > -
> > > -	return 1;
> > > -}
> > > -
> > > -/**
> > > - * find_next_tap_subtest:
> > > - * @fd: file descriptor
> > > - * @record: buffer used to read fd
> > > - * @is_builtin: whether KUnit is built-in or not
> > > - *
> > > - * Returns:
> > > - * 0 if there's missing information
> > > - * -1 if not found
> > > - * -2 if there are problems while reading the file.
> > > - * any other value corresponds to the amount of cases of the next (sub)test
> > > - */
> > > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > > -{
> > > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > > -	long test_count;
> > > -	char *cutoff;
> > > -
> > > -	test_name[0] = '\0';
> > > -	test_name[BUF_LEN] = '\0';
> > > -
> > > -	test_lookup_str = " subtest: ";
> > > -	subtest_lookup_str = " test: ";
> > > -
> > > -	if (!tap_version_present(record, true))
> > > -		return -1;
> > > -
> > > -	if (is_builtin) {
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -	}
> > > -
> > > -	name_rptr = strcasestr(record, test_lookup_str);
> > > -	if (name_rptr != NULL) {
> > > -		name_rptr += strlen(test_lookup_str);
> > > -	} else {
> > > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > > -		if (name_rptr != NULL)
> > > -			name_rptr += strlen(subtest_lookup_str);
> > > -	}
> > > -
> > > -	if (name_rptr == NULL) {
> > > -		if (!is_builtin)
> > > -			/* We've probably found nothing */
> > > -			return -1;
> > > -		igt_info("Missing test name\n");
> > > -	} else {
> > > -		strncpy(test_name, name_rptr, BUF_LEN);
> > > -		/* Cutoff after newline character, in order to not display garbage */
> > > -		cutoff = strchr(test_name, '\n');
> > > -		if (cutoff)
> > > -			cutoff[0] = '\0';
> > > -
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("unknown error reading kmsg (%m)\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -
> > > -		/* Now we can be sure we found tests */
> > > -		if (!is_builtin)
> > > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > > -	}
> > > -
> > > -	/*
> > > -	 * Total test count will almost always appear as 0..N at the beginning
> > > -	 * of a run, so we use it to reliably identify a new run
> > > -	 */
> > > -	test_count = lookup_value(record, "..");
> > > -
> > > -	if (test_count <= 0) {
> > > -		igt_info("Missing test count\n");
> > > -		if (test_name[0] == '\0')
> > > -			return 0;
> > > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > > -				"Running some tests in: %s\n",
> > > -				test_name) < 0)
> > > -			return -2;
> > > -		return 0;
> > > -	} else if (test_name[0] == '\0') {
> > > -		igt_info("Running %ld tests...\n", test_count);
> > > -		return 0;
> > > -	}
> > > -
> > > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > > -			"Executing %ld tests in: %s\n",
> > > -			test_count, test_name) < 0)
> > > -		return -2;
> > > -
> > > -	return test_count;
> > > -}
> > > -
> > > -/**
> > > - * parse_kmsg_for_tap:
> > > - * @fd: file descriptor
> > > - * @record: buffer used to read fd
> > > - * @test_name: buffer to store the test name
> > > - *
> > > - * Returns:
> > > - * 1 if no results were found
> > > - * 0 if a test succeded
> > > - * -1 if a test failed
> > > - * -2 if there are problems reading the file
> > > - */
> > > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > > -{
> > > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > > -	char *test_name_end;
> > > -
> > > -	ok_lookup_str = "ok ";
> > > -	nok_lookup_str = "not ok ";
> > > -
> > > -	lstart = strchrnul(record, ';');
> > > -
> > > -	if (*lstart == '\0') {
> > > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > > -		return -2;
> > > -	}
> > > -
> > > -	lstart++;
> > > -	while (isspace(*lstart))
> > > -		lstart++;
> > > -
> > > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > > -	if (nok_rptr != NULL) {
> > > -		nok_rptr += strlen(nok_lookup_str);
> > > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > > -			nok_rptr++;
> > > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > > -		while (!isspace(*test_name_end))
> > > -			test_name_end++;
> > > -		*test_name_end = '\0';
> > > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > > -			       "%s", lstart) < 0)
> > > -			return -2;
> > > -		return -1;
> > > -	}
> > > -
> > > -	comment_start = strchrnul(lstart, '#');
> > > -
> > > -	/* Check if we're still in a subtest */
> > > -	if (*comment_start != '\0') {
> > > -		comment_start++;
> > > -		value_parse_start = comment_start;
> > > -
> > > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > > -				       "%s", lstart) < 0)
> > > -				return -2;
> > > -			return -1;
> > > -		}
> > > -	}
> > > -
> > > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > > -	if (ok_rptr != NULL) {
> > > -		ok_rptr += strlen(ok_lookup_str);
> > > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > > -			ok_rptr++;
> > > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > > -		while (!isspace(*test_name_end))
> > > -			test_name_end++;
> > > -		*test_name_end = '\0';
> > > -		return 0;
> > > -	}
> > > -
> > > -	return 1;
> > > -}
> > > -
> > > -/**
> > > - * parse_tap_level:
> > > - * @fd: file descriptor
> > > - * @base_test_name: test_name from upper recursion level
> > > - * @test_count: test_count of this level
> > > - * @failed_tests: top level failed_tests pointer
> > > - * @found_tests: top level found_tests pointer
> > > - * @is_builtin: whether the KUnit module is built-in or not
> > > - *
> > > - * Returns:
> > > - * 0 if succeded
> > > - * -1 if error occurred
> > > - */
> > > -__maybe_unused
> > > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > > -			   bool *found_tests, bool is_builtin)
> > > -{
> > > -	char record[BUF_LEN + 1];
> > > -	struct ktap_test_results_element *r;
> > > -	int internal_test_count;
> > > -	char test_name[BUF_LEN + 1];
> > > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > > -
> > > -	for (int i = 0; i < test_count; i++) {
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("error reading kmsg (%m)\n");
> > > -
> > > -			return -1;
> > > -		}
> > > -
> > > -		/* Sublevel found */
> > > -		if (tap_version_present(record, false))
> > > -		{
> > > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > > -								    is_builtin);
> > > -			switch (internal_test_count) {
> > > -			case -2:
> > > -				/* No more data to read */
> > > -				return -1;
> > > -			case -1:
> > > -				/* No test found */
> > > -				return -1;
> > > -			case 0:
> > > -				/* Tests found, but they're missing info */
> > > -				*found_tests = true;
> > > -				return -1;
> > > -			default:
> > > -				*found_tests = true;
> > > -
> > > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > > -				    base_test_name_for_next_level[0])
> > > -					strncat(base_test_name_for_next_level, DELIMITER,
> > > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > > -
> > > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > > -						    internal_test_count, failed_tests, found_tests,
> > > -						    is_builtin) == -1)
> > > -					return -1;
> > > -				break;
> > > -			}
> > > -		}
> > > -
> > > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > > -		case -2:
> > > -			return -1;
> > > -		case -1:
> > > -			*failed_tests = true;
> > > -
> > > -			r = malloc(sizeof(*r));
> > > -
> > > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > > -				if (r->test_name[0])
> > > -					strncat(r->test_name, DELIMITER,
> > > -						BUF_LEN - strlen(r->test_name));
> > > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > > -			       BUF_LEN - strlen(r->test_name));
> > > -			r->test_name[BUF_LEN] = '\0';
> > > -
> > > -			r->passed = false;
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&r->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -
> > > -			test_name[0] = '\0';
> > > -			break;
> > > -		case 0:
> > > -			r = malloc(sizeof(*r));
> > > -
> > > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > > -				if (r->test_name[0])
> > > -					strncat(r->test_name, DELIMITER,
> > > -						BUF_LEN - strlen(r->test_name));
> > > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > > -			       BUF_LEN - strlen(r->test_name));
> > > -			r->test_name[BUF_LEN] = '\0';
> > > -
> > > -			r->passed = true;
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&r->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -
> > > -			test_name[0] = '\0';
> > > -			break;
> > > -		default:
> > > -			break;
> > > -		}
> > > -	}
> > > -	return 0;
> > > -}
> > > -
> > > -/**
> > > - * igt_ktap_parser:
> > > - *
> > > - * This function parses the output of a ktap script and passes it to main thread.
> > > - */
> > > -void *igt_ktap_parser(void *unused)
> > > -{
> > > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > > -	struct igt_ktap_results *ktap = NULL;
> > > -	int fd = ktap_args.fd;
> > > -	IGT_LIST_HEAD(list);
> > > -	int err;
> > > -
> > > -	ktap = igt_ktap_alloc(&list);
> > > -	if (igt_debug_on(!ktap))
> > > -		goto igt_ktap_parser_end;
> > > -
> > > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > > -		struct igt_ktap_result *r, *rn;
> > > -
> > > -		/* skip kmsg continuation lines */
> > > -		if (igt_debug_on(*record == ' '))
> > > -			continue;
> > > -
> > > -		/* NULL-terminate the record */
> > > -		record[err] = '\0';
> > > -
> > > -		/* detect start of log message, continue if not found */
> > > -		buf = strchrnul(record, ';');
> > > -		if (igt_debug_on(*buf == '\0'))
> > > -			continue;
> > > -		buf++;
> > > -
> > > -		err = igt_ktap_parse(buf, ktap);
> > > -
> > > -		/* parsing error */
> > > -		if (err && err != -EINPROGRESS)
> > > -			goto igt_ktap_parser_end;
> > > -
> > > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > > -			struct ktap_test_results_element *result = NULL;
> > > -			int code = r->code;
> > > -
> > > -			if (code != IGT_EXIT_INVALID)
> > > -				result = calloc(1, sizeof(*result));
> > > -
> > > -			if (result) {
> > > -				snprintf(result->test_name, sizeof(result->test_name),
> > > -					 "%s-%s", r->suite_name, r->case_name);
> > > -
> > > -				if (code == IGT_EXIT_SUCCESS)
> > > -					result->passed = true;
> > > -			}
> > > -
> > > -			igt_list_del(&r->link);
> > > -			if (r->suite_name != suite_name) {
> > > -				free(suite_name);
> > > -				suite_name = r->suite_name;
> > > -			}
> > > -			if (r->case_name != case_name) {
> > > -				free(case_name);
> > > -				case_name = r->case_name;
> > > -			}
> > > -			free(r->msg);
> > > -			free(r);
> > > -
> > > -			/*
> > > -			 * no extra result record expected on start
> > > -			 * of parametrized test case -- skip it
> > > -			 */
> > > -			if (code == IGT_EXIT_INVALID)
> > > -				continue;
> > > -
> > > -			if (!result) {
> > > -				err = -ENOMEM;
> > > -				goto igt_ktap_parser_end;
> > > -			}
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&result->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -		}
> > > -
> > > -		/* end of KTAP report */
> > > -		if (!err)
> > > -			goto igt_ktap_parser_end;
> > > -	}
> > > -
> > > -	if (err < 0) {
> > > -		if (errno == EPIPE)
> > > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -		else
> > > -			igt_warn("error reading kmsg (%m)\n");
> > > -	}
> > > -
> > > -igt_ktap_parser_end:
> > > -	free(suite_name);
> > > -	free(case_name);
> > > -
> > > -	if (!err)
> > > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > > -
> > > -	results.still_running = false;
> > > -
> > > -	if (ktap)
> > > -		igt_ktap_free(ktap);
> > > -
> > > -	return NULL;
> > > -}
> > > -
> > > -static pthread_t ktap_parser_thread;
> > > -
> > > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > > -{
> > > -	IGT_INIT_LIST_HEAD(&results.list);
> > > -	pthread_mutex_init(&results.mutex, NULL);
> > > -	results.still_running = true;
> > > -
> > > -	ktap_args.fd = fd;
> > > -	ktap_args.is_builtin = is_builtin;
> > > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > > -
> > > -	return &results;
> > > -}
> > > -
> > > -void ktap_parser_cancel(void)
> > > -{
> > > -	pthread_cancel(ktap_parser_thread);
> > > -}
> > > -
> > > -int ktap_parser_stop(void)
> > > -{
> > > -	pthread_join(ktap_parser_thread, NULL);
> > > -	return ktap_args.ret;
> > > -}
> > > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > > index 6f8da3eab6..c422636bfc 100644
> > > --- a/lib/igt_ktap.h
> > > +++ b/lib/igt_ktap.h
> > > @@ -27,8 +27,6 @@
> > >  
> > >  #define BUF_LEN 4096
> > >  
> > > -#include <pthread.h>
> > > -
> > >  #include "igt_list.h"
> > >  
> > >  struct igt_ktap_result {
> > > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> > >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> > >  void igt_ktap_free(struct igt_ktap_results *ktap);
> > >  
> > > -void *igt_ktap_parser(void *unused);
> > > -
> > > -typedef struct ktap_test_results_element {
> > > -	char test_name[BUF_LEN + 1];
> > > -	bool passed;
> > > -	struct igt_list_head link;
> > > -} ktap_test_results_element;
> > > -
> > > -struct ktap_test_results {
> > > -	struct igt_list_head list;
> > > -	pthread_mutex_t mutex;
> > > -	bool still_running;
> > > -};
> > > -
> > > -
> > > -
> > > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > > -void ktap_parser_cancel(void);
> > > -int ktap_parser_stop(void);
> > > -
> > >  #endif /* IGT_KTAP_H */
> > 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [Intel-xe] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-11 13:00         ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-11 13:00 UTC (permalink / raw)
  To: igt-dev; +Cc: Janusz Krzysztofik, intel-gfx, intel-xe

Hi Janusz,
On 2023-10-10 at 19:49:35 +0200, Janusz Krzysztofik wrote:
> Hi Kamil,
> 
> Thanks for review.
> 
> On Tuesday, 10 October 2023 17:59:56 CEST Kamil Konieczny wrote:
> > Hi Janusz,
> > On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> > > There was an attempt to parse KTAP reports in the background while a kunit
> > > test module is loading.  However, since dynamic sub-subtests can be
> > > executed only from the main thread, that attempt was not quite successful,
> > > as IGT results from all executed kunit test cases were generated only
> > > after loading of kunit test module completed.
> > > 
> > > Now that the parser maintains its state and we can call it separately for
> > > each input line of a KTAP report, it is perfectly possible to call the
> > > parser from the main thread while the module is loading in the background,
> > > and convert results from kunit test cases immediately to results of IGT
> > > dynamic sub-subtests by running an igt_dynamic() section for each result
> > > as soon as returned by the parser.
> > > 
> > > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > > result obtained from igt_ktap_parse() called from the main thread.
> > > 
> > > Also, drop no longer needed functions from igt_ktap soruces.
> > > 
> > > v3: Fix ktap structure not freed on lseek error,
> > >   - fix initial SIGCHLD handler not restored,
> > >   - fix missing handling of potential errors returned by sigaction,
> > >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> > >     synchronization with modprobe thread,
> > >   - fix potentially illegal use of igt_assert() called outside of
> > >     dynamic sub-subtest section,
> > >   - fix unsupported exit code potentially passed to igt_fail(),
> > >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> > >     after a valid result from the parser has been processed,
> > >   - fix trailing newlines missing from error messages,
> > >   - add more debug statements,
> > >   - integrate common code around kunit_result_free() into it.
> > > v2: Interrupt blocking read() on modprobe failure.
> > > 
> > > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > > ---
> > >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> > >  lib/igt_ktap.c | 568 -------------------------------------------------
> > >  lib/igt_ktap.h |  22 --
> > >  3 files changed, 222 insertions(+), 629 deletions(-)
> > > 
> > > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > > index 426ae5b26f..7bca4cdaab 100644
> > > --- a/lib/igt_kmod.c
> > > +++ b/lib/igt_kmod.c
> > > @@ -1,5 +1,5 @@
> > >  /*
> > > - * Copyright © 2016 Intel Corporation
> > > + * Copyright © 2016-2023 Intel Corporation
> > >   *
> > >   * Permission is hereby granted, free of charge, to any person obtaining a
> > >   * copy of this software and associated documentation files (the "Software"),
> > > @@ -26,7 +26,12 @@
> > >  #include <errno.h>
> > >  #include <fcntl.h>
> > >  #include <pthread.h>
> > > +#include <stdlib.h>
> > > +#include <string.h>
> > >  #include <sys/utsname.h>
> > > +#include <unistd.h>
> > > +
> > > +#include "assembler/brw_compat.h"	/* [un]likely() */
> > ------------ ^^^^^^^^^
> > Do we really need this?
> 
> I think the correct question is if wee really need [un]likely().  I'm using it 
> to document unlikely cases, which is a widely accepted method of documenting 
> cases like that, I believe.  Having that clarified, I hope you just tell me if 
> you think we need those cases documented, and how, if not that way.
> 

Now I see, it is for unlikely() - ok it can stay.

> > 
> > >  
> > >  #include "igt_aux.h"
> > >  #include "igt_core.h"
> > > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> > >  }
> > >  
> > >  struct modprobe_data {
> > > +	pthread_t parent;
> > --- ^^^^^^^^^^^^^^^^
> > Please move it below to other related thread data.
> > Also consider a comment why(or for what purpose)
> > did you put this here.
> 
> No problem to move it to the bottom, if that's important to you, but regarding 
> the comment, do you think that the purpose of this field of the structure, 
> compared to other fields, is so unclear form review of the code, despite its 
> name, that it requires a comment, unlike the other fields?
> 

I am ok with only move.

> > 
> > >  	struct kmod_module *kmod;
> > >  	const char *opts;
> > >  	int err;
> > > +	pthread_mutex_t lock;
> > > +	pthread_t thread;
> > >  };
> > >  
> > >  static void *modprobe_task(void *arg)
> > > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> > >  
> > >  	data->err = modprobe(data->kmod, data->opts);
> > >  
> > > +	if (igt_debug_on(data->err)) {
> > > +		int err;
> > > +
> > > +		while (err = pthread_mutex_trylock(&data->lock),
> > > +		       err && !igt_debug_on(err != EBUSY))
> > > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> > > +	} else {
> > > +		/* let main thread use mutex to detect modprobe completion */
> > > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > > +	}
> > > +
> > >  	return NULL;
> > >  }
> > >  
> > > +static void kunit_sigchld_handler(int signal)
> > > +{
> > > +	return;
> > --- ^^^^^^^
> > Why not removing this? checkpatch complains about return from void.
> 
> OK.
> 
> > 
> > > +}
> > > +
> > > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > > +				 struct modprobe_data *modprobe,
> > > +				 int fd, struct igt_ktap_results *ktap)
> > > +{
> > > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > > +			 *saved;
> > > +	char record[BUF_LEN + 1], *buf;
> > > +	unsigned long taints;
> > > +	int ret;
> > > +
> > > +	do {
> > > +		int err;
> > > +
> > > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > > +			return -ENOTRECOVERABLE;
> > > +
> > > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > > +		if (err == -1)
> > > +			return -errno;
> > > +		else if (unlikely(err))
> > > +			return err;
> > > +
> > > +		err = pthread_mutex_lock(&modprobe->lock);
> > > +		switch (err) {
> > > +		case EOWNERDEAD:
> > > +			/* leave the mutex unrecoverable */
> > > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > > +			__attribute__ ((fallthrough));
> > > +		case ENOTRECOVERABLE:
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +			if (igt_debug_on(modprobe->err))
> > > +				return modprobe->err;
> > 
> > Why no 'return -err;' here?
> 
> Because modprobe->err, which can tell us more about what has gone bad in the 
> modprobe thread, is more important here than err which only informs us about 
> completion of the modprobe thread, also if successful, isn't it?
> 

Thank you for explanation.

> > 
> > > +			break;
> > > +		case 0:
> > > +			break;
> > > +		default:
> > > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +			return -err;
> > > +		}
> > > +
> > > +		ret = read(fd, record, BUF_LEN);
> > > +
> > > +		if (!err) {
> >         ^^^^^^^^^^^
> > Looks strange here.
> 
> Here err still carries a return code from pthread_mutex_lock(), then !err 
> means that the modprobe thread was still running and we have 1) acquired 
> the mutex which now we have to release, 2) installed a non-default 
> SIGCHLD handler that we should now reset.  Have you got a better idea 
> how to encode that so it looks less strange?  Would a comment that reminds 
> a reader where that err comes from be sufficient?
> 

Yes please add comment here.

> > 
> > > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +		}
> > > +
> > > +		if (igt_debug_on(!ret))
> > > +			return -ENODATA;
> > > +		if (igt_debug_on(ret == -1))
> > > +			return -errno;
> > > +		if (unlikely(igt_debug_on(ret < 0)))
> > > +			break;
> > > +
> > > +		/* skip kmsg continuation lines */
> > > +		if (igt_debug_on(*record == ' '))
> > ----------- ^^^^^^^^^^^^
> > Why debug here? imho it is not needed.
> 
> Because we don't expect continuation lines while reading a KTAP report. Since 
> the kernel log may still contain them, we want to know about them when 
> something wrong happens to our KTAP parser.  That's the purpose of all those 
> debug_on() calls: provide as much information on unexpected conditions as 
> possible when the test fails, isn't it?
> 

Ok.

> > 
> > > +			continue;
> > > +
> > > +		/* NULL-terminate the record */
> > > +		record[ret] = '\0';
> > > +
> > > +		/* detect start of log message, continue if not found */
> > > +		buf = strchrnul(record, ';');
> > > +		if (igt_debug_on(*buf == '\0'))
> > ----------- ^^^^^^^^^^^^
> > Same here, you just continue loop if no ';'?
> 
> That means we are getting incorrectly formatted records from /dev/kmsg, and 
> I'm sure we would like to be informed about that if something goes wrong with 
> the test, wouldn't we?
> 

Ok.

> > 
> > > +			continue;
> > > +		buf++;
> > > +
> > > +		ret = igt_ktap_parse(buf, ktap);
> > > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > > +			break;
> > > +	} while (igt_list_empty(results));
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void kunit_result_free(struct igt_ktap_result **r,
> > > +			      char **suite_name, char **case_name)
> > > +{
> > > +	if (!*r)
> > > +		return;
> > > +
> > > +	igt_list_del(&(*r)->link);
> > > +
> > > +	if ((*r)->suite_name != *suite_name) {
> > > +		free(*suite_name);
> > > +		*suite_name = (*r)->suite_name;
> > > +	}
> > > +
> > > +	if ((*r)->case_name != *case_name) {
> > > +		free(*case_name);
> > > +		*case_name = (*r)->case_name;
> > > +	}
> > > +
> > > +	free((*r)->msg);
> > > +	free(*r);
> > > +	*r = NULL;
> > > +}
> > > +
> > >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> > >  {
> > > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > > -	struct kmod_module *kunit_kmod;
> > > -	bool is_builtin;
> > > -	struct ktap_test_results *results;
> > > -	pthread_t modprobe_thread;
> > > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > > +	char *suite_name = NULL, *case_name = NULL;
> > > +	struct igt_ktap_result *r, *rn;
> > > +	struct igt_ktap_results *ktap;
> > > +	pthread_mutexattr_t attr;
> > > +	IGT_LIST_HEAD(results);
> > >  	unsigned long taints;
> > >  	int flags, ret;
> > >  
> > > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> > >  
> > >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> > >  
> > > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > > -	kmod_module_unref(kunit_kmod);
> > > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> > >  
> > > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > > +	ktap = igt_ktap_alloc(&results);
> > > +	igt_require(ktap);
> > >  
> > > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> > >  					modprobe_task, &modprobe))) {
> > > -		ktap_parser_cancel();
> > > -		igt_ignore_warn(ktap_parser_stop());
> > > +		igt_ktap_free(ktap);
> > >  		igt_skip("Failed to create a modprobe thread\n");
> > >  	}
> > >  
> > > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > > -	{
> > > -		struct ktap_test_results_element *result;
> > > -
> > > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > > -			ktap_parser_cancel();
> > > +	do {
> > > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > > +					    tst->kmsg, ktap);
> > > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> > >  			break;
> > > -		}
> > >  
> > > -		if (igt_kernel_tainted(&taints)) {
> > > -			ktap_parser_cancel();
> > > -			pthread_cancel(modprobe_thread);
> > > +		if (igt_debug_on(igt_list_empty(&results)))
> > >  			break;
> > > -		}
> > >  
> > > -		pthread_mutex_lock(&results->mutex);
> > > -		if (igt_list_empty(&results->list)) {
> > > -			pthread_mutex_unlock(&results->mutex);
> > > -			continue;
> > > -		}
> > > +		r = igt_list_first_entry(&results, r, link);
> > >  
> > > -		result = igt_list_first_entry(&results->list, result, link);
> > > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > > +			if (r->code == IGT_EXIT_INVALID) {
> > > +				/* parametrized test case, get actual result */
> > > +				kunit_result_free(&r, &suite_name, &case_name);
> > >  
> > > -		igt_list_del(&result->link);
> > > -		pthread_mutex_unlock(&results->mutex);
> > > +				igt_assert(igt_list_empty(&results));
> > >  
> > > -		igt_dynamic(result->test_name) {
> > > -			igt_assert(READ_ONCE(result->passed));
> > > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > > +							    tst->kmsg, ktap);
> > > +				if (ret != -EINPROGRESS)
> > > +					igt_fail_on(ret);
> > > +
> > > +				igt_fail_on(igt_list_empty(&results));
> > > +
> > > +				r = igt_list_first_entry(&results, r, link);
> > > +
> > > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > > +					      "suite_name expected: %s, got: %s\n",
> > > +					      suite_name, r->suite_name);
> > > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > > +					      "case_name expected: %s, got: %s\n",
> > > +					      case_name, r->case_name);
> > > +			}
> > >  
> > > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > > +
> > > +			if (r->msg && *r->msg) {
> > > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > > +					      "%s\n", r->msg);
> > > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > > +					      "%s\n", r->msg);
> > > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > > +					      "%s\n", r->msg);
> > > +			} else {
> > > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > > +				if (r->code == IGT_EXIT_ABORT)
> > > +					igt_fail(r->code);
> > > +			}
> > > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > > +
> > > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > > +			case 0:
> > > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +				break;
> > > +			case EOWNERDEAD:
> > > +				/* leave the mutex unrecoverable */
> > > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +				__attribute__ ((fallthrough));
> > > +			case ENOTRECOVERABLE:
> > >  				igt_assert_eq(modprobe.err, 0);
> > > +				break;
> > 
> > imho we should also break a do-while loop in case of error.
> 
> We are inside igt_dynamic() body, then I don't think we can safely break the 
> outer loop from here, only pass, skip, or fail.
> 
> If we are not able to check whether the modprobe thread hasn't returned an 
> error then I don't think that's a good reason for failing or skipping the 
> current dynamic sub-subtest since we have its KTAP results already collected 
> and we should just report them in that case, I believe.
> 
> If the error persists then the loop will get broken on next attempt to lock 
> the mutex before entering blocking read(), next dynamic sub-subtests won't be 
> executed, and the whole subtest will inform the user about that incompleteness 
> of results from dynamic sub-subtests by returning SKIP.  Isn't that 
> sufficient?
> 

Yes it is, thank you for explaining it.

> > 
> > > +			default:
> > > +				igt_debug("pthread_mutex_lock() failed\n");
> > > +				break;
> > > +			}
> > >  
> > >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> > >  		}
> > >  
> > > -		free(result);
> > > +		kunit_result_free(&r, &suite_name, &case_name);
> > > +
> > > +	} while (ret == -EINPROGRESS);
> > > +
> > > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > > +		kunit_result_free(&r, &suite_name, &case_name);
> > > +
> > > +	free(case_name);
> > > +	free(suite_name);
> > > +
> > > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > > +	case 0:
> > > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > > +		break;
> > > +	case EOWNERDEAD:
> > > +		/* leave the mutex unrecoverable */
> > > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +		break;
> > > +	case ENOTRECOVERABLE:
> > > +		break;
> > > +	default:
> > > +		igt_debug("pthread_mutex_lock() failed\n");
> > > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > > +		break;
> > >  	}
> > >  
> > > -	pthread_join(modprobe_thread, NULL);
> > > -
> > > -	ret = ktap_parser_stop();
> > > +	igt_ktap_free(ktap);
> > >  
> > >  	igt_skip_on(modprobe.err);
> > >  	igt_skip_on(igt_kernel_tainted(&taints));
> > > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > > index 5eac102417..53a6c63288 100644
> > > --- a/lib/igt_ktap.c
> > > +++ b/lib/igt_ktap.c
> > > @@ -4,17 +4,11 @@
> > >   * Copyright © 2023 Intel Corporation
> > >   */
> > >  
> > > -#include <ctype.h>
> > > -#include <limits.h>
> > > -#include <libkmod.h>
> > > -#include <pthread.h>
> > >  #include <errno.h>
> > >  #include <stdio.h>
> > >  #include <stdlib.h>
> > >  #include <string.h>
> > > -#include <unistd.h>
> > >  
> > > -#include "igt_aux.h"
> > >  #include "igt_core.h"
> > >  #include "igt_ktap.h"
> > >  #include "igt_list.h"
> > > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> > >  {
> > >  	free(ktap);
> > >  }
> > > -
> > > -#define DELIMITER "-"
> > > -
> > > -struct ktap_parser_args {
> > > -	int fd;
> > > -	bool is_builtin;
> > > -	int ret;
> > > -} ktap_args;
> > > -
> > > -static struct ktap_test_results results;
> > > -
> > > -static int log_to_end(enum igt_log_level level, int fd,
> > > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > > -
> > > -/**
> > > - * log_to_end:
> > > - * @level: #igt_log_level
> > > - * @record: record to store the read data
> > > - * @format: format string
> > > - * @...: optional arguments used in the format string
> > > - *
> > > - * This is an altered version of the generic structured logging helper function
> > > - * igt_log capable of reading to the end of a given line.
> > > - *
> > > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > > - */
> > > -static int log_to_end(enum igt_log_level level, int fd,
> > > -		      char *record, const char *format, ...)
> > > -{
> > > -	va_list args;
> > > -	const char *lend;
> > > -
> > > -	/* Cutoff after newline character, in order to not display garbage */
> > > -	char *cutoff = strchr(record, '\n');
> > > -	if (cutoff) {
> > > -		if (cutoff - record < BUF_LEN)
> > > -			cutoff[1] = '\0';
> > > -	}
> > > -
> > > -	va_start(args, format);
> > > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > > -	va_end(args);
> > > -
> > > -	lend = strchrnul(record, '\n');
> > > -	while (*lend == '\0') {
> > > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > > -
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -
> > > -		lend = strchrnul(record, '\n');
> > > -	}
> > > -	return 0;
> > > -}
> > > -
> > > -/**
> > > - * lookup_value:
> > > - * @haystack: the string to search in
> > > - * @needle: the string to search for
> > > - *
> > > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > > - */
> > > -static long lookup_value(const char *haystack, const char *needle)
> > > -{
> > > -	const char *needle_rptr;
> > > -	char *needle_end;
> > > -	long num;
> > > -
> > > -	needle_rptr = strcasestr(haystack, needle);
> > > -
> > > -	if (needle_rptr == NULL)
> > > -		return -1;
> > > -
> > > -	/* Skip search string and whitespaces after it */
> > > -	needle_rptr += strlen(needle);
> > > -
> > > -	num = strtol(needle_rptr, &needle_end, 10);
> > > -
> > > -	if (needle_rptr == needle_end)
> > > -		return -1;
> > > -
> > > -	if (num == LONG_MIN || num == LONG_MAX)
> > > -		return 0;
> > > -
> > > -	return num > 0 ? num : 0;
> > > -}
> > > -
> > > -/**
> > > - * tap_version_present:
> > > - * @record: buffer with tap data
> > > - * @print_info: whether tap version should be printed or not
> > > - *
> > > - * Returns:
> > > - * 0 if not found
> > > - * 1 if found
> > > - */
> > > -static int tap_version_present(char* record, bool print_info)
> > > -{
> > > -	/*
> > > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> > 
> > Could you save these two lines comment in some other place?
> 
> Why?  Do you think that just that information from a multi-paragraph 
> description of KTAP format is of particular importance?
> 
> Instead, let me add just the reference to the KTAP standard to a comment above 
> the current parser implementation, OK?

Seems good, please add the reference.

Regards,
Kamil

> 
> Thanks,
> Janusz
> 
> > 
> > Regards,
> > Kamil
> > 
> > > -	 *
> > > -	 * but actually isn't, as it currently depends on the KUnit module
> > > -	 * being built-in, so we can't rely on it every time
> > > -	 */
> > > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > > -	char *cutoff;
> > > -
> > > -	if (version_rptr == NULL)
> > > -		return 0;
> > > -
> > > -	/* Cutoff after newline character, in order to not display garbage */
> > > -	cutoff = strchr(version_rptr, '\n');
> > > -	if (cutoff)
> > > -		cutoff[0] = '\0';
> > > -
> > > -	if (print_info)
> > > -		igt_info("%s\n", version_rptr);
> > > -
> > > -	return 1;
> > > -}
> > > -
> > > -/**
> > > - * find_next_tap_subtest:
> > > - * @fd: file descriptor
> > > - * @record: buffer used to read fd
> > > - * @is_builtin: whether KUnit is built-in or not
> > > - *
> > > - * Returns:
> > > - * 0 if there's missing information
> > > - * -1 if not found
> > > - * -2 if there are problems while reading the file.
> > > - * any other value corresponds to the amount of cases of the next (sub)test
> > > - */
> > > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > > -{
> > > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > > -	long test_count;
> > > -	char *cutoff;
> > > -
> > > -	test_name[0] = '\0';
> > > -	test_name[BUF_LEN] = '\0';
> > > -
> > > -	test_lookup_str = " subtest: ";
> > > -	subtest_lookup_str = " test: ";
> > > -
> > > -	if (!tap_version_present(record, true))
> > > -		return -1;
> > > -
> > > -	if (is_builtin) {
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -	}
> > > -
> > > -	name_rptr = strcasestr(record, test_lookup_str);
> > > -	if (name_rptr != NULL) {
> > > -		name_rptr += strlen(test_lookup_str);
> > > -	} else {
> > > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > > -		if (name_rptr != NULL)
> > > -			name_rptr += strlen(subtest_lookup_str);
> > > -	}
> > > -
> > > -	if (name_rptr == NULL) {
> > > -		if (!is_builtin)
> > > -			/* We've probably found nothing */
> > > -			return -1;
> > > -		igt_info("Missing test name\n");
> > > -	} else {
> > > -		strncpy(test_name, name_rptr, BUF_LEN);
> > > -		/* Cutoff after newline character, in order to not display garbage */
> > > -		cutoff = strchr(test_name, '\n');
> > > -		if (cutoff)
> > > -			cutoff[0] = '\0';
> > > -
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("unknown error reading kmsg (%m)\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -
> > > -		/* Now we can be sure we found tests */
> > > -		if (!is_builtin)
> > > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > > -	}
> > > -
> > > -	/*
> > > -	 * Total test count will almost always appear as 0..N at the beginning
> > > -	 * of a run, so we use it to reliably identify a new run
> > > -	 */
> > > -	test_count = lookup_value(record, "..");
> > > -
> > > -	if (test_count <= 0) {
> > > -		igt_info("Missing test count\n");
> > > -		if (test_name[0] == '\0')
> > > -			return 0;
> > > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > > -				"Running some tests in: %s\n",
> > > -				test_name) < 0)
> > > -			return -2;
> > > -		return 0;
> > > -	} else if (test_name[0] == '\0') {
> > > -		igt_info("Running %ld tests...\n", test_count);
> > > -		return 0;
> > > -	}
> > > -
> > > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > > -			"Executing %ld tests in: %s\n",
> > > -			test_count, test_name) < 0)
> > > -		return -2;
> > > -
> > > -	return test_count;
> > > -}
> > > -
> > > -/**
> > > - * parse_kmsg_for_tap:
> > > - * @fd: file descriptor
> > > - * @record: buffer used to read fd
> > > - * @test_name: buffer to store the test name
> > > - *
> > > - * Returns:
> > > - * 1 if no results were found
> > > - * 0 if a test succeded
> > > - * -1 if a test failed
> > > - * -2 if there are problems reading the file
> > > - */
> > > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > > -{
> > > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > > -	char *test_name_end;
> > > -
> > > -	ok_lookup_str = "ok ";
> > > -	nok_lookup_str = "not ok ";
> > > -
> > > -	lstart = strchrnul(record, ';');
> > > -
> > > -	if (*lstart == '\0') {
> > > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > > -		return -2;
> > > -	}
> > > -
> > > -	lstart++;
> > > -	while (isspace(*lstart))
> > > -		lstart++;
> > > -
> > > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > > -	if (nok_rptr != NULL) {
> > > -		nok_rptr += strlen(nok_lookup_str);
> > > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > > -			nok_rptr++;
> > > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > > -		while (!isspace(*test_name_end))
> > > -			test_name_end++;
> > > -		*test_name_end = '\0';
> > > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > > -			       "%s", lstart) < 0)
> > > -			return -2;
> > > -		return -1;
> > > -	}
> > > -
> > > -	comment_start = strchrnul(lstart, '#');
> > > -
> > > -	/* Check if we're still in a subtest */
> > > -	if (*comment_start != '\0') {
> > > -		comment_start++;
> > > -		value_parse_start = comment_start;
> > > -
> > > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > > -				       "%s", lstart) < 0)
> > > -				return -2;
> > > -			return -1;
> > > -		}
> > > -	}
> > > -
> > > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > > -	if (ok_rptr != NULL) {
> > > -		ok_rptr += strlen(ok_lookup_str);
> > > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > > -			ok_rptr++;
> > > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > > -		while (!isspace(*test_name_end))
> > > -			test_name_end++;
> > > -		*test_name_end = '\0';
> > > -		return 0;
> > > -	}
> > > -
> > > -	return 1;
> > > -}
> > > -
> > > -/**
> > > - * parse_tap_level:
> > > - * @fd: file descriptor
> > > - * @base_test_name: test_name from upper recursion level
> > > - * @test_count: test_count of this level
> > > - * @failed_tests: top level failed_tests pointer
> > > - * @found_tests: top level found_tests pointer
> > > - * @is_builtin: whether the KUnit module is built-in or not
> > > - *
> > > - * Returns:
> > > - * 0 if succeded
> > > - * -1 if error occurred
> > > - */
> > > -__maybe_unused
> > > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > > -			   bool *found_tests, bool is_builtin)
> > > -{
> > > -	char record[BUF_LEN + 1];
> > > -	struct ktap_test_results_element *r;
> > > -	int internal_test_count;
> > > -	char test_name[BUF_LEN + 1];
> > > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > > -
> > > -	for (int i = 0; i < test_count; i++) {
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("error reading kmsg (%m)\n");
> > > -
> > > -			return -1;
> > > -		}
> > > -
> > > -		/* Sublevel found */
> > > -		if (tap_version_present(record, false))
> > > -		{
> > > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > > -								    is_builtin);
> > > -			switch (internal_test_count) {
> > > -			case -2:
> > > -				/* No more data to read */
> > > -				return -1;
> > > -			case -1:
> > > -				/* No test found */
> > > -				return -1;
> > > -			case 0:
> > > -				/* Tests found, but they're missing info */
> > > -				*found_tests = true;
> > > -				return -1;
> > > -			default:
> > > -				*found_tests = true;
> > > -
> > > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > > -				    base_test_name_for_next_level[0])
> > > -					strncat(base_test_name_for_next_level, DELIMITER,
> > > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > > -
> > > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > > -						    internal_test_count, failed_tests, found_tests,
> > > -						    is_builtin) == -1)
> > > -					return -1;
> > > -				break;
> > > -			}
> > > -		}
> > > -
> > > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > > -		case -2:
> > > -			return -1;
> > > -		case -1:
> > > -			*failed_tests = true;
> > > -
> > > -			r = malloc(sizeof(*r));
> > > -
> > > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > > -				if (r->test_name[0])
> > > -					strncat(r->test_name, DELIMITER,
> > > -						BUF_LEN - strlen(r->test_name));
> > > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > > -			       BUF_LEN - strlen(r->test_name));
> > > -			r->test_name[BUF_LEN] = '\0';
> > > -
> > > -			r->passed = false;
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&r->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -
> > > -			test_name[0] = '\0';
> > > -			break;
> > > -		case 0:
> > > -			r = malloc(sizeof(*r));
> > > -
> > > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > > -				if (r->test_name[0])
> > > -					strncat(r->test_name, DELIMITER,
> > > -						BUF_LEN - strlen(r->test_name));
> > > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > > -			       BUF_LEN - strlen(r->test_name));
> > > -			r->test_name[BUF_LEN] = '\0';
> > > -
> > > -			r->passed = true;
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&r->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -
> > > -			test_name[0] = '\0';
> > > -			break;
> > > -		default:
> > > -			break;
> > > -		}
> > > -	}
> > > -	return 0;
> > > -}
> > > -
> > > -/**
> > > - * igt_ktap_parser:
> > > - *
> > > - * This function parses the output of a ktap script and passes it to main thread.
> > > - */
> > > -void *igt_ktap_parser(void *unused)
> > > -{
> > > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > > -	struct igt_ktap_results *ktap = NULL;
> > > -	int fd = ktap_args.fd;
> > > -	IGT_LIST_HEAD(list);
> > > -	int err;
> > > -
> > > -	ktap = igt_ktap_alloc(&list);
> > > -	if (igt_debug_on(!ktap))
> > > -		goto igt_ktap_parser_end;
> > > -
> > > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > > -		struct igt_ktap_result *r, *rn;
> > > -
> > > -		/* skip kmsg continuation lines */
> > > -		if (igt_debug_on(*record == ' '))
> > > -			continue;
> > > -
> > > -		/* NULL-terminate the record */
> > > -		record[err] = '\0';
> > > -
> > > -		/* detect start of log message, continue if not found */
> > > -		buf = strchrnul(record, ';');
> > > -		if (igt_debug_on(*buf == '\0'))
> > > -			continue;
> > > -		buf++;
> > > -
> > > -		err = igt_ktap_parse(buf, ktap);
> > > -
> > > -		/* parsing error */
> > > -		if (err && err != -EINPROGRESS)
> > > -			goto igt_ktap_parser_end;
> > > -
> > > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > > -			struct ktap_test_results_element *result = NULL;
> > > -			int code = r->code;
> > > -
> > > -			if (code != IGT_EXIT_INVALID)
> > > -				result = calloc(1, sizeof(*result));
> > > -
> > > -			if (result) {
> > > -				snprintf(result->test_name, sizeof(result->test_name),
> > > -					 "%s-%s", r->suite_name, r->case_name);
> > > -
> > > -				if (code == IGT_EXIT_SUCCESS)
> > > -					result->passed = true;
> > > -			}
> > > -
> > > -			igt_list_del(&r->link);
> > > -			if (r->suite_name != suite_name) {
> > > -				free(suite_name);
> > > -				suite_name = r->suite_name;
> > > -			}
> > > -			if (r->case_name != case_name) {
> > > -				free(case_name);
> > > -				case_name = r->case_name;
> > > -			}
> > > -			free(r->msg);
> > > -			free(r);
> > > -
> > > -			/*
> > > -			 * no extra result record expected on start
> > > -			 * of parametrized test case -- skip it
> > > -			 */
> > > -			if (code == IGT_EXIT_INVALID)
> > > -				continue;
> > > -
> > > -			if (!result) {
> > > -				err = -ENOMEM;
> > > -				goto igt_ktap_parser_end;
> > > -			}
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&result->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -		}
> > > -
> > > -		/* end of KTAP report */
> > > -		if (!err)
> > > -			goto igt_ktap_parser_end;
> > > -	}
> > > -
> > > -	if (err < 0) {
> > > -		if (errno == EPIPE)
> > > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -		else
> > > -			igt_warn("error reading kmsg (%m)\n");
> > > -	}
> > > -
> > > -igt_ktap_parser_end:
> > > -	free(suite_name);
> > > -	free(case_name);
> > > -
> > > -	if (!err)
> > > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > > -
> > > -	results.still_running = false;
> > > -
> > > -	if (ktap)
> > > -		igt_ktap_free(ktap);
> > > -
> > > -	return NULL;
> > > -}
> > > -
> > > -static pthread_t ktap_parser_thread;
> > > -
> > > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > > -{
> > > -	IGT_INIT_LIST_HEAD(&results.list);
> > > -	pthread_mutex_init(&results.mutex, NULL);
> > > -	results.still_running = true;
> > > -
> > > -	ktap_args.fd = fd;
> > > -	ktap_args.is_builtin = is_builtin;
> > > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > > -
> > > -	return &results;
> > > -}
> > > -
> > > -void ktap_parser_cancel(void)
> > > -{
> > > -	pthread_cancel(ktap_parser_thread);
> > > -}
> > > -
> > > -int ktap_parser_stop(void)
> > > -{
> > > -	pthread_join(ktap_parser_thread, NULL);
> > > -	return ktap_args.ret;
> > > -}
> > > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > > index 6f8da3eab6..c422636bfc 100644
> > > --- a/lib/igt_ktap.h
> > > +++ b/lib/igt_ktap.h
> > > @@ -27,8 +27,6 @@
> > >  
> > >  #define BUF_LEN 4096
> > >  
> > > -#include <pthread.h>
> > > -
> > >  #include "igt_list.h"
> > >  
> > >  struct igt_ktap_result {
> > > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> > >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> > >  void igt_ktap_free(struct igt_ktap_results *ktap);
> > >  
> > > -void *igt_ktap_parser(void *unused);
> > > -
> > > -typedef struct ktap_test_results_element {
> > > -	char test_name[BUF_LEN + 1];
> > > -	bool passed;
> > > -	struct igt_list_head link;
> > > -} ktap_test_results_element;
> > > -
> > > -struct ktap_test_results {
> > > -	struct igt_list_head list;
> > > -	pthread_mutex_t mutex;
> > > -	bool still_running;
> > > -};
> > > -
> > > -
> > > -
> > > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > > -void ktap_parser_cancel(void);
> > > -int ktap_parser_stop(void);
> > > -
> > >  #endif /* IGT_KTAP_H */
> > 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* Re: [igt-dev] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread
@ 2023-10-11 13:00         ` Kamil Konieczny
  0 siblings, 0 replies; 77+ messages in thread
From: Kamil Konieczny @ 2023-10-11 13:00 UTC (permalink / raw)
  To: igt-dev; +Cc: intel-gfx, intel-xe

Hi Janusz,
On 2023-10-10 at 19:49:35 +0200, Janusz Krzysztofik wrote:
> Hi Kamil,
> 
> Thanks for review.
> 
> On Tuesday, 10 October 2023 17:59:56 CEST Kamil Konieczny wrote:
> > Hi Janusz,
> > On 2023-10-09 at 14:27:55 +0200, Janusz Krzysztofik wrote:
> > > There was an attempt to parse KTAP reports in the background while a kunit
> > > test module is loading.  However, since dynamic sub-subtests can be
> > > executed only from the main thread, that attempt was not quite successful,
> > > as IGT results from all executed kunit test cases were generated only
> > > after loading of kunit test module completed.
> > > 
> > > Now that the parser maintains its state and we can call it separately for
> > > each input line of a KTAP report, it is perfectly possible to call the
> > > parser from the main thread while the module is loading in the background,
> > > and convert results from kunit test cases immediately to results of IGT
> > > dynamic sub-subtests by running an igt_dynamic() section for each result
> > > as soon as returned by the parser.
> > > 
> > > Drop igt_ktap_parser() thread and execute igt_dynamic() for each kunit
> > > result obtained from igt_ktap_parse() called from the main thread.
> > > 
> > > Also, drop no longer needed functions from igt_ktap soruces.
> > > 
> > > v3: Fix ktap structure not freed on lseek error,
> > >   - fix initial SIGCHLD handler not restored,
> > >   - fix missing handling of potential errors returned by sigaction,
> > >   - fix potential race of read() vs. ptherad_kill(), use robust mutex for
> > >     synchronization with modprobe thread,
> > >   - fix potentially illegal use of igt_assert() called outside of
> > >     dynamic sub-subtest section,
> > >   - fix unsupported exit code potentially passed to igt_fail(),
> > >   - no need to fail a dynamic sub-subtest on potential KTAP parser error
> > >     after a valid result from the parser has been processed,
> > >   - fix trailing newlines missing from error messages,
> > >   - add more debug statements,
> > >   - integrate common code around kunit_result_free() into it.
> > > v2: Interrupt blocking read() on modprobe failure.
> > > 
> > > Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> > > Acked-by: Mauro Carvalho Chehab <mchehab@kernel.org> # v2
> > > ---
> > >  lib/igt_kmod.c | 261 +++++++++++++++++++----
> > >  lib/igt_ktap.c | 568 -------------------------------------------------
> > >  lib/igt_ktap.h |  22 --
> > >  3 files changed, 222 insertions(+), 629 deletions(-)
> > > 
> > > diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> > > index 426ae5b26f..7bca4cdaab 100644
> > > --- a/lib/igt_kmod.c
> > > +++ b/lib/igt_kmod.c
> > > @@ -1,5 +1,5 @@
> > >  /*
> > > - * Copyright © 2016 Intel Corporation
> > > + * Copyright © 2016-2023 Intel Corporation
> > >   *
> > >   * Permission is hereby granted, free of charge, to any person obtaining a
> > >   * copy of this software and associated documentation files (the "Software"),
> > > @@ -26,7 +26,12 @@
> > >  #include <errno.h>
> > >  #include <fcntl.h>
> > >  #include <pthread.h>
> > > +#include <stdlib.h>
> > > +#include <string.h>
> > >  #include <sys/utsname.h>
> > > +#include <unistd.h>
> > > +
> > > +#include "assembler/brw_compat.h"	/* [un]likely() */
> > ------------ ^^^^^^^^^
> > Do we really need this?
> 
> I think the correct question is if wee really need [un]likely().  I'm using it 
> to document unlikely cases, which is a widely accepted method of documenting 
> cases like that, I believe.  Having that clarified, I hope you just tell me if 
> you think we need those cases documented, and how, if not that way.
> 

Now I see, it is for unlikely() - ok it can stay.

> > 
> > >  
> > >  #include "igt_aux.h"
> > >  #include "igt_core.h"
> > > @@ -748,9 +753,12 @@ void igt_kselftest_get_tests(struct kmod_module *kmod,
> > >  }
> > >  
> > >  struct modprobe_data {
> > > +	pthread_t parent;
> > --- ^^^^^^^^^^^^^^^^
> > Please move it below to other related thread data.
> > Also consider a comment why(or for what purpose)
> > did you put this here.
> 
> No problem to move it to the bottom, if that's important to you, but regarding 
> the comment, do you think that the purpose of this field of the structure, 
> compared to other fields, is so unclear form review of the code, despite its 
> name, that it requires a comment, unlike the other fields?
> 

I am ok with only move.

> > 
> > >  	struct kmod_module *kmod;
> > >  	const char *opts;
> > >  	int err;
> > > +	pthread_mutex_t lock;
> > > +	pthread_t thread;
> > >  };
> > >  
> > >  static void *modprobe_task(void *arg)
> > > @@ -759,16 +767,132 @@ static void *modprobe_task(void *arg)
> > >  
> > >  	data->err = modprobe(data->kmod, data->opts);
> > >  
> > > +	if (igt_debug_on(data->err)) {
> > > +		int err;
> > > +
> > > +		while (err = pthread_mutex_trylock(&data->lock),
> > > +		       err && !igt_debug_on(err != EBUSY))
> > > +			igt_debug_on(pthread_kill(data->parent, SIGCHLD));
> > > +	} else {
> > > +		/* let main thread use mutex to detect modprobe completion */
> > > +		igt_debug_on(pthread_mutex_lock(&data->lock));
> > > +	}
> > > +
> > >  	return NULL;
> > >  }
> > >  
> > > +static void kunit_sigchld_handler(int signal)
> > > +{
> > > +	return;
> > --- ^^^^^^^
> > Why not removing this? checkpatch complains about return from void.
> 
> OK.
> 
> > 
> > > +}
> > > +
> > > +static int kunit_kmsg_result_get(struct igt_list_head *results,
> > > +				 struct modprobe_data *modprobe,
> > > +				 int fd, struct igt_ktap_results *ktap)
> > > +{
> > > +	struct sigaction sigchld = { .sa_handler = kunit_sigchld_handler, },
> > > +			 *saved;
> > > +	char record[BUF_LEN + 1], *buf;
> > > +	unsigned long taints;
> > > +	int ret;
> > > +
> > > +	do {
> > > +		int err;
> > > +
> > > +		if (igt_debug_on(igt_kernel_tainted(&taints)))
> > > +			return -ENOTRECOVERABLE;
> > > +
> > > +		err = igt_debug_on(sigaction(SIGCHLD, &sigchld, saved));
> > > +		if (err == -1)
> > > +			return -errno;
> > > +		else if (unlikely(err))
> > > +			return err;
> > > +
> > > +		err = pthread_mutex_lock(&modprobe->lock);
> > > +		switch (err) {
> > > +		case EOWNERDEAD:
> > > +			/* leave the mutex unrecoverable */
> > > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > > +			__attribute__ ((fallthrough));
> > > +		case ENOTRECOVERABLE:
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +			if (igt_debug_on(modprobe->err))
> > > +				return modprobe->err;
> > 
> > Why no 'return -err;' here?
> 
> Because modprobe->err, which can tell us more about what has gone bad in the 
> modprobe thread, is more important here than err which only informs us about 
> completion of the modprobe thread, also if successful, isn't it?
> 

Thank you for explanation.

> > 
> > > +			break;
> > > +		case 0:
> > > +			break;
> > > +		default:
> > > +			igt_debug("pthread_mutex_lock() error: %d\n", err);
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +			return -err;
> > > +		}
> > > +
> > > +		ret = read(fd, record, BUF_LEN);
> > > +
> > > +		if (!err) {
> >         ^^^^^^^^^^^
> > Looks strange here.
> 
> Here err still carries a return code from pthread_mutex_lock(), then !err 
> means that the modprobe thread was still running and we have 1) acquired 
> the mutex which now we have to release, 2) installed a non-default 
> SIGCHLD handler that we should now reset.  Have you got a better idea 
> how to encode that so it looks less strange?  Would a comment that reminds 
> a reader where that err comes from be sufficient?
> 

Yes please add comment here.

> > 
> > > +			igt_debug_on(pthread_mutex_unlock(&modprobe->lock));
> > > +			igt_debug_on(sigaction(SIGCHLD, saved, NULL));
> > > +		}
> > > +
> > > +		if (igt_debug_on(!ret))
> > > +			return -ENODATA;
> > > +		if (igt_debug_on(ret == -1))
> > > +			return -errno;
> > > +		if (unlikely(igt_debug_on(ret < 0)))
> > > +			break;
> > > +
> > > +		/* skip kmsg continuation lines */
> > > +		if (igt_debug_on(*record == ' '))
> > ----------- ^^^^^^^^^^^^
> > Why debug here? imho it is not needed.
> 
> Because we don't expect continuation lines while reading a KTAP report. Since 
> the kernel log may still contain them, we want to know about them when 
> something wrong happens to our KTAP parser.  That's the purpose of all those 
> debug_on() calls: provide as much information on unexpected conditions as 
> possible when the test fails, isn't it?
> 

Ok.

> > 
> > > +			continue;
> > > +
> > > +		/* NULL-terminate the record */
> > > +		record[ret] = '\0';
> > > +
> > > +		/* detect start of log message, continue if not found */
> > > +		buf = strchrnul(record, ';');
> > > +		if (igt_debug_on(*buf == '\0'))
> > ----------- ^^^^^^^^^^^^
> > Same here, you just continue loop if no ';'?
> 
> That means we are getting incorrectly formatted records from /dev/kmsg, and 
> I'm sure we would like to be informed about that if something goes wrong with 
> the test, wouldn't we?
> 

Ok.

> > 
> > > +			continue;
> > > +		buf++;
> > > +
> > > +		ret = igt_ktap_parse(buf, ktap);
> > > +		if (!ret || igt_debug_on(ret != -EINPROGRESS))
> > > +			break;
> > > +	} while (igt_list_empty(results));
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void kunit_result_free(struct igt_ktap_result **r,
> > > +			      char **suite_name, char **case_name)
> > > +{
> > > +	if (!*r)
> > > +		return;
> > > +
> > > +	igt_list_del(&(*r)->link);
> > > +
> > > +	if ((*r)->suite_name != *suite_name) {
> > > +		free(*suite_name);
> > > +		*suite_name = (*r)->suite_name;
> > > +	}
> > > +
> > > +	if ((*r)->case_name != *case_name) {
> > > +		free(*case_name);
> > > +		*case_name = (*r)->case_name;
> > > +	}
> > > +
> > > +	free((*r)->msg);
> > > +	free(*r);
> > > +	*r = NULL;
> > > +}
> > > +
> > >  static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> > >  {
> > > -	struct modprobe_data modprobe = { tst->kmod, opts, 0, };
> > > -	struct kmod_module *kunit_kmod;
> > > -	bool is_builtin;
> > > -	struct ktap_test_results *results;
> > > -	pthread_t modprobe_thread;
> > > +	struct modprobe_data modprobe = { pthread_self(), tst->kmod, opts, 0, };
> > > +	char *suite_name = NULL, *case_name = NULL;
> > > +	struct igt_ktap_result *r, *rn;
> > > +	struct igt_ktap_results *ktap;
> > > +	pthread_mutexattr_t attr;
> > > +	IGT_LIST_HEAD(results);
> > >  	unsigned long taints;
> > >  	int flags, ret;
> > >  
> > > @@ -780,60 +904,119 @@ static void __igt_kunit(struct igt_ktest *tst, const char *opts)
> > >  
> > >  	igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> > >  
> > > -	igt_skip_on(kmod_module_new_from_name(kmod_ctx(), "kunit", &kunit_kmod));
> > > -	is_builtin = kmod_module_get_initstate(kunit_kmod) == KMOD_MODULE_BUILTIN;
> > > -	kmod_module_unref(kunit_kmod);
> > > +	igt_skip_on(pthread_mutexattr_init(&attr));
> > > +	igt_skip_on(pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST));
> > > +	igt_skip_on(pthread_mutex_init(&modprobe.lock, &attr));
> > >  
> > > -	results = ktap_parser_start(tst->kmsg, is_builtin);
> > > +	ktap = igt_ktap_alloc(&results);
> > > +	igt_require(ktap);
> > >  
> > > -	if (igt_debug_on(pthread_create(&modprobe_thread, NULL,
> > > +	if (igt_debug_on(pthread_create(&modprobe.thread, NULL,
> > >  					modprobe_task, &modprobe))) {
> > > -		ktap_parser_cancel();
> > > -		igt_ignore_warn(ktap_parser_stop());
> > > +		igt_ktap_free(ktap);
> > >  		igt_skip("Failed to create a modprobe thread\n");
> > >  	}
> > >  
> > > -	while (READ_ONCE(results->still_running) || !igt_list_empty(&results->list))
> > > -	{
> > > -		struct ktap_test_results_element *result;
> > > -
> > > -		if (!pthread_tryjoin_np(modprobe_thread, NULL) && modprobe.err) {
> > > -			ktap_parser_cancel();
> > > +	do {
> > > +		ret = kunit_kmsg_result_get(&results, &modprobe,
> > > +					    tst->kmsg, ktap);
> > > +		if (igt_debug_on(ret && ret != -EINPROGRESS))
> > >  			break;
> > > -		}
> > >  
> > > -		if (igt_kernel_tainted(&taints)) {
> > > -			ktap_parser_cancel();
> > > -			pthread_cancel(modprobe_thread);
> > > +		if (igt_debug_on(igt_list_empty(&results)))
> > >  			break;
> > > -		}
> > >  
> > > -		pthread_mutex_lock(&results->mutex);
> > > -		if (igt_list_empty(&results->list)) {
> > > -			pthread_mutex_unlock(&results->mutex);
> > > -			continue;
> > > -		}
> > > +		r = igt_list_first_entry(&results, r, link);
> > >  
> > > -		result = igt_list_first_entry(&results->list, result, link);
> > > +		igt_dynamic_f("%s-%s", r->suite_name, r->case_name) {
> > > +			if (r->code == IGT_EXIT_INVALID) {
> > > +				/* parametrized test case, get actual result */
> > > +				kunit_result_free(&r, &suite_name, &case_name);
> > >  
> > > -		igt_list_del(&result->link);
> > > -		pthread_mutex_unlock(&results->mutex);
> > > +				igt_assert(igt_list_empty(&results));
> > >  
> > > -		igt_dynamic(result->test_name) {
> > > -			igt_assert(READ_ONCE(result->passed));
> > > +				ret = kunit_kmsg_result_get(&results, &modprobe,
> > > +							    tst->kmsg, ktap);
> > > +				if (ret != -EINPROGRESS)
> > > +					igt_fail_on(ret);
> > > +
> > > +				igt_fail_on(igt_list_empty(&results));
> > > +
> > > +				r = igt_list_first_entry(&results, r, link);
> > > +
> > > +				igt_fail_on_f(strcmp(r->suite_name, suite_name),
> > > +					      "suite_name expected: %s, got: %s\n",
> > > +					      suite_name, r->suite_name);
> > > +				igt_fail_on_f(strcmp(r->case_name, case_name),
> > > +					      "case_name expected: %s, got: %s\n",
> > > +					      case_name, r->case_name);
> > > +			}
> > >  
> > > -			if (!pthread_tryjoin_np(modprobe_thread, NULL))
> > > +			igt_assert_neq(r->code, IGT_EXIT_INVALID);
> > > +
> > > +			if (r->msg && *r->msg) {
> > > +				igt_skip_on_f(r->code == IGT_EXIT_SKIP,
> > > +					      "%s\n", r->msg);
> > > +				igt_fail_on_f(r->code == IGT_EXIT_FAILURE,
> > > +					      "%s\n", r->msg);
> > > +				igt_abort_on_f(r->code == IGT_EXIT_ABORT,
> > > +					      "%s\n", r->msg);
> > > +			} else {
> > > +				igt_skip_on(r->code == IGT_EXIT_SKIP);
> > > +				igt_fail_on(r->code == IGT_EXIT_FAILURE);
> > > +				if (r->code == IGT_EXIT_ABORT)
> > > +					igt_fail(r->code);
> > > +			}
> > > +			igt_assert_eq(r->code, IGT_EXIT_SUCCESS);
> > > +
> > > +			switch (pthread_mutex_lock(&modprobe.lock)) {
> > > +			case 0:
> > > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +				break;
> > > +			case EOWNERDEAD:
> > > +				/* leave the mutex unrecoverable */
> > > +				igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +				__attribute__ ((fallthrough));
> > > +			case ENOTRECOVERABLE:
> > >  				igt_assert_eq(modprobe.err, 0);
> > > +				break;
> > 
> > imho we should also break a do-while loop in case of error.
> 
> We are inside igt_dynamic() body, then I don't think we can safely break the 
> outer loop from here, only pass, skip, or fail.
> 
> If we are not able to check whether the modprobe thread hasn't returned an 
> error then I don't think that's a good reason for failing or skipping the 
> current dynamic sub-subtest since we have its KTAP results already collected 
> and we should just report them in that case, I believe.
> 
> If the error persists then the loop will get broken on next attempt to lock 
> the mutex before entering blocking read(), next dynamic sub-subtests won't be 
> executed, and the whole subtest will inform the user about that incompleteness 
> of results from dynamic sub-subtests by returning SKIP.  Isn't that 
> sufficient?
> 

Yes it is, thank you for explaining it.

> > 
> > > +			default:
> > > +				igt_debug("pthread_mutex_lock() failed\n");
> > > +				break;
> > > +			}
> > >  
> > >  			igt_assert_eq(igt_kernel_tainted(&taints), 0);
> > >  		}
> > >  
> > > -		free(result);
> > > +		kunit_result_free(&r, &suite_name, &case_name);
> > > +
> > > +	} while (ret == -EINPROGRESS);
> > > +
> > > +	igt_list_for_each_entry_safe(r, rn, &results, link)
> > > +		kunit_result_free(&r, &suite_name, &case_name);
> > > +
> > > +	free(case_name);
> > > +	free(suite_name);
> > > +
> > > +	switch (pthread_mutex_lock(&modprobe.lock)) {
> > > +	case 0:
> > > +		igt_debug_on(pthread_cancel(modprobe.thread));
> > > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > > +		break;
> > > +	case EOWNERDEAD:
> > > +		/* leave the mutex unrecoverable */
> > > +		igt_debug_on(pthread_mutex_unlock(&modprobe.lock));
> > > +		break;
> > > +	case ENOTRECOVERABLE:
> > > +		break;
> > > +	default:
> > > +		igt_debug("pthread_mutex_lock() failed\n");
> > > +		igt_debug_on(pthread_join(modprobe.thread, NULL));
> > > +		break;
> > >  	}
> > >  
> > > -	pthread_join(modprobe_thread, NULL);
> > > -
> > > -	ret = ktap_parser_stop();
> > > +	igt_ktap_free(ktap);
> > >  
> > >  	igt_skip_on(modprobe.err);
> > >  	igt_skip_on(igt_kernel_tainted(&taints));
> > > diff --git a/lib/igt_ktap.c b/lib/igt_ktap.c
> > > index 5eac102417..53a6c63288 100644
> > > --- a/lib/igt_ktap.c
> > > +++ b/lib/igt_ktap.c
> > > @@ -4,17 +4,11 @@
> > >   * Copyright © 2023 Intel Corporation
> > >   */
> > >  
> > > -#include <ctype.h>
> > > -#include <limits.h>
> > > -#include <libkmod.h>
> > > -#include <pthread.h>
> > >  #include <errno.h>
> > >  #include <stdio.h>
> > >  #include <stdlib.h>
> > >  #include <string.h>
> > > -#include <unistd.h>
> > >  
> > > -#include "igt_aux.h"
> > >  #include "igt_core.h"
> > >  #include "igt_ktap.h"
> > >  #include "igt_list.h"
> > > @@ -312,565 +306,3 @@ void igt_ktap_free(struct igt_ktap_results *ktap)
> > >  {
> > >  	free(ktap);
> > >  }
> > > -
> > > -#define DELIMITER "-"
> > > -
> > > -struct ktap_parser_args {
> > > -	int fd;
> > > -	bool is_builtin;
> > > -	int ret;
> > > -} ktap_args;
> > > -
> > > -static struct ktap_test_results results;
> > > -
> > > -static int log_to_end(enum igt_log_level level, int fd,
> > > -		      char *record, const char *format, ...) __attribute__((format(printf, 4, 5)));
> > > -
> > > -/**
> > > - * log_to_end:
> > > - * @level: #igt_log_level
> > > - * @record: record to store the read data
> > > - * @format: format string
> > > - * @...: optional arguments used in the format string
> > > - *
> > > - * This is an altered version of the generic structured logging helper function
> > > - * igt_log capable of reading to the end of a given line.
> > > - *
> > > - * Returns: 0 for success, or -2 if there's an error reading from the file
> > > - */
> > > -static int log_to_end(enum igt_log_level level, int fd,
> > > -		      char *record, const char *format, ...)
> > > -{
> > > -	va_list args;
> > > -	const char *lend;
> > > -
> > > -	/* Cutoff after newline character, in order to not display garbage */
> > > -	char *cutoff = strchr(record, '\n');
> > > -	if (cutoff) {
> > > -		if (cutoff - record < BUF_LEN)
> > > -			cutoff[1] = '\0';
> > > -	}
> > > -
> > > -	va_start(args, format);
> > > -	igt_vlog(IGT_LOG_DOMAIN, level, format, args);
> > > -	va_end(args);
> > > -
> > > -	lend = strchrnul(record, '\n');
> > > -	while (*lend == '\0') {
> > > -		igt_log(IGT_LOG_DOMAIN, level, "%s", record);
> > > -
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -
> > > -		lend = strchrnul(record, '\n');
> > > -	}
> > > -	return 0;
> > > -}
> > > -
> > > -/**
> > > - * lookup_value:
> > > - * @haystack: the string to search in
> > > - * @needle: the string to search for
> > > - *
> > > - * Returns: the value of the needle in the haystack, or -1 if not found.
> > > - */
> > > -static long lookup_value(const char *haystack, const char *needle)
> > > -{
> > > -	const char *needle_rptr;
> > > -	char *needle_end;
> > > -	long num;
> > > -
> > > -	needle_rptr = strcasestr(haystack, needle);
> > > -
> > > -	if (needle_rptr == NULL)
> > > -		return -1;
> > > -
> > > -	/* Skip search string and whitespaces after it */
> > > -	needle_rptr += strlen(needle);
> > > -
> > > -	num = strtol(needle_rptr, &needle_end, 10);
> > > -
> > > -	if (needle_rptr == needle_end)
> > > -		return -1;
> > > -
> > > -	if (num == LONG_MIN || num == LONG_MAX)
> > > -		return 0;
> > > -
> > > -	return num > 0 ? num : 0;
> > > -}
> > > -
> > > -/**
> > > - * tap_version_present:
> > > - * @record: buffer with tap data
> > > - * @print_info: whether tap version should be printed or not
> > > - *
> > > - * Returns:
> > > - * 0 if not found
> > > - * 1 if found
> > > - */
> > > -static int tap_version_present(char* record, bool print_info)
> > > -{
> > > -	/*
> > > -	 * "(K)TAP version XX" should be the first line on all (sub)tests as per
> > > -	 * https://kernel.org/doc/html/latest/dev-tools/ktap.html#version-lines
> > 
> > Could you save these two lines comment in some other place?
> 
> Why?  Do you think that just that information from a multi-paragraph 
> description of KTAP format is of particular importance?
> 
> Instead, let me add just the reference to the KTAP standard to a comment above 
> the current parser implementation, OK?

Seems good, please add the reference.

Regards,
Kamil

> 
> Thanks,
> Janusz
> 
> > 
> > Regards,
> > Kamil
> > 
> > > -	 *
> > > -	 * but actually isn't, as it currently depends on the KUnit module
> > > -	 * being built-in, so we can't rely on it every time
> > > -	 */
> > > -	const char *version_rptr = strcasestr(record, "TAP version ");
> > > -	char *cutoff;
> > > -
> > > -	if (version_rptr == NULL)
> > > -		return 0;
> > > -
> > > -	/* Cutoff after newline character, in order to not display garbage */
> > > -	cutoff = strchr(version_rptr, '\n');
> > > -	if (cutoff)
> > > -		cutoff[0] = '\0';
> > > -
> > > -	if (print_info)
> > > -		igt_info("%s\n", version_rptr);
> > > -
> > > -	return 1;
> > > -}
> > > -
> > > -/**
> > > - * find_next_tap_subtest:
> > > - * @fd: file descriptor
> > > - * @record: buffer used to read fd
> > > - * @is_builtin: whether KUnit is built-in or not
> > > - *
> > > - * Returns:
> > > - * 0 if there's missing information
> > > - * -1 if not found
> > > - * -2 if there are problems while reading the file.
> > > - * any other value corresponds to the amount of cases of the next (sub)test
> > > - */
> > > -static int find_next_tap_subtest(int fd, char *record, char *test_name, bool is_builtin)
> > > -{
> > > -	const char *test_lookup_str, *subtest_lookup_str, *name_rptr;
> > > -	long test_count;
> > > -	char *cutoff;
> > > -
> > > -	test_name[0] = '\0';
> > > -	test_name[BUF_LEN] = '\0';
> > > -
> > > -	test_lookup_str = " subtest: ";
> > > -	subtest_lookup_str = " test: ";
> > > -
> > > -	if (!tap_version_present(record, true))
> > > -		return -1;
> > > -
> > > -	if (is_builtin) {
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("an error occurred while reading kmsg: %m\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -	}
> > > -
> > > -	name_rptr = strcasestr(record, test_lookup_str);
> > > -	if (name_rptr != NULL) {
> > > -		name_rptr += strlen(test_lookup_str);
> > > -	} else {
> > > -		name_rptr = strcasestr(record, subtest_lookup_str);
> > > -		if (name_rptr != NULL)
> > > -			name_rptr += strlen(subtest_lookup_str);
> > > -	}
> > > -
> > > -	if (name_rptr == NULL) {
> > > -		if (!is_builtin)
> > > -			/* We've probably found nothing */
> > > -			return -1;
> > > -		igt_info("Missing test name\n");
> > > -	} else {
> > > -		strncpy(test_name, name_rptr, BUF_LEN);
> > > -		/* Cutoff after newline character, in order to not display garbage */
> > > -		cutoff = strchr(test_name, '\n');
> > > -		if (cutoff)
> > > -			cutoff[0] = '\0';
> > > -
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("unknown error reading kmsg (%m)\n");
> > > -
> > > -			return -2;
> > > -		}
> > > -
> > > -		/* Now we can be sure we found tests */
> > > -		if (!is_builtin)
> > > -			igt_info("KUnit is not built-in, skipping version check...\n");
> > > -	}
> > > -
> > > -	/*
> > > -	 * Total test count will almost always appear as 0..N at the beginning
> > > -	 * of a run, so we use it to reliably identify a new run
> > > -	 */
> > > -	test_count = lookup_value(record, "..");
> > > -
> > > -	if (test_count <= 0) {
> > > -		igt_info("Missing test count\n");
> > > -		if (test_name[0] == '\0')
> > > -			return 0;
> > > -		if (log_to_end(IGT_LOG_INFO, fd, record,
> > > -				"Running some tests in: %s\n",
> > > -				test_name) < 0)
> > > -			return -2;
> > > -		return 0;
> > > -	} else if (test_name[0] == '\0') {
> > > -		igt_info("Running %ld tests...\n", test_count);
> > > -		return 0;
> > > -	}
> > > -
> > > -	if (log_to_end(IGT_LOG_INFO, fd, record,
> > > -			"Executing %ld tests in: %s\n",
> > > -			test_count, test_name) < 0)
> > > -		return -2;
> > > -
> > > -	return test_count;
> > > -}
> > > -
> > > -/**
> > > - * parse_kmsg_for_tap:
> > > - * @fd: file descriptor
> > > - * @record: buffer used to read fd
> > > - * @test_name: buffer to store the test name
> > > - *
> > > - * Returns:
> > > - * 1 if no results were found
> > > - * 0 if a test succeded
> > > - * -1 if a test failed
> > > - * -2 if there are problems reading the file
> > > - */
> > > -static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
> > > -{
> > > -	const char *lstart, *ok_lookup_str, *nok_lookup_str,
> > > -	      *ok_rptr, *nok_rptr, *comment_start, *value_parse_start;
> > > -	char *test_name_end;
> > > -
> > > -	ok_lookup_str = "ok ";
> > > -	nok_lookup_str = "not ok ";
> > > -
> > > -	lstart = strchrnul(record, ';');
> > > -
> > > -	if (*lstart == '\0') {
> > > -		igt_warn("kmsg truncated: output malformed (%m)\n");
> > > -		return -2;
> > > -	}
> > > -
> > > -	lstart++;
> > > -	while (isspace(*lstart))
> > > -		lstart++;
> > > -
> > > -	nok_rptr = strstr(lstart, nok_lookup_str);
> > > -	if (nok_rptr != NULL) {
> > > -		nok_rptr += strlen(nok_lookup_str);
> > > -		while (isdigit(*nok_rptr) || isspace(*nok_rptr) || *nok_rptr == '-')
> > > -			nok_rptr++;
> > > -		test_name_end = strncpy(test_name, nok_rptr, BUF_LEN);
> > > -		while (!isspace(*test_name_end))
> > > -			test_name_end++;
> > > -		*test_name_end = '\0';
> > > -		if (log_to_end(IGT_LOG_WARN, fd, record,
> > > -			       "%s", lstart) < 0)
> > > -			return -2;
> > > -		return -1;
> > > -	}
> > > -
> > > -	comment_start = strchrnul(lstart, '#');
> > > -
> > > -	/* Check if we're still in a subtest */
> > > -	if (*comment_start != '\0') {
> > > -		comment_start++;
> > > -		value_parse_start = comment_start;
> > > -
> > > -		if (lookup_value(value_parse_start, "fail: ") > 0) {
> > > -			if (log_to_end(IGT_LOG_WARN, fd, record,
> > > -				       "%s", lstart) < 0)
> > > -				return -2;
> > > -			return -1;
> > > -		}
> > > -	}
> > > -
> > > -	ok_rptr = strstr(lstart, ok_lookup_str);
> > > -	if (ok_rptr != NULL) {
> > > -		ok_rptr += strlen(ok_lookup_str);
> > > -		while (isdigit(*ok_rptr) || isspace(*ok_rptr) || *ok_rptr == '-')
> > > -			ok_rptr++;
> > > -		test_name_end = strncpy(test_name, ok_rptr, BUF_LEN);
> > > -		while (!isspace(*test_name_end))
> > > -			test_name_end++;
> > > -		*test_name_end = '\0';
> > > -		return 0;
> > > -	}
> > > -
> > > -	return 1;
> > > -}
> > > -
> > > -/**
> > > - * parse_tap_level:
> > > - * @fd: file descriptor
> > > - * @base_test_name: test_name from upper recursion level
> > > - * @test_count: test_count of this level
> > > - * @failed_tests: top level failed_tests pointer
> > > - * @found_tests: top level found_tests pointer
> > > - * @is_builtin: whether the KUnit module is built-in or not
> > > - *
> > > - * Returns:
> > > - * 0 if succeded
> > > - * -1 if error occurred
> > > - */
> > > -__maybe_unused
> > > -static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
> > > -			   bool *found_tests, bool is_builtin)
> > > -{
> > > -	char record[BUF_LEN + 1];
> > > -	struct ktap_test_results_element *r;
> > > -	int internal_test_count;
> > > -	char test_name[BUF_LEN + 1];
> > > -	char base_test_name_for_next_level[BUF_LEN + 1];
> > > -
> > > -	for (int i = 0; i < test_count; i++) {
> > > -		if (read(fd, record, BUF_LEN) < 0) {
> > > -			if (errno == EPIPE)
> > > -				igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -			else
> > > -				igt_warn("error reading kmsg (%m)\n");
> > > -
> > > -			return -1;
> > > -		}
> > > -
> > > -		/* Sublevel found */
> > > -		if (tap_version_present(record, false))
> > > -		{
> > > -			internal_test_count = find_next_tap_subtest(fd, record, test_name,
> > > -								    is_builtin);
> > > -			switch (internal_test_count) {
> > > -			case -2:
> > > -				/* No more data to read */
> > > -				return -1;
> > > -			case -1:
> > > -				/* No test found */
> > > -				return -1;
> > > -			case 0:
> > > -				/* Tests found, but they're missing info */
> > > -				*found_tests = true;
> > > -				return -1;
> > > -			default:
> > > -				*found_tests = true;
> > > -
> > > -				memcpy(base_test_name_for_next_level, base_test_name, BUF_LEN);
> > > -				if (strlen(base_test_name_for_next_level) < BUF_LEN - 1 &&
> > > -				    base_test_name_for_next_level[0])
> > > -					strncat(base_test_name_for_next_level, DELIMITER,
> > > -						BUF_LEN - strlen(base_test_name_for_next_level));
> > > -				memcpy(base_test_name_for_next_level + strlen(base_test_name_for_next_level),
> > > -				       test_name, BUF_LEN - strlen(base_test_name_for_next_level));
> > > -
> > > -				if (parse_tap_level(fd, base_test_name_for_next_level,
> > > -						    internal_test_count, failed_tests, found_tests,
> > > -						    is_builtin) == -1)
> > > -					return -1;
> > > -				break;
> > > -			}
> > > -		}
> > > -
> > > -		switch (parse_kmsg_for_tap(fd, record, test_name)) {
> > > -		case -2:
> > > -			return -1;
> > > -		case -1:
> > > -			*failed_tests = true;
> > > -
> > > -			r = malloc(sizeof(*r));
> > > -
> > > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > > -				if (r->test_name[0])
> > > -					strncat(r->test_name, DELIMITER,
> > > -						BUF_LEN - strlen(r->test_name));
> > > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > > -			       BUF_LEN - strlen(r->test_name));
> > > -			r->test_name[BUF_LEN] = '\0';
> > > -
> > > -			r->passed = false;
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&r->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -
> > > -			test_name[0] = '\0';
> > > -			break;
> > > -		case 0:
> > > -			r = malloc(sizeof(*r));
> > > -
> > > -			memcpy(r->test_name, base_test_name, BUF_LEN);
> > > -			if (strlen(r->test_name) < BUF_LEN - 1)
> > > -				if (r->test_name[0])
> > > -					strncat(r->test_name, DELIMITER,
> > > -						BUF_LEN - strlen(r->test_name));
> > > -			memcpy(r->test_name + strlen(r->test_name), test_name,
> > > -			       BUF_LEN - strlen(r->test_name));
> > > -			r->test_name[BUF_LEN] = '\0';
> > > -
> > > -			r->passed = true;
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&r->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -
> > > -			test_name[0] = '\0';
> > > -			break;
> > > -		default:
> > > -			break;
> > > -		}
> > > -	}
> > > -	return 0;
> > > -}
> > > -
> > > -/**
> > > - * igt_ktap_parser:
> > > - *
> > > - * This function parses the output of a ktap script and passes it to main thread.
> > > - */
> > > -void *igt_ktap_parser(void *unused)
> > > -{
> > > -	char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
> > > -	struct igt_ktap_results *ktap = NULL;
> > > -	int fd = ktap_args.fd;
> > > -	IGT_LIST_HEAD(list);
> > > -	int err;
> > > -
> > > -	ktap = igt_ktap_alloc(&list);
> > > -	if (igt_debug_on(!ktap))
> > > -		goto igt_ktap_parser_end;
> > > -
> > > -	while (err = read(fd, record, BUF_LEN), err > 0) {
> > > -		struct igt_ktap_result *r, *rn;
> > > -
> > > -		/* skip kmsg continuation lines */
> > > -		if (igt_debug_on(*record == ' '))
> > > -			continue;
> > > -
> > > -		/* NULL-terminate the record */
> > > -		record[err] = '\0';
> > > -
> > > -		/* detect start of log message, continue if not found */
> > > -		buf = strchrnul(record, ';');
> > > -		if (igt_debug_on(*buf == '\0'))
> > > -			continue;
> > > -		buf++;
> > > -
> > > -		err = igt_ktap_parse(buf, ktap);
> > > -
> > > -		/* parsing error */
> > > -		if (err && err != -EINPROGRESS)
> > > -			goto igt_ktap_parser_end;
> > > -
> > > -		igt_list_for_each_entry_safe(r, rn, &list, link) {
> > > -			struct ktap_test_results_element *result = NULL;
> > > -			int code = r->code;
> > > -
> > > -			if (code != IGT_EXIT_INVALID)
> > > -				result = calloc(1, sizeof(*result));
> > > -
> > > -			if (result) {
> > > -				snprintf(result->test_name, sizeof(result->test_name),
> > > -					 "%s-%s", r->suite_name, r->case_name);
> > > -
> > > -				if (code == IGT_EXIT_SUCCESS)
> > > -					result->passed = true;
> > > -			}
> > > -
> > > -			igt_list_del(&r->link);
> > > -			if (r->suite_name != suite_name) {
> > > -				free(suite_name);
> > > -				suite_name = r->suite_name;
> > > -			}
> > > -			if (r->case_name != case_name) {
> > > -				free(case_name);
> > > -				case_name = r->case_name;
> > > -			}
> > > -			free(r->msg);
> > > -			free(r);
> > > -
> > > -			/*
> > > -			 * no extra result record expected on start
> > > -			 * of parametrized test case -- skip it
> > > -			 */
> > > -			if (code == IGT_EXIT_INVALID)
> > > -				continue;
> > > -
> > > -			if (!result) {
> > > -				err = -ENOMEM;
> > > -				goto igt_ktap_parser_end;
> > > -			}
> > > -
> > > -			pthread_mutex_lock(&results.mutex);
> > > -			igt_list_add_tail(&result->link, &results.list);
> > > -			pthread_mutex_unlock(&results.mutex);
> > > -		}
> > > -
> > > -		/* end of KTAP report */
> > > -		if (!err)
> > > -			goto igt_ktap_parser_end;
> > > -	}
> > > -
> > > -	if (err < 0) {
> > > -		if (errno == EPIPE)
> > > -			igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
> > > -		else
> > > -			igt_warn("error reading kmsg (%m)\n");
> > > -	}
> > > -
> > > -igt_ktap_parser_end:
> > > -	free(suite_name);
> > > -	free(case_name);
> > > -
> > > -	if (!err)
> > > -		ktap_args.ret = IGT_EXIT_SUCCESS;
> > > -
> > > -	results.still_running = false;
> > > -
> > > -	if (ktap)
> > > -		igt_ktap_free(ktap);
> > > -
> > > -	return NULL;
> > > -}
> > > -
> > > -static pthread_t ktap_parser_thread;
> > > -
> > > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin)
> > > -{
> > > -	IGT_INIT_LIST_HEAD(&results.list);
> > > -	pthread_mutex_init(&results.mutex, NULL);
> > > -	results.still_running = true;
> > > -
> > > -	ktap_args.fd = fd;
> > > -	ktap_args.is_builtin = is_builtin;
> > > -	ktap_args.ret = IGT_EXIT_FAILURE;
> > > -	pthread_create(&ktap_parser_thread, NULL, igt_ktap_parser, NULL);
> > > -
> > > -	return &results;
> > > -}
> > > -
> > > -void ktap_parser_cancel(void)
> > > -{
> > > -	pthread_cancel(ktap_parser_thread);
> > > -}
> > > -
> > > -int ktap_parser_stop(void)
> > > -{
> > > -	pthread_join(ktap_parser_thread, NULL);
> > > -	return ktap_args.ret;
> > > -}
> > > diff --git a/lib/igt_ktap.h b/lib/igt_ktap.h
> > > index 6f8da3eab6..c422636bfc 100644
> > > --- a/lib/igt_ktap.h
> > > +++ b/lib/igt_ktap.h
> > > @@ -27,8 +27,6 @@
> > >  
> > >  #define BUF_LEN 4096
> > >  
> > > -#include <pthread.h>
> > > -
> > >  #include "igt_list.h"
> > >  
> > >  struct igt_ktap_result {
> > > @@ -45,24 +43,4 @@ struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
> > >  int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
> > >  void igt_ktap_free(struct igt_ktap_results *ktap);
> > >  
> > > -void *igt_ktap_parser(void *unused);
> > > -
> > > -typedef struct ktap_test_results_element {
> > > -	char test_name[BUF_LEN + 1];
> > > -	bool passed;
> > > -	struct igt_list_head link;
> > > -} ktap_test_results_element;
> > > -
> > > -struct ktap_test_results {
> > > -	struct igt_list_head list;
> > > -	pthread_mutex_t mutex;
> > > -	bool still_running;
> > > -};
> > > -
> > > -
> > > -
> > > -struct ktap_test_results *ktap_parser_start(int fd, bool is_builtin);
> > > -void ktap_parser_cancel(void);
> > > -int ktap_parser_stop(void);
> > > -
> > >  #endif /* IGT_KTAP_H */
> > 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 77+ messages in thread

* [igt-dev] ✓ Fi.CI.IGT: success for Kunit fixes and improvements (rev3)
  2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
                   ` (20 preceding siblings ...)
  (?)
@ 2023-10-11 13:51 ` Patchwork
  -1 siblings, 0 replies; 77+ messages in thread
From: Patchwork @ 2023-10-11 13:51 UTC (permalink / raw)
  To: Janusz Krzysztofik; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 96742 bytes --]

== Series Details ==

Series: Kunit fixes and improvements (rev3)
URL   : https://patchwork.freedesktop.org/series/124554/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_13735_full -> IGTPW_9965_full
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html

Participating hosts (9 -> 9)
------------------------------

  No changes in participating hosts

New tests
---------

  New tests have been introduced between CI_DRM_13735_full and IGTPW_9965_full:

### New IGT tests (114) ###

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_limit:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_optimistic:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_pathological:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_pessimistic:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_range:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_buddy@drm_buddy@drm_test_buddy_alloc_smoke:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align32:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_align64:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_bottomup:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color_evict:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_color_evict_range:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_debug:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_evict:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_evict_range:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_frag:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_highest:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_init:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_insert:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_insert_range:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_lowest:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_replace:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_reserve:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@drm_mm@drm_mm@drm_test_mm_topdown:
    - Statuses : 7 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_bpp_extra_and_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_extra_and_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_dvi:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_hdmi:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_d_only_not_digital:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_force_e_only:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_freestanding_force_e_and_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_freestanding_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_hmirror:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_margin_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_multiple_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_bpp_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_name_option:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_panel_orientation:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_off:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on_analog:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_force_on_digital:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_interlaced:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_interlaced_margins_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_bpp_refresh_margins:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_margins_force_on:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_rblank:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_refresh:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa_margins:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_res_vesa_rblank:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_0:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_180:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_270:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_rotate_90:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_tv_options:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_cmdline_parser@drm_test_cmdline_vmirror:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_not_visible:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_one_intersect:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_one_outside:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_fractional_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_no_crtc:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_no_fb:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_not_visible:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_no_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_simple_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_fractional_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_intersect_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_intersect_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_outside_fractional_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_outside_src:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_damage_helper@drm_test_damage_iter_single_damage_src_moved:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_dp_mst_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_dp_mst_helper@drm_test_dp_mst_calc_pbn_mode:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_dp_mst_helper@drm_test_dp_mst_sideband_msg_req_decode:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_one_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_three_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_height_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_one_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_three_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_block_width_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_invalid:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_16bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_24bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_32bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_one_plane_8bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_three_plane_8bpp:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_tiled:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_format@drm_test_format_min_pitch_two_plane:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_framebuffer:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_framebuffer@drm_test_framebuffer_create:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_plane_helper:
    - Statuses :
    - Exec time: [None] s

  * igt@kms_selftest@drm_plane_helper@drm_test_check_invalid_plane_state:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  * igt@kms_selftest@drm_plane_helper@drm_test_check_plane_state:
    - Statuses : 8 pass(s)
    - Exec time: [0.0] s

  

Known issues
------------

  Here are the changes found in IGTPW_9965_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@api_intel_bb@blit-reloc-keep-cache:
    - shard-mtlp:         NOTRUN -> [SKIP][1] ([i915#8411])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@api_intel_bb@blit-reloc-keep-cache.html

  * igt@api_intel_bb@render-ccs:
    - shard-dg2:          NOTRUN -> [FAIL][2] ([i915#6122])
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@api_intel_bb@render-ccs.html

  * igt@device_reset@cold-reset-bound:
    - shard-mtlp:         NOTRUN -> [SKIP][3] ([i915#7701])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@device_reset@cold-reset-bound.html

  * igt@drm_fdinfo@busy-hang@rcs0:
    - shard-mtlp:         NOTRUN -> [SKIP][4] ([i915#8414]) +6 other tests skip
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@drm_fdinfo@busy-hang@rcs0.html

  * igt@drm_fdinfo@most-busy-idle-check-all@vecs1:
    - shard-dg2:          NOTRUN -> [SKIP][5] ([i915#8414]) +21 other tests skip
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@drm_fdinfo@most-busy-idle-check-all@vecs1.html

  * igt@gem_ccs@block-multicopy-inplace:
    - shard-mtlp:         NOTRUN -> [SKIP][6] ([i915#3555])
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@gem_ccs@block-multicopy-inplace.html

  * igt@gem_ccs@suspend-resume@tile64-compressed-compfmt0-lmem0-lmem0:
    - shard-dg2:          NOTRUN -> [INCOMPLETE][7] ([i915#7297])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_ccs@suspend-resume@tile64-compressed-compfmt0-lmem0-lmem0.html

  * igt@gem_close_race@multigpu-basic-process:
    - shard-mtlp:         NOTRUN -> [SKIP][8] ([i915#7697])
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@gem_close_race@multigpu-basic-process.html

  * igt@gem_create@create-ext-cpu-access-big:
    - shard-dg2:          NOTRUN -> [ABORT][9] ([i915#7461])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_create@create-ext-cpu-access-big.html
    - shard-rkl:          NOTRUN -> [SKIP][10] ([i915#6335])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-2/igt@gem_create@create-ext-cpu-access-big.html

  * igt@gem_ctx_isolation@preservation-s3@vcs1:
    - shard-dg2:          NOTRUN -> [FAIL][11] ([fdo#103375])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@gem_ctx_isolation@preservation-s3@vcs1.html

  * igt@gem_ctx_persistence@engines-hang@vcs0:
    - shard-mtlp:         NOTRUN -> [FAIL][12] ([i915#2410])
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@gem_ctx_persistence@engines-hang@vcs0.html

  * igt@gem_ctx_persistence@heartbeat-stop:
    - shard-dg2:          NOTRUN -> [SKIP][13] ([i915#8555])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_ctx_persistence@heartbeat-stop.html

  * igt@gem_ctx_persistence@legacy-engines-cleanup:
    - shard-snb:          NOTRUN -> [SKIP][14] ([fdo#109271] / [i915#1099])
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-snb4/igt@gem_ctx_persistence@legacy-engines-cleanup.html

  * igt@gem_exec_balancer@bonded-semaphore:
    - shard-mtlp:         NOTRUN -> [SKIP][15] ([i915#4812])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_exec_balancer@bonded-semaphore.html

  * igt@gem_exec_balancer@invalid-bonds:
    - shard-dg2:          NOTRUN -> [SKIP][16] ([i915#4036])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_exec_balancer@invalid-bonds.html

  * igt@gem_exec_capture@capture-invisible@smem0:
    - shard-glk:          NOTRUN -> [SKIP][17] ([fdo#109271] / [i915#6334])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk5/igt@gem_exec_capture@capture-invisible@smem0.html

  * igt@gem_exec_fair@basic-none-share@rcs0:
    - shard-tglu:         NOTRUN -> [FAIL][18] ([i915#2842])
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@gem_exec_fair@basic-none-share@rcs0.html

  * igt@gem_exec_fair@basic-none-vip:
    - shard-mtlp:         NOTRUN -> [SKIP][19] ([i915#4473] / [i915#4771])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@gem_exec_fair@basic-none-vip.html

  * igt@gem_exec_fair@basic-pace@rcs0:
    - shard-rkl:          [PASS][20] -> [FAIL][21] ([i915#2842]) +2 other tests fail
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-6/igt@gem_exec_fair@basic-pace@rcs0.html
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@gem_exec_fair@basic-pace@rcs0.html

  * igt@gem_exec_fair@basic-throttle:
    - shard-dg2:          NOTRUN -> [SKIP][22] ([i915#3539]) +1 other test skip
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_fair@basic-throttle.html

  * igt@gem_exec_flush@basic-batch-kernel-default-uc:
    - shard-mtlp:         [PASS][23] -> [DMESG-FAIL][24] ([i915#8962])
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-2/igt@gem_exec_flush@basic-batch-kernel-default-uc.html
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@gem_exec_flush@basic-batch-kernel-default-uc.html

  * igt@gem_exec_flush@basic-uc-rw-default:
    - shard-dg2:          NOTRUN -> [SKIP][25] ([i915#3539] / [i915#4852]) +1 other test skip
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@gem_exec_flush@basic-uc-rw-default.html

  * igt@gem_exec_gttfill@multigpu-basic:
    - shard-dg2:          NOTRUN -> [SKIP][26] ([i915#7697])
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_exec_gttfill@multigpu-basic.html
    - shard-rkl:          NOTRUN -> [SKIP][27] ([i915#7697])
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@gem_exec_gttfill@multigpu-basic.html
    - shard-dg1:          NOTRUN -> [SKIP][28] ([i915#7697])
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@gem_exec_gttfill@multigpu-basic.html

  * igt@gem_exec_reloc@basic-cpu-active:
    - shard-mtlp:         NOTRUN -> [SKIP][29] ([i915#3281]) +3 other tests skip
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@gem_exec_reloc@basic-cpu-active.html

  * igt@gem_exec_reloc@basic-gtt-cpu:
    - shard-dg1:          NOTRUN -> [SKIP][30] ([i915#3281]) +2 other tests skip
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@gem_exec_reloc@basic-gtt-cpu.html

  * igt@gem_exec_reloc@basic-gtt-wc-active:
    - shard-rkl:          NOTRUN -> [SKIP][31] ([i915#3281]) +2 other tests skip
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@gem_exec_reloc@basic-gtt-wc-active.html

  * igt@gem_exec_reloc@basic-write-read-active:
    - shard-dg2:          NOTRUN -> [SKIP][32] ([i915#3281]) +11 other tests skip
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@gem_exec_reloc@basic-write-read-active.html

  * igt@gem_exec_schedule@preempt-queue-contexts:
    - shard-dg2:          NOTRUN -> [SKIP][33] ([i915#4537] / [i915#4812])
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_schedule@preempt-queue-contexts.html

  * igt@gem_exec_schedule@semaphore-power:
    - shard-mtlp:         NOTRUN -> [SKIP][34] ([i915#4537] / [i915#4812]) +1 other test skip
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_exec_schedule@semaphore-power.html

  * igt@gem_exec_suspend@basic-s4-devices@lmem0:
    - shard-dg2:          NOTRUN -> [ABORT][35] ([i915#7975] / [i915#8213])
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_suspend@basic-s4-devices@lmem0.html

  * igt@gem_fence_thrash@bo-write-verify-threaded-none:
    - shard-mtlp:         NOTRUN -> [SKIP][36] ([i915#4860])
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_fence_thrash@bo-write-verify-threaded-none.html

  * igt@gem_fence_thrash@bo-write-verify-y:
    - shard-dg2:          NOTRUN -> [SKIP][37] ([i915#4860])
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@gem_fence_thrash@bo-write-verify-y.html
    - shard-dg1:          NOTRUN -> [SKIP][38] ([i915#4860])
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@gem_fence_thrash@bo-write-verify-y.html

  * igt@gem_lmem_swapping@basic:
    - shard-apl:          NOTRUN -> [SKIP][39] ([fdo#109271] / [i915#4613])
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl6/igt@gem_lmem_swapping@basic.html

  * igt@gem_lmem_swapping@parallel-random:
    - shard-glk:          NOTRUN -> [SKIP][40] ([fdo#109271] / [i915#4613])
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk9/igt@gem_lmem_swapping@parallel-random.html

  * igt@gem_lmem_swapping@parallel-random-verify-ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][41] ([i915#4613]) +2 other tests skip
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@gem_lmem_swapping@parallel-random-verify-ccs.html

  * igt@gem_lmem_swapping@smem-oom@lmem0:
    - shard-dg2:          [PASS][42] -> [DMESG-WARN][43] ([i915#4936] / [i915#5493])
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-2/igt@gem_lmem_swapping@smem-oom@lmem0.html
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_lmem_swapping@smem-oom@lmem0.html

  * igt@gem_media_fill@media-fill:
    - shard-dg2:          NOTRUN -> [SKIP][44] ([i915#8289])
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_media_fill@media-fill.html

  * igt@gem_mmap@short-mmap:
    - shard-dg2:          NOTRUN -> [SKIP][45] ([i915#4083]) +5 other tests skip
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_mmap@short-mmap.html

  * igt@gem_mmap_gtt@cpuset-medium-copy-xy:
    - shard-dg2:          NOTRUN -> [SKIP][46] ([i915#4077]) +11 other tests skip
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@gem_mmap_gtt@cpuset-medium-copy-xy.html

  * igt@gem_mmap_wc@bad-offset:
    - shard-mtlp:         NOTRUN -> [SKIP][47] ([i915#4083])
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gem_mmap_wc@bad-offset.html

  * igt@gem_partial_pwrite_pread@reads:
    - shard-dg2:          NOTRUN -> [SKIP][48] ([i915#3282]) +7 other tests skip
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_partial_pwrite_pread@reads.html

  * igt@gem_partial_pwrite_pread@write-snoop:
    - shard-rkl:          NOTRUN -> [SKIP][49] ([i915#3282]) +1 other test skip
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-2/igt@gem_partial_pwrite_pread@write-snoop.html

  * igt@gem_pxp@create-regular-buffer:
    - shard-mtlp:         NOTRUN -> [SKIP][50] ([i915#4270])
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@gem_pxp@create-regular-buffer.html

  * igt@gem_pxp@create-regular-context-1:
    - shard-dg1:          NOTRUN -> [SKIP][51] ([i915#4270])
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-15/igt@gem_pxp@create-regular-context-1.html

  * igt@gem_pxp@dmabuf-shared-protected-dst-is-context-refcounted:
    - shard-dg2:          NOTRUN -> [SKIP][52] ([i915#4270]) +2 other tests skip
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gem_pxp@dmabuf-shared-protected-dst-is-context-refcounted.html

  * igt@gem_pxp@reject-modify-context-protection-off-2:
    - shard-rkl:          NOTRUN -> [SKIP][53] ([i915#4270]) +1 other test skip
   [53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@gem_pxp@reject-modify-context-protection-off-2.html

  * igt@gem_render_copy@x-tiled-to-vebox-yf-tiled:
    - shard-mtlp:         NOTRUN -> [SKIP][54] ([i915#8428]) +2 other tests skip
   [54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@gem_render_copy@x-tiled-to-vebox-yf-tiled.html

  * igt@gem_set_tiling_vs_blt@untiled-to-tiled:
    - shard-rkl:          NOTRUN -> [SKIP][55] ([i915#8411])
   [55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@gem_set_tiling_vs_blt@untiled-to-tiled.html

  * igt@gem_softpin@evict-snoop:
    - shard-dg2:          NOTRUN -> [SKIP][56] ([i915#4885])
   [56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_softpin@evict-snoop.html

  * igt@gem_softpin@evict-snoop-interruptible:
    - shard-mtlp:         NOTRUN -> [SKIP][57] ([i915#4885])
   [57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@gem_softpin@evict-snoop-interruptible.html

  * igt@gem_spin_batch@spin-all-new:
    - shard-dg2:          NOTRUN -> [FAIL][58] ([i915#5889])
   [58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@gem_spin_batch@spin-all-new.html

  * igt@gem_tiled_pread_pwrite:
    - shard-dg2:          NOTRUN -> [SKIP][59] ([i915#4079]) +1 other test skip
   [59]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@gem_tiled_pread_pwrite.html

  * igt@gem_userptr_blits@access-control:
    - shard-mtlp:         NOTRUN -> [SKIP][60] ([i915#3297])
   [60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@gem_userptr_blits@access-control.html

  * igt@gem_userptr_blits@dmabuf-unsync:
    - shard-dg2:          NOTRUN -> [SKIP][61] ([i915#3297]) +2 other tests skip
   [61]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@gem_userptr_blits@dmabuf-unsync.html

  * igt@gem_userptr_blits@nohangcheck:
    - shard-mtlp:         [PASS][62] -> [FAIL][63] ([i915#9353])
   [62]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-6/igt@gem_userptr_blits@nohangcheck.html
   [63]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@gem_userptr_blits@nohangcheck.html

  * igt@gem_userptr_blits@readonly-unsync:
    - shard-rkl:          NOTRUN -> [SKIP][64] ([i915#3297])
   [64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@gem_userptr_blits@readonly-unsync.html

  * igt@gem_workarounds@suspend-resume-context:
    - shard-dg2:          [PASS][65] -> [FAIL][66] ([fdo#103375]) +1 other test fail
   [65]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-6/igt@gem_workarounds@suspend-resume-context.html
   [66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@gem_workarounds@suspend-resume-context.html

  * igt@gen3_render_mixed_blits:
    - shard-rkl:          NOTRUN -> [SKIP][67] ([fdo#109289]) +2 other tests skip
   [67]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@gen3_render_mixed_blits.html

  * igt@gen7_exec_parse@basic-allocation:
    - shard-dg1:          NOTRUN -> [SKIP][68] ([fdo#109289])
   [68]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-15/igt@gen7_exec_parse@basic-allocation.html

  * igt@gen9_exec_parse@batch-invalid-length:
    - shard-rkl:          NOTRUN -> [SKIP][69] ([i915#2527])
   [69]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@gen9_exec_parse@batch-invalid-length.html
    - shard-dg1:          NOTRUN -> [SKIP][70] ([i915#2527])
   [70]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@gen9_exec_parse@batch-invalid-length.html

  * igt@gen9_exec_parse@bb-large:
    - shard-mtlp:         NOTRUN -> [SKIP][71] ([i915#2856]) +1 other test skip
   [71]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@gen9_exec_parse@bb-large.html

  * igt@gen9_exec_parse@valid-registers:
    - shard-dg2:          NOTRUN -> [SKIP][72] ([i915#2856]) +3 other tests skip
   [72]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@gen9_exec_parse@valid-registers.html

  * igt@i915_fb_tiling:
    - shard-dg2:          NOTRUN -> [SKIP][73] ([i915#4881])
   [73]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@i915_fb_tiling.html

  * igt@i915_hangman@detector@vcs0:
    - shard-mtlp:         [PASS][74] -> [FAIL][75] ([i915#8456]) +2 other tests fail
   [74]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-3/igt@i915_hangman@detector@vcs0.html
   [75]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@i915_hangman@detector@vcs0.html

  * igt@i915_hangman@engine-engine-error@vcs0:
    - shard-mtlp:         [PASS][76] -> [FAIL][77] ([i915#7069])
   [76]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-6/igt@i915_hangman@engine-engine-error@vcs0.html
   [77]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@i915_hangman@engine-engine-error@vcs0.html

  * igt@i915_module_load@load:
    - shard-dg2:          NOTRUN -> [SKIP][78] ([i915#6227])
   [78]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@i915_module_load@load.html

  * igt@i915_pm_rc6_residency@media-rc6-accuracy:
    - shard-tglu:         NOTRUN -> [SKIP][79] ([fdo#109289])
   [79]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@i915_pm_rc6_residency@media-rc6-accuracy.html

  * igt@i915_pm_rpm@dpms-non-lpsp:
    - shard-rkl:          [PASS][80] -> [SKIP][81] ([i915#1397]) +1 other test skip
   [80]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-3/igt@i915_pm_rpm@dpms-non-lpsp.html
   [81]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@i915_pm_rpm@dpms-non-lpsp.html

  * igt@i915_pm_rpm@gem-execbuf-stress-pc8:
    - shard-dg2:          NOTRUN -> [SKIP][82] ([fdo#109506])
   [82]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@i915_pm_rpm@gem-execbuf-stress-pc8.html

  * igt@i915_pm_rpm@modeset-lpsp-stress:
    - shard-dg2:          NOTRUN -> [SKIP][83] ([i915#1397])
   [83]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@i915_pm_rpm@modeset-lpsp-stress.html

  * igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait:
    - shard-dg1:          [PASS][84] -> [SKIP][85] ([i915#1397])
   [84]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-15/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html
   [85]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html

  * igt@i915_pm_rps@min-max-config-idle:
    - shard-dg2:          NOTRUN -> [SKIP][86] ([i915#6621])
   [86]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@i915_pm_rps@min-max-config-idle.html

  * igt@i915_pm_rps@thresholds-idle@gt0:
    - shard-dg2:          NOTRUN -> [SKIP][87] ([i915#8925])
   [87]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@i915_pm_rps@thresholds-idle@gt0.html
    - shard-dg1:          NOTRUN -> [SKIP][88] ([i915#8925])
   [88]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@i915_pm_rps@thresholds-idle@gt0.html

  * igt@i915_pm_rps@thresholds@gt1:
    - shard-mtlp:         NOTRUN -> [SKIP][89] ([i915#8925]) +1 other test skip
   [89]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-8/igt@i915_pm_rps@thresholds@gt1.html

  * igt@i915_selftest@live@requests:
    - shard-mtlp:         [PASS][90] -> [ABORT][91] ([i915#9414])
   [90]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-7/igt@i915_selftest@live@requests.html
   [91]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@i915_selftest@live@requests.html

  * igt@i915_suspend@fence-restore-tiled2untiled:
    - shard-mtlp:         NOTRUN -> [SKIP][92] ([i915#4077]) +4 other tests skip
   [92]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@i915_suspend@fence-restore-tiled2untiled.html

  * igt@kms_addfb_basic@basic-x-tiled-legacy:
    - shard-dg2:          NOTRUN -> [SKIP][93] ([i915#4212])
   [93]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_addfb_basic@basic-x-tiled-legacy.html

  * igt@kms_addfb_basic@clobberred-modifier:
    - shard-mtlp:         NOTRUN -> [SKIP][94] ([i915#4212])
   [94]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@kms_addfb_basic@clobberred-modifier.html

  * igt@kms_addfb_basic@invalid-smem-bo-on-discrete:
    - shard-rkl:          NOTRUN -> [SKIP][95] ([i915#3826])
   [95]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_addfb_basic@invalid-smem-bo-on-discrete.html

  * igt@kms_addfb_basic@tile-pitch-mismatch:
    - shard-dg2:          NOTRUN -> [SKIP][96] ([i915#4212] / [i915#5608])
   [96]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_addfb_basic@tile-pitch-mismatch.html

  * igt@kms_async_flips@async-flip-with-page-flip-events@pipe-a-hdmi-a-3-4-mc_ccs:
    - shard-dg2:          NOTRUN -> [SKIP][97] ([i915#8502] / [i915#8709]) +11 other tests skip
   [97]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_async_flips@async-flip-with-page-flip-events@pipe-a-hdmi-a-3-4-mc_ccs.html

  * igt@kms_async_flips@crc@pipe-b-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [FAIL][98] ([i915#8247]) +3 other tests fail
   [98]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_async_flips@crc@pipe-b-hdmi-a-3.html

  * igt@kms_atomic@plane-primary-overlay-mutable-zpos:
    - shard-dg2:          NOTRUN -> [SKIP][99] ([i915#404])
   [99]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_atomic@plane-primary-overlay-mutable-zpos.html

  * igt@kms_atomic_transition@plane-all-modeset-transition:
    - shard-mtlp:         NOTRUN -> [SKIP][100] ([i915#1769] / [i915#3555])
   [100]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@kms_atomic_transition@plane-all-modeset-transition.html

  * igt@kms_atomic_transition@plane-all-modeset-transition-internal-panels:
    - shard-dg2:          NOTRUN -> [SKIP][101] ([i915#1769] / [i915#3555])
   [101]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_atomic_transition@plane-all-modeset-transition-internal-panels.html

  * igt@kms_big_fb@4-tiled-16bpp-rotate-90:
    - shard-dg1:          NOTRUN -> [SKIP][102] ([i915#4538] / [i915#5286])
   [102]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@kms_big_fb@4-tiled-16bpp-rotate-90.html

  * igt@kms_big_fb@4-tiled-32bpp-rotate-0:
    - shard-rkl:          NOTRUN -> [SKIP][103] ([i915#5286]) +1 other test skip
   [103]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_big_fb@4-tiled-32bpp-rotate-0.html

  * igt@kms_big_fb@4-tiled-64bpp-rotate-0:
    - shard-tglu:         NOTRUN -> [SKIP][104] ([fdo#111615] / [i915#5286])
   [104]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-7/igt@kms_big_fb@4-tiled-64bpp-rotate-0.html

  * igt@kms_big_fb@linear-16bpp-rotate-90:
    - shard-dg1:          NOTRUN -> [SKIP][105] ([i915#3638])
   [105]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@kms_big_fb@linear-16bpp-rotate-90.html

  * igt@kms_big_fb@linear-32bpp-rotate-270:
    - shard-rkl:          NOTRUN -> [SKIP][106] ([fdo#111614] / [i915#3638])
   [106]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_big_fb@linear-32bpp-rotate-270.html

  * igt@kms_big_fb@x-tiled-16bpp-rotate-90:
    - shard-dg2:          NOTRUN -> [SKIP][107] ([fdo#111614]) +2 other tests skip
   [107]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_big_fb@x-tiled-16bpp-rotate-90.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip:
    - shard-mtlp:         NOTRUN -> [SKIP][108] ([fdo#111615]) +5 other tests skip
   [108]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip:
    - shard-tglu:         [PASS][109] -> [FAIL][110] ([i915#3743])
   [109]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-tglu-9/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html
   [110]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-9/igt@kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-async-flip:
    - shard-dg2:          NOTRUN -> [SKIP][111] ([i915#5190]) +18 other tests skip
   [111]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-async-flip.html

  * igt@kms_big_fb@yf-tiled-8bpp-rotate-90:
    - shard-rkl:          NOTRUN -> [SKIP][112] ([fdo#110723])
   [112]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_big_fb@yf-tiled-8bpp-rotate-90.html
    - shard-dg1:          NOTRUN -> [SKIP][113] ([i915#4538])
   [113]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@kms_big_fb@yf-tiled-8bpp-rotate-90.html

  * igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0:
    - shard-dg2:          NOTRUN -> [SKIP][114] ([i915#4538] / [i915#5190]) +7 other tests skip
   [114]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0.html

  * igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180-async-flip:
    - shard-tglu:         NOTRUN -> [SKIP][115] ([fdo#111615])
   [115]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180-async-flip.html

  * igt@kms_ccs@pipe-a-ccs-on-another-bo-y_tiled_gen12_mc_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][116] ([i915#3689] / [i915#3886] / [i915#5354] / [i915#6095])
   [116]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-4/igt@kms_ccs@pipe-a-ccs-on-another-bo-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-b-bad-pixel-format-y_tiled_gen12_mc_ccs:
    - shard-rkl:          NOTRUN -> [SKIP][117] ([i915#3886] / [i915#5354] / [i915#6095])
   [117]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@kms_ccs@pipe-b-bad-pixel-format-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-b-ccs-on-another-bo-y_tiled_gen12_rc_ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][118] ([i915#5354] / [i915#6095]) +16 other tests skip
   [118]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_ccs@pipe-b-ccs-on-another-bo-y_tiled_gen12_rc_ccs.html

  * igt@kms_ccs@pipe-b-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc:
    - shard-glk:          NOTRUN -> [SKIP][119] ([fdo#109271] / [i915#3886])
   [119]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk2/igt@kms_ccs@pipe-b-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc:
    - shard-rkl:          NOTRUN -> [SKIP][120] ([i915#5354] / [i915#6095]) +4 other tests skip
   [120]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_ccs@pipe-b-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs:
    - shard-mtlp:         NOTRUN -> [SKIP][121] ([i915#3886] / [i915#5354] / [i915#6095]) +5 other tests skip
   [121]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc:
    - shard-dg2:          NOTRUN -> [SKIP][122] ([i915#3689] / [i915#3886] / [i915#5354]) +12 other tests skip
   [122]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_ccs@pipe-b-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-missing-ccs-buffer-4_tiled_mtl_rc_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][123] ([i915#5354] / [i915#6095])
   [123]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@kms_ccs@pipe-b-missing-ccs-buffer-4_tiled_mtl_rc_ccs.html

  * igt@kms_ccs@pipe-b-random-ccs-data-4_tiled_dg2_rc_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][124] ([i915#3689] / [i915#5354] / [i915#6095]) +1 other test skip
   [124]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-10/igt@kms_ccs@pipe-b-random-ccs-data-4_tiled_dg2_rc_ccs.html

  * igt@kms_ccs@pipe-c-bad-rotation-90-y_tiled_gen12_mc_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][125] ([i915#3689] / [i915#3886] / [i915#5354] / [i915#6095])
   [125]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_ccs@pipe-c-bad-rotation-90-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-c-crc-primary-basic-4_tiled_mtl_rc_ccs:
    - shard-rkl:          NOTRUN -> [SKIP][126] ([i915#5354]) +7 other tests skip
   [126]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_ccs@pipe-c-crc-primary-basic-4_tiled_mtl_rc_ccs.html

  * igt@kms_ccs@pipe-c-crc-primary-basic-y_tiled_gen12_mc_ccs:
    - shard-apl:          NOTRUN -> [SKIP][127] ([fdo#109271] / [i915#3886]) +3 other tests skip
   [127]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl1/igt@kms_ccs@pipe-c-crc-primary-basic-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-c-crc-sprite-planes-basic-y_tiled_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][128] ([i915#3689] / [i915#5354] / [i915#6095]) +1 other test skip
   [128]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_ccs@pipe-c-crc-sprite-planes-basic-y_tiled_ccs.html

  * igt@kms_ccs@pipe-d-bad-pixel-format-yf_tiled_ccs:
    - shard-tglu:         NOTRUN -> [SKIP][129] ([fdo#111615] / [i915#3689] / [i915#5354] / [i915#6095])
   [129]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-4/igt@kms_ccs@pipe-d-bad-pixel-format-yf_tiled_ccs.html

  * igt@kms_ccs@pipe-d-crc-primary-basic-y_tiled_gen12_rc_ccs:
    - shard-dg2:          NOTRUN -> [SKIP][130] ([i915#3689] / [i915#5354]) +25 other tests skip
   [130]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_ccs@pipe-d-crc-primary-basic-y_tiled_gen12_rc_ccs.html

  * igt@kms_ccs@pipe-d-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs:
    - shard-dg1:          NOTRUN -> [SKIP][131] ([i915#5354] / [i915#6095]) +4 other tests skip
   [131]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@kms_ccs@pipe-d-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs.html

  * igt@kms_cdclk@plane-scaling@pipe-c-hdmi-a-3:
    - shard-dg2:          NOTRUN -> [SKIP][132] ([i915#4087]) +3 other tests skip
   [132]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_cdclk@plane-scaling@pipe-c-hdmi-a-3.html

  * igt@kms_chamelium_audio@hdmi-audio-edid:
    - shard-tglu:         NOTRUN -> [SKIP][133] ([i915#7828]) +1 other test skip
   [133]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@kms_chamelium_audio@hdmi-audio-edid.html

  * igt@kms_chamelium_color@ctm-0-75:
    - shard-mtlp:         NOTRUN -> [SKIP][134] ([fdo#111827])
   [134]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@kms_chamelium_color@ctm-0-75.html

  * igt@kms_chamelium_color@gamma:
    - shard-dg2:          NOTRUN -> [SKIP][135] ([fdo#111827]) +1 other test skip
   [135]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_chamelium_color@gamma.html

  * igt@kms_chamelium_edid@hdmi-edid-stress-resolution-non-4k:
    - shard-dg2:          NOTRUN -> [SKIP][136] ([i915#7828]) +13 other tests skip
   [136]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@kms_chamelium_edid@hdmi-edid-stress-resolution-non-4k.html

  * igt@kms_chamelium_hpd@dp-hpd-for-each-pipe:
    - shard-mtlp:         NOTRUN -> [SKIP][137] ([i915#7828]) +3 other tests skip
   [137]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_chamelium_hpd@dp-hpd-for-each-pipe.html

  * igt@kms_chamelium_hpd@vga-hpd-fast:
    - shard-rkl:          NOTRUN -> [SKIP][138] ([i915#7828]) +3 other tests skip
   [138]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_chamelium_hpd@vga-hpd-fast.html
    - shard-dg1:          NOTRUN -> [SKIP][139] ([i915#7828]) +3 other tests skip
   [139]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@kms_chamelium_hpd@vga-hpd-fast.html

  * igt@kms_content_protection@dp-mst-lic-type-0:
    - shard-dg2:          NOTRUN -> [SKIP][140] ([i915#3299])
   [140]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_content_protection@dp-mst-lic-type-0.html

  * igt@kms_content_protection@legacy:
    - shard-mtlp:         NOTRUN -> [SKIP][141] ([i915#6944])
   [141]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-8/igt@kms_content_protection@legacy.html

  * igt@kms_content_protection@lic:
    - shard-rkl:          NOTRUN -> [SKIP][142] ([i915#7118])
   [142]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_content_protection@lic.html

  * igt@kms_content_protection@srm:
    - shard-dg2:          NOTRUN -> [SKIP][143] ([i915#7118])
   [143]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_content_protection@srm.html

  * igt@kms_content_protection@srm@pipe-a-dp-1:
    - shard-apl:          NOTRUN -> [TIMEOUT][144] ([i915#7173])
   [144]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl6/igt@kms_content_protection@srm@pipe-a-dp-1.html

  * igt@kms_cursor_crc@cursor-offscreen-512x512:
    - shard-mtlp:         NOTRUN -> [SKIP][145] ([i915#3359])
   [145]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@kms_cursor_crc@cursor-offscreen-512x512.html

  * igt@kms_cursor_crc@cursor-onscreen-512x170:
    - shard-dg2:          NOTRUN -> [SKIP][146] ([i915#3359])
   [146]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_cursor_crc@cursor-onscreen-512x170.html

  * igt@kms_cursor_crc@cursor-sliding-32x10:
    - shard-dg2:          NOTRUN -> [SKIP][147] ([i915#3555]) +8 other tests skip
   [147]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_cursor_crc@cursor-sliding-32x10.html
    - shard-rkl:          NOTRUN -> [SKIP][148] ([i915#3555]) +4 other tests skip
   [148]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_cursor_crc@cursor-sliding-32x10.html
    - shard-dg1:          NOTRUN -> [SKIP][149] ([i915#3555]) +2 other tests skip
   [149]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@kms_cursor_crc@cursor-sliding-32x10.html

  * igt@kms_cursor_legacy@2x-flip-vs-cursor-atomic:
    - shard-dg2:          NOTRUN -> [SKIP][150] ([fdo#109274] / [fdo#111767] / [i915#5354]) +1 other test skip
   [150]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_cursor_legacy@2x-flip-vs-cursor-atomic.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic:
    - shard-mtlp:         NOTRUN -> [SKIP][151] ([i915#4213])
   [151]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-8/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic.html

  * igt@kms_cursor_legacy@cursora-vs-flipb-varying-size:
    - shard-mtlp:         NOTRUN -> [SKIP][152] ([i915#3546]) +2 other tests skip
   [152]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_cursor_legacy@cursora-vs-flipb-varying-size.html

  * igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions:
    - shard-dg2:          NOTRUN -> [SKIP][153] ([fdo#109274] / [i915#5354]) +7 other tests skip
   [153]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions.html

  * igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions-varying-size:
    - shard-apl:          NOTRUN -> [SKIP][154] ([fdo#109271]) +84 other tests skip
   [154]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl2/igt@kms_cursor_legacy@cursorb-vs-flipa-atomic-transitions-varying-size.html

  * igt@kms_cursor_legacy@cursorb-vs-flipb-atomic-transitions:
    - shard-mtlp:         NOTRUN -> [SKIP][155] ([fdo#111767] / [i915#3546])
   [155]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_cursor_legacy@cursorb-vs-flipb-atomic-transitions.html

  * igt@kms_cursor_legacy@cursorb-vs-flipb-varying-size:
    - shard-tglu:         NOTRUN -> [SKIP][156] ([fdo#109274])
   [156]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@kms_cursor_legacy@cursorb-vs-flipb-varying-size.html

  * igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions:
    - shard-glk:          [PASS][157] -> [FAIL][158] ([i915#2346])
   [157]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk3/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions.html
   [158]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk4/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions.html

  * igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions:
    - shard-dg2:          NOTRUN -> [SKIP][159] ([i915#4103] / [i915#4213]) +1 other test skip
   [159]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions.html
    - shard-rkl:          NOTRUN -> [SKIP][160] ([i915#4103])
   [160]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions.html
    - shard-dg1:          NOTRUN -> [SKIP][161] ([i915#4103] / [i915#4213])
   [161]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions.html

  * igt@kms_dirtyfb@dirtyfb-ioctl@drrs-hdmi-a-2:
    - shard-dg2:          NOTRUN -> [SKIP][162] ([i915#9226] / [i915#9261]) +1 other test skip
   [162]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_dirtyfb@dirtyfb-ioctl@drrs-hdmi-a-2.html

  * igt@kms_dirtyfb@dirtyfb-ioctl@fbc-hdmi-a-2:
    - shard-dg2:          NOTRUN -> [SKIP][163] ([i915#9227])
   [163]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_dirtyfb@dirtyfb-ioctl@fbc-hdmi-a-2.html

  * igt@kms_dither@fb-8bpc-vs-panel-6bpc@pipe-a-hdmi-a-1:
    - shard-rkl:          NOTRUN -> [SKIP][164] ([i915#3804])
   [164]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_dither@fb-8bpc-vs-panel-6bpc@pipe-a-hdmi-a-1.html

  * igt@kms_draw_crc@draw-method-mmap-wc:
    - shard-dg2:          NOTRUN -> [SKIP][165] ([i915#8812]) +1 other test skip
   [165]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_draw_crc@draw-method-mmap-wc.html

  * igt@kms_dsc@dsc-with-output-formats:
    - shard-dg2:          NOTRUN -> [SKIP][166] ([i915#3555] / [i915#3840]) +1 other test skip
   [166]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_dsc@dsc-with-output-formats.html

  * igt@kms_fbcon_fbt@fbc-suspend:
    - shard-dg2:          [PASS][167] -> [FAIL][168] ([i915#4767])
   [167]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-3/igt@kms_fbcon_fbt@fbc-suspend.html
   [168]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_fbcon_fbt@fbc-suspend.html

  * igt@kms_fbcon_fbt@psr-suspend:
    - shard-tglu:         NOTRUN -> [SKIP][169] ([i915#3469])
   [169]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@kms_fbcon_fbt@psr-suspend.html

  * igt@kms_fence_pin_leak:
    - shard-mtlp:         NOTRUN -> [SKIP][170] ([i915#4881])
   [170]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_fence_pin_leak.html

  * igt@kms_flip@2x-blocking-absolute-wf_vblank:
    - shard-mtlp:         NOTRUN -> [SKIP][171] ([i915#3637]) +3 other tests skip
   [171]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_flip@2x-blocking-absolute-wf_vblank.html

  * igt@kms_flip@2x-flip-vs-blocking-wf-vblank:
    - shard-mtlp:         NOTRUN -> [SKIP][172] ([fdo#111767] / [i915#3637])
   [172]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_flip@2x-flip-vs-blocking-wf-vblank.html

  * igt@kms_flip@2x-flip-vs-expired-vblank-interruptible:
    - shard-rkl:          NOTRUN -> [SKIP][173] ([fdo#111767] / [fdo#111825])
   [173]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible.html

  * igt@kms_flip@2x-flip-vs-fences:
    - shard-dg2:          NOTRUN -> [SKIP][174] ([i915#8381]) +1 other test skip
   [174]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_flip@2x-flip-vs-fences.html

  * igt@kms_flip@2x-plain-flip:
    - shard-tglu:         NOTRUN -> [SKIP][175] ([fdo#109274] / [i915#3637])
   [175]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-7/igt@kms_flip@2x-plain-flip.html

  * igt@kms_flip@2x-plain-flip-fb-recreate-interruptible:
    - shard-dg1:          NOTRUN -> [SKIP][176] ([fdo#111825]) +4 other tests skip
   [176]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@kms_flip@2x-plain-flip-fb-recreate-interruptible.html

  * igt@kms_flip@2x-wf_vblank-ts-check:
    - shard-dg2:          NOTRUN -> [SKIP][177] ([fdo#109274]) +9 other tests skip
   [177]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_flip@2x-wf_vblank-ts-check.html

  * igt@kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytileccs-downscaling@pipe-a-default-mode:
    - shard-mtlp:         NOTRUN -> [SKIP][178] ([i915#2672] / [i915#3555])
   [178]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytileccs-downscaling@pipe-a-default-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tile-downscaling@pipe-a-default-mode:
    - shard-mtlp:         NOTRUN -> [SKIP][179] ([i915#8810])
   [179]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tile-downscaling@pipe-a-default-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode:
    - shard-rkl:          NOTRUN -> [SKIP][180] ([i915#2672])
   [180]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode.html
    - shard-dg1:          NOTRUN -> [SKIP][181] ([i915#2587] / [i915#2672])
   [181]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-15/igt@kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling@pipe-a-valid-mode:
    - shard-dg2:          NOTRUN -> [SKIP][182] ([i915#2672]) +3 other tests skip
   [182]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling@pipe-a-valid-mode.html

  * igt@kms_force_connector_basic@force-load-detect:
    - shard-mtlp:         NOTRUN -> [SKIP][183] ([fdo#109285])
   [183]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@kms_force_connector_basic@force-load-detect.html

  * igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-mmap-gtt:
    - shard-dg2:          NOTRUN -> [SKIP][184] ([i915#8708]) +29 other tests skip
   [184]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-pwrite:
    - shard-mtlp:         NOTRUN -> [SKIP][185] ([i915#1825]) +21 other tests skip
   [185]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-pwrite.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-gtt:
    - shard-rkl:          NOTRUN -> [SKIP][186] ([fdo#111825] / [i915#1825]) +7 other tests skip
   [186]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-gtt.html
    - shard-dg1:          NOTRUN -> [SKIP][187] ([i915#8708]) +3 other tests skip
   [187]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-19/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@fbc-2p-shrfb-fliptrack-mmap-gtt:
    - shard-rkl:          NOTRUN -> [SKIP][188] ([fdo#111825]) +4 other tests skip
   [188]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_frontbuffer_tracking@fbc-2p-shrfb-fliptrack-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@fbc-rgb565-draw-pwrite:
    - shard-dg2:          [PASS][189] -> [FAIL][190] ([i915#6880])
   [189]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-3/igt@kms_frontbuffer_tracking@fbc-rgb565-draw-pwrite.html
   [190]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_frontbuffer_tracking@fbc-rgb565-draw-pwrite.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-cpu:
    - shard-dg2:          NOTRUN -> [SKIP][191] ([i915#3458]) +16 other tests skip
   [191]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-3/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-cpu.html

  * igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-render:
    - shard-dg2:          NOTRUN -> [SKIP][192] ([i915#5354]) +56 other tests skip
   [192]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-render.html

  * igt@kms_frontbuffer_tracking@fbcpsr-tiling-y:
    - shard-tglu:         NOTRUN -> [SKIP][193] ([fdo#110189]) +2 other tests skip
   [193]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-7/igt@kms_frontbuffer_tracking@fbcpsr-tiling-y.html

  * igt@kms_frontbuffer_tracking@psr-1p-primscrn-shrfb-msflip-blt:
    - shard-dg1:          NOTRUN -> [SKIP][194] ([i915#3458]) +4 other tests skip
   [194]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@kms_frontbuffer_tracking@psr-1p-primscrn-shrfb-msflip-blt.html

  * igt@kms_frontbuffer_tracking@psr-2p-scndscrn-indfb-msflip-blt:
    - shard-tglu:         NOTRUN -> [SKIP][195] ([fdo#109280]) +5 other tests skip
   [195]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-6/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-indfb-msflip-blt.html

  * igt@kms_frontbuffer_tracking@psr-rgb565-draw-mmap-gtt:
    - shard-mtlp:         NOTRUN -> [SKIP][196] ([i915#8708]) +3 other tests skip
   [196]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_frontbuffer_tracking@psr-rgb565-draw-mmap-gtt.html

  * igt@kms_frontbuffer_tracking@psr-suspend:
    - shard-rkl:          NOTRUN -> [SKIP][197] ([i915#3023]) +5 other tests skip
   [197]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_frontbuffer_tracking@psr-suspend.html

  * igt@kms_hdr@bpc-switch:
    - shard-rkl:          NOTRUN -> [SKIP][198] ([i915#3555] / [i915#8228]) +1 other test skip
   [198]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_hdr@bpc-switch.html

  * igt@kms_hdr@static-toggle:
    - shard-dg2:          NOTRUN -> [SKIP][199] ([i915#3555] / [i915#8228]) +1 other test skip
   [199]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_hdr@static-toggle.html

  * igt@kms_multipipe_modeset@basic-max-pipe-crc-check:
    - shard-dg2:          NOTRUN -> [SKIP][200] ([i915#4816])
   [200]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@kms_multipipe_modeset@basic-max-pipe-crc-check.html

  * igt@kms_pipe_b_c_ivb@from-pipe-c-to-b-with-3-lanes:
    - shard-dg2:          NOTRUN -> [SKIP][201] ([fdo#109289]) +5 other tests skip
   [201]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_pipe_b_c_ivb@from-pipe-c-to-b-with-3-lanes.html

  * igt@kms_pipe_b_c_ivb@pipe-b-double-modeset-then-modeset-pipe-c:
    - shard-mtlp:         NOTRUN -> [SKIP][202] ([fdo#109289])
   [202]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_pipe_b_c_ivb@pipe-b-double-modeset-then-modeset-pipe-c.html

  * igt@kms_plane_alpha_blend@constant-alpha-max@pipe-c-hdmi-a-1:
    - shard-glk:          NOTRUN -> [FAIL][203] ([i915#4573]) +1 other test fail
   [203]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk2/igt@kms_plane_alpha_blend@constant-alpha-max@pipe-c-hdmi-a-1.html

  * igt@kms_plane_lowres@tiling-yf:
    - shard-mtlp:         NOTRUN -> [SKIP][204] ([i915#3555] / [i915#8821])
   [204]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-6/igt@kms_plane_lowres@tiling-yf.html

  * igt@kms_plane_scaling@intel-max-src-size:
    - shard-mtlp:         NOTRUN -> [SKIP][205] ([i915#6953])
   [205]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_plane_scaling@intel-max-src-size.html

  * igt@kms_plane_scaling@intel-max-src-size@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [FAIL][206] ([i915#8292])
   [206]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_plane_scaling@intel-max-src-size@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [SKIP][207] ([i915#5176] / [i915#9423]) +3 other tests skip
   [207]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-b-hdmi-a-1:
    - shard-tglu:         NOTRUN -> [SKIP][208] ([i915#5176] / [i915#9423]) +3 other tests skip
   [208]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-9/igt@kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation@pipe-b-hdmi-a-1.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-a-vga-1:
    - shard-snb:          NOTRUN -> [SKIP][209] ([fdo#109271]) +32 other tests skip
   [209]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-snb4/igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-a-vga-1.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-d-hdmi-a-3:
    - shard-dg2:          NOTRUN -> [SKIP][210] ([i915#5235]) +11 other tests skip
   [210]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25@pipe-d-hdmi-a-3.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-25@pipe-c-hdmi-a-1:
    - shard-tglu:         NOTRUN -> [SKIP][211] ([i915#5235]) +3 other tests skip
   [211]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-6/igt@kms_plane_scaling@planes-downscale-factor-0-25@pipe-c-hdmi-a-1.html

  * igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-a-hdmi-a-3:
    - shard-dg1:          NOTRUN -> [SKIP][212] ([i915#5235]) +7 other tests skip
   [212]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-a-hdmi-a-3.html

  * igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-b-edp-1:
    - shard-mtlp:         NOTRUN -> [SKIP][213] ([i915#5235]) +3 other tests skip
   [213]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25@pipe-b-edp-1.html

  * igt@kms_prime@basic-modeset-hybrid:
    - shard-tglu:         NOTRUN -> [SKIP][214] ([i915#6524])
   [214]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-5/igt@kms_prime@basic-modeset-hybrid.html

  * igt@kms_psr2_sf@overlay-plane-move-continuous-sf:
    - shard-apl:          NOTRUN -> [SKIP][215] ([fdo#109271] / [i915#658])
   [215]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl1/igt@kms_psr2_sf@overlay-plane-move-continuous-sf.html

  * igt@kms_psr2_sf@overlay-plane-update-sf-dmg-area:
    - shard-tglu:         NOTRUN -> [SKIP][216] ([fdo#111068] / [i915#658])
   [216]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-5/igt@kms_psr2_sf@overlay-plane-update-sf-dmg-area.html

  * igt@kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb:
    - shard-dg2:          NOTRUN -> [SKIP][217] ([i915#658]) +2 other tests skip
   [217]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb.html
    - shard-dg1:          NOTRUN -> [SKIP][218] ([i915#658])
   [218]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb.html

  * igt@kms_psr2_su@frontbuffer-xrgb8888:
    - shard-mtlp:         NOTRUN -> [SKIP][219] ([i915#4348]) +1 other test skip
   [219]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_psr2_su@frontbuffer-xrgb8888.html

  * igt@kms_psr@psr2_sprite_mmap_gtt:
    - shard-rkl:          NOTRUN -> [SKIP][220] ([i915#1072]) +4 other tests skip
   [220]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_psr@psr2_sprite_mmap_gtt.html

  * igt@kms_psr@sprite_blt:
    - shard-dg2:          NOTRUN -> [SKIP][221] ([i915#1072]) +8 other tests skip
   [221]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_psr@sprite_blt.html

  * igt@kms_psr@sprite_plane_move:
    - shard-dg1:          NOTRUN -> [SKIP][222] ([i915#1072] / [i915#4078]) +1 other test skip
   [222]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-12/igt@kms_psr@sprite_plane_move.html

  * igt@kms_psr_stress_test@flip-primary-invalidate-overlay:
    - shard-dg2:          NOTRUN -> [SKIP][223] ([i915#5461] / [i915#658])
   [223]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_psr_stress_test@flip-primary-invalidate-overlay.html

  * igt@kms_rotation_crc@bad-pixel-format:
    - shard-dg2:          NOTRUN -> [SKIP][224] ([i915#4235])
   [224]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@kms_rotation_crc@bad-pixel-format.html

  * igt@kms_rotation_crc@primary-y-tiled-reflect-x-0:
    - shard-rkl:          [PASS][225] -> [INCOMPLETE][226] ([i915#8875])
   [225]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-2/igt@kms_rotation_crc@primary-y-tiled-reflect-x-0.html
   [226]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-7/igt@kms_rotation_crc@primary-y-tiled-reflect-x-0.html

  * igt@kms_rotation_crc@primary-y-tiled-reflect-x-270:
    - shard-rkl:          [PASS][227] -> [INCOMPLETE][228] ([i915#8875] / [i915#9475])
   [227]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-2/igt@kms_rotation_crc@primary-y-tiled-reflect-x-270.html
   [228]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-4/igt@kms_rotation_crc@primary-y-tiled-reflect-x-270.html

  * igt@kms_rotation_crc@primary-yf-tiled-reflect-x-90:
    - shard-dg2:          NOTRUN -> [SKIP][229] ([i915#4235] / [i915#5190]) +1 other test skip
   [229]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-6/igt@kms_rotation_crc@primary-yf-tiled-reflect-x-90.html

  * igt@kms_setmode@invalid-clone-exclusive-crtc:
    - shard-mtlp:         NOTRUN -> [SKIP][230] ([i915#3555] / [i915#8823])
   [230]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@kms_setmode@invalid-clone-exclusive-crtc.html

  * igt@kms_setmode@invalid-clone-single-crtc:
    - shard-mtlp:         NOTRUN -> [SKIP][231] ([i915#3555] / [i915#8809])
   [231]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-7/igt@kms_setmode@invalid-clone-single-crtc.html

  * igt@kms_vblank@pipe-d-ts-continuation-modeset-rpm:
    - shard-rkl:          NOTRUN -> [SKIP][232] ([i915#4070] / [i915#533] / [i915#6768]) +1 other test skip
   [232]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-1/igt@kms_vblank@pipe-d-ts-continuation-modeset-rpm.html

  * igt@kms_writeback@writeback-invalid-parameters:
    - shard-mtlp:         NOTRUN -> [SKIP][233] ([i915#2437])
   [233]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_writeback@writeback-invalid-parameters.html

  * igt@kms_writeback@writeback-pixel-formats:
    - shard-dg2:          NOTRUN -> [SKIP][234] ([i915#2437]) +1 other test skip
   [234]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_writeback@writeback-pixel-formats.html

  * igt@perf@global-sseu-config-invalid:
    - shard-dg2:          NOTRUN -> [SKIP][235] ([i915#7387])
   [235]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@perf@global-sseu-config-invalid.html

  * igt@perf_pmu@cpu-hotplug:
    - shard-glk:          NOTRUN -> [SKIP][236] ([fdo#109271]) +52 other tests skip
   [236]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk2/igt@perf_pmu@cpu-hotplug.html

  * igt@perf_pmu@event-wait@rcs0:
    - shard-dg2:          NOTRUN -> [SKIP][237] ([fdo#112283]) +1 other test skip
   [237]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@perf_pmu@event-wait@rcs0.html

  * igt@perf_pmu@module-unload:
    - shard-dg2:          NOTRUN -> [FAIL][238] ([i915#5793])
   [238]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@perf_pmu@module-unload.html

  * igt@prime_mmap@test_aperture_limit@test_aperture_limit-smem:
    - shard-dg2:          NOTRUN -> [CRASH][239] ([i915#9351])
   [239]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@prime_mmap@test_aperture_limit@test_aperture_limit-smem.html

  * igt@prime_vgem@basic-read:
    - shard-dg2:          NOTRUN -> [SKIP][240] ([i915#3291] / [i915#3708])
   [240]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-11/igt@prime_vgem@basic-read.html

  * igt@prime_vgem@fence-write-hang:
    - shard-dg2:          NOTRUN -> [SKIP][241] ([i915#3708]) +1 other test skip
   [241]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@prime_vgem@fence-write-hang.html

  * igt@sysfs_timeslice_duration@timeout@vecs0:
    - shard-mtlp:         NOTRUN -> [TIMEOUT][242] ([i915#9516])
   [242]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@sysfs_timeslice_duration@timeout@vecs0.html

  * igt@tools_test@sysfs_l3_parity:
    - shard-dg2:          NOTRUN -> [SKIP][243] ([i915#4818])
   [243]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@tools_test@sysfs_l3_parity.html

  * igt@v3d/v3d_job_submission@array-job-submission:
    - shard-dg2:          NOTRUN -> [SKIP][244] ([i915#2575]) +18 other tests skip
   [244]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@v3d/v3d_job_submission@array-job-submission.html

  * igt@v3d/v3d_submit_cl@bad-multisync-out-sync:
    - shard-tglu:         NOTRUN -> [SKIP][245] ([fdo#109315] / [i915#2575])
   [245]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-3/igt@v3d/v3d_submit_cl@bad-multisync-out-sync.html

  * igt@v3d/v3d_submit_csd@bad-multisync-extension:
    - shard-dg1:          NOTRUN -> [SKIP][246] ([i915#2575]) +2 other tests skip
   [246]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@v3d/v3d_submit_csd@bad-multisync-extension.html

  * igt@v3d/v3d_wait_bo@bad-bo:
    - shard-rkl:          NOTRUN -> [SKIP][247] ([fdo#109315]) +5 other tests skip
   [247]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@v3d/v3d_wait_bo@bad-bo.html

  * igt@v3d/v3d_wait_bo@used-bo:
    - shard-mtlp:         NOTRUN -> [SKIP][248] ([i915#2575]) +7 other tests skip
   [248]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-3/igt@v3d/v3d_wait_bo@used-bo.html

  * igt@vc4/vc4_lookup_fail@bad-color-write:
    - shard-rkl:          NOTRUN -> [SKIP][249] ([i915#7711]) +2 other tests skip
   [249]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@vc4/vc4_lookup_fail@bad-color-write.html
    - shard-dg1:          NOTRUN -> [SKIP][250] ([i915#7711]) +1 other test skip
   [250]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-16/igt@vc4/vc4_lookup_fail@bad-color-write.html

  * igt@vc4/vc4_tiling@get-bad-modifier:
    - shard-dg2:          NOTRUN -> [SKIP][251] ([i915#7711]) +9 other tests skip
   [251]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-1/igt@vc4/vc4_tiling@get-bad-modifier.html

  * igt@vc4/vc4_wait_bo@used-bo-0ns:
    - shard-mtlp:         NOTRUN -> [SKIP][252] ([i915#7711]) +3 other tests skip
   [252]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-2/igt@vc4/vc4_wait_bo@used-bo-0ns.html

  
#### Possible fixes ####

  * igt@gem_ctx_exec@basic-nohangcheck:
    - shard-rkl:          [FAIL][253] ([i915#6268]) -> [PASS][254]
   [253]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-7/igt@gem_ctx_exec@basic-nohangcheck.html
   [254]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@gem_ctx_exec@basic-nohangcheck.html

  * igt@gem_ctx_isolation@preservation-s3@vcs0:
    - shard-dg2:          [INCOMPLETE][255] ([i915#9162]) -> [PASS][256]
   [255]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-11/igt@gem_ctx_isolation@preservation-s3@vcs0.html
   [256]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-5/igt@gem_ctx_isolation@preservation-s3@vcs0.html

  * igt@gem_eio@reset-stress:
    - shard-dg1:          [FAIL][257] ([i915#5784]) -> [PASS][258]
   [257]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-16/igt@gem_eio@reset-stress.html
   [258]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-17/igt@gem_eio@reset-stress.html

  * igt@gem_exec_fair@basic-deadline:
    - shard-rkl:          [FAIL][259] ([i915#2846]) -> [PASS][260]
   [259]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-6/igt@gem_exec_fair@basic-deadline.html
   [260]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@gem_exec_fair@basic-deadline.html

  * igt@gem_exec_fair@basic-pace-share@rcs0:
    - shard-glk:          [FAIL][261] ([i915#2876]) -> [PASS][262]
   [261]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk3/igt@gem_exec_fair@basic-pace-share@rcs0.html
   [262]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk4/igt@gem_exec_fair@basic-pace-share@rcs0.html
    - shard-tglu:         [FAIL][263] ([i915#2842]) -> [PASS][264]
   [263]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-tglu-4/igt@gem_exec_fair@basic-pace-share@rcs0.html
   [264]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-2/igt@gem_exec_fair@basic-pace-share@rcs0.html

  * igt@gem_exec_fair@basic-pace-solo@rcs0:
    - shard-rkl:          [FAIL][265] ([i915#2842]) -> [PASS][266]
   [265]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-4/igt@gem_exec_fair@basic-pace-solo@rcs0.html
   [266]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@gem_exec_fair@basic-pace-solo@rcs0.html

  * igt@gem_exec_fair@basic-pace@rcs0:
    - shard-glk:          [FAIL][267] ([i915#2842]) -> [PASS][268]
   [267]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk8/igt@gem_exec_fair@basic-pace@rcs0.html
   [268]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk9/igt@gem_exec_fair@basic-pace@rcs0.html

  * igt@gem_exec_suspend@basic-s0@smem:
    - shard-dg2:          [INCOMPLETE][269] -> [PASS][270]
   [269]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-1/igt@gem_exec_suspend@basic-s0@smem.html
   [270]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-7/igt@gem_exec_suspend@basic-s0@smem.html

  * igt@gen9_exec_parse@allowed-single:
    - shard-apl:          [ABORT][271] ([i915#5566]) -> [PASS][272]
   [271]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl7/igt@gen9_exec_parse@allowed-single.html
   [272]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl1/igt@gen9_exec_parse@allowed-single.html

  * igt@i915_hangman@gt-engine-error@vcs0:
    - shard-mtlp:         [FAIL][273] ([i915#7069]) -> [PASS][274]
   [273]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-3/igt@i915_hangman@gt-engine-error@vcs0.html
   [274]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-4/igt@i915_hangman@gt-engine-error@vcs0.html

  * igt@i915_pm_rc6_residency@rc6-idle@rcs0:
    - shard-dg1:          [FAIL][275] ([i915#3591]) -> [PASS][276] +1 other test pass
   [275]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-19/igt@i915_pm_rc6_residency@rc6-idle@rcs0.html
   [276]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-14/igt@i915_pm_rc6_residency@rc6-idle@rcs0.html

  * igt@i915_pm_rpm@dpms-mode-unset-non-lpsp:
    - shard-dg1:          [SKIP][277] ([i915#1397]) -> [PASS][278] +2 other tests pass
   [277]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg1-19/igt@i915_pm_rpm@dpms-mode-unset-non-lpsp.html
   [278]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg1-18/igt@i915_pm_rpm@dpms-mode-unset-non-lpsp.html

  * igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait:
    - shard-rkl:          [SKIP][279] ([i915#1397]) -> [PASS][280]
   [279]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-7/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html
   [280]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@i915_pm_rpm@modeset-non-lpsp-stress-no-wait.html

  * igt@i915_pm_rps@reset:
    - shard-snb:          [INCOMPLETE][281] ([i915#7790]) -> [PASS][282]
   [281]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-snb2/igt@i915_pm_rps@reset.html
   [282]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-snb5/igt@i915_pm_rps@reset.html

  * igt@i915_suspend@basic-s3-without-i915:
    - shard-rkl:          [FAIL][283] ([fdo#103375]) -> [PASS][284] +1 other test pass
   [283]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-6/igt@i915_suspend@basic-s3-without-i915.html
   [284]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-3/igt@i915_suspend@basic-s3-without-i915.html

  * igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0:
    - shard-mtlp:         [FAIL][285] ([i915#5138]) -> [PASS][286]
   [285]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-2/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0.html
   [286]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-5/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0.html

  * igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions-varying-size:
    - shard-glk:          [FAIL][287] ([i915#2346]) -> [PASS][288]
   [287]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-glk2/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions-varying-size.html
   [288]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-glk9/igt@kms_cursor_legacy@flip-vs-cursor-atomic-transitions-varying-size.html

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-pgflip-blt:
    - shard-dg2:          [FAIL][289] ([i915#6880]) -> [PASS][290] +1 other test pass
   [289]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-dg2-6/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-pgflip-blt.html
   [290]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-dg2-2/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-pgflip-blt.html

  * {igt@kms_lease@page-flip-implicit-plane@pipe-d-edp-1}:
    - shard-mtlp:         [ABORT][291] ([i915#9414]) -> [PASS][292] +3 other tests pass
   [291]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-2/igt@kms_lease@page-flip-implicit-plane@pipe-d-edp-1.html
   [292]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@kms_lease@page-flip-implicit-plane@pipe-d-edp-1.html

  * igt@kms_pipe_crc_basic@suspend-read-crc@pipe-b-dp-1:
    - shard-apl:          [INCOMPLETE][293] ([i915#180] / [i915#9392]) -> [PASS][294]
   [293]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl2/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-b-dp-1.html
   [294]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl4/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-b-dp-1.html

  * {igt@kms_pm_dc@dc9-dpms}:
    - shard-apl:          [SKIP][295] ([fdo#109271]) -> [PASS][296]
   [295]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl1/igt@kms_pm_dc@dc9-dpms.html
   [296]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl4/igt@kms_pm_dc@dc9-dpms.html

  * igt@kms_rotation_crc@sprite-rotation-270:
    - shard-rkl:          [INCOMPLETE][297] ([i915#8875]) -> [PASS][298]
   [297]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-rkl-4/igt@kms_rotation_crc@sprite-rotation-270.html
   [298]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-rkl-6/igt@kms_rotation_crc@sprite-rotation-270.html

  * igt@kms_universal_plane@cursor-fb-leak@pipe-a-hdmi-a-1:
    - shard-tglu:         [FAIL][299] ([i915#9196]) -> [PASS][300] +1 other test pass
   [299]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-tglu-2/igt@kms_universal_plane@cursor-fb-leak@pipe-a-hdmi-a-1.html
   [300]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-tglu-6/igt@kms_universal_plane@cursor-fb-leak@pipe-a-hdmi-a-1.html

  * igt@kms_vblank@pipe-a-ts-continuation-dpms-suspend:
    - shard-apl:          [INCOMPLETE][301] ([i915#9392]) -> [PASS][302]
   [301]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-apl6/igt@kms_vblank@pipe-a-ts-continuation-dpms-suspend.html
   [302]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-apl2/igt@kms_vblank@pipe-a-ts-continuation-dpms-suspend.html

  * igt@perf_pmu@busy-double-start@rcs0:
    - shard-mtlp:         [FAIL][303] ([i915#4349]) -> [PASS][304] +1 other test pass
   [303]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_13735/shard-mtlp-8/igt@perf_pmu@busy-double-start@rcs0.html
   [304]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/shard-mtlp-1/igt@perf_pmu@busy-double-start@rcs0.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#103375]: https://bugs.freedesktop.org/show_bug.cgi?id=103375
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109274]: https://bugs.freedesktop.org/show_bug.cgi?id=109274
  [fdo#109280]: https://bugs.freedesktop.org/show_bug.cgi?id=109280
  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [fdo#109289]: https://bugs.freedesktop.org/show_bug.cgi?id=109289
  [fdo#109315]: https://bugs.freedesktop.org/show_bug.cgi?id=109315
  [fdo#109506]: https://bugs.freedesktop.org/show_bug.cgi?id=109506
  [fdo#110189]: https://bugs.freedesktop.org/show_bug.cgi?id=110189
  [fdo#110723]: https://bugs.freedesktop.org/show_bug.cgi?id=110723
  [fdo#111068]: https://bugs.freedesktop.org/show_bug.cgi?id=111068
  [fdo#111614]: https://bugs.freedesktop.org/show_bug.cgi?id=111614
  [fdo#111615]: https://bugs.freedesktop.org/show_bug.cgi?id=111615
  [fdo#111767]: https://bugs.freedesktop.org/show_bug.cgi?id=111767
  [fdo#111825]: https://bugs.freedesktop.org/show_bug.cgi?id=111825
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [fdo#112283]: https://bugs.freedesktop.org/show_bug.cgi?id=112283
  [i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
  [i915#1099]: https://gitlab.freedesktop.org/drm/intel/issues/1099
  [i915#1397]: https://gitlab.freedesktop.org/drm/intel/issues/1397
  [i915#1769]: https://gitlab.freedesktop.org/drm/intel/issues/1769
  [i915#180]: https://gitlab.freedesktop.org/drm/intel/issues/180
  [i915#1825]: https://gitlab.freedesktop.org/drm/intel/issues/1825
  [i915#1937]: https://gitlab.freedesktop.org/drm/intel/issues/1937
  [i915#2346]: https://gitlab.freedesktop.org/drm/intel/issues/2346
  [i915#2410]: https://gitlab.freedesktop.org/drm/intel/issues/2410
  [i915#2437]: https://gitlab.freedesktop.org/drm/intel/issues/2437
  [i915#2527]: https://gitlab.freedesktop.org/drm/intel/issues/2527
  [i915#2575]: https://gitlab.freedesktop.org/drm/intel/issues/2575
  [i915#2587]: https://gitlab.freedesktop.org/drm/intel/issues/2587
  [i915#2672]: https://gitlab.freedesktop.org/drm/intel/issues/2672
  [i915#2842]: https://gitlab.freedesktop.org/drm/intel/issues/2842
  [i915#2846]: https://gitlab.freedesktop.org/drm/intel/issues/2846
  [i915#2856]: https://gitlab.freedesktop.org/drm/intel/issues/2856
  [i915#2876]: https://gitlab.freedesktop.org/drm/intel/issues/2876
  [i915#3023]: https://gitlab.freedesktop.org/drm/intel/issues/3023
  [i915#3281]: https://gitlab.freedesktop.org/drm/intel/issues/3281
  [i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
  [i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
  [i915#3297]: https://gitlab.freedesktop.org/drm/intel/issues/3297
  [i915#3299]: https://gitlab.freedesktop.org/drm/intel/issues/3299
  [i915#3359]: https://gitlab.freedesktop.org/drm/intel/issues/3359
  [i915#3458]: https://gitlab.freedesktop.org/drm/intel/issues/3458
  [i915#3469]: https://gitlab.freedesktop.org/drm/intel/issues/3469
  [i915#3539]: https://gitlab.freedesktop.org/drm/intel/issues/3539
  [i915#3546]: https://gitlab.freedesktop.org/drm/intel/issues/3546
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3591]: https://gitlab.freedesktop.org/drm/intel/issues/3591
  [i915#3637]: https://gitlab.freedesktop.org/drm/intel/issues/3637
  [i915#3638]: https://gitlab.freedesktop.org/drm/intel/issues/3638
  [i915#3689]: https://gitlab.freedesktop.org/drm/intel/issues/3689
  [i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
  [i915#3743]: https://gitlab.freedesktop.org/drm/intel/issues/3743
  [i915#3804]: https://gitlab.freedesktop.org/drm/intel/issues/3804
  [i915#3826]: https://gitlab.freedesktop.org/drm/intel/issues/3826
  [i915#3840]: https://gitlab.freedesktop.org/drm/intel/issues/3840
  [i915#3886]: https://gitlab.freedesktop.org/drm/intel/issues/3886
  [i915#4036]: https://gitlab.freedesktop.org/drm/intel/issues/4036
  [i915#404]: https://gitlab.freedesktop.org/drm/intel/issues/404
  [i915#4070]: https://gitlab.freedesktop.org/drm/intel/issues/4070
  [i915#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077
  [i915#4078]: https://gitlab.freedesktop.org/drm/intel/issues/4078
  [i915#4079]: https://gitlab.freedesktop.org/drm/intel/issues/4079
  [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083
  [i915#4087]: https://gitlab.freedesktop.org/drm/intel/issues/4087
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4212]: https://gitlab.freedesktop.org/drm/intel/issues/4212
  [i915#4213]: https://gitlab.freedesktop.org/drm/intel/issues/4213
  [i915#4235]: https://gitlab.freedesktop.org/drm/intel/issues/4235
  [i915#4270]: https://gitlab.freedesktop.org/drm/intel/issues/4270
  [i915#4348]: https://gitlab.freedesktop.org/drm/intel/issues/4348
  [i915#4349]: https://gitlab.freedesktop.org/drm/intel/issues/4349
  [i915#4473]: https://gitlab.freedesktop.org/drm/intel/issues/4473
  [i915#4537]: https://gitlab.freedesktop.org/drm/intel/issues/4537
  [i915#4538]: https://gitlab.freedesktop.org/drm/intel/issues/4538
  [i915#4573]: https://gitlab.freedesktop.org/drm/intel/issues/4573
  [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
  [i915#4767]: https://gitlab.freedesktop.org/drm/intel/issues/4767
  [i915#4771]: https://gitlab.freedesktop.org/drm/intel/issues/4771
  [i915#4812]: https://gitlab.freedesktop.org/drm/intel/issues/4812
  [i915#4816]: https://gitlab.freedesktop.org/drm/intel/issues/4816
  [i915#4818]: https://gitlab.freedesktop.org/drm/intel/issues/4818
  [i915#4852]: https://gitlab.freedesktop.org/drm/intel/issues/4852
  [i915#4860]: https://gitlab.freedesktop.org/drm/intel/issues/4860
  [i915#4881]: https://gitlab.freedesktop.org/drm/intel/issues/4881
  [i915#4885]: https://gitlab.freedesktop.org/drm/intel/issues/4885
  [i915#4936]: https://gitlab.freedesktop.org/drm/intel/issues/4936
  [i915#5138]: https://gitlab.freedesktop.org/drm/intel/issues/5138
  [i915#5176]: https://gitlab.freedesktop.org/drm/intel/issues/5176
  [i915#5190]: https://gitlab.freedesktop.org/drm/intel/issues/5190
  [i915#5235]: https://gitlab.freedesktop.org/drm/intel/issues/5235
  [i915#5286]: https://gitlab.freedesktop.org/drm/intel/issues/5286
  [i915#533]: https://gitlab.freedesktop.org/drm/intel/issues/533
  [i915#5354]: https://gitlab.freedesktop.org/drm/intel/issues/5354
  [i915#5461]: https://gitlab.freedesktop.org/drm/intel/issues/5461
  [i915#5493]: https://gitlab.freedesktop.org/drm/intel/issues/5493
  [i915#5566]: https://gitlab.freedesktop.org/drm/intel/issues/5566
  [i915#5608]: https://gitlab.freedesktop.org/drm/intel/issues/5608
  [i915#5784]: https://gitlab.freedesktop.org/drm/intel/issues/5784
  [i915#5793]: https://gitlab.freedesktop.org/drm/intel/issues/5793
  [i915#5889]: https://gitlab.freedesktop.org/drm/intel/issues/5889
  [i915#6095]: https://gitlab.freedesktop.org/drm/intel/issues/6095
  [i915#6122]: https://gitlab.freedesktop.org/drm/intel/issues/6122
  [i915#6227]: https://gitlab.freedesktop.org/drm/intel/issues/6227
  [i915#6268]: https://gitlab.freedesktop.org/drm/intel/issues/6268
  [i915#6334]: https://gitlab.freedesktop.org/drm/intel/issues/6334
  [i915#6335]: https://gitlab.freedesktop.org/drm/intel/issues/6335
  [i915#6524]: https://gitlab.freedesktop.org/drm/intel/issues/6524
  [i915#658]: https://gitlab.freedesktop.org/drm/intel/issues/658
  [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621
  [i915#6768]: https://gitlab.freedesktop.org/drm/intel/issues/6768
  [i915#6880]: https://gitlab.freedesktop.org/drm/intel/issues/6880
  [i915#6944]: https://gitlab.freedesktop.org/drm/intel/issues/6944
  [i915#6953]: https://gitlab.freedesktop.org/drm/intel/issues/6953
  [i915#7069]: https://gitlab.freedesktop.org/drm/intel/issues/7069
  [i915#7118]: https://gitlab.freedesktop.org/drm/intel/issues/7118
  [i915#7173]: https://gitlab.freedesktop.org/drm/intel/issues/7173
  [i915#7297]: https://gitlab.freedesktop.org/drm/intel/issues/7297
  [i915#7387]: https://gitlab.freedesktop.org/drm/intel/issues/7387
  [i915#7461]: https://gitlab.freedesktop.org/drm/intel/issues/7461
  [i915#7697]: https://gitlab.freedesktop.org/drm/intel/issues/7697
  [i915#7701]: https://gitlab.freedesktop.org/drm/intel/issues/7701
  [i915#7707]: https://gitlab.freedesktop.org/drm/intel/issues/7707
  [i915#7711]: https://gitlab.freedesktop.org/drm/intel/issues/7711
  [i915#7790]: https://gitlab.freedesktop.org/drm/intel/issues/7790
  [i915#7828]: https://gitlab.freedesktop.org/drm/intel/issues/7828
  [i915#7975]: https://gitlab.freedesktop.org/drm/intel/issues/7975
  [i915#8213]: https://gitlab.freedesktop.org/drm/intel/issues/8213
  [i915#8228]: https://gitlab.freedesktop.org/drm/intel/issues/8228
  [i915#8247]: https://gitlab.freedesktop.org/drm/intel/issues/8247
  [i915#8289]: https://gitlab.freedesktop.org/drm/intel/issues/8289
  [i915#8292]: https://gitlab.freedesktop.org/drm/intel/issues/8292
  [i915#8381]: https://gitlab.freedesktop.org/drm/intel/issues/8381
  [i915#8411]: https://gitlab.freedesktop.org/drm/intel/issues/8411
  [i915#8414]: https://gitlab.freedesktop.org/drm/intel/issues/8414
  [i915#8428]: https://gitlab.freedesktop.org/drm/intel/issues/8428
  [i915#8430]: https://gitlab.freedesktop.org/drm/intel/issues/8430
  [i915#8456]: https://gitlab.freedesktop.org/drm/intel/issues/8456
  [i915#8502]: https://gitlab.freedesktop.org/drm/intel/issues/8502
  [i915#8555]: https://gitlab.freedesktop.org/drm/intel/issues/8555
  [i915#8708]: https://gitlab.freedesktop.org/drm/intel/issues/8708
  [i915#8709]: https://gitlab.freedesktop.org/drm/intel/issues/8709
  [i915#8809]: https://gitlab.freedesktop.org/drm/intel/issues/8809
  [i915#8810]: https://gitlab.freedesktop.org/drm/intel/issues/8810
  [i915#8812]: https://gitlab.freedesktop.org/drm/intel/issues/8812
  [i915#8821]: https://gitlab.freedesktop.org/drm/intel/issues/8821
  [i915#8823]: https://gitlab.freedesktop.org/drm/intel/issues/8823
  [i915#8875]: https://gitlab.freedesktop.org/drm/intel/issues/8875
  [i915#8925]: https://gitlab.freedesktop.org/drm/intel/issues/8925
  [i915#8962]: https://gitlab.freedesktop.org/drm/intel/issues/8962
  [i915#9067]: https://gitlab.freedesktop.org/drm/intel/issues/9067
  [i915#9162]: https://gitlab.freedesktop.org/drm/intel/issues/9162
  [i915#9196]: https://gitlab.freedesktop.org/drm/intel/issues/9196
  [i915#9226]: https://gitlab.freedesktop.org/drm/intel/issues/9226
  [i915#9227]: https://gitlab.freedesktop.org/drm/intel/issues/9227
  [i915#9261]: https://gitlab.freedesktop.org/drm/intel/issues/9261
  [i915#9351]: https://gitlab.freedesktop.org/drm/intel/issues/9351
  [i915#9353]: https://gitlab.freedesktop.org/drm/intel/issues/9353
  [i915#9392]: https://gitlab.freedesktop.org/drm/intel/issues/9392
  [i915#9412]: https://gitlab.freedesktop.org/drm/intel/issues/9412
  [i915#9414]: https://gitlab.freedesktop.org/drm/intel/issues/9414
  [i915#9423]: https://gitlab.freedesktop.org/drm/intel/issues/9423
  [i915#9475]: https://gitlab.freedesktop.org/drm/intel/issues/9475
  [i915#9516]: https://gitlab.freedesktop.org/drm/intel/issues/9516


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7527 -> IGTPW_9965
  * Piglit: piglit_4509 -> None

  CI-20190529: 20190529
  CI_DRM_13735: 78e2c2b4839d929de3f7393d8b93d4e1faae3cd8 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_9965: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html
  IGT_7527: 46f98a3041f73a1d6ee7ec3ace6eba79b15369c4 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_9965/index.html

[-- Attachment #2: Type: text/html, Size: 118506 bytes --]

^ permalink raw reply	[flat|nested] 77+ messages in thread

end of thread, other threads:[~2023-10-11 13:51 UTC | newest]

Thread overview: 77+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-09 12:27 [Intel-gfx] [PATCH i-g-t v2 00/11] Kunit fixes and improvements Janusz Krzysztofik
2023-10-09 12:27 ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27 ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 01/11] lib/kunit: Fix handling of potential errors from F_GETFL Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 02/11] lib/kunit: Be more verbose on errors Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 03/11] lib/kunit: Fix misplaced igt_kunit() doc Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 04/11] lib/kunit: Parse KTAP report from the main process thread Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-10 13:33   ` [Intel-gfx] " Mauro Carvalho Chehab
2023-10-10 13:33     ` [igt-dev] " Mauro Carvalho Chehab
2023-10-10 13:33     ` [Intel-xe] " Mauro Carvalho Chehab
2023-10-10 17:54     ` Janusz Krzysztofik
2023-10-10 17:54       ` [igt-dev] " Janusz Krzysztofik
2023-10-10 17:54       ` [Intel-xe] " Janusz Krzysztofik
2023-10-10 15:59   ` Kamil Konieczny
2023-10-10 15:59     ` [igt-dev] " Kamil Konieczny
2023-10-10 15:59     ` [Intel-xe] " Kamil Konieczny
2023-10-10 17:49     ` [Intel-gfx] " Janusz Krzysztofik
2023-10-10 17:49       ` [Intel-xe] " Janusz Krzysztofik
2023-10-11 13:00       ` [Intel-gfx] " Kamil Konieczny
2023-10-11 13:00         ` [igt-dev] " Kamil Konieczny
2023-10-11 13:00         ` [Intel-xe] " Kamil Konieczny
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 05/11] lib/kunit: Omit suite name prefix if the same as subtest name Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 06/11] tests/kms_selftest: Let subtest names match suite names Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 07/11] lib/ktap: Drop workaround for missing top level KTAP headers Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 17:09   ` [Intel-gfx] " Kamil Konieczny
2023-10-09 17:09     ` [igt-dev] " Kamil Konieczny
2023-10-09 17:09     ` [Intel-xe] " Kamil Konieczny
2023-10-09 12:27 ` [Intel-gfx] [PATCH i-g-t v2 08/11] lib/kunit: Provide all results cleanup helper Janusz Krzysztofik
2023-10-09 12:27   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:27   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 17:33   ` [Intel-gfx] " Kamil Konieczny
2023-10-09 17:33     ` [igt-dev] " Kamil Konieczny
2023-10-09 17:33     ` [Intel-xe] " Kamil Konieczny
2023-10-09 12:28 ` [Intel-gfx] [PATCH i-g-t v2 09/11] lib/kunit: Prepare for KTAP parsing after modprobe completed Janusz Krzysztofik
2023-10-09 12:28   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:28   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 17:37   ` [Intel-gfx] " Kamil Konieczny
2023-10-09 17:37     ` [igt-dev] " Kamil Konieczny
2023-10-09 17:37     ` [Intel-xe] " Kamil Konieczny
2023-10-10 10:33     ` Janusz Krzysztofik
2023-10-10 10:33       ` [igt-dev] " Janusz Krzysztofik
2023-10-10 10:33       ` [Intel-gfx] " Janusz Krzysztofik
2023-10-09 12:28 ` [Intel-gfx] [PATCH i-g-t v2 10/11] lib/kunit: Fetch a list of test cases in advance Janusz Krzysztofik
2023-10-09 12:28   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:28   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 17:45   ` [Intel-gfx] " Kamil Konieczny
2023-10-09 17:45     ` [igt-dev] " Kamil Konieczny
2023-10-09 17:45     ` [Intel-xe] " Kamil Konieczny
2023-10-09 12:28 ` [Intel-gfx] [PATCH i-g-t v2 11/11] lib/kunit: Execute kunit test cases only when needed Janusz Krzysztofik
2023-10-09 12:28   ` [igt-dev] " Janusz Krzysztofik
2023-10-09 12:28   ` [Intel-xe] " Janusz Krzysztofik
2023-10-09 17:10   ` [Intel-gfx] " Kamil Konieczny
2023-10-09 17:10     ` [igt-dev] " Kamil Konieczny
2023-10-09 17:10     ` [Intel-xe] " Kamil Konieczny
2023-10-09 12:48 ` [Intel-xe] ✗ CI.Patch_applied: failure for Kunit fixes and improvements (rev2) Patchwork
2023-10-09 15:48 ` [igt-dev] ✗ Fi.CI.BAT: " Patchwork
2023-10-09 15:50 ` [igt-dev] ✗ CI.xeBAT: " Patchwork
2023-10-10 11:15 ` [igt-dev] ✓ Fi.CI.BAT: success " Patchwork
2023-10-10 18:32 ` [igt-dev] ✓ Fi.CI.BAT: success for Kunit fixes and improvements (rev3) Patchwork
2023-10-10 19:09 ` [igt-dev] ✓ CI.xeBAT: " Patchwork
2023-10-10 19:11 ` [igt-dev] ✓ Fi.CI.IGT: success for Kunit fixes and improvements (rev2) Patchwork
2023-10-11  4:43 ` [igt-dev] ✗ Fi.CI.IGT: failure for Kunit fixes and improvements (rev3) Patchwork
2023-10-11 13:51 ` [igt-dev] ✓ Fi.CI.IGT: success " Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.