aboutsummaryrefslogtreecommitdiffstats
path: root/lib/crc_kunit.c
diff options
context:
space:
mode:
authorKees Cook <kees@kernel.org>2024-12-02 15:55:40 +0800
committerKees Cook <kees@kernel.org>2025-02-10 18:25:39 -0800
commitdb6fe4d61ece24193eb4d94a82d967501d53358c (patch)
tree6c4cd0e856fb6249cfb0cb6a45b87f5dfafbb6f8 /lib/crc_kunit.c
parentlib/math: Add int_log test suite (diff)
downloadlinux-rng-db6fe4d61ece24193eb4d94a82d967501d53358c.tar.xz
linux-rng-db6fe4d61ece24193eb4d94a82d967501d53358c.zip
lib: Move KUnit tests into tests/ subdirectory
Following from the recent KUnit file naming discussion[1], move all KUnit tests in lib/ into lib/tests/. Link: https://lore.kernel.org/lkml/20240720165441.it.320-kees@kernel.org/ [1] Acked-by: Steven Rostedt (Google) <rostedt@goodmis.org> Acked-by: Jakub Kicinski <kuba@kernel.org> Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Reviewed-by: David Gow <davidgow@google.com> Acked-by: Vlastimil Babka <vbabka@suse.cz> Reviewed-by: Rae Moar <rmoar@google.com> Link: https://lore.kernel.org/r/20241202075545.3648096-4-davidgow@google.com Signed-off-by: Kees Cook <kees@kernel.org>
Diffstat (limited to 'lib/crc_kunit.c')
-rw-r--r--lib/crc_kunit.c435
1 files changed, 0 insertions, 435 deletions
diff --git a/lib/crc_kunit.c b/lib/crc_kunit.c
deleted file mode 100644
index 6a61d4b5fd45..000000000000
--- a/lib/crc_kunit.c
+++ /dev/null
@@ -1,435 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Unit tests and benchmarks for the CRC library functions
- *
- * Copyright 2024 Google LLC
- *
- * Author: Eric Biggers <ebiggers@google.com>
- */
-#include <kunit/test.h>
-#include <linux/crc16.h>
-#include <linux/crc-t10dif.h>
-#include <linux/crc32.h>
-#include <linux/crc32c.h>
-#include <linux/crc64.h>
-#include <linux/prandom.h>
-#include <linux/vmalloc.h>
-
-#define CRC_KUNIT_SEED 42
-#define CRC_KUNIT_MAX_LEN 16384
-#define CRC_KUNIT_NUM_TEST_ITERS 1000
-
-static struct rnd_state rng;
-static u8 *test_buffer;
-static size_t test_buflen;
-
-/**
- * struct crc_variant - describes a CRC variant
- * @bits: Number of bits in the CRC, 1 <= @bits <= 64.
- * @le: true if it's a "little endian" CRC (reversed mapping between bits and
- * polynomial coefficients in each byte), false if it's a "big endian" CRC
- * (natural mapping between bits and polynomial coefficients in each byte)
- * @poly: The generator polynomial with the highest-order term omitted.
- * Bit-reversed if @le is true.
- * @func: The function to compute a CRC. The type signature uses u64 so that it
- * can fit any CRC up to CRC-64.
- * @combine_func: Optional function to combine two CRCs.
- */
-struct crc_variant {
- int bits;
- bool le;
- u64 poly;
- u64 (*func)(u64 crc, const u8 *p, size_t len);
- u64 (*combine_func)(u64 crc1, u64 crc2, size_t len2);
-};
-
-static u32 rand32(void)
-{
- return prandom_u32_state(&rng);
-}
-
-static u64 rand64(void)
-{
- u32 n = rand32();
-
- return ((u64)n << 32) | rand32();
-}
-
-static u64 crc_mask(const struct crc_variant *v)
-{
- return (u64)-1 >> (64 - v->bits);
-}
-
-/* Reference implementation of any CRC variant */
-static u64 crc_ref(const struct crc_variant *v,
- u64 crc, const u8 *p, size_t len)
-{
- size_t i, j;
-
- for (i = 0; i < len; i++) {
- for (j = 0; j < 8; j++) {
- if (v->le) {
- crc ^= (p[i] >> j) & 1;
- crc = (crc >> 1) ^ ((crc & 1) ? v->poly : 0);
- } else {
- crc ^= (u64)((p[i] >> (7 - j)) & 1) <<
- (v->bits - 1);
- if (crc & (1ULL << (v->bits - 1)))
- crc = ((crc << 1) ^ v->poly) &
- crc_mask(v);
- else
- crc <<= 1;
- }
- }
- }
- return crc;
-}
-
-static int crc_suite_init(struct kunit_suite *suite)
-{
- /*
- * Allocate the test buffer using vmalloc() with a page-aligned length
- * so that it is immediately followed by a guard page. This allows
- * buffer overreads to be detected, even in assembly code.
- */
- test_buflen = round_up(CRC_KUNIT_MAX_LEN, PAGE_SIZE);
- test_buffer = vmalloc(test_buflen);
- if (!test_buffer)
- return -ENOMEM;
-
- prandom_seed_state(&rng, CRC_KUNIT_SEED);
- prandom_bytes_state(&rng, test_buffer, test_buflen);
- return 0;
-}
-
-static void crc_suite_exit(struct kunit_suite *suite)
-{
- vfree(test_buffer);
- test_buffer = NULL;
-}
-
-/* Generate a random initial CRC. */
-static u64 generate_random_initial_crc(const struct crc_variant *v)
-{
- switch (rand32() % 4) {
- case 0:
- return 0;
- case 1:
- return crc_mask(v); /* All 1 bits */
- default:
- return rand64() & crc_mask(v);
- }
-}
-
-/* Generate a random length, preferring small lengths. */
-static size_t generate_random_length(size_t max_length)
-{
- size_t len;
-
- switch (rand32() % 3) {
- case 0:
- len = rand32() % 128;
- break;
- case 1:
- len = rand32() % 3072;
- break;
- default:
- len = rand32();
- break;
- }
- return len % (max_length + 1);
-}
-
-/* Test that v->func gives the same CRCs as a reference implementation. */
-static void crc_main_test(struct kunit *test, const struct crc_variant *v)
-{
- size_t i;
-
- for (i = 0; i < CRC_KUNIT_NUM_TEST_ITERS; i++) {
- u64 init_crc, expected_crc, actual_crc;
- size_t len, offset;
- bool nosimd;
-
- init_crc = generate_random_initial_crc(v);
- len = generate_random_length(CRC_KUNIT_MAX_LEN);
-
- /* Generate a random offset. */
- if (rand32() % 2 == 0) {
- /* Use a random alignment mod 64 */
- offset = rand32() % 64;
- offset = min(offset, CRC_KUNIT_MAX_LEN - len);
- } else {
- /* Go up to the guard page, to catch buffer overreads */
- offset = test_buflen - len;
- }
-
- if (rand32() % 8 == 0)
- /* Refresh the data occasionally. */
- prandom_bytes_state(&rng, &test_buffer[offset], len);
-
- nosimd = rand32() % 8 == 0;
-
- /*
- * Compute the CRC, and verify that it equals the CRC computed
- * by a simple bit-at-a-time reference implementation.
- */
- expected_crc = crc_ref(v, init_crc, &test_buffer[offset], len);
- if (nosimd)
- local_irq_disable();
- actual_crc = v->func(init_crc, &test_buffer[offset], len);
- if (nosimd)
- local_irq_enable();
- KUNIT_EXPECT_EQ_MSG(test, expected_crc, actual_crc,
- "Wrong result with len=%zu offset=%zu nosimd=%d",
- len, offset, nosimd);
- }
-}
-
-/* Test that CRC(concat(A, B)) == combine_CRCs(CRC(A), CRC(B), len(B)). */
-static void crc_combine_test(struct kunit *test, const struct crc_variant *v)
-{
- int i;
-
- for (i = 0; i < 100; i++) {
- u64 init_crc = generate_random_initial_crc(v);
- size_t len1 = generate_random_length(CRC_KUNIT_MAX_LEN);
- size_t len2 = generate_random_length(CRC_KUNIT_MAX_LEN - len1);
- u64 crc1, crc2, expected_crc, actual_crc;
-
- prandom_bytes_state(&rng, test_buffer, len1 + len2);
- crc1 = v->func(init_crc, test_buffer, len1);
- crc2 = v->func(0, &test_buffer[len1], len2);
- expected_crc = v->func(init_crc, test_buffer, len1 + len2);
- actual_crc = v->combine_func(crc1, crc2, len2);
- KUNIT_EXPECT_EQ_MSG(test, expected_crc, actual_crc,
- "CRC combination gave wrong result with len1=%zu len2=%zu\n",
- len1, len2);
- }
-}
-
-static void crc_test(struct kunit *test, const struct crc_variant *v)
-{
- crc_main_test(test, v);
- if (v->combine_func)
- crc_combine_test(test, v);
-}
-
-static __always_inline void
-crc_benchmark(struct kunit *test,
- u64 (*crc_func)(u64 crc, const u8 *p, size_t len))
-{
- static const size_t lens_to_test[] = {
- 1, 16, 64, 127, 128, 200, 256, 511, 512, 1024, 3173, 4096, 16384,
- };
- size_t len, i, j, num_iters;
- /*
- * Some of the CRC library functions are marked as __pure, so use
- * volatile to ensure that all calls are really made as intended.
- */
- volatile u64 crc = 0;
- u64 t;
-
- if (!IS_ENABLED(CONFIG_CRC_BENCHMARK))
- kunit_skip(test, "not enabled");
-
- /* warm-up */
- for (i = 0; i < 10000000; i += CRC_KUNIT_MAX_LEN)
- crc = crc_func(crc, test_buffer, CRC_KUNIT_MAX_LEN);
-
- for (i = 0; i < ARRAY_SIZE(lens_to_test); i++) {
- len = lens_to_test[i];
- KUNIT_ASSERT_LE(test, len, CRC_KUNIT_MAX_LEN);
- num_iters = 10000000 / (len + 128);
- preempt_disable();
- t = ktime_get_ns();
- for (j = 0; j < num_iters; j++)
- crc = crc_func(crc, test_buffer, len);
- t = ktime_get_ns() - t;
- preempt_enable();
- kunit_info(test, "len=%zu: %llu MB/s\n",
- len, div64_u64((u64)len * num_iters * 1000, t));
- }
-}
-
-/* crc16 */
-
-static u64 crc16_wrapper(u64 crc, const u8 *p, size_t len)
-{
- return crc16(crc, p, len);
-}
-
-static const struct crc_variant crc_variant_crc16 = {
- .bits = 16,
- .le = true,
- .poly = 0xa001,
- .func = crc16_wrapper,
-};
-
-static void crc16_test(struct kunit *test)
-{
- crc_test(test, &crc_variant_crc16);
-}
-
-static void crc16_benchmark(struct kunit *test)
-{
- crc_benchmark(test, crc16_wrapper);
-}
-
-/* crc_t10dif */
-
-static u64 crc_t10dif_wrapper(u64 crc, const u8 *p, size_t len)
-{
- return crc_t10dif_update(crc, p, len);
-}
-
-static const struct crc_variant crc_variant_crc_t10dif = {
- .bits = 16,
- .le = false,
- .poly = 0x8bb7,
- .func = crc_t10dif_wrapper,
-};
-
-static void crc_t10dif_test(struct kunit *test)
-{
- crc_test(test, &crc_variant_crc_t10dif);
-}
-
-static void crc_t10dif_benchmark(struct kunit *test)
-{
- crc_benchmark(test, crc_t10dif_wrapper);
-}
-
-/* crc32_le */
-
-static u64 crc32_le_wrapper(u64 crc, const u8 *p, size_t len)
-{
- return crc32_le(crc, p, len);
-}
-
-static u64 crc32_le_combine_wrapper(u64 crc1, u64 crc2, size_t len2)
-{
- return crc32_le_combine(crc1, crc2, len2);
-}
-
-static const struct crc_variant crc_variant_crc32_le = {
- .bits = 32,
- .le = true,
- .poly = 0xedb88320,
- .func = crc32_le_wrapper,
- .combine_func = crc32_le_combine_wrapper,
-};
-
-static void crc32_le_test(struct kunit *test)
-{
- crc_test(test, &crc_variant_crc32_le);
-}
-
-static void crc32_le_benchmark(struct kunit *test)
-{
- crc_benchmark(test, crc32_le_wrapper);
-}
-
-/* crc32_be */
-
-static u64 crc32_be_wrapper(u64 crc, const u8 *p, size_t len)
-{
- return crc32_be(crc, p, len);
-}
-
-static const struct crc_variant crc_variant_crc32_be = {
- .bits = 32,
- .le = false,
- .poly = 0x04c11db7,
- .func = crc32_be_wrapper,
-};
-
-static void crc32_be_test(struct kunit *test)
-{
- crc_test(test, &crc_variant_crc32_be);
-}
-
-static void crc32_be_benchmark(struct kunit *test)
-{
- crc_benchmark(test, crc32_be_wrapper);
-}
-
-/* crc32c */
-
-static u64 crc32c_wrapper(u64 crc, const u8 *p, size_t len)
-{
- return crc32c(crc, p, len);
-}
-
-static u64 crc32c_combine_wrapper(u64 crc1, u64 crc2, size_t len2)
-{
- return __crc32c_le_combine(crc1, crc2, len2);
-}
-
-static const struct crc_variant crc_variant_crc32c = {
- .bits = 32,
- .le = true,
- .poly = 0x82f63b78,
- .func = crc32c_wrapper,
- .combine_func = crc32c_combine_wrapper,
-};
-
-static void crc32c_test(struct kunit *test)
-{
- crc_test(test, &crc_variant_crc32c);
-}
-
-static void crc32c_benchmark(struct kunit *test)
-{
- crc_benchmark(test, crc32c_wrapper);
-}
-
-/* crc64_be */
-
-static u64 crc64_be_wrapper(u64 crc, const u8 *p, size_t len)
-{
- return crc64_be(crc, p, len);
-}
-
-static const struct crc_variant crc_variant_crc64_be = {
- .bits = 64,
- .le = false,
- .poly = 0x42f0e1eba9ea3693,
- .func = crc64_be_wrapper,
-};
-
-static void crc64_be_test(struct kunit *test)
-{
- crc_test(test, &crc_variant_crc64_be);
-}
-
-static void crc64_be_benchmark(struct kunit *test)
-{
- crc_benchmark(test, crc64_be_wrapper);
-}
-
-static struct kunit_case crc_test_cases[] = {
- KUNIT_CASE(crc16_test),
- KUNIT_CASE(crc16_benchmark),
- KUNIT_CASE(crc_t10dif_test),
- KUNIT_CASE(crc_t10dif_benchmark),
- KUNIT_CASE(crc32_le_test),
- KUNIT_CASE(crc32_le_benchmark),
- KUNIT_CASE(crc32_be_test),
- KUNIT_CASE(crc32_be_benchmark),
- KUNIT_CASE(crc32c_test),
- KUNIT_CASE(crc32c_benchmark),
- KUNIT_CASE(crc64_be_test),
- KUNIT_CASE(crc64_be_benchmark),
- {},
-};
-
-static struct kunit_suite crc_test_suite = {
- .name = "crc",
- .test_cases = crc_test_cases,
- .suite_init = crc_suite_init,
- .suite_exit = crc_suite_exit,
-};
-kunit_test_suite(crc_test_suite);
-
-MODULE_DESCRIPTION("Unit tests and benchmarks for the CRC library functions");
-MODULE_LICENSE("GPL");