Hi Janusz,
On 2024-03-18 at 11:13:30 +0100, Janusz Krzysztofik wrote:
> Up to now we were loading a KUnit test module in test execution mode only
> once per subtest, in background, and then, in parallel with execution of
> test cases while the module was loading, we were looking through dmesg for
> KTAP results from each expected test case.  As a consequence, our IGT
> messages were more or less delayed, never in full sync with kernel
> messages.  Moreover, parsing of KTAP results from already completed test
> cases could be abandoned on a failure from loading the test module or
> kernel taint caused by a subsequent test case.  Also, parsing of KTAP
> results from all subsequent test cases could be abandoned on a failure of
> the parser caused by any test case.  Other than that, if a user requested
> a single dynamic sub-subtest, all test cases were executed anyway while
> results from only one of them that corresponded to the selected dynamic
> sub-subtest were reported.  That way, kernel messages from unrelated test
> cases, not only the selected one, could contribute to dmesg-fail or dmesg-
> warn CI results from that sub-subtest.
> 
> Since recent KUnit implementation is capable of executing only those test
> cases that match a user filter, stop executing all of them asynchronously
> and parsing their KTAP results as they appear.  Instead, reload the test
> module once per each dynamic sub-subtest with a filter that selects a
> specific test case and wait for its completion.  If successful and no
> kernel taint has occurred then parse the whole KTAP report from a single
> test case it has produced and translate it to IGT result of that single
> corresponding sub-subtest.
> 
> With that in place, we no longer need to skip the whole subtest on a
> failure from module loading or KTAP reading or parsing.  Since such event
> is now local to execution of an individual test case, only fail its
> corresponding dynamic sub-subtests and continue with subsequent ones.
> However, still omit execution of subsequent test cases once the kernel
> gets tainted.
> 
> v3: Refresh on top of changes to struct igt_ktap_results pointer handling,
>   - use "for(;;) {}" instead of "do {} while();" when processing results
>     from parametrized test cases (Kamil).
> v2: Refresh on top of changes to KUnit filters handling,
>   - include the code of a new helper from a previously separate patch,
>   - actually limit the scope of the helper to fetching a KTAP report from
>     a file descriptor, and let the caller decide on how other steps, like
>     setting up filters or loading a test module, and errors they return
>     are handled,
>   - similar to kernel taint handling, just omit any remaining dynamic sub-
>     subtests if unloading the test module fails,
>   - update commit description with a more detailed justification of why we
>     need these changes.
> 
> Signed-off-by: Janusz Krzysztofik <janusz.krzyszto...@linux.intel.com>
> Cc: Mauro Carvalho Chehab <mche...@kernel.org>
> Cc: Jonathan Cavitt <jonathan.cav...@intel.com>
> Cc: Kamil Konieczny <kamil.koniec...@linux.intel.com>

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

