linux_kselftest-kunit-fixes-7.0-rc3

kunit:
 - Fixes rust warnings when CONFIG_PRINTK is disabled.
 - Reduces stack usage in kunit_run_tests() to fix warnings when
   CONFIG_FRAME_WARN is set to a relatively low value.
 - Updates email address for David Gow.
 
 kunit tool:
 - Copies caller args in run_kernel to prevent mutation.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmmrNf4ACgkQCwJExA0N
 QxyVNxAAtemv6AH1pP/cR23teXd5uuBThTM8EASEAC9/VJmCuPjg9mb6df816EXx
 2ZrTKGR5k/5basYDhNKIAJTGGjT3pcau7OKmD7Piy3bbhJEq5kb31rVbbmCV1STl
 vzJcBbSNP5Z+otz/flmewoeiL+xYarQvCrfFll5Y+gMer3HFevG0y0rCpEe0+7tH
 avgq01m7rHFg0DLYxJhjE3NFgFlgYaKdHyQPrGYOtZUXJDSNUO1cULJ8ZrMG3skc
 JWngB/5eS6hP/3QSaBm9coHKbrAYatpPWLfg9wtgfuj3be2oEAnWeynb/vWrdEV3
 uid96CEs1UPG8Z/jTtRK86q1md+5I2QX6s7B74EXnzCo0bg150h1qtT2J/za1Kxu
 7CP6Rq59VHCL6SMpQ4cks00zpWffCCtAphto2yk9ZphbisyVUGN8zEiCdd0FrLob
 putdMLeJVLXbnWFCnLA/8gFfx6Inp+PXNoVr+6LNZo/qB4W/EJOLDUvjaVJ7abpY
 yUxvJVj0+dwOlYUYnpldGCz9pfywgIyLI7ERtu4KIwJ5EMsVo8yJgykEd2MkQbZ+
 2Db3omyalnAn/vXP5p48vawI9oj6scMNxukBRTdqZsQGRma/jj6FnZCi77llQY+g
 V6pTQPjJRhRGLIwow+dCkJ4CucafNacp1RVuf61MM/I03XOQEwU=
 =j8w0
 -----END PGP SIGNATURE-----

Merge tag 'linux_kselftest-kunit-fixes-7.0-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull kunit fixes from Shuah Khan:

 - Fix rust warnings when CONFIG_PRINTK is disabled

 - Reduce stack usage in kunit_run_tests() to fix warnings when
   CONFIG_FRAME_WARN is set to a relatively low value

 - Update email address for David Gow

 - Copy caller args in kunit tool in run_kernel to prevent mutation

* tag 'linux_kselftest-kunit-fixes-7.0-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
  kunit: reduce stack usage in kunit_run_tests()
  kunit: tool: copy caller args in run_kernel to prevent mutation
  rust: kunit: fix warning when !CONFIG_PRINTK
  MAINTAINERS: Update email address for David Gow
This commit is contained in:
Linus Torvalds 2026-03-06 12:34:49 -08:00
commit 3593e678f5
6 changed files with 166 additions and 110 deletions

View file

@ -219,6 +219,7 @@ Daniele Alessandrelli <daniele.alessandrelli@gmail.com> <daniele.alessandrelli@i
Danilo Krummrich <dakr@kernel.org> <dakr@redhat.com> Danilo Krummrich <dakr@kernel.org> <dakr@redhat.com>
David Brownell <david-b@pacbell.net> David Brownell <david-b@pacbell.net>
David Collins <quic_collinsd@quicinc.com> <collinsd@codeaurora.org> David Collins <quic_collinsd@quicinc.com> <collinsd@codeaurora.org>
David Gow <david@davidgow.net> <davidgow@google.com>
David Heidelberg <david@ixit.cz> <d.okias@gmail.com> David Heidelberg <david@ixit.cz> <d.okias@gmail.com>
David Hildenbrand <david@kernel.org> <david@redhat.com> David Hildenbrand <david@kernel.org> <david@redhat.com>
David Rheinsberg <david@readahead.eu> <dh.herrmann@gmail.com> David Rheinsberg <david@readahead.eu> <dh.herrmann@gmail.com>

