Skip to content

Commit e2fffe1

Browse files
committed
Merge tag 'crc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux
Pull CRC updates from Eric Biggers: "Update crc_kunit to test the CRC functions in softirq and hardirq contexts, similar to what the lib/crypto/ KUnit tests do. Move the helper function needed to do this into a common header. This is useful mainly to test fallback code paths for when FPU/SIMD/vector registers are unusable" * tag 'crc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux: Documentation/staging: Fix typo and incorrect citation in crc32.rst lib/crc: Drop inline from all *_mod_init_arch() functions lib/crc: Use underlying functions instead of crypto_simd_usable() lib/crc: crc_kunit: Test CRC computation in interrupt contexts kunit, lib/crypto: Move run_irq_test() to common header
2 parents d60ac92 + 136d029 commit e2fffe1

17 files changed

Lines changed: 219 additions & 165 deletions

File tree

Documentation/staging/crc32.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ do it in the right order, matching the endianness.
3434
Just like with ordinary division, you proceed one digit (bit) at a time.
3535
Each step of the division you take one more digit (bit) of the dividend
3636
and append it to the current remainder. Then you figure out the
37-
appropriate multiple of the divisor to subtract to being the remainder
37+
appropriate multiple of the divisor to subtract to bring the remainder
3838
back into range. In binary, this is easy - it has to be either 0 or 1,
3939
and to make the XOR cancel, it's just a copy of bit 32 of the remainder.
4040

@@ -116,7 +116,7 @@ for any fractional bytes at the end.
116116
To reduce the number of conditional branches, software commonly uses
117117
the byte-at-a-time table method, popularized by Dilip V. Sarwate,
118118
"Computation of Cyclic Redundancy Checks via Table Look-Up", Comm. ACM
119-
v.31 no.8 (August 1998) p. 1008-1013.
119+
v.31 no.8 (August 1988) p. 1008-1013.
120120