> ---
>  lib/igt_kmod.c | 156 +++++++++++++++++--------------------------------
>  1 file changed, 54 insertions(+), 102 deletions(-)
> 
> diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
> index f0e4d5ec76..c495d11b16 100644
> --- a/lib/igt_kmod.c
> +++ b/lib/igt_kmod.c
> @@ -1070,6 +1070,25 @@ static void kunit_results_free(struct igt_list_head 
> *results,
>       free(*suite_name);
>  }
>  
> +static int kunit_get_results(struct igt_list_head *results, int kmsg_fd,
> +                          struct igt_ktap_results **ktap)
> +{
> +     int err;
> +
> +     *ktap = igt_ktap_alloc(results);
> +     if (igt_debug_on(!*ktap))
> +             return -ENOMEM;
> +
> +     do
> +             igt_debug_on((err = kunit_kmsg_result_get(results, NULL, 
> kmsg_fd, *ktap),
> +                           err && err != -EINPROGRESS));
> +     while (err == -EINPROGRESS);
> +
> +     igt_ktap_free(ktap);
> +
> +     return err;
> +}
> +
>  static void __igt_kunit_legacy(struct igt_ktest *tst,
>                              const char *subtest,
>                              const char *opts)
> @@ -1277,82 +1296,52 @@ static void __igt_kunit(struct igt_ktest *tst,
>                       struct igt_list_head *tests,
>                       struct igt_ktap_results **ktap)
>  {
> -     struct modprobe_data modprobe = { tst->kmod, opts, 0, pthread_self(), };
> -     char *suite_name = NULL, *case_name = NULL;
> -     struct igt_ktap_result *t, *r = NULL;
> -     pthread_mutexattr_t attr;
> -     IGT_LIST_HEAD(results);
> -     int ret = -EINPROGRESS;
> -     unsigned long taints;
> -
> -     igt_skip_on(lseek(tst->kmsg, 0, SEEK_END) < 0);
> -
> -     *ktap = igt_ktap_alloc(&results);
> -     igt_require(*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;
>  
> -                     if (!modprobe.thread) {
> -                             igt_require(kunit_set_filtering(suite, NULL, 
> NULL));
> +                     igt_skip_on(igt_kernel_tainted(&taints));
>  
> -                             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);
> +                     igt_fail_on(lseek(tst->kmsg, 0, SEEK_END) == -1 && 
> errno);
>  
> -                             modprobe.err = pthread_create(&modprobe.thread,
> -                                                           NULL,
> -                                                           modprobe_task,
> -                                                           &modprobe);
> -                             igt_assert_eq(modprobe.err, 0);
> +                     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(igt_list_empty(&results));
> -                             igt_assert_eq(ret, -EINPROGRESS);
> -                             ret = kunit_kmsg_result_get(&results, &modprobe,
> -                                                         tst->kmsg, *ktap);
> +                     igt_assert_eq(modprobe(tst->kmod, opts), 0);
> +                     igt_assert_eq(igt_kernel_tainted(&taints), 0);
> +
> +                     igt_assert_eq(kunit_get_results(&results, tst->kmsg, 
> 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);
> -                     }
>  
> -                     while (igt_debug_on_f(strcmp(r->suite_name, 
> t->suite_name),
> +                             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_debug_on_f(strcmp(r->case_name, 
> t->case_name),
> +                                           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) ||
> -                            r->code == IGT_EXIT_INVALID) {
> +                                           t->case_name, r->case_name);
>  
> -                             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;
> +                             if (r->code != IGT_EXIT_INVALID)
> +                                     break;
>  
>                               /* 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);
> -                             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);
> @@ -1371,58 +1360,21 @@ static void __igt_kunit(struct igt_ktest *tst,
>                                       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);
>               }
>  
> -             if (igt_debug_on(ret != -EINPROGRESS))
> -                     break;
> -     }
> -
> -     kunit_results_free(&results, &suite_name, &case_name);
> +             kunit_results_free(&results, &suite_name, &case_name);
>  
> -     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));
> +             if (igt_debug_on(igt_kernel_tainted(&taints))) {
> +                     igt_info("Kernel tainted, not executing more 
> selftests.\n");
>                       break;
> -             case ENOTRECOVERABLE:
> -                     break;
> -             default:
> -                     igt_debug("pthread_mutex_lock() failed\n");
> -                     igt_debug_on(pthread_join(modprobe.thread, NULL));
> +             }
> +
> +             if (igt_debug_on(kmod_module_remove_module(tst->kmod,
> +                                                        KMOD_REMOVE_FORCE))) 
> {
> +                     igt_info("Unloading test module failed, not executing 
> more selftests.\n");
>                       break;
>               }
>       }
> -
> -     igt_ktap_free(ktap);
> -
> -     igt_skip_on(modprobe.err);
> -     igt_skip_on(igt_kernel_tainted(&taints));
> -     if (ret != -EINPROGRESS)
> -             igt_skip_on_f(ret, "KTAP parser failed\n");
>  }
>  
>  /**
> -- 
> 2.43.0
> 

Reply via email to