kunit: Adjust kunit_test timeout based on test_{suite,case} speed

Currently, the in-kernel kunit test case timeout is 300 seconds. (There
is a separate timeout mechanism for the whole test execution in
kunit.py, but that's unrelated.) However, tests marked 'slow' or 'very
slow' may timeout, particularly on slower machines.

Implement a multiplier to the test-case timeout, so that slower tests
have longer to complete:
- DEFAULT -> 1x default timeout
- KUNIT_SPEED_SLOW -> 3x default timeout
- KUNIT_SPEED_VERY_SLOW -> 12x default timeout

A further change is planned to allow user configuration of the
default/base timeout to allow people with faster or slower machines to
adjust these to their use-cases.

Link: https://lore.kernel.org/r/20250614084711.2654593-2-davidgow@google.com
Signed-off-by: Ujwal Jain <ujwaljain@google.com>
Co-developed-by: David Gow <davidgow@google.com>
Signed-off-by: David Gow <davidgow@google.com>
Reviewed-by: Rae Moar <rmoar@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
This commit is contained in:
Ujwal Jain 2025-06-14 16:47:11 +08:00 committed by Shuah Khan
parent e42ad39318
commit 63d0a91231
5 changed files with 56 additions and 33 deletions

View file

@ -47,6 +47,7 @@ struct kunit_try_catch {
int try_result;
kunit_try_catch_func_t try;
kunit_try_catch_func_t catch;
unsigned long timeout;
void *context;
};

View file

@ -44,7 +44,8 @@ static void kunit_test_try_catch_successful_try_no_catch(struct kunit *test)
kunit_try_catch_init(try_catch,
test,
kunit_test_successful_try,
kunit_test_no_catch);
kunit_test_no_catch,
300 * msecs_to_jiffies(MSEC_PER_SEC));
kunit_try_catch_run(try_catch, test);
KUNIT_EXPECT_TRUE(test, ctx->function_called);
@ -76,7 +77,8 @@ static void kunit_test_try_catch_unsuccessful_try_does_catch(struct kunit *test)
kunit_try_catch_init(try_catch,
test,
kunit_test_unsuccessful_try,
kunit_test_catch);
kunit_test_catch,
300 * msecs_to_jiffies(MSEC_PER_SEC));
kunit_try_catch_run(try_catch, test);
KUNIT_EXPECT_TRUE(test, ctx->function_called);
@ -130,7 +132,8 @@ static void kunit_test_fault_null_dereference(struct kunit *test)
kunit_try_catch_init(try_catch,
test,
kunit_test_null_dereference,
kunit_test_catch);
kunit_test_catch,
300 * msecs_to_jiffies(MSEC_PER_SEC));
kunit_try_catch_run(try_catch, test);
KUNIT_EXPECT_EQ(test, try_catch->try_result, -EINTR);

View file

@ -373,6 +373,46 @@ static void kunit_run_case_check_speed(struct kunit *test,
duration.tv_sec, duration.tv_nsec);
}
/* Returns timeout multiplier based on speed.
* DEFAULT: 1
* KUNIT_SPEED_SLOW: 3
* KUNIT_SPEED_VERY_SLOW: 12
*/
static int kunit_timeout_mult(enum kunit_speed speed)
{
switch (speed) {
case KUNIT_SPEED_SLOW:
return 3;
case KUNIT_SPEED_VERY_SLOW:
return 12;
default:
return 1;
}
}
static unsigned long kunit_test_timeout(struct kunit_suite *suite, struct kunit_case *test_case)
{
int mult = 1;
/*
* TODO: Make the default (base) timeout configurable, so that users with
* particularly slow or fast machines can successfully run tests, while
* still taking advantage of the relative speed.
*/
unsigned long default_timeout = 300;
/*
* The default test timeout is 300 seconds and will be adjusted by mult
* based on the test speed. The test speed will be overridden by the
* innermost test component.
*/
if (suite->attr.speed != KUNIT_SPEED_UNSET)
mult = kunit_timeout_mult(suite->attr.speed);
if (test_case->attr.speed != KUNIT_SPEED_UNSET)
mult = kunit_timeout_mult(test_case->attr.speed);
return mult * default_timeout * msecs_to_jiffies(MSEC_PER_SEC);
}
/*
* Initializes and runs test case. Does not clean up or do post validations.
*/
@ -527,7 +567,8 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
kunit_try_catch_init(try_catch,
test,
kunit_try_run_case,
kunit_catch_run_case);
kunit_catch_run_case,
kunit_test_timeout(suite, test_case));
context.test = test;
context.suite = suite;
context.test_case = test_case;
@ -537,7 +578,8 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
kunit_try_catch_init(try_catch,
test,
kunit_try_run_case_cleanup,
kunit_catch_run_case_cleanup);
kunit_catch_run_case_cleanup,
kunit_test_timeout(suite, test_case));
kunit_try_catch_run(try_catch, &context);
/* Propagate the parameter result to the test case. */

View file

@ -17,11 +17,13 @@ struct kunit;
static inline void kunit_try_catch_init(struct kunit_try_catch *try_catch,
struct kunit *test,
kunit_try_catch_func_t try,
kunit_try_catch_func_t catch)
kunit_try_catch_func_t catch,
unsigned long timeout)
{
try_catch->test = test;
try_catch->try = try;
try_catch->catch = catch;
try_catch->timeout = timeout;
}
#endif /* _KUNIT_TRY_CATCH_IMPL_H */

View file

@ -34,31 +34,6 @@ static int kunit_generic_run_threadfn_adapter(void *data)
return 0;
}
static unsigned long kunit_test_timeout(void)
{
/*
* TODO(brendanhiggins@google.com): We should probably have some type of
* variable timeout here. The only question is what that timeout value
* should be.
*
* The intention has always been, at some point, to be able to label
* tests with some type of size bucket (unit/small, integration/medium,
* large/system/end-to-end, etc), where each size bucket would get a
* default timeout value kind of like what Bazel does:
* https://docs.bazel.build/versions/master/be/common-definitions.html#test.size
* There is still some debate to be had on exactly how we do this. (For
* one, we probably want to have some sort of test runner level
* timeout.)
*
* For more background on this topic, see:
* https://mike-bland.com/2011/11/01/small-medium-large.html
*
* If tests timeout due to exceeding sysctl_hung_task_timeout_secs,
* the task will be killed and an oops generated.
*/
return 300 * msecs_to_jiffies(MSEC_PER_SEC); /* 5 min */
}
void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context)
{
struct kunit *test = try_catch->test;
@ -85,8 +60,8 @@ void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context)
task_done = task_struct->vfork_done;
wake_up_process(task_struct);
time_remaining = wait_for_completion_timeout(task_done,
kunit_test_timeout());
time_remaining = wait_for_completion_timeout(
task_done, try_catch->timeout);
if (time_remaining == 0) {
try_catch->try_result = -ETIMEDOUT;
kthread_stop(task_struct);