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

lib/crc: Prepare for arch-optimized code in subdirs of lib/crc/

Rework how lib/crc/ supports arch-optimized code. First, instead of the
arch-optimized CRC code being in arch/$(SRCARCH)/lib/, it will now be in
lib/crc/$(SRCARCH)/. Second, the API functions (e.g. crc32c()),
arch-optimized functions (e.g. crc32c_arch()), and generic functions
(e.g. crc32c_base()) will now be part of a single module for each CRC
type, allowing better inlining and dead code elimination. The second
change is made possible by the first.

As an example, consider CONFIG_CRC32=m on x86. We'll now have just
crc32.ko instead of both crc32-x86.ko and crc32.ko. The two modules
were already coupled together and always both got loaded together via
direct symbol dependency, so the separation provided no benefit.

Note: later I'd like to apply the same design to lib/crypto/ too, where
often the API functions are out-of-line so this will work even better.
In those cases, for each algorithm we currently have 3 modules all
coupled together, e.g. libsha256.ko, libsha256-generic.ko, and
sha256-x86.ko. We should have just one, inline things properly, and
rely on the compiler's dead code elimination to decide the inclusion of
the generic code instead of manually setting it via kconfig.

Having arch-specific code outside arch/ was somewhat controversial when
Zinc proposed it back in 2018. But I don't think the concerns are
warranted. It's better from a technical perspective, as it enables the
improvements mentioned above. This model is already successfully used
in other places in the kernel such as lib/raid6/. The community of each
architecture still remains free to work on the code, even if it's not in
arch/. At the time there was also a desire to put the library code in
the same files as the old-school crypto API, but that was a mistake; now
that the library is separate, that's no longer a constraint either.

Reviewed-by: "Martin K. Petersen" <martin.petersen@oracle.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Acked-by: "Jason A. Donenfeld" <Jason@zx2c4.com>
Link: https://lore.kernel.org/r/20250607200454.73587-3-ebiggers@kernel.org
Link: https://lore.kernel.org/r/20250612054514.142728-1-ebiggers@kernel.org
Link: https://lore.kernel.org/r/20250621012221.4351-1-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>

