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 */
