The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice
@ 2026-08-25 22:06 Usama Arif
  2026-08-25 22:06 ` [PATCH 1/2] crypto: zstd - Avoid redundant cstream initialization Usama Arif
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Usama Arif @ 2026-08-25 22:06 UTC (permalink / raw)
  To: davem, dsterba, Herbert Xu, linux-crypto, linux-kernel, terrelln
  Cc: hannes, yosry, nphamcs, chengming.zhou, shakeel.butt, kernel-team,
	Usama Arif

Both zstd_compress() and zstd_decompress() set up the shared per-CPU
workspace as a C/DStream before walking the request, and then, when the
first source and destination fragments each span the whole request, hand
off to zstd_compress_one()/zstd_decompress_one(), which immediately
overwrite that same ctx->wksp with a CCtx/DCtx. The stream setup is
discarded without a byte having been processed.

That one-shot path is not a corner case: zswap always takes it when
storing, and takes it for a load whenever the stored object lies within a
single zsmalloc page.

These two patches defer the stream initialization to the first walk
iteration that actually streams, guarded by a flag because that iteration
can be reached more than once.

A 4 KiB crypto_acomp benchmark [1], twelve runs of nine 30,000-operation
rounds. Bare metal is an Intel Xeon Platinum 8321HC, turbo off,
performance governor, pinned to one core; the VM is a one-vCPU KVM guest
on a faster host.

                  baseline    patched     delta
  bare metal
    compress      52,283 ns   51,038 ns   1,245 ns   2.4%
    decompress     2,317 ns    1,998 ns     319 ns  13.8%
  one-vCPU KVM
    compress      16,675 ns   15,050 ns   1,625 ns   9.8%
    decompress     3,516 ns    2,265 ns   1,251 ns  35.6%

The guest numbers are larger because the two CPUID instructions in
ZSTD_cpuid() become unconditional VM exits there. 

[1] https://gist.github.com/uarif1/5cf02f0e22c23f0d1b3d84348f12914c
 
Usama Arif (2):
  crypto: zstd - Avoid redundant cstream initialization
  crypto: zstd - Avoid redundant dstream initialization

 crypto/zstd.c | 40 ++++++++++++++++++++++++++++------------
 1 file changed, 28 insertions(+), 12 deletions(-)


base-commit: 4b18edbd8e70f7e6860d56370f13244896d0f95c
-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] crypto: zstd - Avoid redundant cstream initialization
  2026-08-25 22:06 [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice Usama Arif
@ 2026-08-25 22:06 ` Usama Arif
  2026-08-25 22:06 ` [PATCH 2/2] crypto: zstd - Avoid redundant dstream initialization Usama Arif
  2026-08-25 22:27 ` [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice Yosry Ahmed
  2 siblings, 0 replies; 4+ messages in thread
From: Usama Arif @ 2026-08-25 22:06 UTC (permalink / raw)
  To: davem, dsterba, Herbert Xu, linux-crypto, linux-kernel, terrelln
  Cc: hannes, yosry, nphamcs, chengming.zhou, shakeel.butt, kernel-team,
	Usama Arif

zstd_compress() initializes the shared workspace as a CStream before
entering the walk loop. If the first source and destination fragments
each span the whole request it then hands off to zstd_compress_one(),
which initializes that same ctx->wksp as a CCtx, discarding the CStream
setup without having compressed a byte. zswap always takes this one-shot
path when storing, so every page it stores paid for both.

Neither is cheap: zstd_init_cstream() redoes the cwksp layout, zeroes the
ZSTD_CCtx, probes for BMI2 through ZSTD_cpuid(), then resets the session
and parameters and replays ten validated ZSTD_CCtx_setParameter() calls.

Defer the CStream initialization to the first walk iteration that needs
it, guarded by a flag because that iteration can be reached more than
once. The first inner iteration either takes the one-shot path and
returns or initializes the CStream, so the trailing zstd_end_stream()
cannot pick up the stale context left in ctx->cctx by an earlier request.
Unlike the old call site the new one runs with the walk's fragments
mapped, so it has to release them before failing.

For a 4 KB crypto_acomp benchmark for compression, twelve runs of nine
30K operation rounds, on the bare-metal host the median per-round mean
request time fell from 52,283 ns to 51,038 ns (2.4%). In the one-vCPU
KVM guest it fell from 16,675 ns to 15,050 ns (9.8%).
The larger improvement in guest is because of the pair of CPUID
instructions in ZSTD_cpuid() that the removed initialization runs.

Signed-off-by: Usama Arif <usama.arif@linux.dev>
---
 crypto/zstd.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/crypto/zstd.c b/crypto/zstd.c
index 556f5d2bdd5fb..d64472f3e11e5 100644
--- a/crypto/zstd.c
+++ b/crypto/zstd.c
@@ -96,6 +96,7 @@ static int zstd_compress_one(struct acomp_req *req, struct zstd_ctx *ctx,
 
 static int zstd_compress(struct acomp_req *req)
 {
+	bool stream_initialized = false;
 	struct crypto_acomp_stream *s;
 	unsigned int pos, scur, dcur;
 	unsigned int total_out = 0;
@@ -115,12 +116,6 @@ static int zstd_compress(struct acomp_req *req)
 	if (ret)
 		goto out;
 
-	ctx->cctx = zstd_init_cstream(&ctx->params, 0, ctx->wksp, ctx->wksp_size);
-	if (!ctx->cctx) {
-		ret = -EINVAL;
-		goto out;
-	}
-
 	do {
 		dcur = acomp_walk_next_dst(&walk);
 		if (!dcur) {
@@ -142,6 +137,19 @@ static int zstd_compress(struct acomp_req *req)
 				goto out;
 			}
 
+			if (!stream_initialized) {
+				ctx->cctx = zstd_init_cstream(&ctx->params, 0,
+							      ctx->wksp, ctx->wksp_size);
+				if (!ctx->cctx) {
+					/* Release in the reverse of the map order. */
+					acomp_walk_done_src(&walk, 0);
+					acomp_walk_done_dst(&walk, 0);
+					ret = -EINVAL;
+					goto out;
+				}
+				stream_initialized = true;
+			}
+
 			if (scur) {
 				inbuf.pos = 0;
 				inbuf.src = walk.src.virt.addr;
-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] crypto: zstd - Avoid redundant dstream initialization
  2026-08-25 22:06 [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice Usama Arif
  2026-08-25 22:06 ` [PATCH 1/2] crypto: zstd - Avoid redundant cstream initialization Usama Arif
@ 2026-08-25 22:06 ` Usama Arif
  2026-08-25 22:27 ` [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice Yosry Ahmed
  2 siblings, 0 replies; 4+ messages in thread
From: Usama Arif @ 2026-08-25 22:06 UTC (permalink / raw)
  To: davem, dsterba, Herbert Xu, linux-crypto, linux-kernel, terrelln
  Cc: hannes, yosry, nphamcs, chengming.zhou, shakeel.butt, kernel-team,
	Usama Arif

zstd_decompress() initializes the shared workspace as a DStream before
entering the walk loop. If the first source and destination fragments
each span the whole request it then hands off to zstd_decompress_one(),
which initializes that same ctx->wksp as a DCtx, discarding the DStream
setup without having decompressed a byte. The two are in fact the same
routine, as ZSTD_initStaticDStream() is a tail call to
ZSTD_initStaticDCtx() and zstd_init_dstream() discards its
max_window_size argument, so the work was done twice byte for byte.

zswap takes this path whenever the stored object lies within a single
zsmalloc page, which is the common case; an object straddling a page
boundary comes back from zs_obj_read_sg_begin() as a two-entry source
scatterlist and streams instead.

Defer the DStream initialization to the first walk iteration that reaches
the streaming path, guarded by a flag because that iteration can be
reached more than once. Within this function ctx->dctx is read only by
the zstd_decompress_stream() call immediately below, so no stale context
can be picked up. As in the previous patch the new call site runs with
the walk's fragments mapped and has to release them before failing.

For a 4 KB crypto_acomp benchmark for decompression, twelve runs of nine
30K operation rounds, on the bare-metal host the median per-round mean
request time fell from 2,317 ns to 1,998 ns (13.8%).
In the one-vCPU KVM guest it fell from 3,516 ns to 2,265 ns (35.6%).

Signed-off-by: Usama Arif <usama.arif@linux.dev>
---
 crypto/zstd.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/crypto/zstd.c b/crypto/zstd.c
index d64472f3e11e5..76bdaca327330 100644
--- a/crypto/zstd.c
+++ b/crypto/zstd.c
@@ -215,6 +215,7 @@ static int zstd_decompress_one(struct acomp_req *req, struct zstd_ctx *ctx,
 
 static int zstd_decompress(struct acomp_req *req)
 {
+	bool stream_initialized = false;
 	struct crypto_acomp_stream *s;
 	unsigned int total_out = 0;
 	unsigned int scur, dcur;
@@ -232,12 +233,6 @@ static int zstd_decompress(struct acomp_req *req)
 	if (ret)
 		goto out;
 
-	ctx->dctx = zstd_init_dstream(ZSTD_MAX_SIZE, ctx->wksp, ctx->wksp_size);
-	if (!ctx->dctx) {
-		ret = -EINVAL;
-		goto out;
-	}
-
 	do {
 		scur = acomp_walk_next_src(&walk);
 		if (scur) {
@@ -263,6 +258,19 @@ static int zstd_decompress(struct acomp_req *req)
 				goto out;
 			}
 
+			if (!stream_initialized) {
+				ctx->dctx = zstd_init_dstream(ZSTD_MAX_SIZE, ctx->wksp,
+							      ctx->wksp_size);
+				if (!ctx->dctx) {
+					/* Release in the reverse of the map order. */
+					acomp_walk_done_dst(&walk, 0);
+					acomp_walk_done_src(&walk, 0);
+					ret = -EINVAL;
+					goto out;
+				}
+				stream_initialized = true;
+			}
+
 			outbuf.pos = 0;
 			outbuf.dst = (u8 *)walk.dst.virt.addr;
 			outbuf.size = dcur;
-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice
  2026-08-25 22:06 [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice Usama Arif
  2026-08-25 22:06 ` [PATCH 1/2] crypto: zstd - Avoid redundant cstream initialization Usama Arif
  2026-08-25 22:06 ` [PATCH 2/2] crypto: zstd - Avoid redundant dstream initialization Usama Arif
@ 2026-08-25 22:27 ` Yosry Ahmed
  2 siblings, 0 replies; 4+ messages in thread
From: Yosry Ahmed @ 2026-08-25 22:27 UTC (permalink / raw)
  To: Usama Arif
  Cc: davem, dsterba, Herbert Xu, linux-crypto, linux-kernel, terrelln,
	hannes, nphamcs, chengming.zhou, shakeel.butt, kernel-team

On Tue, Aug 25, 2026 at 3:06 PM Usama Arif <usama.arif@linux.dev> wrote:
>
> Both zstd_compress() and zstd_decompress() set up the shared per-CPU
> workspace as a C/DStream before walking the request, and then, when the
> first source and destination fragments each span the whole request, hand
> off to zstd_compress_one()/zstd_decompress_one(), which immediately
> overwrite that same ctx->wksp with a CCtx/DCtx. The stream setup is
> discarded without a byte having been processed.
>
> That one-shot path is not a corner case: zswap always takes it when
> storing, and takes it for a load whenever the stored object lies within a
> single zsmalloc page.
>
> These two patches defer the stream initialization to the first walk
> iteration that actually streams, guarded by a flag because that iteration
> can be reached more than once.
>
> A 4 KiB crypto_acomp benchmark [1], twelve runs of nine 30,000-operation
> rounds. Bare metal is an Intel Xeon Platinum 8321HC, turbo off,
> performance governor, pinned to one core; the VM is a one-vCPU KVM guest
> on a faster host.
>
>                   baseline    patched     delta
>   bare metal
>     compress      52,283 ns   51,038 ns   1,245 ns   2.4%
>     decompress     2,317 ns    1,998 ns     319 ns  13.8%
>   one-vCPU KVM
>     compress      16,675 ns   15,050 ns   1,625 ns   9.8%
>     decompress     3,516 ns    2,265 ns   1,251 ns  35.6%

Nice!

>
> The guest numbers are larger because the two CPUID instructions in
> ZSTD_cpuid() become unconditional VM exits there.

I wonder how much benefit we get from just doing CPUID once in
ZSTD_cpuid() and cache the results. There might still be value in
avoiding the overall initialization, but I think ZSTD_cpuid() should
only be executing CPUID once anyway (e.g. in case it's called in other
paths)?

>
> [1] https://gist.github.com/uarif1/5cf02f0e22c23f0d1b3d84348f12914c
>
> Usama Arif (2):
>   crypto: zstd - Avoid redundant cstream initialization
>   crypto: zstd - Avoid redundant dstream initialization
>
>  crypto/zstd.c | 40 ++++++++++++++++++++++++++++------------
>  1 file changed, 28 insertions(+), 12 deletions(-)
>
>
> base-commit: 4b18edbd8e70f7e6860d56370f13244896d0f95c
> --
> 2.53.0-Meta
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-08-25 22:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25 22:06 [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice Usama Arif
2026-08-25 22:06 ` [PATCH 1/2] crypto: zstd - Avoid redundant cstream initialization Usama Arif
2026-08-25 22:06 ` [PATCH 2/2] crypto: zstd - Avoid redundant dstream initialization Usama Arif
2026-08-25 22:27 ` [PATCH 0/2] crypto: zstd - avoid initializing the workspace twice Yosry Ahmed

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox