Skip to content

Commit 201ceb9

Browse files
author
Eric Biggers
committed
kunit: irq: Ensure timer doesn't fire too frequently
Fix a bug where kunit_run_irq_test() could hang if the system is too slow. This was noticed with the crypto library tests in certain VMs. Specifically, if kunit_irq_test_timer_func() and the associated hrtimer code took over 5us to run, then the CPU would spend all its time executing that code in hardirq context. As a result, the task executing kunit_run_irq_test() never had a chance to run, exit the loop, and cancel the timer. To fix it, make kunit_irq_test_timer_func() increase the timer interval when the other contexts aren't having a chance to run. Fixes: 950a812 ("lib/crypto: tests: Add hash-test-template.h and gen-hash-testvecs.py") Cc: stable@vger.kernel.org Reviewed-by: David Gow <david@davidgow.net> Link: https://lore.kernel.org/r/20260224033751.97615-1-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org>
1 parent 6de23f8 commit 201ceb9

1 file changed

Lines changed: 28 additions & 16 deletions

File tree

include/kunit/run-in-irq-context.h

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212
#include <linux/hrtimer.h>
1313
#include <linux/workqueue.h>
1414

15-
#define KUNIT_IRQ_TEST_HRTIMER_INTERVAL us_to_ktime(5)
16-
1715
struct kunit_irq_test_state {
1816
bool (*func)(void *test_specific_state);
1917
void *test_specific_state;
2018
bool task_func_reported_failure;
2119
bool hardirq_func_reported_failure;
2220
bool softirq_func_reported_failure;
21+
atomic_t task_func_calls;
2322
atomic_t hardirq_func_calls;
2423
atomic_t softirq_func_calls;
24+
ktime_t interval;
2525
struct hrtimer timer;
2626
struct work_struct bh_work;
2727
};
@@ -30,14 +30,25 @@ static enum hrtimer_restart kunit_irq_test_timer_func(struct hrtimer *timer)
3030
{
3131
struct kunit_irq_test_state *state =
3232
container_of(timer, typeof(*state), timer);
33+
int task_calls, hardirq_calls, softirq_calls;
3334

3435
WARN_ON_ONCE(!in_hardirq());
35-
atomic_inc(&state->hardirq_func_calls);
36+
task_calls = atomic_read(&state->task_func_calls);
37+
hardirq_calls = atomic_inc_return(&state->hardirq_func_calls);
38+
softirq_calls = atomic_read(&state->softirq_func_calls);
39+
40+
/*
41+
* If the timer is firing too often for the softirq or task to ever have
42+
* a chance to run, increase the timer interval. This is needed on very
43+
* slow systems.
44+
*/
45+
if (hardirq_calls >= 20 && (softirq_calls == 0 || task_calls == 0))
46+
state->interval = ktime_add_ns(state->interval, 250);
3647

3748
if (!state->func(state->test_specific_state))
3849
state->hardirq_func_reported_failure = true;
3950

40-
hrtimer_forward_now(&state->timer, KUNIT_IRQ_TEST_HRTIMER_INTERVAL);
51+
hrtimer_forward_now(&state->timer, state->interval);
4152
queue_work(system_bh_wq, &state->bh_work);
4253
return HRTIMER_RESTART;
4354
}
@@ -86,10 +97,14 @@ static inline void kunit_run_irq_test(struct kunit *test, bool (*func)(void *),
8697
struct kunit_irq_test_state state = {
8798
.func = func,
8899
.test_specific_state = test_specific_state,
100+
/*
101+
* Start with a 5us timer interval. If the system can't keep
102+
* up, kunit_irq_test_timer_func() will increase it.
103+
*/
104+
.interval = us_to_ktime(5),
89105
};
90106
unsigned long end_jiffies;
91-
int hardirq_calls, softirq_calls;
92-
bool allctx = false;
107+
int task_calls, hardirq_calls, softirq_calls;
93108

94109
/*
95110
* Set up a hrtimer (the way we access hardirq context) and a work
@@ -104,21 +119,18 @@ static inline void kunit_run_irq_test(struct kunit *test, bool (*func)(void *),
104119
* and hardirq), or 1 second, whichever comes first.
105120
*/
106121
end_jiffies = jiffies + HZ;
107-
hrtimer_start(&state.timer, KUNIT_IRQ_TEST_HRTIMER_INTERVAL,
108-
HRTIMER_MODE_REL_HARD);
109-
for (int task_calls = 0, calls = 0;
110-
((calls < max_iterations) || !allctx) &&
111-
!time_after(jiffies, end_jiffies);
112-
task_calls++) {
122+
hrtimer_start(&state.timer, state.interval, HRTIMER_MODE_REL_HARD);
123+
do {
113124
if (!func(test_specific_state))
114125
state.task_func_reported_failure = true;
115126

127+
task_calls = atomic_inc_return(&state.task_func_calls);
116128
hardirq_calls = atomic_read(&state.hardirq_func_calls);
117129
softirq_calls = atomic_read(&state.softirq_func_calls);
118-
calls = task_calls + hardirq_calls + softirq_calls;
119-
allctx = (task_calls > 0) && (hardirq_calls > 0) &&
120-
(softirq_calls > 0);
121-
}
130+
} while ((task_calls + hardirq_calls + softirq_calls < max_iterations ||
131+
(task_calls == 0 || hardirq_calls == 0 ||
132+
softirq_calls == 0)) &&
133+
!time_after(jiffies, end_jiffies));
122134

123135
/* Cancel the timer and work. */
124136
hrtimer_cancel(&state.timer);

0 commit comments

Comments
 (0)