View file

@ -13938,7 +13938,7 @@ F: fs/smb/server/
KERNEL UNIT TESTING FRAMEWORK (KUnit) KERNEL UNIT TESTING FRAMEWORK (KUnit)
M: Brendan Higgins <brendan.higgins@linux.dev> M: Brendan Higgins <brendan.higgins@linux.dev>
M: David Gow <davidgow@google.com> M: David Gow <david@davidgow.net>
R: Rae Moar <raemoar63@gmail.com> R: Rae Moar <raemoar63@gmail.com>
L: linux-kselftest@vger.kernel.org L: linux-kselftest@vger.kernel.org
L: kunit-dev@googlegroups.com L: kunit-dev@googlegroups.com
@ -14758,7 +14758,7 @@ F: drivers/misc/lis3lv02d/
F: drivers/platform/x86/hp/hp_accel.c F: drivers/platform/x86/hp/hp_accel.c
LIST KUNIT TEST LIST KUNIT TEST
M: David Gow <davidgow@google.com> M: David Gow <david@davidgow.net>
L: linux-kselftest@vger.kernel.org L: linux-kselftest@vger.kernel.org
L: kunit-dev@googlegroups.com L: kunit-dev@googlegroups.com
S: Maintained S: Maintained

View file

@ -94,7 +94,7 @@ struct kunit_result_stats {
unsigned long total; unsigned long total;
}; };
static bool kunit_should_print_stats(struct kunit_result_stats stats) static bool kunit_should_print_stats(struct kunit_result_stats *stats)
{ {
if (kunit_stats_enabled == 0) if (kunit_stats_enabled == 0)
return false; return false;
@ -102,11 +102,11 @@ static bool kunit_should_print_stats(struct kunit_result_stats stats)
if (kunit_stats_enabled == 2) if (kunit_stats_enabled == 2)
return true; return true;
return (stats.total > 1); return (stats->total > 1);
} }
static void kunit_print_test_stats(struct kunit *test, static void kunit_print_test_stats(struct kunit *test,
struct kunit_result_stats stats) struct kunit_result_stats *stats)
{ {
if (!kunit_should_print_stats(stats)) if (!kunit_should_print_stats(stats))
return; return;
@ -115,10 +115,10 @@ static void kunit_print_test_stats(struct kunit *test,
KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
"# %s: pass:%lu fail:%lu skip:%lu total:%lu", "# %s: pass:%lu fail:%lu skip:%lu total:%lu",
test->name, test->name,
stats.passed, stats->passed,
stats.failed, stats->failed,
stats.skipped, stats->skipped,
stats.total); stats->total);
} }
/* Append formatted message to log. */ /* Append formatted message to log. */
@ -600,26 +600,26 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
} }
static void kunit_print_suite_stats(struct kunit_suite *suite, static void kunit_print_suite_stats(struct kunit_suite *suite,
struct kunit_result_stats suite_stats, struct kunit_result_stats *suite_stats,
struct kunit_result_stats param_stats) struct kunit_result_stats *param_stats)
{ {
if (kunit_should_print_stats(suite_stats)) { if (kunit_should_print_stats(suite_stats)) {
kunit_log(KERN_INFO, suite, kunit_log(KERN_INFO, suite,
"# %s: pass:%lu fail:%lu skip:%lu total:%lu", "# %s: pass:%lu fail:%lu skip:%lu total:%lu",
suite->name, suite->name,
suite_stats.passed, suite_stats->passed,
suite_stats.failed, suite_stats->failed,
suite_stats.skipped, suite_stats->skipped,
suite_stats.total); suite_stats->total);
} }
if (kunit_should_print_stats(param_stats)) { if (kunit_should_print_stats(param_stats)) {
kunit_log(KERN_INFO, suite, kunit_log(KERN_INFO, suite,
"# Totals: pass:%lu fail:%lu skip:%lu total:%lu", "# Totals: pass:%lu fail:%lu skip:%lu total:%lu",
param_stats.passed, param_stats->passed,
param_stats.failed, param_stats->failed,
param_stats.skipped, param_stats->skipped,
param_stats.total); param_stats->total);
} }
} }
@ -681,13 +681,116 @@ static void kunit_init_parent_param_test(struct kunit_case *test_case, struct ku
} }
} }
int kunit_run_tests(struct kunit_suite *suite) static noinline_for_stack void
kunit_run_param_test(struct kunit_suite *suite, struct kunit_case *test_case,
struct kunit *test,
struct kunit_result_stats *suite_stats,
struct kunit_result_stats *total_stats,
struct kunit_result_stats *param_stats)
{ {
char param_desc[KUNIT_PARAM_DESC_SIZE]; char param_desc[KUNIT_PARAM_DESC_SIZE];
const void *curr_param;
kunit_init_parent_param_test(test_case, test);
if (test_case->status == KUNIT_FAILURE) {
kunit_update_stats(param_stats, test->status);
return;
}
/* Get initial param. */
param_desc[0] = '\0';
/* TODO: Make generate_params try-catch */
curr_param = test_case->generate_params(test, NULL, param_desc);
test_case->status = KUNIT_SKIPPED;
kunit_log(KERN_INFO, test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
"KTAP version 1\n");
kunit_log(KERN_INFO, test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
"# Subtest: %s", test_case->name);
if (test->params_array.params &&
test_case->generate_params == kunit_array_gen_params) {
kunit_log(KERN_INFO, test, KUNIT_SUBTEST_INDENT
KUNIT_SUBTEST_INDENT "1..%zd\n",
test->params_array.num_params);
}
while (curr_param) {
struct kunit param_test = {
.param_value = curr_param,
.param_index = ++test->param_index,
.parent = test,
};
kunit_init_test(&param_test, test_case->name, NULL);
param_test.log = test_case->log;
kunit_run_case_catch_errors(suite, test_case, &param_test);
if (param_desc[0] == '\0') {
snprintf(param_desc, sizeof(param_desc),
"param-%d", param_test.param_index);
}
kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
param_test.status,
param_test.param_index,
param_desc,
param_test.status_comment);
kunit_update_stats(param_stats, param_test.status);
/* Get next param. */
param_desc[0] = '\0';
curr_param = test_case->generate_params(test, curr_param,
param_desc);
}
/*
* TODO: Put into a try catch. Since we don't need suite->exit
* for it we can't reuse kunit_try_run_cleanup for this yet.
*/
if (test_case->param_exit)
test_case->param_exit(test);
/* TODO: Put this kunit_cleanup into a try-catch. */
kunit_cleanup(test);
}
static noinline_for_stack void
kunit_run_one_test(struct kunit_suite *suite, struct kunit_case *test_case,
struct kunit_result_stats *suite_stats,
struct kunit_result_stats *total_stats)
{
struct kunit test = { .param_value = NULL, .param_index = 0 };
struct kunit_result_stats param_stats = { 0 };
kunit_init_test(&test, test_case->name, test_case->log);
if (test_case->status == KUNIT_SKIPPED) {
/* Test marked as skip */
test.status = KUNIT_SKIPPED;
kunit_update_stats(&param_stats, test.status);
} else if (!test_case->generate_params) {
/* Non-parameterised test. */
test_case->status = KUNIT_SKIPPED;
kunit_run_case_catch_errors(suite, test_case, &test);
kunit_update_stats(&param_stats, test.status);
} else {
kunit_run_param_test(suite, test_case, &test, suite_stats,
total_stats, &param_stats);
}
kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
kunit_print_test_stats(&test, &param_stats);
kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE, test_case->status,
kunit_test_case_num(suite, test_case),
test_case->name,
test.status_comment);
kunit_update_stats(suite_stats, test_case->status);
kunit_accumulate_stats(total_stats, param_stats);
}
int kunit_run_tests(struct kunit_suite *suite)
{
struct kunit_case *test_case; struct kunit_case *test_case;
struct kunit_result_stats suite_stats = { 0 }; struct kunit_result_stats suite_stats = { 0 };
struct kunit_result_stats total_stats = { 0 }; struct kunit_result_stats total_stats = { 0 };
const void *curr_param;
/* Taint the kernel so we know we've run tests. */ /* Taint the kernel so we know we've run tests. */
add_taint(TAINT_TEST, LOCKDEP_STILL_OK); add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
@ -703,97 +806,13 @@ int kunit_run_tests(struct kunit_suite *suite)
kunit_print_suite_start(suite); kunit_print_suite_start(suite);
kunit_suite_for_each_test_case(suite, test_case) { kunit_suite_for_each_test_case(suite, test_case)
struct kunit test = { .param_value = NULL, .param_index = 0 }; kunit_run_one_test(suite, test_case, &suite_stats, &total_stats);
struct kunit_result_stats param_stats = { 0 };
kunit_init_test(&test, test_case->name, test_case->log);
if (test_case->status == KUNIT_SKIPPED) {
/* Test marked as skip */
test.status = KUNIT_SKIPPED;
kunit_update_stats(&param_stats, test.status);
} else if (!test_case->generate_params) {
/* Non-parameterised test. */
test_case->status = KUNIT_SKIPPED;
kunit_run_case_catch_errors(suite, test_case, &test);
kunit_update_stats(&param_stats, test.status);
} else {
kunit_init_parent_param_test(test_case, &test);
if (test_case->status == KUNIT_FAILURE) {
kunit_update_stats(&param_stats, test.status);
goto test_case_end;
}
/* Get initial param. */
param_desc[0] = '\0';
/* TODO: Make generate_params try-catch */
curr_param = test_case->generate_params(&test, NULL, param_desc);
test_case->status = KUNIT_SKIPPED;
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
"KTAP version 1\n");
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
"# Subtest: %s", test_case->name);
if (test.params_array.params &&
test_case->generate_params == kunit_array_gen_params) {
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT
KUNIT_SUBTEST_INDENT "1..%zd\n",
test.params_array.num_params);
}
while (curr_param) {
struct kunit param_test = {
.param_value = curr_param,
.param_index = ++test.param_index,
.parent = &test,
};
kunit_init_test(&param_test, test_case->name, NULL);
param_test.log = test_case->log;
kunit_run_case_catch_errors(suite, test_case, &param_test);
if (param_desc[0] == '\0') {
snprintf(param_desc, sizeof(param_desc),
"param-%d", param_test.param_index);
}
kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
param_test.status,
param_test.param_index,
param_desc,
param_test.status_comment);
kunit_update_stats(&param_stats, param_test.status);
/* Get next param. */
param_desc[0] = '\0';
curr_param = test_case->generate_params(&test, curr_param,
param_desc);
}
/*
* TODO: Put into a try catch. Since we don't need suite->exit
* for it we can't reuse kunit_try_run_cleanup for this yet.
*/
if (test_case->param_exit)
test_case->param_exit(&test);
/* TODO: Put this kunit_cleanup into a try-catch. */
kunit_cleanup(&test);
}
test_case_end:
kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
kunit_print_test_stats(&test, param_stats);
kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE, test_case->status,
kunit_test_case_num(suite, test_case),
test_case->name,
test.status_comment);
kunit_update_stats(&suite_stats, test_case->status);
kunit_accumulate_stats(&total_stats, param_stats);
}
if (suite->suite_exit) if (suite->suite_exit)
suite->suite_exit(suite); suite->suite_exit(suite);
kunit_print_suite_stats(suite, suite_stats, total_stats); kunit_print_suite_stats(suite, &suite_stats, &total_stats);
suite_end: suite_end:
kunit_print_suite_end(suite); kunit_print_suite_end(suite);