121121
Here, rather than just shifting one bit of the remainder to decide
122122
in the correct multiple to subtract, we can shift a byte at a time.

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

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
/*
3+
* Helper function for testing code in interrupt contexts
4+
*
5+
* Copyright 2025 Google LLC
6+
*/
7+
#ifndef _KUNIT_RUN_IN_IRQ_CONTEXT_H
8+
#define _KUNIT_RUN_IN_IRQ_CONTEXT_H
9+
10+
#include <kunit/test.h>
11+
#include <linux/timekeeping.h>
12+
#include <linux/hrtimer.h>
13+
#include <linux/workqueue.h>
14+
15+
#define KUNIT_IRQ_TEST_HRTIMER_INTERVAL us_to_ktime(5)
16+
17+
struct kunit_irq_test_state {
18+
bool (*func)(void *test_specific_state);
19+
void *test_specific_state;
20+
bool task_func_reported_failure;
21+
bool hardirq_func_reported_failure;
22+
bool softirq_func_reported_failure;
23+
unsigned long hardirq_func_calls;
24+
unsigned long softirq_func_calls;
25+
struct hrtimer timer;
26+
struct work_struct bh_work;
27+
};
28+
29+
static enum hrtimer_restart kunit_irq_test_timer_func(struct hrtimer *timer)
30+
{
31+
struct kunit_irq_test_state *state =
32+
container_of(timer, typeof(*state), timer);
33+
34+
WARN_ON_ONCE(!in_hardirq());
35+
state->hardirq_func_calls++;
36+
37+
if (!state->func(state->test_specific_state))
38+
state->hardirq_func_reported_failure = true;
39+
40+
hrtimer_forward_now(&state->timer, KUNIT_IRQ_TEST_HRTIMER_INTERVAL);
41+
queue_work(system_bh_wq, &state->bh_work);
42+
return HRTIMER_RESTART;
43+
}
44+
45+
static void kunit_irq_test_bh_work_func(struct work_struct *work)
46+
{
47+
struct kunit_irq_test_state *state =
48+
container_of(work, typeof(*state), bh_work);
49+
50+
WARN_ON_ONCE(!in_serving_softirq());
51+
state->softirq_func_calls++;
52+
53+
if (!state->func(state->test_specific_state))
54+
state->softirq_func_reported_failure = true;
55+
}
56+
57+
/*
58+
* Helper function which repeatedly runs the given @func in task, softirq, and
59+
* hardirq context concurrently, and reports a failure to KUnit if any
60+
* invocation of @func in any context returns false. @func is passed
61+
* @test_specific_state as its argument. At most 3 invocations of @func will
62+
* run concurrently: one in each of task, softirq, and hardirq context.
63+
*
64+
* The main purpose of this interrupt context testing is to validate fallback
65+
* code paths that run in contexts where the normal code path cannot be used,
66+
* typically due to the FPU or vector registers already being in-use in kernel
67+
* mode. These code paths aren't covered when the test code is executed only by
68+
* the KUnit test runner thread in task context. The reason for the concurrency
69+
* is because merely using hardirq context is not sufficient to reach a fallback
70+
* code path on some architectures; the hardirq actually has to occur while the
71+
* FPU or vector unit was already in-use in kernel mode.
72+
*
73+
* Another purpose of this testing is to detect issues with the architecture's
74+
* irq_fpu_usable() and kernel_fpu_begin/end() or equivalent functions,
75+
* especially in softirq context when the softirq may have interrupted a task
76+
* already using kernel-mode FPU or vector (if the arch didn't prevent that).
77+
* Crypto functions are often executed in softirqs, so this is important.
78+
*/
79+
static inline void kunit_run_irq_test(struct kunit *test, bool (*func)(void *),
80+
int max_iterations,
81+
void *test_specific_state)
82+
{
83+
struct kunit_irq_test_state state = {
84+
.func = func,
85+
.test_specific_state = test_specific_state,
86+
};
87+
unsigned long end_jiffies;
88+
89+
/*
90+
* Set up a hrtimer (the way we access hardirq context) and a work
91+
* struct for the BH workqueue (the way we access softirq context).
92+
*/
93+
hrtimer_setup_on_stack(&state.timer, kunit_irq_test_timer_func,
94+
CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
95+
INIT_WORK_ONSTACK(&state.bh_work, kunit_irq_test_bh_work_func);
96+
97+
/* Run for up to max_iterations or 1 second, whichever comes first. */
98+
end_jiffies = jiffies + HZ;
99+
hrtimer_start(&state.timer, KUNIT_IRQ_TEST_HRTIMER_INTERVAL,
100+
HRTIMER_MODE_REL_HARD);
101+
for (int i = 0; i < max_iterations && !time_after(jiffies, end_jiffies);
102+
i++) {
103+
if (!func(test_specific_state))
104+
state.task_func_reported_failure = true;
105+
}
106+
107+
/* Cancel the timer and work. */
108+
hrtimer_cancel(&state.timer);
109+
flush_work(&state.bh_work);
110+
111+
/* Sanity check: the timer and BH functions should have been run. */
112+
KUNIT_EXPECT_GT_MSG(test, state.hardirq_func_calls, 0,
113+
"Timer function was not called");
114+
KUNIT_EXPECT_GT_MSG(test, state.softirq_func_calls, 0,
115+
"BH work function was not called");
116+
117+
/* Check for incorrect hash values reported from any context. */
118+
KUNIT_EXPECT_FALSE_MSG(
119+
test, state.task_func_reported_failure,
120+
"Incorrect hash values reported from task context");
121+
KUNIT_EXPECT_FALSE_MSG(
122+
test, state.hardirq_func_reported_failure,
123+
"Incorrect hash values reported from hardirq context");
124+
KUNIT_EXPECT_FALSE_MSG(
125+
test, state.softirq_func_reported_failure,
126+
"Incorrect hash values reported from softirq context");
127+
}
128+
129+
#endif /* _KUNIT_RUN_IN_IRQ_CONTEXT_H */

lib/crc/arm/crc-t10dif.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
* Copyright (C) 2016 Linaro Ltd <ard.biesheuvel@linaro.org>
66
*/
77

8-
#include <crypto/internal/simd.h>
9-
108
#include <asm/neon.h>
119
#include <asm/simd.h>
1210

@@ -23,15 +21,15 @@ static inline u16 crc_t10dif_arch(u16 crc, const u8 *data, size_t length)
2321
{
2422
if (length >= CRC_T10DIF_PMULL_CHUNK_SIZE) {
2523
if (static_branch_likely(&have_pmull)) {
26-
if (crypto_simd_usable()) {
24+
if (likely(may_use_simd())) {
2725
kernel_neon_begin();
2826
crc = crc_t10dif_pmull64(crc, data, length);
2927
kernel_neon_end();
3028
return crc;
3129
}
3230
} else if (length > CRC_T10DIF_PMULL_CHUNK_SIZE &&
3331
static_branch_likely(&have_neon) &&
34-
crypto_simd_usable()) {
32+
likely(may_use_simd())) {
3533
u8 buf[16] __aligned(16);
3634

3735
kernel_neon_begin();
@@ -45,7 +43,7 @@ static inline u16 crc_t10dif_arch(u16 crc, const u8 *data, size_t length)
4543
}
4644

4745
#define crc_t10dif_mod_init_arch crc_t10dif_mod_init_arch
48-
static inline void crc_t10dif_mod_init_arch(void)
46+
static void crc_t10dif_mod_init_arch(void)
4947
{
5048
if (elf_hwcap & HWCAP_NEON) {
5149
static_branch_enable(&have_neon);

lib/crc/arm/crc32.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
#include <linux/cpufeature.h>
99

10-
#include <crypto/internal/simd.h>
11-
1210
#include <asm/hwcap.h>
1311
#include <asm/neon.h>
1412
#include <asm/simd.h>
@@ -34,7 +32,7 @@ static inline u32 crc32_le_scalar(u32 crc, const u8 *p, size_t len)
3432
static inline u32 crc32_le_arch(u32 crc, const u8 *p, size_t len)
3533
{
3634
if (len >= PMULL_MIN_LEN + 15 &&
37-
static_branch_likely(&have_pmull) && crypto_simd_usable()) {
35+
static_branch_likely(&have_pmull) && likely(may_use_simd())) {
3836
size_t n = -(uintptr_t)p & 15;
3937

4038
/* align p to 16-byte boundary */
@@ -63,7 +61,7 @@ static inline u32 crc32c_scalar(u32 crc, const u8 *p, size_t len)
6361
static inline u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
6462
{
6563
if (len >= PMULL_MIN_LEN + 15 &&
66-
static_branch_likely(&have_pmull) && crypto_simd_usable()) {
64+
static_branch_likely(&have_pmull) && likely(may_use_simd())) {
6765
size_t n = -(uintptr_t)p & 15;
6866

6967
/* align p to 16-byte boundary */
@@ -85,7 +83,7 @@ static inline u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
8583
#define crc32_be_arch crc32_be_base /* not implemented on this arch */
8684

8785
#define crc32_mod_init_arch crc32_mod_init_arch
88-
static inline void crc32_mod_init_arch(void)
86+
static void crc32_mod_init_arch(void)
8987
{
9088
if (elf_hwcap2 & HWCAP2_CRC32)
9189
static_branch_enable(&have_crc32);

lib/crc/arm64/crc-t10dif.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
#include <linux/cpufeature.h>
99

10-
#include <crypto/internal/simd.h>
11-
1210
#include <asm/neon.h>
1311
#include <asm/simd.h>
1412

@@ -25,15 +23,15 @@ static inline u16 crc_t10dif_arch(u16 crc, const u8 *data, size_t length)
2523
{
2624
if (length >= CRC_T10DIF_PMULL_CHUNK_SIZE) {
2725
if (static_branch_likely(&have_pmull)) {
28-
if (crypto_simd_usable()) {
26+
if (likely(may_use_simd())) {
2927
kernel_neon_begin();
3028
crc = crc_t10dif_pmull_p64(crc, data, length);
3129
kernel_neon_end();
3230
return crc;
3331
}
3432
} else if (length > CRC_T10DIF_PMULL_CHUNK_SIZE &&
3533
static_branch_likely(&have_asimd) &&
36-
crypto_simd_usable()) {
34+
likely(may_use_simd())) {
3735
u8 buf[16];
3836

3937
kernel_neon_begin();
@@ -47,7 +45,7 @@ static inline u16 crc_t10dif_arch(u16 crc, const u8 *data, size_t length)
4745
}
4846

4947
#define crc_t10dif_mod_init_arch crc_t10dif_mod_init_arch
50-
static inline void crc_t10dif_mod_init_arch(void)
48+
static void crc_t10dif_mod_init_arch(void)
5149
{
5250
if (cpu_have_named_feature(ASIMD)) {
5351
static_branch_enable(&have_asimd);

lib/crc/arm64/crc32.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
#include <asm/neon.h>
66
#include <asm/simd.h>
77

8-
#include <crypto/internal/simd.h>
9-
108
// The minimum input length to consider the 4-way interleaved code path
119
static const size_t min_len = 1024;
1210

@@ -23,7 +21,8 @@ static inline u32 crc32_le_arch(u32 crc, const u8 *p, size_t len)
2321
if (!alternative_has_cap_likely(ARM64_HAS_CRC32))
2422
return crc32_le_base(crc, p, len);
2523

26-
if (len >= min_len && cpu_have_named_feature(PMULL) && crypto_simd_usable()) {
24+
if (len >= min_len && cpu_have_named_feature(PMULL) &&
25+
likely(may_use_simd())) {
2726
kernel_neon_begin();
2827
crc = crc32_le_arm64_4way(crc, p, len);
2928
kernel_neon_end();
@@ -43,7 +42,8 @@ static inline u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
4342
if (!alternative_has_cap_likely(ARM64_HAS_CRC32))
4443
return crc32c_base(crc, p, len);
4544

46-
if (len >= min_len && cpu_have_named_feature(PMULL) && crypto_simd_usable()) {
45+
if (len >= min_len && cpu_have_named_feature(PMULL) &&
46+
likely(may_use_simd())) {
4747
kernel_neon_begin();
4848
crc = crc32c_le_arm64_4way(crc, p, len);
4949
kernel_neon_end();
@@ -63,7 +63,8 @@ static inline u32 crc32_be_arch(u32 crc, const u8 *p, size_t len)
6363
if (!alternative_has_cap_likely(ARM64_HAS_CRC32))
6464
return crc32_be_base(crc, p, len);
6565

66-
if (len >= min_len && cpu_have_named_feature(PMULL) && crypto_simd_usable()) {
66+
if (len >= min_len && cpu_have_named_feature(PMULL) &&
67+
likely(may_use_simd())) {
6768
kernel_neon_begin();
6869
crc = crc32_be_arm64_4way(crc, p, len);
6970
kernel_neon_end();

lib/crc/loongarch/crc32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ static inline u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
101101
#define crc32_be_arch crc32_be_base /* not implemented on this arch */
102102

103103
#define crc32_mod_init_arch crc32_mod_init_arch
104-
static inline void crc32_mod_init_arch(void)
104+
static void crc32_mod_init_arch(void)
105105
{
106106
if (cpu_has_crc32)
107107
static_branch_enable(&have_crc32);

lib/crc/mips/crc32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ static inline u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
148148
#define crc32_be_arch crc32_be_base /* not implemented on this arch */
149149

150150
#define crc32_mod_init_arch crc32_mod_init_arch
151-
static inline void crc32_mod_init_arch(void)
151+
static void crc32_mod_init_arch(void)
152152
{
153153
if (cpu_have_feature(cpu_feature(MIPS_CRC32)))
154154
static_branch_enable(&have_crc32);

lib/crc/powerpc/crc-t10dif.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* [based on crc32c-vpmsum_glue.c]
77
*/
88

9+
#include <asm/simd.h>
910
#include <asm/switch_to.h>
10-
#include <crypto/internal/simd.h>
1111
#include <linux/cpufeature.h>
1212
#include <linux/jump_label.h>
1313
#include <linux/preempt.h>
@@ -29,7 +29,8 @@ static inline u16 crc_t10dif_arch(u16 crci, const u8 *p, size_t len)
2929
u32 crc = crci;
3030

3131
if (len < (VECTOR_BREAKPOINT + VMX_ALIGN) ||
32-
!static_branch_likely(&have_vec_crypto) || !crypto_simd_usable())
32+
!static_branch_likely(&have_vec_crypto) ||
33+
unlikely(!may_use_simd()))
3334
return crc_t10dif_generic(crc, p, len);
3435

3536
if ((unsigned long)p & VMX_ALIGN_MASK) {
@@ -61,7 +62,7 @@ static inline u16 crc_t10dif_arch(u16 crci, const u8 *p, size_t len)
6162
}
6263

6364
#define crc_t10dif_mod_init_arch crc_t10dif_mod_init_arch
64-
static inline void crc_t10dif_mod_init_arch(void)
65+
static void crc_t10dif_mod_init_arch(void)
6566
{
6667
if (cpu_has_feature(CPU_FTR_ARCH_207S) &&
6768
(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO))

lib/crc/powerpc/crc32.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0-only
2+
#include <asm/simd.h>
23
#include <asm/switch_to.h>
3-
#include <crypto/internal/simd.h>
44
#include <linux/cpufeature.h>
55
#include <linux/jump_label.h>
66
#include <linux/preempt.h>
@@ -24,7 +24,8 @@ static inline u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
2424
unsigned int tail;
2525

2626
if (len < (VECTOR_BREAKPOINT + VMX_ALIGN) ||
27-
!static_branch_likely(&have_vec_crypto) || !crypto_simd_usable())
27+
!static_branch_likely(&have_vec_crypto) ||
28+
unlikely(!may_use_simd()))
2829
return crc32c_base(crc, p, len);
2930

3031
if ((unsigned long)p & VMX_ALIGN_MASK) {
@@ -54,7 +55,7 @@ static inline u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
5455
}
5556

5657
#define crc32_mod_init_arch crc32_mod_init_arch
57-
static inline void crc32_mod_init_arch(void)
58+
static void crc32_mod_init_arch(void)
5859
{
5960
if (cpu_has_feature(CPU_FTR_ARCH_207S) &&
6061
(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO))

0 commit comments

Comments
 (0)