Skip to content

Commit 005f476

Browse files
committed
zstd: support external sequence producer API
Cherry-picks support for using zstd's external sequence producer API in the kernel. This unblocks the use of QuickAssist hardware acceleration for zstd in applications such as BTRFS. Some context: the kernel uses ZSTD_initStaticCCtx() to create compression contexts. This function builds the compression context in a user-provided buffer which must be sized according to the compression parameters. zstd provides size estimation functions for this purpose, but until now they were not compatible with the external sequence producer API. This cherry-pick fixes that incompatibility. More specifically, it pulls in zstd upstream PRs #3839 and #3854. PR #3854 includes a unit test (upstream only) which validates that the external sequence producer API works correctly in conjunction with ZSTD_initStaticCCtx(). To build this commit, I first cherry-picked the relevant commits onto the upstream v1.5.5-kernel tag: cd ~/repos/zstd git checkout tags/v1.5.5-kernel git cherry-pick -m 1 126ec2669c927b24acd38ea903a211c1b5416588 git cherry-pick c6cabf94417d84ebb5da62e05d8b8a9623763585 I then ran "make import" to copy the changes into my fork of Linux: cd ~/repos/zstd/contrib/linux-kernel/ make import Signed-off-by: Elliot Gorokhovsky <embg@meta.com>
1 parent 3f832df commit 005f476

File tree

4 files changed

+72
-56
lines changed

4 files changed

+72
-56
lines changed

include/linux/zstd_lib.h

+18-5
Original file line numberDiff line numberDiff line change
@@ -1632,9 +1632,6 @@ ZSTDLIB_API unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size);
16321632
*
16331633
* Note : only single-threaded compression is supported.
16341634
* ZSTD_estimateCCtxSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1.
1635-
*
1636-
* Note 2 : ZSTD_estimateCCtxSize* functions are not compatible with the Block-Level Sequence Producer API at this time.
1637-
* Size estimates assume that no external sequence producer is registered.
16381635
*/
16391636
ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize(int compressionLevel);
16401637
ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams);
@@ -2736,7 +2733,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
27362733

27372734
#define ZSTD_SEQUENCE_PRODUCER_ERROR ((size_t)(-1))
27382735

2739-
typedef size_t ZSTD_sequenceProducer_F (
2736+
typedef size_t (*ZSTD_sequenceProducer_F) (
27402737
void* sequenceProducerState,
27412738
ZSTD_Sequence* outSeqs, size_t outSeqsCapacity,
27422739
const void* src, size_t srcSize,
@@ -2768,7 +2765,23 @@ ZSTDLIB_STATIC_API void
27682765
ZSTD_registerSequenceProducer(
27692766
ZSTD_CCtx* cctx,
27702767
void* sequenceProducerState,
2771-
ZSTD_sequenceProducer_F* sequenceProducer
2768+
ZSTD_sequenceProducer_F sequenceProducer
2769+
);
2770+
2771+
/*! ZSTD_CCtxParams_registerSequenceProducer() :
2772+
* Same as ZSTD_registerSequenceProducer(), but operates on ZSTD_CCtx_params.
2773+
* This is used for accurate size estimation with ZSTD_estimateCCtxSize_usingCCtxParams(),
2774+
* which is needed when creating a ZSTD_CCtx with ZSTD_initStaticCCtx().
2775+
*
2776+
* If you are using the external sequence producer API in a scenario where ZSTD_initStaticCCtx()
2777+
* is required, then this function is for you. Otherwise, you probably don't need it.
2778+
*
2779+
* See tests/zstreamtest.c for example usage. */
2780+
ZSTDLIB_STATIC_API void
2781+
ZSTD_CCtxParams_registerSequenceProducer(
2782+
ZSTD_CCtx_params* params,
2783+
void* sequenceProducerState,
2784+
ZSTD_sequenceProducer_F sequenceProducer
27722785
);
27732786

27742787

lib/zstd/common/debug.c

-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,4 @@
2222

2323
#include "debug.h"
2424

25-
#if (DEBUGLEVEL>=2)
2625
int g_debuglevel = DEBUGLEVEL;
27-
#endif

lib/zstd/compress/zstd_compress.c

+42-35
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,6 @@ size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)
12951295
RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
12961296
"Reset parameters is only possible during init stage.");
12971297
ZSTD_clearAllDicts(cctx);
1298-
ZSTD_memset(&cctx->externalMatchCtx, 0, sizeof(cctx->externalMatchCtx));
12991298
return ZSTD_CCtxParams_reset(&cctx->requestedParams);
13001299
}
13011300
return 0;
@@ -1639,7 +1638,7 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params)
16391638
* be needed. However, we still allocate two 0-sized buffers, which can
16401639
* take space under ASAN. */
16411640
return ZSTD_estimateCCtxSize_usingCCtxParams_internal(
1642-
&cParams, &params->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize);
1641+
&cParams, &params->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN, ZSTD_hasExtSeqProd(params), params->maxBlockSize);
16431642
}
16441643