+164 -94
+1 -1
Documentation/core-api/kernel-api.rst
··· 148 148 .. kernel-doc:: lib/crc/crc16.c 149 149 :export: 150 150 151 - .. kernel-doc:: lib/crc/crc32.c 151 + .. kernel-doc:: lib/crc/crc32-main.c 152 152 153 153 .. kernel-doc:: lib/crc/crc-ccitt.c 154 154 :export:
-1
MAINTAINERS
··· 6359 6359 S: Maintained 6360 6360 T: git https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git crc-next 6361 6361 F: Documentation/staging/crc* 6362 - F: arch/*/lib/crc* 6363 6362 F: include/linux/crc* 6364 6363 F: lib/crc/ 6365 6364 F: scripts/gen-crc-consts.py
+1 -9
include/linux/crc-t10dif.h
··· 4 4 5 5 #include <linux/types.h> 6 6 7 - u16 crc_t10dif_arch(u16 crc, const u8 *p, size_t len); 8 - u16 crc_t10dif_generic(u16 crc, const u8 *p, size_t len); 9 - 10 - static inline u16 crc_t10dif_update(u16 crc, const u8 *p, size_t len) 11 - { 12 - if (IS_ENABLED(CONFIG_CRC_T10DIF_ARCH)) 13 - return crc_t10dif_arch(crc, p, len); 14 - return crc_t10dif_generic(crc, p, len); 15 - } 7 + u16 crc_t10dif_update(u16 crc, const u8 *p, size_t len); 16 8 17 9 static inline u16 crc_t10dif(const u8 *p, size_t len) 18 10 {
+3 -27
include/linux/crc32.h
··· 5 5 #include <linux/types.h> 6 6 #include <linux/bitrev.h> 7 7 8 - u32 crc32_le_arch(u32 crc, const u8 *p, size_t len); 9 - u32 crc32_le_base(u32 crc, const u8 *p, size_t len); 10 - u32 crc32_be_arch(u32 crc, const u8 *p, size_t len); 11 - u32 crc32_be_base(u32 crc, const u8 *p, size_t len); 12 - u32 crc32c_arch(u32 crc, const u8 *p, size_t len); 13 - u32 crc32c_base(u32 crc, const u8 *p, size_t len); 14 - 15 - static inline u32 crc32_le(u32 crc, const void *p, size_t len) 16 - { 17 - if (IS_ENABLED(CONFIG_CRC32_ARCH)) 18 - return crc32_le_arch(crc, p, len); 19 - return crc32_le_base(crc, p, len); 20 - } 21 - 22 - static inline u32 crc32_be(u32 crc, const void *p, size_t len) 23 - { 24 - if (IS_ENABLED(CONFIG_CRC32_ARCH)) 25 - return crc32_be_arch(crc, p, len); 26 - return crc32_be_base(crc, p, len); 27 - } 28 - 29 - static inline u32 crc32c(u32 crc, const void *p, size_t len) 30 - { 31 - if (IS_ENABLED(CONFIG_CRC32_ARCH)) 32 - return crc32c_arch(crc, p, len); 33 - return crc32c_base(crc, p, len); 34 - } 8 + u32 crc32_le(u32 crc, const void *p, size_t len); 9 + u32 crc32_be(u32 crc, const void *p, size_t len); 10 + u32 crc32c(u32 crc, const void *p, size_t len); 35 11 36 12 /* 37 13 * crc32_optimizations() returns flags that indicate which CRC32 library
+2 -17
include/linux/crc64.h
··· 4 4 5 5 #include <linux/types.h> 6 6 7 - u64 crc64_be_arch(u64 crc, const u8 *p, size_t len); 8 - u64 crc64_be_generic(u64 crc, const u8 *p, size_t len); 9 - u64 crc64_nvme_arch(u64 crc, const u8 *p, size_t len); 10 - u64 crc64_nvme_generic(u64 crc, const u8 *p, size_t len); 11 - 12 7 /** 13 8 * crc64_be - Calculate bitwise big-endian ECMA-182 CRC64 14 9 * @crc: seed value for computation. 0 or (u64)~0 for a new CRC calculation, ··· 11 16 * @p: pointer to buffer over which CRC64 is run 12 17 * @len: length of buffer @p 13 18 */ 14 - static inline u64 crc64_be(u64 crc, const void *p, size_t len) 15 - { 16 - if (IS_ENABLED(CONFIG_CRC64_ARCH)) 17 - return crc64_be_arch(crc, p, len); 18 - return crc64_be_generic(crc, p, len); 19 - } 19 + u64 crc64_be(u64 crc, const void *p, size_t len); 20 20 21 21 /** 22 22 * crc64_nvme - Calculate CRC64-NVME ··· 23 33 * This computes the CRC64 defined in the NVME NVM Command Set Specification, 24 34 * *including the bitwise inversion at the beginning and end*. 25 35 */ 26 - static inline u64 crc64_nvme(u64 crc, const void *p, size_t len) 27 - { 28 - if (IS_ENABLED(CONFIG_CRC64_ARCH)) 29 - return ~crc64_nvme_arch(~crc, p, len); 30 - return ~crc64_nvme_generic(~crc, p, len); 31 - } 36 + u64 crc64_nvme(u64 crc, const void *p, size_t len); 32 37 33 38 #endif /* _LINUX_CRC64_H */
+7 -6
lib/crc/Kconfig
··· 48 48 bool 49 49 50 50 config CRC_T10DIF_ARCH 51 - tristate 52 - default CRC_T10DIF if ARCH_HAS_CRC_T10DIF && CRC_OPTIMIZATIONS 51 + bool 52 + depends on CRC_T10DIF && CRC_OPTIMIZATIONS 53 53 54 54 config CRC32 55 55 tristate ··· 62 62 bool 63 63 64 64 config CRC32_ARCH 65 - tristate 66 - default CRC32 if ARCH_HAS_CRC32 && CRC_OPTIMIZATIONS 65 + bool 66 + depends on CRC32 && CRC_OPTIMIZATIONS 67 67 68 68 config CRC64 69 69 tristate ··· 75 75 bool 76 76 77 77 config CRC64_ARCH 78 - tristate 79 - default CRC64 if ARCH_HAS_CRC64 && CRC_OPTIMIZATIONS 78 + bool 79 + depends on CRC64 && CRC_OPTIMIZATIONS 80 80 81 81 config CRC_OPTIMIZATIONS 82 82 bool "Enable optimized CRC implementations" if EXPERT 83 + depends on !UML 83 84 default y 84 85 help 85 86 Disabling this option reduces code size slightly by disabling the
+18 -2
lib/crc/Makefile
··· 8 8 obj-$(CONFIG_CRC16) += crc16.o 9 9 obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o 10 10 obj-$(CONFIG_CRC_ITU_T) += crc-itu-t.o 11 + 11 12 obj-$(CONFIG_CRC_T10DIF) += crc-t10dif.o 13 + crc-t10dif-y := crc-t10dif-main.o 14 + ifeq ($(CONFIG_CRC_T10DIF_ARCH),y) 15 + CFLAGS_crc-t10dif-main.o += -I$(src)/$(SRCARCH) 16 + endif 17 + 12 18 obj-$(CONFIG_CRC32) += crc32.o 19 + crc32-y := crc32-main.o 20 + ifeq ($(CONFIG_CRC32_ARCH),y) 21 + CFLAGS_crc32-main.o += -I$(src)/$(SRCARCH) 22 + endif 23 + 13 24 obj-$(CONFIG_CRC64) += crc64.o 25 + crc64-y := crc64-main.o 26 + ifeq ($(CONFIG_CRC64_ARCH),y) 27 + CFLAGS_crc64-main.o += -I$(src)/$(SRCARCH) 28 + endif 29 + 14 30 obj-y += tests/ 15 31 16 32 hostprogs := gen_crc32table gen_crc64table 17 33 clean-files := crc32table.h crc64table.h 18 34 19 - $(obj)/crc32.o: $(obj)/crc32table.h 20 - $(obj)/crc64.o: $(obj)/crc64table.h 35 + $(obj)/crc32-main.o: $(obj)/crc32table.h 36 + $(obj)/crc64-main.o: $(obj)/crc64table.h 21 37 22 38 quiet_cmd_crc32 = GEN $@ 23 39 cmd_crc32 = $< > $@
+31 -8
lib/crc/crc-t10dif.c lib/crc/crc-t10dif-main.c
··· 50 50 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 51 51 }; 52 52 53 - u16 crc_t10dif_generic(u16 crc, const u8 *p, size_t len) 53 + static inline u16 __maybe_unused 54 + crc_t10dif_generic(u16 crc, const u8 *p, size_t len) 54 55 { 55 - size_t i; 56 - 57 - for (i = 0; i < len; i++) 58 - crc = (crc << 8) ^ t10_dif_crc_table[(crc >> 8) ^ p[i]]; 59 - 56 + while (len--) 57 + crc = (crc << 8) ^ t10_dif_crc_table[(crc >> 8) ^ *p++]; 60 58 return crc; 61 59 } 62 - EXPORT_SYMBOL(crc_t10dif_generic); 63 60 64 - MODULE_DESCRIPTION("T10 DIF CRC calculation"); 61 + #ifdef CONFIG_CRC_T10DIF_ARCH 62 + #include "crc-t10dif.h" /* $(SRCARCH)/crc-t10dif.h */ 63 + #else 64 + #define crc_t10dif_arch crc_t10dif_generic 65 + #endif 66 + 67 + u16 crc_t10dif_update(u16 crc, const u8 *p, size_t len) 68 + { 69 + return crc_t10dif_arch(crc, p, len); 70 + } 71 + EXPORT_SYMBOL(crc_t10dif_update); 72 + 73 + #ifdef crc_t10dif_mod_init_arch 74 + static int __init crc_t10dif_mod_init(void) 75 + { 76 + crc_t10dif_mod_init_arch(); 77 + return 0; 78 + } 79 + subsys_initcall(crc_t10dif_mod_init); 80 + 81 + static void __exit crc_t10dif_mod_exit(void) 82 + { 83 + } 84 + module_exit(crc_t10dif_mod_exit); 85 + #endif 86 + 87 + MODULE_DESCRIPTION("CRC-T10DIF library functions"); 65 88 MODULE_LICENSE("GPL");
+61 -16
lib/crc/crc32.c lib/crc/crc32-main.c
··· 30 30 31 31 #include "crc32table.h" 32 32 33 - MODULE_AUTHOR("Matt Domsch <Matt_Domsch@dell.com>"); 34 - MODULE_DESCRIPTION("Various CRC32 calculations"); 35 - MODULE_LICENSE("GPL"); 36 - 37 - u32 crc32_le_base(u32 crc, const u8 *p, size_t len) 33 + static inline u32 __maybe_unused 34 + crc32_le_base(u32 crc, const u8 *p, size_t len) 38 35 { 39 36 while (len--) 40 37 crc = (crc >> 8) ^ crc32table_le[(crc & 255) ^ *p++]; 41 38 return crc; 42 39 } 43 - EXPORT_SYMBOL(crc32_le_base); 44 40 45 - u32 crc32c_base(u32 crc, const u8 *p, size_t len) 46 - { 47 - while (len--) 48 - crc = (crc >> 8) ^ crc32ctable_le[(crc & 255) ^ *p++]; 49 - return crc; 50 - } 51 - EXPORT_SYMBOL(crc32c_base); 52 - 53 - u32 crc32_be_base(u32 crc, const u8 *p, size_t len) 41 + static inline u32 __maybe_unused 42 + crc32_be_base(u32 crc, const u8 *p, size_t len) 54 43 { 55 44 while (len--) 56 45 crc = (crc << 8) ^ crc32table_be[(crc >> 24) ^ *p++]; 57 46 return crc; 58 47 } 59 - EXPORT_SYMBOL(crc32_be_base); 48 + 49 + static inline u32 __maybe_unused 50 + crc32c_base(u32 crc, const u8 *p, size_t len) 51 + { 52 + while (len--) 53 + crc = (crc >> 8) ^ crc32ctable_le[(crc & 255) ^ *p++]; 54 + return crc; 55 + } 56 + 57 + #ifdef CONFIG_CRC32_ARCH 58 + #include "crc32.h" /* $(SRCARCH)/crc32.h */ 59 + 60 + u32 crc32_optimizations(void) 61 + { 62 + return crc32_optimizations_arch(); 63 + } 64 + EXPORT_SYMBOL(crc32_optimizations); 65 + #else 66 + #define crc32_le_arch crc32_le_base 67 + #define crc32_be_arch crc32_be_base 68 + #define crc32c_arch crc32c_base 69 + #endif 70 + 71 + u32 crc32_le(u32 crc, const void *p, size_t len) 72 + { 73 + return crc32_le_arch(crc, p, len); 74 + } 75 + EXPORT_SYMBOL(crc32_le); 76 + 77 + u32 crc32_be(u32 crc, const void *p, size_t len) 78 + { 79 + return crc32_be_arch(crc, p, len); 80 + } 81 + EXPORT_SYMBOL(crc32_be); 82 + 83 + u32 crc32c(u32 crc, const void *p, size_t len) 84 + { 85 + return crc32c_arch(crc, p, len); 86 + } 87 + EXPORT_SYMBOL(crc32c); 88 + 89 + #ifdef crc32_mod_init_arch 90 + static int __init crc32_mod_init(void) 91 + { 92 + crc32_mod_init_arch(); 93 + return 0; 94 + } 95 + subsys_initcall(crc32_mod_init); 96 + 97 + static void __exit crc32_mod_exit(void) 98 + { 99 + } 100 + module_exit(crc32_mod_exit); 101 + #endif 102 + 103 + MODULE_DESCRIPTION("CRC32 library functions"); 104 + MODULE_LICENSE("GPL");
+40 -7
lib/crc/crc64.c lib/crc/crc64-main.c
··· 38 38 #include <linux/crc64.h> 39 39 #include "crc64table.h" 40 40 41 - MODULE_DESCRIPTION("CRC64 calculations"); 42 - MODULE_LICENSE("GPL v2"); 43 - 44 - u64 crc64_be_generic(u64 crc, const u8 *p, size_t len) 41 + static inline u64 __maybe_unused 42 + crc64_be_generic(u64 crc, const u8 *p, size_t len) 45 43 { 46 44 while (len--) 47 45 crc = (crc << 8) ^ crc64table[(crc >> 56) ^ *p++]; 48 46 return crc; 49 47 } 50 - EXPORT_SYMBOL_GPL(crc64_be_generic); 51 48 52 - u64 crc64_nvme_generic(u64 crc, const u8 *p, size_t len) 49 + static inline u64 __maybe_unused 50 + crc64_nvme_generic(u64 crc, const u8 *p, size_t len) 53 51 { 54 52 while (len--) 55 53 crc = (crc >> 8) ^ crc64nvmetable[(crc & 0xff) ^ *p++]; 56 54 return crc; 57 55 } 58 - EXPORT_SYMBOL_GPL(crc64_nvme_generic); 56 + 57 + #ifdef CONFIG_CRC64_ARCH 58 + #include "crc64.h" /* $(SRCARCH)/crc64.h */ 59 + #else 60 + #define crc64_be_arch crc64_be_generic 61 + #define crc64_nvme_arch crc64_nvme_generic 62 + #endif 63 + 64 + u64 crc64_be(u64 crc, const void *p, size_t len) 65 + { 66 + return crc64_be_arch(crc, p, len); 67 + } 68 + EXPORT_SYMBOL_GPL(crc64_be); 69 + 70 + u64 crc64_nvme(u64 crc, const void *p, size_t len) 71 + { 72 + return ~crc64_nvme_arch(~crc, p, len); 73 + } 74 + EXPORT_SYMBOL_GPL(crc64_nvme); 75 + 76 + #ifdef crc64_mod_init_arch 77 + static int __init crc64_mod_init(void) 78 + { 79 + crc64_mod_init_arch(); 80 + return 0; 81 + } 82 + subsys_initcall(crc64_mod_init); 83 + 84 + static void __exit crc64_mod_exit(void) 85 + { 86 + } 87 + module_exit(crc64_mod_exit); 88 + #endif 89 + 90 + MODULE_DESCRIPTION("CRC64 library functions"); 91 + MODULE_LICENSE("GPL");