[PATCH 1/4] crypto: move cycle benchmark helpers out of tcrypt

Jihong Min <[email protected]> Tue, 28 Jul 2026 17:43:29 +0900
Newsgroups org.kernel.vger.linux-crypto,org.kernel.vger.linux-kernel
Message-ID <e0ce93088c1d9772e928fcbd113a446b7fb7442f.1785226804.git.hurryman2212@gmail.com>
Move the reusable AEAD, ahash, and skcipher cycle measurement loops
into a dedicated benchmark module. Preserve tcrypt's existing output
and benchmark behavior while allowing crypto drivers to use the same
helpers for runtime comparisons.

Signed-off-by: Jihong Min <[email protected]>
---
 crypto/Kconfig             |  15 ++-
 crypto/Makefile            |   2 +
 crypto/benchmark.c         | 180 +++++++++++++++++++++++++++++++
 crypto/tcrypt.c            | 213 +++++--------------------------------
 include/crypto/benchmark.h |  24 +++++
 5 files changed, 242 insertions(+), 192 deletions(-)
 create mode 100644 crypto/benchmark.c
 create mode 100644 include/crypto/benchmark.h

diff --git a/crypto/Kconfig b/crypto/Kconfig
index f1e372195273..0cba261e3b88 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -244,13 +244,22 @@ config CRYPTO_KRB5ENC
 	  profile.  This is required for Kerberos 5-style encryption, used by
 	  sunrpc/NFS and rxrpc/AFS.
 
-config CRYPTO_BENCHMARK
-	tristate "Crypto benchmarking module"
-	depends on m || EXPERT
+config CRYPTO_BENCHMARK_LIB
+	tristate
 	select CRYPTO_AEAD
 	select CRYPTO_HASH
 	select CRYPTO_MANAGER
 	select CRYPTO_SKCIPHER
+	help
+	  Build reusable benchmark helpers for Crypto API algorithms.  This
+	  internal library is shared by tcrypt and drivers that select
+	  implementations from measurements made while their devices are
+	  initialized.
+
+config CRYPTO_BENCHMARK
+	tristate "Crypto benchmarking module"
+	depends on m || EXPERT
+	select CRYPTO_BENCHMARK_LIB
 	help
 	  Quick & dirty crypto benchmarking module.
 
diff --git a/crypto/Makefile b/crypto/Makefile
index 8386d55a9755..77720b746503 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -165,6 +165,8 @@ UBSAN_SANITIZE_jitterentropy.o = n
 jitterentropy_rng-y := jitterentropy.o jitterentropy-kcapi.o
 obj-$(CONFIG_CRYPTO_JITTERENTROPY_TESTINTERFACE) += jitterentropy-testing.o
 obj-$(CONFIG_CRYPTO_BENCHMARK) += tcrypt.o
+obj-$(CONFIG_CRYPTO_BENCHMARK_LIB) += crypt-benchmark.o
+crypt-benchmark-y := benchmark.o
 obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
 obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
 obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