16451644
size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams)
@@ -1700,7 +1699,7 @@ size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params)
17001699

17011700
return ZSTD_estimateCCtxSize_usingCCtxParams_internal(
17021701
&cParams, &params->ldmParams, 1, useRowMatchFinder, inBuffSize, outBuffSize,
1703-
ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize);
1702+
ZSTD_CONTENTSIZE_UNKNOWN, ZSTD_hasExtSeqProd(params), params->maxBlockSize);
17041703
}
17051704
}
17061705

@@ -1996,7 +1995,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
19961995

19971996
{ size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params->cParams.windowLog), pledgedSrcSize));
19981997
size_t const blockSize = MIN(params->maxBlockSize, windowSize);
1999-
size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, params->cParams.minMatch, params->useSequenceProducer);
1998+
size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, params->cParams.minMatch, ZSTD_hasExtSeqProd(params));
20001999
size_t const buffOutSize = (zbuff == ZSTDb_buffered && params->outBufferMode == ZSTD_bm_buffered)
20012000
? ZSTD_compressBound(blockSize) + 1
20022001
: 0;
@@ -2013,7 +2012,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
20132012
size_t const neededSpace =
20142013
ZSTD_estimateCCtxSize_usingCCtxParams_internal(
20152014
&params->cParams, &params->ldmParams, zc->staticSize != 0, params->useRowMatchFinder,
2016-
buffInSize, buffOutSize, pledgedSrcSize, params->useSequenceProducer, params->maxBlockSize);
2015+
buffInSize, buffOutSize, pledgedSrcSize, ZSTD_hasExtSeqProd(params), params->maxBlockSize);
20172016
int resizeWorkspace;
20182017

20192018
FORWARD_IF_ERROR(neededSpace, "cctx size estimate failed!");
@@ -2098,10 +2097,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
20982097
}
20992098

21002099
/* reserve space for block-level external sequences */
2101-
if (params->useSequenceProducer) {
2100+
if (ZSTD_hasExtSeqProd(params)) {
21022101
size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize);
2103-
zc->externalMatchCtx.seqBufferCapacity = maxNbExternalSeq;
2104-
zc->externalMatchCtx.seqBuffer =
2102+
zc->extSeqBufCapacity = maxNbExternalSeq;
2103+
zc->extSeqBuf =
21052104
(ZSTD_Sequence*)ZSTD_cwksp_reserve_aligned(ws, maxNbExternalSeq * sizeof(ZSTD_Sequence));
21062105
}
21072106

