From: Carlos Llamas <cmllamas@google.com>
To: Arnd Bergmann <arnd@kernel.org>
Cc: "Brendan Higgins" <brendan.higgins@linux.dev>,
"David Gow" <davidgow@google.com>,
"Arnd Bergmann" <arnd@arndb.de>, "Rae Moar" <raemoar63@gmail.com>,
"Shuah Khan" <skhan@linuxfoundation.org>,
"Marie Zhussupova" <marievic@google.com>,
"Daniel Gomez" <da.gomez@samsung.com>,
"Stanislav Kinsburskii" <skinsburskii@linux.microsoft.com>,
"Thomas Weißschuh" <thomas.weissschuh@linutronix.de>,
"Ujwal Jain" <ujwaljain@google.com>,
linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH] kunit: reduce stack usage in kunit_run_tests()
Date: Sun, 25 Jan 2026 18:54:15 +0000 [thread overview]
Message-ID: <aXZm10CCRgdVou90@google.com> (raw)
In-Reply-To: <20251204101050.1035801-1-arnd@kernel.org>
On Thu, Dec 04, 2025 at 11:10:46AM +0100, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
>
> Some of the recent changes to the kunit framework caused the stack usage
> for kunit_run_tests() to grow higher than most other kernel functions,
> which triggers a warning when CONFIG_FRAME_WARN is set to a relatively
> low value:
>
> lib/kunit/test.c: In function 'kunit_run_tests':
> lib/kunit/test.c:801:1: error: the frame size of 1312 bytes is larger than 1280 bytes [-Werror=frame-larger-than=]
>
> Split out the inner loop into a separate function to ensure that each
> function remains under the limit, and pass the kunit_result_stats
> structures by reference to avoid excessive copies.
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> Sending it now as I ran into the build failure while testing 6.18.
> I only build-tested this, so please test it properly before applying.
I just ran this through our CI with no problems.
Tested-by: Carlos Llamas <cmllamas@google.com>
> ---
> lib/kunit/test.c | 221 ++++++++++++++++++++++++-----------------------
> 1 file changed, 115 insertions(+), 106 deletions(-)
>
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 62eb529824c6..c5fce199d880 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -94,7 +94,7 @@ struct kunit_result_stats {
> 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)
> return false;
> @@ -102,11 +102,11 @@ static bool kunit_should_print_stats(struct kunit_result_stats stats)
> if (kunit_stats_enabled == 2)
> return true;
>
> - return (stats.total > 1);
> + return (stats->total > 1);
> }
>
> 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))
> return;
> @@ -115,10 +115,10 @@ static void kunit_print_test_stats(struct kunit *test,
> KUNIT_SUBTEST_INDENT
> "# %s: pass:%lu fail:%lu skip:%lu total:%lu",
> test->name,
> - stats.passed,
> - stats.failed,
> - stats.skipped,
> - stats.total);
> + stats->passed,
> + stats->failed,
> + stats->skipped,
> + stats->total);
> }
>
> /* 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,
> - struct kunit_result_stats suite_stats,
> - struct kunit_result_stats param_stats)
> + struct kunit_result_stats *suite_stats,
> + struct kunit_result_stats *param_stats)
> {
> if (kunit_should_print_stats(suite_stats)) {
> kunit_log(KERN_INFO, suite,
> "# %s: pass:%lu fail:%lu skip:%lu total:%lu",
> suite->name,
> - suite_stats.passed,
> - suite_stats.failed,
> - suite_stats.skipped,
> - suite_stats.total);
> + suite_stats->passed,
> + suite_stats->failed,
> + suite_stats->skipped,
> + suite_stats->total);
> }
>
> if (kunit_should_print_stats(param_stats)) {
> kunit_log(KERN_INFO, suite,
> "# Totals: pass:%lu fail:%lu skip:%lu total:%lu",
> - param_stats.passed,
> - param_stats.failed,
> - param_stats.skipped,
> - param_stats.total);
> + param_stats->passed,
> + param_stats->failed,
> + param_stats->skipped,
> + param_stats->total);
> }
> }
>
> @@ -681,13 +681,106 @@ static void kunit_init_parent_param_test(struct kunit_case *test_case, struct ku
> }
> }
>
> -int kunit_run_tests(struct kunit_suite *suite)
> +static int 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 };
> char param_desc[KUNIT_PARAM_DESC_SIZE];
> + const void *curr_param;
> +
> + 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(¶m_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(¶m_stats, test.status);
> + } else {
> + kunit_init_parent_param_test(test_case, &test);
> + if (test_case->status == KUNIT_FAILURE) {
> + kunit_update_stats(¶m_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(¶m_test, test_case->name, NULL);
> + param_test.log = test_case->log;
> + kunit_run_case_catch_errors(suite, test_case, ¶m_test);
> +
> + if (param_desc[0] == '\0') {
> + snprintf(param_desc, sizeof(param_desc),
> + "param-%d", param_test.param_index);
> + }
> +
> + kunit_print_ok_not_ok(¶m_test, KUNIT_LEVEL_CASE_PARAM,
> + param_test.status,
> + param_test.param_index,
> + param_desc,
> + param_test.status_comment);
> +
> + kunit_update_stats(¶m_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, ¶m_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);
> +
> + return 0;
nit: the return value is always zero and it's not being used either, so
maybe switch to void?
> +}
> +
> +
> +int kunit_run_tests(struct kunit_suite *suite)
> +{
> struct kunit_case *test_case;
> struct kunit_result_stats suite_stats = { 0 };
> struct kunit_result_stats total_stats = { 0 };
> - const void *curr_param;
>
> /* Taint the kernel so we know we've run tests. */
> add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
> @@ -703,97 +796,13 @@ int kunit_run_tests(struct kunit_suite *suite)
>
> kunit_print_suite_start(suite);
>
> - kunit_suite_for_each_test_case(suite, test_case) {
> - 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(¶m_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(¶m_stats, test.status);
> - } else {
> - kunit_init_parent_param_test(test_case, &test);
> - if (test_case->status == KUNIT_FAILURE) {
> - kunit_update_stats(¶m_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(¶m_test, test_case->name, NULL);
> - param_test.log = test_case->log;
> - kunit_run_case_catch_errors(suite, test_case, ¶m_test);
> -
> - if (param_desc[0] == '\0') {
> - snprintf(param_desc, sizeof(param_desc),
> - "param-%d", param_test.param_index);
> - }
> -
> - kunit_print_ok_not_ok(¶m_test, KUNIT_LEVEL_CASE_PARAM,
> - param_test.status,
> - param_test.param_index,
> - param_desc,
> - param_test.status_comment);
> -
> - kunit_update_stats(¶m_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);
> - }
> + kunit_suite_for_each_test_case(suite, test_case)
> + kunit_run_one_test(suite, test_case, &suite_stats, &total_stats);
>
> if (suite->suite_exit)
> suite->suite_exit(suite);
>
> - kunit_print_suite_stats(suite, suite_stats, total_stats);
> + kunit_print_suite_stats(suite, &suite_stats, &total_stats);
> suite_end:
> kunit_print_suite_end(suite);
>
> --
I don't see any logical changes, everything seems to have been extracted
cleanly into kunit_run_one_test(). So this LGTM, just a minor nit.
--
Carlos LLamas
prev parent reply other threads:[~2026-01-25 18:54 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-04 10:10 [PATCH] kunit: reduce stack usage in kunit_run_tests() Arnd Bergmann
2026-01-25 18:54 ` Carlos Llamas [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aXZm10CCRgdVou90@google.com \
--to=cmllamas@google.com \
--cc=arnd@arndb.de \
--cc=arnd@kernel.org \
--cc=brendan.higgins@linux.dev \
--cc=da.gomez@samsung.com \
--cc=davidgow@google.com \
--cc=kunit-dev@googlegroups.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=marievic@google.com \
--cc=raemoar63@gmail.com \
--cc=skhan@linuxfoundation.org \
--cc=skinsburskii@linux.microsoft.com \
--cc=thomas.weissschuh@linutronix.de \
--cc=ujwaljain@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.