diff --git a/crypto/benchmark.c b/crypto/benchmark.c
new file mode 100644
index 000000000000..ad27cdaa7491
--- /dev/null
+++ b/crypto/benchmark.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <crypto/aead.h>
+#include <crypto/benchmark.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/module.h>
+#include <linux/timex.h>
+
+int crypto_benchmark_aead_cycles(struct aead_request *req, bool encrypt,
+				 unsigned int warmup_runs, unsigned int runs,
+				 u64 *total_cycles)
+{
+	struct crypto_wait *wait;
+	unsigned int i;
+	u64 cycles = 0;
+	int ret;
+
+	if (!req || !req->base.data || !runs || !total_cycles)
+		return -EINVAL;
+
+	wait = req->base.data;
+	*total_cycles = 0;
+
+	for (i = 0; i < warmup_runs; i++) {
+		if (encrypt)
+			ret = crypto_wait_req(crypto_aead_encrypt(req), wait);
+		else
+			ret = crypto_wait_req(crypto_aead_decrypt(req), wait);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < runs; i++) {
+		cycles_t start, end;
+
+		start = get_cycles();
+		if (encrypt)
+			ret = crypto_wait_req(crypto_aead_encrypt(req), wait);
+		else
+			ret = crypto_wait_req(crypto_aead_decrypt(req), wait);
+		end = get_cycles();
+		if (ret)
+			return ret;
+
+		cycles += end - start;
+	}
+
+	*total_cycles = cycles;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_benchmark_aead_cycles);
+
+int crypto_benchmark_ahash_cycles(struct ahash_request *req,
+				  unsigned int block_size,
+				  unsigned int update_size,
+				  unsigned int warmup_runs, unsigned int runs,
+				  u64 *total_cycles)
+{
+	struct crypto_wait *wait;
+	unsigned int processed;
+	unsigned int i;
+	u64 cycles = 0;
+	int ret;
+
+	if (!req || !req->base.data || !block_size || !update_size ||
+	    block_size % update_size || !runs || !total_cycles)
+		return -EINVAL;
+
+	wait = req->base.data;
+	*total_cycles = 0;
+
+	for (i = 0; i < warmup_runs; i++) {
+		if (update_size == block_size) {
+			ret = crypto_wait_req(crypto_ahash_digest(req), wait);
+			if (ret)
+				return ret;
+			continue;
+		}
+
+		ret = crypto_wait_req(crypto_ahash_init(req), wait);
+		if (ret)
+			return ret;
+		for (processed = 0; processed < block_size;
+		     processed += update_size) {
+			ret = crypto_wait_req(crypto_ahash_update(req), wait);
+			if (ret)
+				return ret;
+		}
+		ret = crypto_wait_req(crypto_ahash_final(req), wait);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < runs; i++) {
+		cycles_t start, end;
+
+		start = get_cycles();
+		if (update_size == block_size) {
+			ret = crypto_wait_req(crypto_ahash_digest(req), wait);
+		} else {
+			ret = crypto_wait_req(crypto_ahash_init(req), wait);
+			if (ret)
+				goto measure_end;
+			for (processed = 0; processed < block_size;
+			     processed += update_size) {
+				ret = crypto_wait_req(crypto_ahash_update(req),
+						      wait);
+				if (ret)
+					goto measure_end;
+			}
+			ret = crypto_wait_req(crypto_ahash_final(req), wait);
+		}
+measure_end:
+		end = get_cycles();
+		if (ret)
+			return ret;
+
+		cycles += end - start;
+	}
+
+	*total_cycles = cycles;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_benchmark_ahash_cycles);
+
+int crypto_benchmark_skcipher_cycles(struct skcipher_request *req, bool encrypt,
+				     unsigned int warmup_runs,
+				     unsigned int runs, u64 *total_cycles)
+{
+	struct crypto_wait *wait;
+	unsigned int i;
+	u64 cycles = 0;
+	int ret;
+
+	if (!req || !req->base.data || !runs || !total_cycles)
+		return -EINVAL;
+
+	wait = req->base.data;
+	*total_cycles = 0;
+
+	for (i = 0; i < warmup_runs; i++) {
+		if (encrypt)
+			ret = crypto_wait_req(crypto_skcipher_encrypt(req),
+					      wait);
+		else
+			ret = crypto_wait_req(crypto_skcipher_decrypt(req),
+					      wait);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < runs; i++) {
+		cycles_t start, end;
+
+		start = get_cycles();
+		if (encrypt)
+			ret = crypto_wait_req(crypto_skcipher_encrypt(req),
+					      wait);
+		else
+			ret = crypto_wait_req(crypto_skcipher_decrypt(req),
+					      wait);
+		end = get_cycles();
+		if (ret)
+			return ret;
+
+		cycles += end - start;
+	}
+
+	*total_cycles = cycles;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_benchmark_skcipher_cycles);
+
+MODULE_AUTHOR("Jihong Min <[email protected]>");
+MODULE_DESCRIPTION("Crypto API benchmark helpers");
+MODULE_LICENSE("GPL");
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 61a2501bfe9b..81c77f3d7a17 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -20,6 +20,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <crypto/aead.h>
+#include <crypto/benchmark.h>
 #include <crypto/hash.h>
 #include <crypto/skcipher.h>
 #include <linux/err.h>
@@ -473,48 +474,6 @@ static int test_aead_jiffies(struct aead_request *req, int enc,
 	return 0;
 }
 
-static int test_aead_cycles(struct aead_request *req, int enc, int blen)
-{
-	unsigned long cycles = 0;
-	int ret = 0;
-	int i;
-
-	/* Warm-up run. */
-	for (i = 0; i < 4; i++) {
-		if (enc)
-			ret = do_one_aead_op(req, crypto_aead_encrypt(req));
-		else
-			ret = do_one_aead_op(req, crypto_aead_decrypt(req));
-
-		if (ret)
-			goto out;
-	}
-
-	/* The real thing. */
-	for (i = 0; i < 8; i++) {
-		cycles_t start, end;
-
-		start = get_cycles();
-		if (enc)
-			ret = do_one_aead_op(req, crypto_aead_encrypt(req));
-		else
-			ret = do_one_aead_op(req, crypto_aead_decrypt(req));
-		end = get_cycles();
-
-		if (ret)
-			goto out;
-
-		cycles += end - start;
-	}
-
-out:
-	if (ret == 0)
-		pr_cont("1 operation in %lu cycles (%d bytes)\n",
-			(cycles + 4) / 8, blen);
-
-	return ret;
-}
-
 static void test_aead_speed(const char *algo, int enc, unsigned int secs,
 			    struct aead_speed_template *template,
 			    unsigned int tcount, u8 authsize,
@@ -669,7 +628,13 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
 							secs);
 				cond_resched();
 			} else {
-				ret = test_aead_cycles(req, enc, bs);
+				u64 cycles;
+
+				ret = crypto_benchmark_aead_cycles(req, enc, 4,
+								   8, &cycles);
+				if (!ret)
+					pr_cont("1 operation in %llu cycles (%d bytes)\n",
+						(cycles + 4) / 8, bs);
 			}
 
 			if (ret) {
@@ -768,101 +733,6 @@ static int test_ahash_jiffies(struct ahash_request *req, int blen,
 	return 0;
 }
 
-static int test_ahash_cycles_digest(struct ahash_request *req, int blen,
-				    char *out)
-{
-	unsigned long cycles = 0;
-	int ret, i;
-
-	/* Warm-up run. */
-	for (i = 0; i < 4; i++) {
-		ret = do_one_ahash_op(req, crypto_ahash_digest(req));
-		if (ret)
-			goto out;
-	}
-
-	/* The real thing. */
-	for (i = 0; i < 8; i++) {
-		cycles_t start, end;
-
-		start = get_cycles();
-
-		ret = do_one_ahash_op(req, crypto_ahash_digest(req));
-		if (ret)
-			goto out;
-
-		end = get_cycles();
-
-		cycles += end - start;
-	}
-
-out:
-	if (ret)
-		return ret;
-
-	pr_cont("%6lu cycles/operation, %4lu cycles/byte\n",
-		cycles / 8, cycles / (8 * blen));
-
-	return 0;
-}
-
-static int test_ahash_cycles(struct ahash_request *req, int blen,
-			     int plen, char *out)
-{
-	unsigned long cycles = 0;
-	int i, pcount, ret;
-
-	if (plen == blen)
-		return test_ahash_cycles_digest(req, blen, out);
-
-	/* Warm-up run. */
-	for (i = 0; i < 4; i++) {
-		ret = do_one_ahash_op(req, crypto_ahash_init(req));
-		if (ret)
-			goto out;
-		for (pcount = 0; pcount < blen; pcount += plen) {
-			ret = do_one_ahash_op(req, crypto_ahash_update(req));
-			if (ret)
-				goto out;
-		}
-		ret = do_one_ahash_op(req, crypto_ahash_final(req));
-		if (ret)
-			goto out;
-	}
-
-	/* The real thing. */
-	for (i = 0; i < 8; i++) {
-		cycles_t start, end;
-
-		start = get_cycles();
-
-		ret = do_one_ahash_op(req, crypto_ahash_init(req));
-		if (ret)
-			goto out;
-		for (pcount = 0; pcount < blen; pcount += plen) {
-			ret = do_one_ahash_op(req, crypto_ahash_update(req));
-			if (ret)
-				goto out;
-		}
-		ret = do_one_ahash_op(req, crypto_ahash_final(req));
-		if (ret)
-			goto out;
-
-		end = get_cycles();
-
-		cycles += end - start;
-	}
-
-out:
-	if (ret)
-		return ret;
-
-	pr_cont("%6lu cycles/operation, %4lu cycles/byte\n",
-		cycles / 8, cycles / (8 * blen));
-
-	return 0;
-}
-
 static void test_ahash_speed_common(const char *algo, unsigned int secs,
 				    struct hash_speed *speed, unsigned mask)
 {
@@ -931,8 +801,15 @@ static void test_ahash_speed_common(const char *algo, unsigned int secs,
 						 speed[i].plen, output, secs);
 			cond_resched();
 		} else {
-			ret = test_ahash_cycles(req, speed[i].blen,
-						speed[i].plen, output);
+			u64 cycles;
+
+			ret = crypto_benchmark_ahash_cycles(req, speed[i].blen,
+							    speed[i].plen, 4, 8,
+							    &cycles);
+			if (!ret)
+				pr_cont("%6llu cycles/operation, %4llu cycles/byte\n",
+					cycles / 8,
+					cycles / (8 * speed[i].blen));
 		}
 
 		if (ret) {
@@ -1249,53 +1126,6 @@ static int test_acipher_jiffies(struct skcipher_request *req, int enc,
 	return 0;
 }
 
-static int test_acipher_cycles(struct skcipher_request *req, int enc,
-			       int blen)
-{
-	unsigned long cycles = 0;
-	int ret = 0;
-	int i;
-
-	/* Warm-up run. */
-	for (i = 0; i < 4; i++) {
-		if (enc)
-			ret = do_one_acipher_op(req,
-						crypto_skcipher_encrypt(req));
-		else
-			ret = do_one_acipher_op(req,
-						crypto_skcipher_decrypt(req));
-
-		if (ret)
-			goto out;
-	}
-
-	/* The real thing. */
-	for (i = 0; i < 8; i++) {
-		cycles_t start, end;
-
-		start = get_cycles();
-		if (enc)
-			ret = do_one_acipher_op(req,
-						crypto_skcipher_encrypt(req));
-		else
-			ret = do_one_acipher_op(req,
-						crypto_skcipher_decrypt(req));
-		end = get_cycles();
-
-		if (ret)
-			goto out;
-
-		cycles += end - start;
-	}
-
-out:
-	if (ret == 0)
-		pr_cont("1 operation in %lu cycles (%d bytes)\n",
-			(cycles + 4) / 8, blen);
-
-	return ret;
-}
-
 static void test_skcipher_speed(const char *algo, int enc, unsigned int secs,
 				struct cipher_speed_template *template,
 				unsigned int tcount, u8 *keysize, bool async)
@@ -1405,8 +1235,13 @@ static void test_skcipher_speed(const char *algo, int enc, unsigned int secs,
 							   bs, secs);
 				cond_resched();
 			} else {
-				ret = test_acipher_cycles(req, enc,
-							  bs);
+				u64 cycles;
+
+				ret = crypto_benchmark_skcipher_cycles(req, enc, 4,
+								       8, &cycles);
+				if (!ret)
+					pr_cont("1 operation in %llu cycles (%d bytes)\n",
+						(cycles + 4) / 8, bs);
 			}
 
 			if (ret) {
diff --git a/include/crypto/benchmark.h b/include/crypto/benchmark.h
new file mode 100644
index 000000000000..09dc0fb38483
--- /dev/null
+++ b/include/crypto/benchmark.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CRYPTO_BENCHMARK_H
+#define _CRYPTO_BENCHMARK_H
+
+#include <linux/types.h>
+
+struct aead_request;
+struct ahash_request;
+struct skcipher_request;
+
+/* Requests must use crypto_req_done() with struct crypto_wait callback data. */
+int crypto_benchmark_aead_cycles(struct aead_request *req, bool encrypt,
+				 unsigned int warmup_runs, unsigned int runs,
+				 u64 *total_cycles);
+int crypto_benchmark_ahash_cycles(struct ahash_request *req,
+				  unsigned int block_size,
+				  unsigned int update_size,
+				  unsigned int warmup_runs, unsigned int runs,
+				  u64 *total_cycles);
+int crypto_benchmark_skcipher_cycles(struct skcipher_request *req, bool encrypt,
+				     unsigned int warmup_runs,
+				     unsigned int runs, u64 *total_cycles);
+
+#endif /* _CRYPTO_BENCHMARK_H */
-- 
2.53.0