View file

@ -14,6 +14,10 @@ use crate::prelude::*;
/// Public but hidden since it should only be used from KUnit generated code. /// Public but hidden since it should only be used from KUnit generated code.
#[doc(hidden)] #[doc(hidden)]
pub fn err(args: fmt::Arguments<'_>) { pub fn err(args: fmt::Arguments<'_>) {
// `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
#[cfg(not(CONFIG_PRINTK))]
let _ = args;
// SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we // SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
// are passing. // are passing.
#[cfg(CONFIG_PRINTK)] #[cfg(CONFIG_PRINTK)]
@ -30,6 +34,10 @@ pub fn err(args: fmt::Arguments<'_>) {
/// Public but hidden since it should only be used from KUnit generated code. /// Public but hidden since it should only be used from KUnit generated code.
#[doc(hidden)] #[doc(hidden)]
pub fn info(args: fmt::Arguments<'_>) { pub fn info(args: fmt::Arguments<'_>) {
// `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
#[cfg(not(CONFIG_PRINTK))]
let _ = args;
// SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we // SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
// are passing. // are passing.
#[cfg(CONFIG_PRINTK)] #[cfg(CONFIG_PRINTK)]

View file

@ -346,8 +346,10 @@ class LinuxSourceTree:
return self.validate_config(build_dir) return self.validate_config(build_dir)
def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None) -> Iterator[str]: def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None) -> Iterator[str]:
if not args: # Copy to avoid mutating the caller-supplied list. exec_tests() reuses
args = [] # the same args across repeated run_kernel() calls (e.g. --run_isolated),
# so appending to the original would accumulate stale flags on each call.
args = list(args) if args else []
if filter_glob: if filter_glob:
args.append('kunit.filter_glob=' + filter_glob) args.append('kunit.filter_glob=' + filter_glob)
if filter: if filter:

View file

@ -503,6 +503,32 @@ class LinuxSourceTreeTest(unittest.TestCase):
with open(kunit_kernel.get_outfile_path(build_dir), 'rt') as outfile: with open(kunit_kernel.get_outfile_path(build_dir), 'rt') as outfile:
self.assertEqual(outfile.read(), 'hi\nbye\n', msg='Missing some output') self.assertEqual(outfile.read(), 'hi\nbye\n', msg='Missing some output')
def test_run_kernel_args_not_mutated(self):
"""Verify run_kernel() copies args so callers can reuse them."""
start_calls = []
def fake_start(start_args, unused_build_dir):
start_calls.append(list(start_args))
return subprocess.Popen(['printf', 'KTAP version 1\n'],
text=True, stdout=subprocess.PIPE)
with tempfile.TemporaryDirectory('') as build_dir:
tree = kunit_kernel.LinuxSourceTree(build_dir,
kunitconfig_paths=[os.devnull])
with mock.patch.object(tree._ops, 'start', side_effect=fake_start), \
mock.patch.object(kunit_kernel.subprocess, 'call'):
kernel_args = ['mem=1G']
for _ in tree.run_kernel(args=kernel_args, build_dir=build_dir,
filter_glob='suite.test1'):
pass
for _ in tree.run_kernel(args=kernel_args, build_dir=build_dir,
filter_glob='suite.test2'):
pass
self.assertEqual(kernel_args, ['mem=1G'],
'run_kernel() should not modify caller args')
self.assertIn('kunit.filter_glob=suite.test1', start_calls[0])
self.assertIn('kunit.filter_glob=suite.test2', start_calls[1])
def test_build_reconfig_no_config(self): def test_build_reconfig_no_config(self):
with tempfile.TemporaryDirectory('') as build_dir: with tempfile.TemporaryDirectory('') as build_dir:
with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f: with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f: