* [PATCH] lib/crypto: x86/chacha: Add a 16-block AVX-512 variant
@ 2026-07-22 15:32 Martin Willi
2026-07-22 23:24 ` Eric Biggers
0 siblings, 1 reply; 2+ messages in thread
From: Martin Willi @ 2026-07-22 15:32 UTC (permalink / raw)
To: Eric Biggers; +Cc: Jason A. Donenfeld, Ard Biesheuvel, linux-crypto
The existing AVX-512VL code processes at most eight blocks at a time
using 256-bit ymm registers. This width was chosen deliberately to
avoid the heavy core down-clocking that 512-bit zmm instructions
triggered on Skylake-X.
That penalty is gone on more recent AVX-512 microarchitectures, where
full 512-bit zmm registers can encrypt sixteen blocks per invocation
and roughly double the data-level parallelism for bulk traffic. Add
such a 16-block variant and dispatch to it for inputs larger than
eight blocks, ahead of the AVX-512VL path which still handles the
remainder.
On a Zen 5, the tcrypt speed test for chacha20 with 1024-byte blocks
reports 7.5 GB/s with the new variant versus 4.2 GB/s for the
AVX-512VL path, roughly a 1.8x improvement.
Enable it only on CPUs advertising AVX-512F with full zmm XSAVE state,
and keep it disabled when X86_FEATURE_PREFER_YMM is set so
down-clocking parts continue to use the ymm-based AVX-512VL path.
Signed-off-by: Martin Willi <martin@strongswan.org>
---
Eric,
This will conflict with your cpu_has_xfeatures() removal [1]. Let me know
if I shall drop the cpu_has_xfeatures(XFEATURE_MASK_AVX512) line.
[1] https://lore.kernel.org/all/20260626043731.319287-5-ebiggers@kernel.org/
---
lib/crypto/Makefile | 1 +
lib/crypto/x86/chacha-avx512-x86_64.S | 403 ++++++++++++++++++++++++++
lib/crypto/x86/chacha.h | 30 +-
3 files changed, 433 insertions(+), 1 deletion(-)
create mode 100644 lib/crypto/x86/chacha-avx512-x86_64.S
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index f1e9bf89785f..6b9668d414c2 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -118,6 +118,7 @@ libchacha-$(CONFIG_RISCV) += riscv/chacha-riscv64-zvkb.o
libchacha-$(CONFIG_S390) += s390/chacha-s390.o
libchacha-$(CONFIG_X86) += x86/chacha-ssse3-x86_64.o \
x86/chacha-avx2-x86_64.o \
+ x86/chacha-avx512-x86_64.o \
x86/chacha-avx512vl-x86_64.o
endif # CONFIG_CRYPTO_LIB_CHACHA_ARCH
diff --git a/lib/crypto/x86/chacha-avx512-x86_64.S b/lib/crypto/x86/chacha-avx512-x86_64.S
new file mode 100644
index 000000000000..c7e35fa3a5e7
--- /dev/null
+++ b/lib/crypto/x86/chacha-avx512-x86_64.S
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * ChaCha 256-bit cipher algorithm, x64 AVX-512 functions
+ *
+ * Copyright (C) 2026 Martin Willi
+ */
+
+#include <linux/linkage.h>
+
+.section .rodata.cst64.CTR16BL, "aM", @progbits, 64
+.align 64
+CTR16BL: .octa 0x00000003000000020000000100000000
+ .octa 0x00000007000000060000000500000004
+ .octa 0x0000000b0000000a0000000900000008
+ .octa 0x0000000f0000000e0000000d0000000c
+
+.text
+
+SYM_FUNC_START(chacha_16block_xor_avx512)
+ # %rdi: Input state matrix, s
+ # %rsi: up to 16 data blocks output, o
+ # %rdx: up to 16 data blocks input, i
+ # %rcx: input/output length in bytes
+ # %r8d: nrounds
+
+ # This function encrypts sixteen consecutive ChaCha blocks by loading
+ # the state matrix in 512-bit AVX-512 (zmm) registers sixteen times.
+ # Compared to AVX-512VL, this doubles the data-level parallelism using
+ # full-width zmm registers, which pays off on CPUs that do not throttle
+ # on 512-bit instructions.
+
+ vzeroupper
+
+ # x0..15[0-15] = s[0..15]
+ vpbroadcastd 0x00(%rdi),%zmm0
+ vpbroadcastd 0x04(%rdi),%zmm1
+ vpbroadcastd 0x08(%rdi),%zmm2
+ vpbroadcastd 0x0c(%rdi),%zmm3
+ vpbroadcastd 0x10(%rdi),%zmm4
+ vpbroadcastd 0x14(%rdi),%zmm5
+ vpbroadcastd 0x18(%rdi),%zmm6
+ vpbroadcastd 0x1c(%rdi),%zmm7
+ vpbroadcastd 0x20(%rdi),%zmm8
+ vpbroadcastd 0x24(%rdi),%zmm9
+ vpbroadcastd 0x28(%rdi),%zmm10
+ vpbroadcastd 0x2c(%rdi),%zmm11
+ vpbroadcastd 0x30(%rdi),%zmm12
+ vpbroadcastd 0x34(%rdi),%zmm13
+ vpbroadcastd 0x38(%rdi),%zmm14
+ vpbroadcastd 0x3c(%rdi),%zmm15
+
+ # x12 += counter values 0-15
+ vpaddd CTR16BL(%rip),%zmm12,%zmm12
+
+ # Only x12 carries a per-block value (the counter), so only it needs a
+ # saved copy for the final state add. The other fifteen words are scalar
+ # broadcasts of s[], re-added at the end via {1to16} embedded broadcast
+ # straight from the state matrix, dropping fifteen vmovdqa64 copies.
+ vmovdqa64 %zmm12,%zmm28
+
+.Ldoubleround16:
+ # x0 += x4, x12 = rotl32(x12 ^ x0, 16)
+ vpaddd %zmm0,%zmm4,%zmm0
+ vpxord %zmm0,%zmm12,%zmm12
+ vprold $16,%zmm12,%zmm12
+ # x1 += x5, x13 = rotl32(x13 ^ x1, 16)
+ vpaddd %zmm1,%zmm5,%zmm1
+ vpxord %zmm1,%zmm13,%zmm13
+ vprold $16,%zmm13,%zmm13
+ # x2 += x6, x14 = rotl32(x14 ^ x2, 16)
+ vpaddd %zmm2,%zmm6,%zmm2
+ vpxord %zmm2,%zmm14,%zmm14
+ vprold $16,%zmm14,%zmm14
+ # x3 += x7, x15 = rotl32(x15 ^ x3, 16)
+ vpaddd %zmm3,%zmm7,%zmm3
+ vpxord %zmm3,%zmm15,%zmm15
+ vprold $16,%zmm15,%zmm15
+
+ # x8 += x12, x4 = rotl32(x4 ^ x8, 12)
+ vpaddd %zmm12,%zmm8,%zmm8
+ vpxord %zmm8,%zmm4,%zmm4
+ vprold $12,%zmm4,%zmm4
+ # x9 += x13, x5 = rotl32(x5 ^ x9, 12)
+ vpaddd %zmm13,%zmm9,%zmm9
+ vpxord %zmm9,%zmm5,%zmm5
+ vprold $12,%zmm5,%zmm5
+ # x10 += x14, x6 = rotl32(x6 ^ x10, 12)
+ vpaddd %zmm14,%zmm10,%zmm10
+ vpxord %zmm10,%zmm6,%zmm6
+ vprold $12,%zmm6,%zmm6
+ # x11 += x15, x7 = rotl32(x7 ^ x11, 12)
+ vpaddd %zmm15,%zmm11,%zmm11
+ vpxord %zmm11,%zmm7,%zmm7
+ vprold $12,%zmm7,%zmm7
+
+ # x0 += x4, x12 = rotl32(x12 ^ x0, 8)
+ vpaddd %zmm0,%zmm4,%zmm0
+ vpxord %zmm0,%zmm12,%zmm12
+ vprold $8,%zmm12,%zmm12
+ # x1 += x5, x13 = rotl32(x13 ^ x1, 8)
+ vpaddd %zmm1,%zmm5,%zmm1
+ vpxord %zmm1,%zmm13,%zmm13
+ vprold $8,%zmm13,%zmm13
+ # x2 += x6, x14 = rotl32(x14 ^ x2, 8)
+ vpaddd %zmm2,%zmm6,%zmm2
+ vpxord %zmm2,%zmm14,%zmm14
+ vprold $8,%zmm14,%zmm14
+ # x3 += x7, x15 = rotl32(x15 ^ x3, 8)
+ vpaddd %zmm3,%zmm7,%zmm3
+ vpxord %zmm3,%zmm15,%zmm15
+ vprold $8,%zmm15,%zmm15
+
+ # x8 += x12, x4 = rotl32(x4 ^ x8, 7)
+ vpaddd %zmm12,%zmm8,%zmm8
+ vpxord %zmm8,%zmm4,%zmm4
+ vprold $7,%zmm4,%zmm4
+ # x9 += x13, x5 = rotl32(x5 ^ x9, 7)
+ vpaddd %zmm13,%zmm9,%zmm9
+ vpxord %zmm9,%zmm5,%zmm5
+ vprold $7,%zmm5,%zmm5
+ # x10 += x14, x6 = rotl32(x6 ^ x10, 7)
+ vpaddd %zmm14,%zmm10,%zmm10
+ vpxord %zmm10,%zmm6,%zmm6
+ vprold $7,%zmm6,%zmm6
+ # x11 += x15, x7 = rotl32(x7 ^ x11, 7)
+ vpaddd %zmm15,%zmm11,%zmm11
+ vpxord %zmm11,%zmm7,%zmm7
+ vprold $7,%zmm7,%zmm7
+
+ # x0 += x5, x15 = rotl32(x15 ^ x0, 16)
+ vpaddd %zmm0,%zmm5,%zmm0
+ vpxord %zmm0,%zmm15,%zmm15
+ vprold $16,%zmm15,%zmm15
+ # x1 += x6, x12 = rotl32(x12 ^ x1, 16)
+ vpaddd %zmm1,%zmm6,%zmm1
+ vpxord %zmm1,%zmm12,%zmm12
+ vprold $16,%zmm12,%zmm12
+ # x2 += x7, x13 = rotl32(x13 ^ x2, 16)
+ vpaddd %zmm2,%zmm7,%zmm2
+ vpxord %zmm2,%zmm13,%zmm13
+ vprold $16,%zmm13,%zmm13
+ # x3 += x4, x14 = rotl32(x14 ^ x3, 16)
+ vpaddd %zmm3,%zmm4,%zmm3
+ vpxord %zmm3,%zmm14,%zmm14
+ vprold $16,%zmm14,%zmm14
+
+ # x10 += x15, x5 = rotl32(x5 ^ x10, 12)
+ vpaddd %zmm15,%zmm10,%zmm10
+ vpxord %zmm10,%zmm5,%zmm5
+ vprold $12,%zmm5,%zmm5
+ # x11 += x12, x6 = rotl32(x6 ^ x11, 12)
+ vpaddd %zmm12,%zmm11,%zmm11
+ vpxord %zmm11,%zmm6,%zmm6
+ vprold $12,%zmm6,%zmm6
+ # x8 += x13, x7 = rotl32(x7 ^ x8, 12)
+ vpaddd %zmm13,%zmm8,%zmm8
+ vpxord %zmm8,%zmm7,%zmm7
+ vprold $12,%zmm7,%zmm7
+ # x9 += x14, x4 = rotl32(x4 ^ x9, 12)
+ vpaddd %zmm14,%zmm9,%zmm9
+ vpxord %zmm9,%zmm4,%zmm4
+ vprold $12,%zmm4,%zmm4
+
+ # x0 += x5, x15 = rotl32(x15 ^ x0, 8)
+ vpaddd %zmm0,%zmm5,%zmm0
+ vpxord %zmm0,%zmm15,%zmm15
+ vprold $8,%zmm15,%zmm15
+ # x1 += x6, x12 = rotl32(x12 ^ x1, 8)
+ vpaddd %zmm1,%zmm6,%zmm1
+ vpxord %zmm1,%zmm12,%zmm12
+ vprold $8,%zmm12,%zmm12
+ # x2 += x7, x13 = rotl32(x13 ^ x2, 8)
+ vpaddd %zmm2,%zmm7,%zmm2
+ vpxord %zmm2,%zmm13,%zmm13
+ vprold $8,%zmm13,%zmm13
+ # x3 += x4, x14 = rotl32(x14 ^ x3, 8)
+ vpaddd %zmm3,%zmm4,%zmm3
+ vpxord %zmm3,%zmm14,%zmm14
+ vprold $8,%zmm14,%zmm14
+
+ # x10 += x15, x5 = rotl32(x5 ^ x10, 7)
+ vpaddd %zmm15,%zmm10,%zmm10
+ vpxord %zmm10,%zmm5,%zmm5
+ vprold $7,%zmm5,%zmm5
+ # x11 += x12, x6 = rotl32(x6 ^ x11, 7)
+ vpaddd %zmm12,%zmm11,%zmm11
+ vpxord %zmm11,%zmm6,%zmm6
+ vprold $7,%zmm6,%zmm6
+ # x8 += x13, x7 = rotl32(x7 ^ x8, 7)
+ vpaddd %zmm13,%zmm8,%zmm8
+ vpxord %zmm8,%zmm7,%zmm7
+ vprold $7,%zmm7,%zmm7
+ # x9 += x14, x4 = rotl32(x4 ^ x9, 7)
+ vpaddd %zmm14,%zmm9,%zmm9
+ vpxord %zmm9,%zmm4,%zmm4
+ vprold $7,%zmm4,%zmm4
+
+ sub $2,%r8d
+ jnz .Ldoubleround16
+
+ # x0..15[0-15] += s[0..15]; all but x12 broadcast straight from s[]
+ vpaddd 0x00(%rdi){1to16},%zmm0,%zmm0
+ vpaddd 0x04(%rdi){1to16},%zmm1,%zmm1
+ vpaddd 0x08(%rdi){1to16},%zmm2,%zmm2
+ vpaddd 0x0c(%rdi){1to16},%zmm3,%zmm3
+ vpaddd 0x10(%rdi){1to16},%zmm4,%zmm4
+ vpaddd 0x14(%rdi){1to16},%zmm5,%zmm5
+ vpaddd 0x18(%rdi){1to16},%zmm6,%zmm6
+ vpaddd 0x1c(%rdi){1to16},%zmm7,%zmm7
+ vpaddd 0x20(%rdi){1to16},%zmm8,%zmm8
+ vpaddd 0x24(%rdi){1to16},%zmm9,%zmm9
+ vpaddd 0x28(%rdi){1to16},%zmm10,%zmm10
+ vpaddd 0x2c(%rdi){1to16},%zmm11,%zmm11
+ vpaddd %zmm28,%zmm12,%zmm12
+ vpaddd 0x34(%rdi){1to16},%zmm13,%zmm13
+ vpaddd 0x38(%rdi){1to16},%zmm14,%zmm14
+ vpaddd 0x3c(%rdi){1to16},%zmm15,%zmm15
+
+ # Transpose the 16x16 dword matrix: register n holds word n of all 16
+ # blocks, but we need register n to hold all 16 words of block n. This
+ # is the 8-block (vperm2i128) transpose extended by one level, since a
+ # zmm holds four 128-bit lanes instead of two.
+
+ # interleave 32-bit words in state n, n+1 -> zmm16..31
+ vpunpckldq %zmm1,%zmm0,%zmm16
+ vpunpckhdq %zmm1,%zmm0,%zmm17
+ vpunpckldq %zmm3,%zmm2,%zmm18
+ vpunpckhdq %zmm3,%zmm2,%zmm19
+ vpunpckldq %zmm5,%zmm4,%zmm20
+ vpunpckhdq %zmm5,%zmm4,%zmm21
+ vpunpckldq %zmm7,%zmm6,%zmm22
+ vpunpckhdq %zmm7,%zmm6,%zmm23
+ vpunpckldq %zmm9,%zmm8,%zmm24
+ vpunpckhdq %zmm9,%zmm8,%zmm25
+ vpunpckldq %zmm11,%zmm10,%zmm26
+ vpunpckhdq %zmm11,%zmm10,%zmm27
+ vpunpckldq %zmm13,%zmm12,%zmm28
+ vpunpckhdq %zmm13,%zmm12,%zmm29
+ vpunpckldq %zmm15,%zmm14,%zmm30
+ vpunpckhdq %zmm15,%zmm14,%zmm31
+
+ # interleave 64-bit words in state n, n+2 -> zmm0..15
+ vpunpcklqdq %zmm18,%zmm16,%zmm0
+ vpunpckhqdq %zmm18,%zmm16,%zmm1
+ vpunpcklqdq %zmm19,%zmm17,%zmm2
+ vpunpckhqdq %zmm19,%zmm17,%zmm3
+ vpunpcklqdq %zmm22,%zmm20,%zmm4
+ vpunpckhqdq %zmm22,%zmm20,%zmm5
+ vpunpcklqdq %zmm23,%zmm21,%zmm6
+ vpunpckhqdq %zmm23,%zmm21,%zmm7
+ vpunpcklqdq %zmm26,%zmm24,%zmm8
+ vpunpckhqdq %zmm26,%zmm24,%zmm9
+ vpunpcklqdq %zmm27,%zmm25,%zmm10
+ vpunpckhqdq %zmm27,%zmm25,%zmm11
+ vpunpcklqdq %zmm30,%zmm28,%zmm12
+ vpunpckhqdq %zmm30,%zmm28,%zmm13
+ vpunpcklqdq %zmm31,%zmm29,%zmm14
+ vpunpckhqdq %zmm31,%zmm29,%zmm15
+
+ # At this point lane L of zmm{r}, zmm{4+r}, zmm{8+r}, zmm{12+r} holds
+ # word groups 0-3, 4-7, 8-11, 12-15 of block (4*L + r). Gather the four
+ # 128-bit lanes of a block into one register with two levels of 128-bit
+ # lane shuffles.
+
+ # interleave 128-bit lanes in state n, n+4 -> zmm16..31
+ vshufi64x2 $0x88,%zmm4,%zmm0,%zmm16
+ vshufi64x2 $0x88,%zmm12,%zmm8,%zmm20
+ vshufi64x2 $0xdd,%zmm4,%zmm0,%zmm24
+ vshufi64x2 $0xdd,%zmm12,%zmm8,%zmm28
+ vshufi64x2 $0x88,%zmm5,%zmm1,%zmm17
+ vshufi64x2 $0x88,%zmm13,%zmm9,%zmm21
+ vshufi64x2 $0xdd,%zmm5,%zmm1,%zmm25
+ vshufi64x2 $0xdd,%zmm13,%zmm9,%zmm29
+ vshufi64x2 $0x88,%zmm6,%zmm2,%zmm18
+ vshufi64x2 $0x88,%zmm14,%zmm10,%zmm22
+ vshufi64x2 $0xdd,%zmm6,%zmm2,%zmm26
+ vshufi64x2 $0xdd,%zmm14,%zmm10,%zmm30
+ vshufi64x2 $0x88,%zmm7,%zmm3,%zmm19
+ vshufi64x2 $0x88,%zmm15,%zmm11,%zmm23
+ vshufi64x2 $0xdd,%zmm7,%zmm3,%zmm27
+ vshufi64x2 $0xdd,%zmm15,%zmm11,%zmm31
+
+ # Fuse the final 256-bit interleave into the xor/store ladder.
+ vshufi64x2 $0x88,%zmm20,%zmm16,%zmm0
+ cmp $0x0040,%rcx
+ jl .Lxorpart16
+ vpxord 0x0000(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0000(%rsi)
+
+ vshufi64x2 $0x88,%zmm21,%zmm17,%zmm0
+ cmp $0x0080,%rcx
+ jl .Lxorpart16
+ vpxord 0x0040(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0040(%rsi)
+
+ vshufi64x2 $0x88,%zmm22,%zmm18,%zmm0
+ cmp $0x00c0,%rcx
+ jl .Lxorpart16
+ vpxord 0x0080(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0080(%rsi)
+
+ vshufi64x2 $0x88,%zmm23,%zmm19,%zmm0
+ cmp $0x0100,%rcx
+ jl .Lxorpart16
+ vpxord 0x00c0(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x00c0(%rsi)
+
+ vshufi64x2 $0x88,%zmm28,%zmm24,%zmm0
+ cmp $0x0140,%rcx
+ jl .Lxorpart16
+ vpxord 0x0100(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0100(%rsi)
+
+ vshufi64x2 $0x88,%zmm29,%zmm25,%zmm0
+ cmp $0x0180,%rcx
+ jl .Lxorpart16
+ vpxord 0x0140(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0140(%rsi)
+
+ vshufi64x2 $0x88,%zmm30,%zmm26,%zmm0
+ cmp $0x01c0,%rcx
+ jl .Lxorpart16
+ vpxord 0x0180(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0180(%rsi)
+
+ vshufi64x2 $0x88,%zmm31,%zmm27,%zmm0
+ cmp $0x0200,%rcx
+ jl .Lxorpart16
+ vpxord 0x01c0(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x01c0(%rsi)
+
+ vshufi64x2 $0xdd,%zmm20,%zmm16,%zmm0
+ cmp $0x0240,%rcx
+ jl .Lxorpart16
+ vpxord 0x0200(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0200(%rsi)
+
+ vshufi64x2 $0xdd,%zmm21,%zmm17,%zmm0
+ cmp $0x0280,%rcx
+ jl .Lxorpart16
+ vpxord 0x0240(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0240(%rsi)
+
+ vshufi64x2 $0xdd,%zmm22,%zmm18,%zmm0
+ cmp $0x02c0,%rcx
+ jl .Lxorpart16
+ vpxord 0x0280(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0280(%rsi)
+
+ vshufi64x2 $0xdd,%zmm23,%zmm19,%zmm0
+ cmp $0x0300,%rcx
+ jl .Lxorpart16
+ vpxord 0x02c0(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x02c0(%rsi)
+
+ vshufi64x2 $0xdd,%zmm28,%zmm24,%zmm0
+ cmp $0x0340,%rcx
+ jl .Lxorpart16
+ vpxord 0x0300(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0300(%rsi)
+
+ vshufi64x2 $0xdd,%zmm29,%zmm25,%zmm0
+ cmp $0x0380,%rcx
+ jl .Lxorpart16
+ vpxord 0x0340(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0340(%rsi)
+
+ vshufi64x2 $0xdd,%zmm30,%zmm26,%zmm0
+ cmp $0x03c0,%rcx
+ jl .Lxorpart16
+ vpxord 0x0380(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x0380(%rsi)
+
+ vshufi64x2 $0xdd,%zmm31,%zmm27,%zmm0
+ cmp $0x0400,%rcx
+ jl .Lxorpart16
+ vpxord 0x03c0(%rdx),%zmm0,%zmm0
+ vmovdqu64 %zmm0,0x03c0(%rsi)
+
+.Ldone16:
+ vzeroupper
+ RET
+
+.Lxorpart16:
+ # xor remaining bytes from partial register into output
+ mov %rcx,%rax
+ and $0x3f,%rcx
+ jz .Ldone16
+ mov %rax,%r9
+ and $~0x3f,%r9
+
+ mov $1,%rax
+ shld %cl,%rax,%rax
+ sub $1,%rax
+ kmovq %rax,%k1
+
+ vmovdqu8 (%rdx,%r9),%zmm1{%k1}{z}
+ vpxord %zmm0,%zmm1,%zmm1
+ vmovdqu8 %zmm1,(%rsi,%r9){%k1}
+
+ jmp .Ldone16
+
+SYM_FUNC_END(chacha_16block_xor_avx512)
diff --git a/lib/crypto/x86/chacha.h b/lib/crypto/x86/chacha.h
index 10cf8f1c569d..a42b3f45c281 100644
--- a/lib/crypto/x86/chacha.h
+++ b/lib/crypto/x86/chacha.h
@@ -39,9 +39,14 @@ asmlinkage void chacha_8block_xor_avx512vl(const struct chacha_state *state,
u8 *dst, const u8 *src,
unsigned int len, int nrounds);
+asmlinkage void chacha_16block_xor_avx512(const struct chacha_state *state,
+ u8 *dst, const u8 *src,
+ unsigned int len, int nrounds);
+
static __ro_after_init DEFINE_STATIC_KEY_FALSE(chacha_use_simd);
static __ro_after_init DEFINE_STATIC_KEY_FALSE(chacha_use_avx2);
static __ro_after_init DEFINE_STATIC_KEY_FALSE(chacha_use_avx512vl);
+static __ro_after_init DEFINE_STATIC_KEY_FALSE(chacha_use_avx512);
static unsigned int chacha_advance(unsigned int len, unsigned int maxblocks)
{
@@ -52,6 +57,23 @@ static unsigned int chacha_advance(unsigned int len, unsigned int maxblocks)
static void chacha_dosimd(struct chacha_state *state, u8 *dst, const u8 *src,
unsigned int bytes, int nrounds)
{
+ if (static_branch_likely(&chacha_use_avx512)) {
+ while (bytes >= CHACHA_BLOCK_SIZE * 16) {
+ chacha_16block_xor_avx512(state, dst, src, bytes,
+ nrounds);
+ bytes -= CHACHA_BLOCK_SIZE * 16;
+ src += CHACHA_BLOCK_SIZE * 16;
+ dst += CHACHA_BLOCK_SIZE * 16;
+ state->x[12] += 16;
+ }
+ if (bytes > CHACHA_BLOCK_SIZE * 8) {
+ chacha_16block_xor_avx512(state, dst, src, bytes,
+ nrounds);
+ state->x[12] += chacha_advance(bytes, 16);
+ return;
+ }
+ }
+
if (static_branch_likely(&chacha_use_avx512vl)) {
while (bytes >= CHACHA_BLOCK_SIZE * 8) {
chacha_8block_xor_avx512vl(state, dst, src, bytes,
@@ -170,7 +192,13 @@ static void chacha_mod_init_arch(void)
static_branch_enable(&chacha_use_avx2);
if (boot_cpu_has(X86_FEATURE_AVX512VL) &&
- boot_cpu_has(X86_FEATURE_AVX512BW)) /* kmovq */
+ boot_cpu_has(X86_FEATURE_AVX512BW)) { /* kmovq */
static_branch_enable(&chacha_use_avx512vl);
+
+ if (boot_cpu_has(X86_FEATURE_AVX512F) &&
+ !boot_cpu_has(X86_FEATURE_PREFER_YMM) &&
+ cpu_has_xfeatures(XFEATURE_MASK_AVX512, NULL))
+ static_branch_enable(&chacha_use_avx512);
+ }
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] lib/crypto: x86/chacha: Add a 16-block AVX-512 variant
2026-07-22 15:32 [PATCH] lib/crypto: x86/chacha: Add a 16-block AVX-512 variant Martin Willi
@ 2026-07-22 23:24 ` Eric Biggers
0 siblings, 0 replies; 2+ messages in thread
From: Eric Biggers @ 2026-07-22 23:24 UTC (permalink / raw)
To: Martin Willi; +Cc: Jason A. Donenfeld, Ard Biesheuvel, linux-crypto
On Wed, Jul 22, 2026 at 05:32:47PM +0200, Martin Willi wrote:
> The existing AVX-512VL code processes at most eight blocks at a time
> using 256-bit ymm registers. This width was chosen deliberately to
> avoid the heavy core down-clocking that 512-bit zmm instructions
> triggered on Skylake-X.
>
> That penalty is gone on more recent AVX-512 microarchitectures, where
> full 512-bit zmm registers can encrypt sixteen blocks per invocation
> and roughly double the data-level parallelism for bulk traffic. Add
> such a 16-block variant and dispatch to it for inputs larger than
> eight blocks, ahead of the AVX-512VL path which still handles the
> remainder.
>
> On a Zen 5, the tcrypt speed test for chacha20 with 1024-byte blocks
> reports 7.5 GB/s with the new variant versus 4.2 GB/s for the
> AVX-512VL path, roughly a 1.8x improvement.
>
> Enable it only on CPUs advertising AVX-512F with full zmm XSAVE state,
> and keep it disabled when X86_FEATURE_PREFER_YMM is set so
> down-clocking parts continue to use the ymm-based AVX-512VL path.
>
> Signed-off-by: Martin Willi <martin@strongswan.org>
Thanks Martin!
This is very similar to the existing chacha_8block_xor_avx512vl(), just
with 512-bit vectors instead of 256-bit. They even use the same CPU
instruction set extensions: there are actually no cases where a CPU can
run chacha_8block_xor_avx512vl() but not chacha_16block_xor_avx512()
(the choice of vector length is just going to be made by policy).
Given that, could we consolidate these implementations? I'd suggest:
- Rename the existing chacha-avx512vl-x86_64.S to chacha-avx512-x86_64.S
- Add the CTR16BL rodata, overlapped with CTR8BL such that CTR8BL is
simply the first 8 entries of CTR16BL.
- Add a macro that expands into either chacha_8block_xor_avx512vl() or
chacha_16block_xor_avx512(), using a similar approach to
arch/x86/crypto/aes-xts-avx-x86_64.S. Use it to generate both.
Does that make sense?
> This will conflict with your cpu_has_xfeatures() removal [1]. Let me know
> if I shall drop the cpu_has_xfeatures(XFEATURE_MASK_AVX512) line.
Well, this is one of the cases where
cpu_has_xfeatures(XFEATURE_MASK_AVX512) was already required by the
existing code (since all AVX512 bits need to be enabled for any EVEX
coded instructions to work, even on xmm/ymm). But it was never checked,
and no one ever noticed. I would just continue to leave it out.
You can also drop the check of X86_FEATURE_AVX512F, which is implied by
X86_FEATURE_AVX512VL and X86_FEATURE_AVX512BW already.
- Eric
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-22 23:24 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-22 15:32 [PATCH] lib/crypto: x86/chacha: Add a 16-block AVX-512 variant Martin Willi
2026-07-22 23:24 ` Eric Biggers
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.