@@ -3102,7 +3101,7 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
31023101
/* External matchfinder + LDM is technically possible, just not implemented yet.
31033102
* We need to revisit soon and implement it. */
31043103
RETURN_ERROR_IF(
3105-
zc->appliedParams.useSequenceProducer,
3104+
ZSTD_hasExtSeqProd(&zc->appliedParams),
31063105
parameter_combination_unsupported,
31073106
"Long-distance matching with external sequence producer enabled is not currently supported."
31083107
);
@@ -3121,7 +3120,7 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
31213120
/* External matchfinder + LDM is technically possible, just not implemented yet.
31223121
* We need to revisit soon and implement it. */
31233122
RETURN_ERROR_IF(
3124-
zc->appliedParams.useSequenceProducer,
3123+
ZSTD_hasExtSeqProd(&zc->appliedParams),
31253124
parameter_combination_unsupported,
31263125
"Long-distance matching with external sequence producer enabled is not currently supported."
31273126
);
@@ -3140,40 +3139,40 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
31403139
zc->appliedParams.useRowMatchFinder,
31413140
src, srcSize);
31423141
assert(ldmSeqStore.pos == ldmSeqStore.size);
3143-
} else if (zc->appliedParams.useSequenceProducer) {
3142+
} else if (ZSTD_hasExtSeqProd(&zc->appliedParams)) {
31443143
assert(
3145-
zc->externalMatchCtx.seqBufferCapacity >= ZSTD_sequenceBound(srcSize)
3144+
zc->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize)
31463145
);
3147-
assert(zc->externalMatchCtx.mFinder != NULL);
3146+
assert(zc->appliedParams.extSeqProdFunc != NULL);
31483147

31493148
{ U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog;
31503149

3151-
size_t const nbExternalSeqs = (zc->externalMatchCtx.mFinder)(
3152-
zc->externalMatchCtx.mState,
3153-
zc->externalMatchCtx.seqBuffer,
3154-
zc->externalMatchCtx.seqBufferCapacity,
3150+
size_t const nbExternalSeqs = (zc->appliedParams.extSeqProdFunc)(
3151+
zc->appliedParams.extSeqProdState,
3152+
zc->extSeqBuf,
3153+
zc->extSeqBufCapacity,
31553154
src, srcSize,
31563155
NULL, 0, /* dict and dictSize, currently not supported */
31573156
zc->appliedParams.compressionLevel,
31583157
windowSize
31593158
);
31603159

31613160
size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult(
3162-
zc->externalMatchCtx.seqBuffer,
3161+
zc->extSeqBuf,
31633162
nbExternalSeqs,
3164-
zc->externalMatchCtx.seqBufferCapacity,
3163+
zc->extSeqBufCapacity,
31653164
srcSize
31663165
);
31673166

31683167
/* Return early if there is no error, since we don't need to worry about last literals */
31693168
if (!ZSTD_isError(nbPostProcessedSeqs)) {
31703169
ZSTD_sequencePosition seqPos = {0,0,0};
3171-
size_t const seqLenSum = ZSTD_fastSequenceLengthSum(zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs);
3170+
size_t const seqLenSum = ZSTD_fastSequenceLengthSum(zc->extSeqBuf, nbPostProcessedSeqs);
31723171
RETURN_ERROR_IF(seqLenSum > srcSize, externalSequences_invalid, "External sequences imply too large a block!");
31733172
FORWARD_IF_ERROR(
31743173
ZSTD_copySequencesToSeqStoreExplicitBlockDelim(
31753174
zc, &seqPos,
3176-
zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs,
3175+
zc->extSeqBuf, nbPostProcessedSeqs,
31773176
src, srcSize,
31783177
zc->appliedParams.searchForExternalRepcodes
31793178
),
@@ -6217,7 +6216,7 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx,
62176216
if (cctx->appliedParams.validateSequences) {
62186217
seqPos->posInSrc += litLength + matchLength;
62196218
FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc,
6220-
cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer),
6219+
cctx->appliedParams.cParams.windowLog, dictSize, ZSTD_hasExtSeqProd(&cctx->appliedParams)),
62216220
"Sequence validation failed");
62226221
}
62236222
RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid,
@@ -6355,7 +6354,7 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition*
63556354
if (cctx->appliedParams.validateSequences) {
63566355
seqPos->posInSrc += litLength + matchLength;
63576356
FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc,
6358-
cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer),
6357+
cctx->appliedParams.cParams.windowLog, dictSize, ZSTD_hasExtSeqProd(&cctx->appliedParams)),
63596358
"Sequence validation failed");
63606359
}
63616360
DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength);
@@ -6801,19 +6800,27 @@ ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeH
68016800
}
68026801

