/* * 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"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "assembler/brw_compat.h" /* [un]likely() */ #include "igt_aux.h" #include "igt_core.h" #include "igt_debugfs.h" #include "igt_kmod.h" #include "igt_ktap.h" #include "igt_sysfs.h" #include "igt_taints.h" /** * SECTION:igt_kmod * @short_description: Wrappers around libkmod for module loading/unloading * @title: kmod * @include: igt.h * * This library provides helpers to load/unload module driver. * * Note on loading/reloading: * * Loading/unload/reloading the driver requires that resources to /dev/dri to * be released (closed). A potential mistake would be to submit commands to the * GPU by having a fd returned by @drm_open_driver, which is closed by atexit * signal handler so reloading/unloading the driver will fail if performed * afterwards. One possible solution to this issue is to use * @__drm_open_driver() or use @igt_set_module_param() to set module parameters * dynamically. */ static void squelch(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { } static struct kmod_ctx *kmod_ctx(void) { static struct kmod_ctx *ctx; const char **config_paths = NULL; char *config_paths_str; char *dirname; if (ctx) goto out; dirname = getenv("IGT_KMOD_DIRNAME"); if (dirname) igt_debug("kmod dirname = %s\n", dirname); config_paths_str = getenv("IGT_KMOD_CONFIG_PATHS"); if (config_paths_str) igt_debug("kmod config paths = %s\n", config_paths_str); if (config_paths_str) { unsigned count = !!strlen(config_paths_str); unsigned i; char* p; p = config_paths_str; while ((p = strchr(p, ':'))) p++, count++; config_paths = malloc(sizeof(*config_paths) * (count + 1)); igt_assert(config_paths != NULL); p = config_paths_str; for (i = 0; i < count; ++i) { igt_assert(p != NULL); config_paths[i] = p; if ((p = strchr(p, ':'))) *p++ = '\0'; } config_paths[i] = NULL; } ctx = kmod_new(dirname, config_paths); igt_assert(ctx != NULL); free(config_paths); kmod_set_log_fn(ctx, squelch, NULL); out: return ctx; } /** * igt_kmod_is_loaded: * @mod_name: The name of the module. * * Returns: True in case the module has been found or false otherwise. * * Function to check the existance of module @mod_name in list of loaded kernel * modules. * */ bool igt_kmod_is_loaded(const char *mod_name) { struct kmod_ctx *ctx = kmod_ctx(); struct kmod_list *mod, *list; bool ret = false; if (kmod_module_new_from_loaded(ctx, &list) < 0) goto out; kmod_list_foreach(mod, list) { struct kmod_module *kmod = kmod_module_get_module(mod); const char *kmod_name = kmod_module_get_name(kmod); if (!strcmp(kmod_name, mod_name)) { kmod_module_unref(kmod); ret = true; break; } kmod_module_unref(kmod); } kmod_module_unref_list(list); out: return ret; } static bool igt_kmod_is_loading(struct kmod_module *kmod) { return kmod_module_get_initstate(kmod) == KMOD_MODULE_COMING; } static int modprobe(struct kmod_module *kmod, const char *options) { unsigned int flags; flags = 0; if (options) /* force a fresh load to set the new options */ flags |= KMOD_PROBE_FAIL_ON_LOADED; return kmod_module_probe_insert_module(kmod, flags, options, NULL, NULL, NULL); } /** * igt_kmod_has_param: * @mod_name: The name of the module * @param: The name of the parameter * * Returns: true if the module has the parameter, false otherwise. */ bool igt_kmod_has_param(const char *module_name, const char *param) { struct kmod_module *kmod; struct kmod_list *d, *pre; bool result = false; if (kmod_module_new_from_name(kmod_ctx(), module_name, &kmod)) return false; pre = NULL; if (!kmod_module_get_info(kmod, &pre)) goto out; kmod_list_foreach(d, pre) { const char *key, *val; key = kmod_module_info_get_key(d); if (strcmp(key, "parmtype")) continue; val = kmod_module_info_get_value(d); if (val && strncmp(val, param, strlen(param)) == 0) { result = true; break; } } kmod_module_info_free_list(pre); out: kmod_module_unref(kmod); return result; } /** * igt_kmod_load: * @mod_name: The name of the module * @opts: Parameters for the module. NULL in case no parameters * are to be passed, or a '\0' terminated string otherwise. * * This function loads a kernel module using the name specified in @mod_name. * * Returns: 0 in case of success or -errno in case the module could not * be loaded. */ int igt_kmod_load(const char *mod_name, const char *opts) { struct kmod_ctx *ctx = kmod_ctx(); struct kmod_module *kmod; int err = 0; err = kmod_module_new_from_name(ctx, mod_name, &kmod); if (err < 0) goto out; err = modprobe(kmod, opts); if (err < 0) { switch (err) { case -EEXIST: igt_debug("Module %s already inserted\n", kmod_module_get_name(kmod)); break; case -ENOENT: igt_debug("Unknown symbol in module %s or " "unknown parameter\n", kmod_module_get_name(kmod)); break; default: igt_debug("Could not insert %s (%s)\n", kmod_module_get_name(kmod), strerror(-err)); break; } } out: kmod_module_unref(kmod); return err < 0 ? err : 0; } static int igt_kmod_unload_r(struct kmod_module *kmod) { #define MAX_TRIES 20 #define SLEEP_DURATION 500000 struct kmod_list *holders, *pos; int err = 0, tries; const char *mod_name = kmod_module_get_name(kmod); if (kmod_module_get_initstate(kmod) == KMOD_MODULE_BUILTIN) return 0; holders = kmod_module_get_holders(kmod); kmod_list_foreach(pos, holders) { struct kmod_module *it = kmod_module_get_module(pos); err = igt_kmod_unload_r(it); kmod_module_unref(it); if (err < 0) break; } kmod_module_unref_list(holders); if (err < 0) return err; if (igt_kmod_is_loading(kmod)) { igt_debug("%s still initializing\n", mod_name); err = igt_wait(!igt_kmod_is_loading(kmod), 10000, 100); if (err < 0) { igt_debug("%s failed to complete init within the timeout\n", mod_name); return err; } } for (tries = 0; tries < MAX_TRIES; tries++) { err = kmod_module_remove_module(kmod, 0); /* Only loop in the following cases */ if (err != -EBUSY && err != -EAGAIN) break; igt_debug("Module %s failed to unload with err: %d on attempt: %i\n", mod_name, err, tries + 1); if (tries < MAX_TRIES - 1) usleep(SLEEP_DURATION); } if (err == -ENOENT) igt_debug("Module %s could not be found or does not exist. err: %d\n", mod_name, err); else if (err == -ENOTSUP) igt_debug("Module %s cannot be unloaded. err: %d\n", mod_name, err); else if (err) igt_debug("Module %s failed to unload with err: %d after ~%.1fms\n", mod_name, err, SLEEP_DURATION*tries/1000.); else if (tries) igt_debug("Module %s unload took ~%.1fms over %i attempts\n", mod_name, SLEEP_DURATION*tries/1000., tries + 1); else igt_debug("Module %s unloaded immediately\n", mod_name); return err; } static void igt_drop_devcoredump(const char *driver) { char sysfspath[PATH_MAX]; DIR *dir; char *devcoredump; FILE *data; struct dirent *entry; int len, ret; len = snprintf(sysfspath, sizeof(sysfspath), "/sys/bus/pci/drivers/%s", driver); igt_assert(len < sizeof(sysfspath)); /* Not a PCI module */ if (access(sysfspath, F_OK)) return; devcoredump = sysfspath + len; dir = opendir(sysfspath); igt_assert(dir); while ((entry = readdir(dir)) != NULL) { if (entry->d_type != DT_LNK || strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; ret = snprintf(devcoredump, sizeof(sysfspath) - len, "/%s/devcoredump", entry->d_name); igt_assert(ret < sizeof(sysfspath) - len); if (access(sysfspath, F_OK) != -1) { igt_info("Removing devcoredump before module unload: %s\n", sysfspath); strcat(sysfspath, "/data"); data = fopen(sysfspath, "w"); igt_assert(data); /* * Write anything to devcoredump/data to * force its deletion */ fprintf(data, "1\n"); fclose(data); } } closedir(dir); } /** * igt_kmod_unload: * @mod_name: Module name. * * Returns: 0 in case of success or -errno otherwise. * * Removes the module @mod_name. */ int igt_kmod_unload(const char *mod_name) { struct kmod_ctx *ctx = kmod_ctx(); struct kmod_module *kmod; int err; igt_drop_devcoredump(mod_name); err = kmod_module_new_from_name(ctx, mod_name, &kmod); if (err < 0) { igt_debug("Could not use module %s (%s)\n", mod_name, strerror(-err)); goto out; } err = igt_kmod_unload_r(kmod); if (err < 0) { igt_debug("Could not remove module %s (%s)\n", mod_name, strerror(-err)); } out: kmod_module_unref(kmod); return err < 0 ? err : 0; } /** * * igt_kmod_list_loaded: List all modules currently loaded. * */ void igt_kmod_list_loaded(void) { struct kmod_ctx *ctx = kmod_ctx(); struct kmod_list *module, *list; if (kmod_module_new_from_loaded(ctx, &list) < 0) return; igt_info("Module\t\t Used by\n"); kmod_list_foreach(module, list) { struct kmod_module *kmod = kmod_module_get_module(module); struct kmod_list *module_deps, *module_deps_list; igt_info("%-24s", kmod_module_get_name(kmod)); module_deps_list = kmod_module_get_holders(kmod); if (module_deps_list) { kmod_list_foreach(module_deps, module_deps_list) { struct kmod_module *kmod_dep; kmod_dep = kmod_module_get_module(module_deps); igt_info("%s", kmod_module_get_name(kmod_dep)); if (kmod_list_next(module_deps_list, module_deps)) igt_info(","); kmod_module_unref(kmod_dep); } } kmod_module_unref_list(module_deps_list); igt_info("\n"); kmod_module_unref(kmod); } kmod_module_unref_list(list); } static void *strdup_realloc(char *origptr, const char *strdata) { size_t nbytes = strlen(strdata) + 1; char *newptr = realloc(origptr, nbytes); memcpy(newptr, strdata, nbytes); return newptr; } /** * igt_intel_driver_load: * @opts: options to pass to Intel driver * * Loads an Intel driver and its dependencies. * */ int igt_intel_driver_load(const char *opts, const char *driver) { int ret; if (opts) igt_info("Reloading %s with %s\n\n", driver, opts); ret = igt_kmod_load(driver, opts); if (ret) { igt_debug("Could not load %s\n", driver); return ret; } bind_fbcon(true); igt_kmod_load("snd_hda_intel", NULL); return 0; } /** * kick_snd_hda_intel: * * This function unbinds the snd_hda_intel driver so the module can be * unloaded. * */ static void kick_snd_hda_intel(void) { DIR *dir; struct dirent *snd_hda; int fd; size_t len; const char *dpath = "/sys/bus/pci/drivers/snd_hda_intel"; const char *path = "/sys/bus/pci/drivers/snd_hda_intel/unbind"; const char *devid = "0000:"; fd = open(path, O_WRONLY); if (fd < 0) { return; } dir = opendir(dpath); if (!dir) goto out; len = strlen(devid); while ((snd_hda = readdir(dir))) { struct stat st; char fpath[PATH_MAX]; if (*snd_hda->d_name == '.') continue; snprintf(fpath, sizeof(fpath), "%s/%s", dpath, snd_hda->d_name); if (lstat(fpath, &st)) continue; if (!S_ISLNK(st.st_mode)) continue; if (!strncmp(devid, snd_hda->d_name, len)) { igt_ignore_warn(write(fd, snd_hda->d_name, strlen(snd_hda->d_name))); } } closedir(dir); out: close(fd); } static int igt_always_unload_audio_driver(char **who) { int ret; const char *sound[] = { "snd_hda_intel", "snd_hdmi_lpe_audio", NULL, }; /* * With old Kernels, the dependencies between audio and DRM drivers * are not shown. So, it may not be mandatory to remove the audio * driver before unload/unbind the DRM one. So, let's print warnings, * but return 0 on errors, as, if the dependency is mandatory, this * will be detected later when trying to unbind/unload the DRM driver. */ for (const char **m = sound; *m; m++) { if (igt_kmod_is_loaded(*m)) { if (who) *who = strdup_realloc(*who, *m); ret = igt_lsof_kill_audio_processes(); if (ret) { igt_warn("Could not stop %d audio process(es)\n", ret); igt_kmod_list_loaded(); igt_lsof("/dev/snd"); return 0; } ret = pipewire_pulse_start_reserve(); if (ret) igt_warn("Failed to notify pipewire_pulse\n"); kick_snd_hda_intel(); ret = igt_kmod_unload(*m); pipewire_pulse_stop_reserve(); if (ret) { igt_warn("Could not unload audio driver %s\n", *m); igt_kmod_list_loaded(); igt_lsof("/dev/snd"); return 0; } } } return 0; } struct module_ref { char *name; unsigned long mem; unsigned int ref_count; unsigned int num_required; unsigned int *required_by; }; int igt_audio_driver_unload(char **who) { /* * Currently, there's no way to check if the audio driver binds into the * DRM one. So, always remove audio drivers that might be binding. * This may change in future, once kernel/module gets fixed. So, let's * keep this boilerplace, in order to make easier to add the new code, * once upstream is fixed. */ return igt_always_unload_audio_driver(who); } int __igt_intel_driver_unload(char **who, const char *driver) { int ret; const char *aux[] = { /* gen5: ips uses symbol_get() so only a soft module dependency */ "intel_ips", /* mei_gsc uses an i915 aux dev and the other mei mods depend on it */ "mei_pxp", "mei_hdcp", "mei_gsc", NULL, }; /* unbind vt */ bind_fbcon(false); ret = igt_audio_driver_unload(who); if (ret) return ret; for (const char **m = aux; *m; m++) { if (!igt_kmod_is_loaded(*m)) continue; ret = igt_kmod_unload(*m); if (ret) { if (who) *who = strdup_realloc(*who, *m); return ret; } } if (igt_kmod_is_loaded(driver)) { ret = igt_kmod_unload(driver); if (ret) { if (who) *who = strdup_realloc(*who, driver); return ret; } } return 0; } /** * igt_intel_driver_unload: * * Unloads an Intel driver and its dependencies. * */ int igt_intel_driver_unload(const char *driver) { char *who = NULL; int ret; ret = __igt_intel_driver_unload(&who, driver); if (ret) { igt_warn("Could not unload %s\n", who); igt_kmod_list_loaded(); igt_lsof("/dev/dri"); igt_lsof("/dev/snd"); free(who); return ret; } free(who); if (igt_kmod_is_loaded("intel-gtt")) igt_kmod_unload("intel-gtt"); igt_kmod_unload("drm_kms_helper"); igt_kmod_unload("drm"); if (igt_kmod_is_loaded("driver")) { igt_warn("%s.ko still loaded!\n", driver); return -EBUSY; } return 0; } /** * igt_amdgpu_driver_load: * @opts: options to pass to amdgpu driver * * Returns: IGT_EXIT_SUCCESS or IGT_EXIT_FAILURE. * * Loads the amdgpu driver and its dependencies. * */ int igt_amdgpu_driver_load(const char *opts) { if (opts) igt_info("Reloading amdgpu with %s\n\n", opts); if (igt_kmod_load("amdgpu", opts)) { igt_warn("Could not load amdgpu\n"); return IGT_EXIT_FAILURE; } bind_fbcon(true); return IGT_EXIT_SUCCESS; } /** * igt_amdgpu_driver_unload: * * Returns: IGT_EXIT_SUCCESS on success, IGT_EXIT_FAILURE on failure * and IGT_EXIT_SKIP if amdgpu could not be unloaded. * * Unloads the amdgpu driver and its dependencies. * */ int igt_amdgpu_driver_unload(void) { bind_fbcon(false); if (igt_kmod_is_loaded("amdgpu")) { if (igt_kmod_unload("amdgpu")) { igt_warn("Could not unload amdgpu\n"); igt_kmod_list_loaded(); igt_lsof("/dev/dri"); return IGT_EXIT_SKIP; } } igt_kmod_unload("drm_kms_helper"); igt_kmod_unload("drm"); if (igt_kmod_is_loaded("amdgpu")) { igt_warn("amdgpu.ko still loaded!\n"); return IGT_EXIT_FAILURE; } return IGT_EXIT_SUCCESS; } static void kmsg_dump(int fd) { char record[4096 + 1]; if (fd == -1) { igt_warn("Unable to retrieve kernel log (from /dev/kmsg)\n"); return; } record[sizeof(record) - 1] = '\0'; for (;;) { const char *start, *end; ssize_t r; r = read(fd, record, sizeof(record) - 1); if (r < 0) { if (errno == EINTR) continue; if (errno == EPIPE) { igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n"); continue; } if (errno != EAGAIN) igt_warn("kmsg truncated: unknown error (%m)\n"); break; } start = strchr(record, ';'); if (start) { start++; end = strchrnul(start, '\n'); igt_warn("%.*s\n", (int)(end - start), start); } } } static void tests_add(struct igt_kselftest_list *tl, struct igt_list_head *list) { struct igt_kselftest_list *pos; igt_list_for_each_entry(pos, list, link) if (pos->number > tl->number) break; igt_list_add_tail(&tl->link, &pos->link); } void igt_kselftest_get_tests(struct kmod_module *kmod, const char *filter, struct igt_list_head *tests) { const char *param_prefix = "igt__"; const int prefix_len = strlen(param_prefix); struct kmod_list *d, *pre; struct igt_kselftest_list *tl; pre = NULL; if (!kmod_module_get_info(kmod, &pre)) return; kmod_list_foreach(d, pre) { const char *key, *val; char *colon; int offset; key = kmod_module_info_get_key(d); if (strcmp(key, "parmtype")) continue; val = kmod_module_info_get_value(d); if (!val || strncmp(val, param_prefix, prefix_len)) continue; offset = strlen(val) + 1; tl = malloc(sizeof(*tl) + offset); if (!tl) continue; memcpy(tl->param, val, offset); colon = strchr(tl->param, ':'); *colon = '\0'; tl->number = 0; tl->name = tl->param + prefix_len; if (sscanf(tl->name, "%u__%n", &tl->number, &offset) == 1) tl->name += offset; if (filter && strncmp(tl->name, filter, strlen(filter))) { free(tl); continue; } tests_add(tl, tests); } kmod_module_info_free_list(pre); } static int open_parameters(const char *module_name) { char path[256]; snprintf(path, sizeof(path), "/sys/module/%s/parameters", module_name); return open(path, O_RDONLY); } static void kunit_debugfs_path(char *kunit_path) { const char *debugfs_path = igt_debugfs_mount(); if (igt_debug_on(!debugfs_path)) return; if (igt_debug_on(strlen(debugfs_path) + strlen("/kunit/") >= PATH_MAX)) return; strcpy(stpcpy(kunit_path, debugfs_path), "/kunit/"); } static bool kunit_set_filtering(const char *filter_glob, const char *filter, const char *filter_action) { int params; bool ret; params = open_parameters("kunit"); if (igt_debug_on(params < 0)) return false; /* * Default values of the KUnit base module filtering parameters * are all NULLs. Reapplying those NULLs over sysfs once * overwritten with non-NULL strings seems not possible. * As a workaround, we use non-NULL strings that exhibit * the same behaviour as if default NULLs were in place. */ ret = !igt_debug_on(!igt_sysfs_set(params, "filter_glob", filter_glob ?: "*")); if (!ret) goto close; ret = !igt_debug_on(!igt_sysfs_set(params, "filter", filter ?: "module!=none")); if (!ret) goto close; ret = !igt_debug_on(!igt_sysfs_set(params, "filter_action", filter_action ?: "")); /* * TODO: Drop the extra check below as soon as igt_sysfs_set() * can correctly process empty strings which we are using * as filter_action NULL equivalent. * * We need this check only when NULL is requested for "filter_action" * and not for "filter" parameter, otherwise, even if "filter_action" * was previously set to "skip", we don't care since our * "module!=none" default filter guarantees that no test cases are * filtered out to be processed as "filter_action" says. */ if (ret && !filter_action && filter) { filter_action = igt_sysfs_get(params, "filter_action"); ret = !(igt_debug_on_f(!filter_action, "open() failed\n") || igt_debug_on_f(strlen(filter_action), "empty string not applied\n")); free((char *)filter_action); } close: close(params); return ret; } struct modprobe_data { struct kmod_module *kmod; const char *opts; int err; pthread_t parent; pthread_mutex_t lock; pthread_t thread; }; static void *modprobe_task(void *arg) { struct modprobe_data *data = arg; data->err = modprobe(data->kmod, data->opts); if (igt_debug_on(data->err)) { bool once = false; int err; while (err = pthread_mutex_trylock(&data->lock), err && !igt_debug_on(err != EBUSY)) { igt_debug_on(pthread_kill(data->parent, SIGCHLD) && !once); once = true; } } 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) { } 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; 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 (modprobe && !err) { /* pthread_mutex_lock() succeeded */ 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 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 int kunit_get_results(struct igt_list_head *results, const char *debugfs_path, const char *suite, struct igt_ktap_results **ktap) { char results_path[PATH_MAX]; FILE *results_stream; char *buf = NULL; size_t size = 0; ssize_t len; int err; if (igt_debug_on(strlen(debugfs_path) + strlen(suite) + strlen("/results") >= PATH_MAX)) return -ENOSPC; strcpy(stpcpy(stpcpy(results_path, debugfs_path), suite), "/results"); results_stream = fopen(results_path, "r"); if (igt_debug_on(!results_stream)) return -errno; *ktap = igt_ktap_alloc(results); if (igt_debug_on(!*ktap)) { err = -ENOMEM; goto out_fclose; } while (len = getline(&buf, &size, results_stream), len > 0) { err = igt_ktap_parse(buf, *ktap); if (err != -EINPROGRESS) break; } free(buf); igt_ktap_free(ktap); out_fclose: fclose(results_stream); return err; } static void __igt_kunit_legacy(struct igt_ktest *tst, const char *subtest, const char *opts) { struct modprobe_data modprobe = { tst->kmod, opts, 0, pthread_self(), }; char *suite_name = NULL, *case_name = NULL; struct igt_ktap_results *ktap; struct igt_ktap_result *r; pthread_mutexattr_t attr; IGT_LIST_HEAD(results); unsigned long taints; int flags, ret; igt_skip_on_f(tst->kmsg < 0, "Could not open /dev/kmsg\n"); 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); 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"); } 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_dynamic_f("%s%s%s", strcmp(r->suite_name, subtest) ? r->suite_name : "", strcmp(r->suite_name, subtest) ? "-" : "", 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)); 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); } 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); } kunit_result_free(&r, &suite_name, &case_name); } while (ret == -EINPROGRESS); 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; } igt_ktap_free(&ktap); igt_skip_on(modprobe.err); igt_skip_on(igt_kernel_tainted(&taints)); igt_skip_on_f(ret, "KTAP parser failed\n"); } static bool kunit_get_tests(struct igt_list_head *tests, struct igt_ktest *tst, const char *suite, const char *opts, const char *debugfs_path, DIR **debugfs_dir, struct igt_ktap_results **ktap) { struct igt_ktap_result *r, *rn; struct dirent *subdir; unsigned long taints; *debugfs_dir = opendir(debugfs_path); if (igt_debug_on(!*debugfs_dir)) return false; /* * 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 try to use action=list kunit parameter to get the * listing, however, parsing a structured KTAP report -- something that * we already can do perfectly -- seems to be more safe than extracting * a free text list of unknown length from /dev/kmsg. */ if (igt_debug_on(!kunit_set_filtering(suite, "module=none", "skip"))) return false; if (!suite) { seekdir(*debugfs_dir, 2); /* directory itself and its parent */ errno = 0; igt_skip_on_f(readdir(*debugfs_dir) || errno, "Require empty KUnit debugfs directory\n"); rewinddir(*debugfs_dir); } igt_skip_on(modprobe(tst->kmod, opts)); igt_skip_on(igt_kernel_tainted(&taints)); while (subdir = readdir(*debugfs_dir), subdir) { if (!(subdir->d_type & DT_DIR)) continue; if (!strcmp(subdir->d_name, ".") || !strcmp(subdir->d_name, "..")) continue; if (suite && strcmp(subdir->d_name, suite)) continue; igt_warn_on_f(kunit_get_results(tests, debugfs_path, subdir->d_name, ktap), "parsing KTAP report from test suite \"%s\" failed\n", subdir->d_name); if (suite) break; } closedir(*debugfs_dir); *debugfs_dir = NULL; igt_list_for_each_entry_safe(r, rn, tests, link) igt_require_f(r->code == IGT_EXIT_SKIP, "Unexpected non-SKIP result while listing test cases\n"); return true; } static void __igt_kunit(struct igt_ktest *tst, const char *subtest, const char *opts, const char *debugfs_path, struct igt_list_head *tests, struct igt_ktap_results **ktap) { struct igt_ktap_result *t; igt_list_for_each_entry(t, tests, link) { char *suite_name = NULL, *case_name = NULL; IGT_LIST_HEAD(results); unsigned long taints; igt_dynamic_f("%s%s%s", strcmp(t->suite_name, subtest) ? t->suite_name : "", strcmp(t->suite_name, subtest) ? "-" : "", t->case_name) { struct igt_ktap_result *r = NULL; char glob[1024]; int i; igt_skip_on(kmod_module_remove_module(tst->kmod, KMOD_REMOVE_FORCE)); igt_skip_on(igt_kernel_tainted(&taints)); igt_assert_lt(snprintf(glob, sizeof(glob), "%s.%s", t->suite_name, t->case_name), sizeof(glob)); igt_assert(kunit_set_filtering(glob, NULL, NULL)); igt_assert_eq(modprobe(tst->kmod, opts), 0); igt_assert_eq(igt_kernel_tainted(&taints), 0); igt_assert_eq(kunit_get_results(&results, debugfs_path, t->suite_name, ktap), 0); for (i = 0; i < 2; i++) { kunit_result_free(&r, &suite_name, &case_name); igt_fail_on(igt_list_empty(&results)); r = igt_list_first_entry(&results, r, link); igt_fail_on_f(strcmp(r->suite_name, t->suite_name), "suite_name expected: %s, got: %s\n", t->suite_name, r->suite_name); igt_fail_on_f(strcmp(r->case_name, t->case_name), "case_name expected: %s, got: %s\n", t->case_name, r->case_name); if (r->code != IGT_EXIT_INVALID) break; /* result from parametrized test case */ } 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); } kunit_results_free(&results, &suite_name, &case_name); if (igt_debug_on(igt_kernel_tainted(&taints))) { igt_info("Kernel tainted, not executing more selftests.\n"); break; } } } /** * igt_kunit: * @module_name: the name of the module * @suite: the name of test suite to be executed, also used as subtest name; * if NULL then test cases from all test suites provided by the module * are executed as dynamic sub-subtests of one IGT subtest, which name * is derived from the module name by cutting off its optional trailing * _test or _kunit suffix * @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 *suite, const char *opts) { char debugfs_path[PATH_MAX] = { '\0', }; struct igt_ktest tst = { .kmsg = -1, }; struct igt_ktap_results *ktap = NULL; const char *subtest = suite; DIR *debugfs_dir = NULL; IGT_LIST_HEAD(tests); /* * If the caller (an IGT test) provides no test suite name then * we take the module name, drop the trailing "_test" or "_kunit" * suffix, if any, and use the result as our IGT subtest name. */ if (!subtest) { subtest = strdup(module_name); if (!igt_debug_on(!subtest)) { char *suffix = strstr(subtest, "_test"); if (!suffix) suffix = strstr(subtest, "_kunit"); if (suffix) *suffix = '\0'; } } igt_fixture { igt_require(subtest); igt_skip_on(igt_ktest_init(&tst, module_name)); igt_skip_on(igt_ktest_begin(&tst)); igt_assert(igt_list_empty(&tests)); } /* We need the base KUnit module loaded if not built-in */ igt_ignore_warn(igt_kmod_load("kunit", NULL)); /* * We need to use igt_subtest here, as otherwise it may crash with: * skipping is allowed only in fixtures, subtests or igt_simple_main * if used on igt_main. This is also needed in order to provide * proper namespace for dynamic subtests, with is required for CI * and for documentation. */ igt_subtest_with_dynamic(subtest) { /* * TODO: As soon as no longer needed by major Linux * distributions, replace the fallback to * __igt_kunit_legacy() processing path, required by * LTS kernels not capable of using KUnit filters for * listing test cases in KTAP format, with igt_require. */ kunit_debugfs_path(debugfs_path); if (igt_debug_on(!*debugfs_path) || !kunit_get_tests(&tests, &tst, suite, opts, debugfs_path, &debugfs_dir, &ktap)) __igt_kunit_legacy(&tst, subtest, opts); else __igt_kunit(&tst, subtest, opts, debugfs_path, &tests, &ktap); } igt_fixture { char *suite_name = NULL, *case_name = NULL; igt_ktap_free(&ktap); kunit_results_free(&tests, &suite_name, &case_name); if (debugfs_dir) closedir(debugfs_dir); igt_ktest_end(&tst); } igt_ktest_fini(&tst); } int igt_ktest_init(struct igt_ktest *tst, const char *module_name) { struct kmod_list *l = NULL; int err; memset(tst, 0, sizeof(*tst)); tst->module_name = strdup(module_name); if (!tst->module_name) return 1; tst->kmsg = -1; err = kmod_module_new_from_lookup(kmod_ctx(), module_name, &l); /* * Check for -ENOSYS to workaround bug in kmod_module_new_from_lookup() * from libkmod <= 29 */ if (err < 0 && err != -ENOSYS) return err; /* * Lookup may not resolve to a module when used to just list subtests, * where module is not available. Fallback to _new_from_name(). */ if (!l) return kmod_module_new_from_name(kmod_ctx(), module_name, &tst->kmod); tst->kmod = kmod_module_get_module(l); kmod_module_unref_list(l); return 0; } int igt_ktest_begin(struct igt_ktest *tst) { int err; if (strcmp(tst->module_name, "i915") == 0) igt_i915_driver_unload(); err = kmod_module_remove_module(tst->kmod, 0); igt_require(err == 0 || err == -ENOENT); tst->kmsg = open("/dev/kmsg", O_RDONLY | O_NONBLOCK); return 0; } int igt_kselftest_execute(struct igt_ktest *tst, struct igt_kselftest_list *tl, const char *options, const char *result) { unsigned long taints; char buf[1024]; int err; igt_skip_on(igt_kernel_tainted(&taints)); lseek(tst->kmsg, 0, SEEK_END); snprintf(buf, sizeof(buf), "%s=1 %s", tl->param, options ?: ""); err = modprobe(tst->kmod, buf); if (err == 0 && result) { int dir = open_parameters(tst->module_name); igt_sysfs_scanf(dir, result, "%d", &err); close(dir); } if (err == -ENOTTY) /* special case */ err = 0; if (err) kmsg_dump(tst->kmsg); kmod_module_remove_module(tst->kmod, 0); errno = 0; igt_assert_f(err == 0, "kselftest \"%s %s\" failed: %s [%d]\n", tst->module_name, buf, strerror(-err), -err); igt_assert_eq(igt_kernel_tainted(&taints), 0); return err; } void igt_ktest_end(struct igt_ktest *tst) { kmod_module_remove_module(tst->kmod, 0); close(tst->kmsg); } void igt_ktest_fini(struct igt_ktest *tst) { free(tst->module_name); kmod_module_unref(tst->kmod); } static const char *unfilter(const char *filter, const char *name) { if (!filter) return name; name += strlen(filter); if (!isalpha(*name)) name++; return name; } void igt_kselftests(const char *module_name, const char *options, const char *result, const char *filter) { struct igt_ktest tst; IGT_LIST_HEAD(tests); struct igt_kselftest_list *tl, *tn; if (igt_ktest_init(&tst, module_name) != 0) return; igt_fixture igt_require(igt_ktest_begin(&tst) == 0); igt_kselftest_get_tests(tst.kmod, filter, &tests); igt_subtest_with_dynamic(filter ?: "all-tests") { igt_list_for_each_entry_safe(tl, tn, &tests, link) { unsigned long taints; igt_dynamic_f("%s", unfilter(filter, tl->name)) igt_kselftest_execute(&tst, tl, options, result); free(tl); if (igt_kernel_tainted(&taints)) { igt_info("Kernel tainted, not executing more selftests.\n"); break; } } } igt_fixture { igt_ktest_end(&tst); igt_require(!igt_list_empty(&tests)); } igt_ktest_fini(&tst); }