Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 950a81224e8b ("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>

+28 -16
+28 -16
include/kunit/run-in-irq-context.h
··· 12 12 #include <linux/hrtimer.h> 13 13 #include <linux/workqueue.h> 14 14 15 - #define KUNIT_IRQ_TEST_HRTIMER_INTERVAL us_to_ktime(5) 16 - 17 15 struct kunit_irq_test_state { 18 16 bool (*func)(void *test_specific_state); 19 17 void *test_specific_state; 20 18 bool task_func_reported_failure; 21 19 bool hardirq_func_reported_failure; 22 20 bool softirq_func_reported_failure; 21 + atomic_t task_func_calls; 23 22 atomic_t hardirq_func_calls; 24 23 atomic_t softirq_func_calls; 24 + ktime_t interval; 25 25 struct hrtimer timer; 26 26 struct work_struct bh_work; 27 27 }; ··· 30 30 { 31 31 struct kunit_irq_test_state *state = 32 32 container_of(timer, typeof(*state), timer); 33 + int task_calls, hardirq_calls, softirq_calls; 33 34 34 35 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); 36 47 37 48 if (!state->func(state->test_specific_state)) 38 49 state->hardirq_func_reported_failure = true; 39 50 40 - hrtimer_forward_now(&state->timer, KUNIT_IRQ_TEST_HRTIMER_INTERVAL); 51 + hrtimer_forward_now(&state->timer, state->interval); 41 52 queue_work(system_bh_wq, &state->bh_work); 42 53 return HRTIMER_RESTART; 43 54 } ··· 97 86 struct kunit_irq_test_state state = { 98 87 .func = func, 99 88 .test_specific_state = test_specific_state, 89 + /* 90 + * Start with a 5us timer interval. If the system can't keep 91 + * up, kunit_irq_test_timer_func() will increase it. 92 + */ 93 + .interval = us_to_ktime(5), 100 94 }; 101 95 unsigned long end_jiffies; 102 - int hardirq_calls, softirq_calls; 103 - bool allctx = false; 96 + int task_calls, hardirq_calls, softirq_calls; 104 97 105 98 /* 106 99 * Set up a hrtimer (the way we access hardirq context) and a work ··· 119 104 * and hardirq), or 1 second, whichever comes first. 120 105 */ 121 106 end_jiffies = jiffies + HZ; 122 - hrtimer_start(&state.timer, KUNIT_IRQ_TEST_HRTIMER_INTERVAL, 123 - HRTIMER_MODE_REL_HARD); 124 - for (int task_calls = 0, calls = 0; 125 - ((calls < max_iterations) || !allctx) && 126 - !time_after(jiffies, end_jiffies); 127 - task_calls++) { 107 + hrtimer_start(&state.timer, state.interval, HRTIMER_MODE_REL_HARD); 108 + do { 128 109 if (!func(test_specific_state)) 129 110 state.task_func_reported_failure = true; 130 111 112 + task_calls = atomic_inc_return(&state.task_func_calls); 131 113 hardirq_calls = atomic_read(&state.hardirq_func_calls); 132 114 softirq_calls = atomic_read(&state.softirq_func_calls); 133 - calls = task_calls + hardirq_calls + softirq_calls; 134 - allctx = (task_calls > 0) && (hardirq_calls > 0) && 135 - (softirq_calls > 0); 136 - } 115 + } while ((task_calls + hardirq_calls + softirq_calls < max_iterations || 116 + (task_calls == 0 || hardirq_calls == 0 || 117 + softirq_calls == 0)) && 118 + !time_after(jiffies, end_jiffies)); 137 119 138 120 /* Cancel the timer and work. */ 139 121 hrtimer_cancel(&state.timer);