68036802
void ZSTD_registerSequenceProducer(
6804-
ZSTD_CCtx* zc, void* mState,
6805-
ZSTD_sequenceProducer_F* mFinder
6803+
ZSTD_CCtx* zc,
6804+
void* extSeqProdState,
6805+
ZSTD_sequenceProducer_F extSeqProdFunc
68066806
) {
6807-
if (mFinder != NULL) {
6808-
ZSTD_externalMatchCtx emctx;
6809-
emctx.mState = mState;
6810-
emctx.mFinder = mFinder;
6811-
emctx.seqBuffer = NULL;
6812-
emctx.seqBufferCapacity = 0;
6813-
zc->externalMatchCtx = emctx;
6814-
zc->requestedParams.useSequenceProducer = 1;
6807+
assert(zc != NULL);
6808+
ZSTD_CCtxParams_registerSequenceProducer(
6809+
&zc->requestedParams, extSeqProdState, extSeqProdFunc
6810+
);
6811+
}
6812+
6813+
void ZSTD_CCtxParams_registerSequenceProducer(
6814+
ZSTD_CCtx_params* params,
6815+
void* extSeqProdState,
6816+
ZSTD_sequenceProducer_F extSeqProdFunc
6817+
) {
6818+
assert(params != NULL);
6819+
if (extSeqProdFunc != NULL) {
6820+
params->extSeqProdFunc = extSeqProdFunc;
6821+
params->extSeqProdState = extSeqProdState;
68156822
} else {
6816-
ZSTD_memset(&zc->externalMatchCtx, 0, sizeof(zc->externalMatchCtx));
6817-
zc->requestedParams.useSequenceProducer = 0;
6823+
params->extSeqProdFunc = NULL;
6824+
params->extSeqProdState = NULL;
68186825
}
68196826
}

lib/zstd/compress/zstd_compress_internal.h

+12-14
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,11 @@ struct ZSTD_CCtx_params_s {
355355
* if the external matchfinder returns an error code. */
356356
int enableMatchFinderFallback;
357357

358-
/* Indicates whether an external matchfinder has been referenced.
359-
* Users can't set this externally.
360-
* It is set internally in ZSTD_registerSequenceProducer(). */
361-
int useSequenceProducer;
358+
/* Parameters for the external sequence producer API.
359+
* Users set these parameters through ZSTD_registerSequenceProducer().
360+
* It is not possible to set these parameters individually through the public API. */
361+
void* extSeqProdState;
362+
ZSTD_sequenceProducer_F extSeqProdFunc;
362363

363364
/* Adjust the max block size*/
364365
size_t maxBlockSize;
@@ -396,14 +397,6 @@ typedef struct {
396397
ZSTD_entropyCTablesMetadata_t entropyMetadata;
397398
} ZSTD_blockSplitCtx;
398399

399-
/* Context for block-level external matchfinder API */
400-
typedef struct {
401-
void* mState;
402-
ZSTD_sequenceProducer_F* mFinder;
403-
ZSTD_Sequence* seqBuffer;
404-
size_t seqBufferCapacity;
405-
} ZSTD_externalMatchCtx;
406-
407400
struct ZSTD_CCtx_s {
408401
ZSTD_compressionStage_e stage;
409402
int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */
@@ -468,8 +461,9 @@ struct ZSTD_CCtx_s {
468461
/* Workspace for block splitter */
469462
ZSTD_blockSplitCtx blockSplitCtx;
470463

471-
/* Workspace for external matchfinder */
472-
ZSTD_externalMatchCtx externalMatchCtx;
464+
/* Buffer for output from external sequence producer */
465+
ZSTD_Sequence* extSeqBuf;
466+
size_t extSeqBufCapacity;
473467
};
474468

475469
typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e;
@@ -1495,6 +1489,10 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition*
14951489
const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
14961490
const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch);
14971491

1492+
/* Returns 1 if an external sequence producer is registered, otherwise returns 0. */
1493+
MEM_STATIC int ZSTD_hasExtSeqProd(const ZSTD_CCtx_params* params) {
1494+
return params->extSeqProdFunc != NULL;
1495+
}
14981496

14991497
/* ===============================================================
15001498
* Deprecated definitions that are still used internally to avoid

0 commit comments

Comments
 (0)