Contributors: 9
Author Tokens Token Proportion Commits Commit Proportion
Giovanni Cabiddu 3352 93.95% 6 28.57%
Lucas Segarra Fernandez 90 2.52% 2 9.52%
Tadeusz Struk 64 1.79% 3 14.29%
Bruce W Allan 23 0.64% 1 4.76%
Marco Chiappero 16 0.45% 3 14.29%
Herbert Xu 10 0.28% 3 14.29%
Zeng Xin 8 0.22% 1 4.76%
Suman Kumar Chakraborty 4 0.11% 1 4.76%
Kees Cook 1 0.03% 1 4.76%
Total 3568 21


// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation */
#include <linux/crypto.h>
#include <crypto/acompress.h>
#include <crypto/internal/acompress.h>
#include <crypto/scatterwalk.h>
#include <linux/dma-mapping.h>
#include <linux/workqueue.h>
#include <linux/zstd.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "adf_dc.h"
#include "qat_bl.h"
#include "qat_comp_req.h"
#include "qat_compression.h"
#include "qat_algs_send.h"
#include "qat_comp_zstd_utils.h"

#define QAT_ZSTD_SCRATCH_SIZE		524288
#define QAT_ZSTD_MAX_BLOCK_SIZE		65535
#define QAT_ZSTD_MAX_CONTENT_SIZE	4096
#define QAT_LZ4S_MIN_INPUT_SIZE		8192
#define QAT_LZ4S_MAX_OUTPUT_SIZE	QAT_ZSTD_SCRATCH_SIZE
#define QAT_MAX_SEQUENCES		(128 * 1024)

static DEFINE_MUTEX(algs_lock);
static unsigned int active_devs_deflate;
static unsigned int active_devs_lz4s;
static unsigned int active_devs_zstd;

struct qat_zstd_scratch {
	size_t		cctx_buffer_size;
	void		*lz4s;
	void		*literals;
	void		*out_seqs;
	void		*workspace;
	ZSTD_CCtx	*ctx;
};

static void *qat_zstd_alloc_scratch(void)
{
	struct qat_zstd_scratch *scratch;
	ZSTD_parameters params;
	size_t cctx_size;
	ZSTD_CCtx *ctx;
	size_t zret;
	int ret;

	ret = -ENOMEM;
	scratch = kzalloc_obj(*scratch);
	if (!scratch)
		return ERR_PTR(ret);

	scratch->lz4s = kvmalloc(QAT_ZSTD_SCRATCH_SIZE, GFP_KERNEL);
	if (!scratch->lz4s)
		goto error;

	scratch->literals = kvmalloc(QAT_ZSTD_SCRATCH_SIZE, GFP_KERNEL);
	if (!scratch->literals)
		goto error;

	scratch->out_seqs = kvcalloc(QAT_MAX_SEQUENCES, sizeof(ZSTD_Sequence),
				     GFP_KERNEL);
	if (!scratch->out_seqs)
		goto error;

	params = zstd_get_params(zstd_max_clevel(), QAT_ZSTD_SCRATCH_SIZE);
	cctx_size = zstd_cctx_workspace_bound(&params.cParams);

	scratch->workspace = kvmalloc(cctx_size, GFP_KERNEL | __GFP_ZERO);
	if (!scratch->workspace)
		goto error;

	ret = -EINVAL;
	ctx = zstd_init_cctx(scratch->workspace, cctx_size);
	if (!ctx)
		goto error;

	scratch->ctx = ctx;
	scratch->cctx_buffer_size = cctx_size;

	zret = zstd_cctx_set_param(ctx, ZSTD_c_blockDelimiters, ZSTD_sf_explicitBlockDelimiters);
	if (zstd_is_error(zret))
		goto error;

	return scratch;

error:
	kvfree(scratch->lz4s);
	kvfree(scratch->literals);
	kvfree(scratch->out_seqs);
	kvfree(scratch->workspace);
	kfree(scratch);
	return ERR_PTR(ret);
}

static void qat_zstd_free_scratch(void *ctx)
{
	struct qat_zstd_scratch *scratch = ctx;

	if (!scratch)
		return;

	kvfree(scratch->lz4s);
	kvfree(scratch->literals);
	kvfree(scratch->out_seqs);
	kvfree(scratch->workspace);
	kfree(scratch);
}

static struct crypto_acomp_streams qat_zstd_streams = {
	.alloc_ctx = qat_zstd_alloc_scratch,
	.free_ctx = qat_zstd_free_scratch,
};

enum direction {
	DECOMPRESSION = 0,
	COMPRESSION = 1,
};

struct qat_compression_req;

struct qat_callback_params {
	unsigned int produced;
	unsigned int dlen;
	bool plain;
};

struct qat_compression_ctx {
	u8 comp_ctx[QAT_COMP_CTX_SIZE];
	struct qat_compression_instance *inst;
	int (*qat_comp_callback)(struct qat_compression_req *qat_req, void *resp,
				 struct qat_callback_params *params);
	struct crypto_acomp *ftfm;
};

struct qat_compression_req {
	u8 req[QAT_COMP_REQ_SIZE];
	struct qat_compression_ctx *qat_compression_ctx;
	struct acomp_req *acompress_req;
	struct qat_request_buffs buf;
	enum direction dir;
	int actual_dlen;
	struct qat_alg_req alg_req;
};

static int qat_alg_send_dc_message(struct qat_compression_req *qat_req,
				   struct qat_compression_instance *inst,
				   struct crypto_async_request *base)
{
	struct qat_alg_req *alg_req = &qat_req->alg_req;

	alg_req->fw_req = (u32 *)&qat_req->req;
	alg_req->tx_ring = inst->dc_tx;
	alg_req->base = base;
	alg_req->backlog = &inst->backlog;

	return qat_alg_send_message(alg_req);
}

static void qat_comp_generic_callback(struct qat_compression_req *qat_req,
				      void *resp)
{
	struct acomp_req *areq = qat_req->acompress_req;
	struct qat_compression_ctx *ctx = qat_req->qat_compression_ctx;
	struct adf_accel_dev *accel_dev = ctx->inst->accel_dev;
	struct crypto_acomp *tfm = crypto_acomp_reqtfm(areq);
	struct qat_compression_instance *inst = ctx->inst;
	struct qat_callback_params params = { };
	int consumed, produced;
	s8 cmp_err, xlt_err;
	int res = -EBADMSG;
	int status;
	u8 cnv;

	status = qat_comp_get_cmp_status(resp);
	status |= qat_comp_get_xlt_status(resp);
	cmp_err = qat_comp_get_cmp_err(resp);
	xlt_err = qat_comp_get_xlt_err(resp);

	consumed = qat_comp_get_consumed_ctr(resp);
	produced = qat_comp_get_produced_ctr(resp);

	/* Cache parameters for algorithm specific callback */
	params.produced = produced;
	params.dlen = areq->dlen;

	dev_dbg(&GET_DEV(accel_dev),
		"[%s][%s][%s] slen = %8d dlen = %8d consumed = %8d produced = %8d cmp_err = %3d xlt_err = %3d",
		crypto_tfm_alg_driver_name(crypto_acomp_tfm(tfm)),
		qat_req->dir == COMPRESSION ? "comp  " : "decomp",
		status ? "ERR" : "OK ",
		areq->slen, areq->dlen, consumed, produced, cmp_err, xlt_err);

	if (unlikely(status != ICP_QAT_FW_COMN_STATUS_FLAG_OK)) {
		if (cmp_err == ERR_CODE_OVERFLOW_ERROR || xlt_err == ERR_CODE_OVERFLOW_ERROR)
			res = -E2BIG;

		areq->dlen = 0;
		goto end;
	}

	if (qat_req->dir == COMPRESSION) {
		cnv = qat_comp_get_cmp_cnv_flag(resp);
		if (unlikely(!cnv)) {
			dev_err(&GET_DEV(accel_dev),
				"Verified compression not supported\n");
			areq->dlen = 0;
			goto end;
		}

		if (unlikely(produced > qat_req->actual_dlen)) {
			memset(inst->dc_data->ovf_buff, 0,
			       inst->dc_data->ovf_buff_sz);
			dev_dbg(&GET_DEV(accel_dev),
				"Actual buffer overflow: produced=%d, dlen=%d\n",
				produced, qat_req->actual_dlen);

			res = -E2BIG;
			areq->dlen = 0;
			goto end;
		}

		params.plain = !!qat_comp_get_cmp_uncomp_flag(resp);
	}

	res = 0;
	areq->dlen = produced;

	if (ctx->qat_comp_callback)
		res = ctx->qat_comp_callback(qat_req, resp, &params);

end:
	qat_bl_free_bufl(accel_dev, &qat_req->buf);
	acomp_request_complete(areq, res);
	qat_alg_send_backlog(qat_req->alg_req.backlog);
}

void qat_comp_alg_callback(void *resp)
{
	struct qat_compression_req *qat_req =
			(void *)(__force long)qat_comp_get_opaque(resp);

	qat_comp_generic_callback(qat_req, resp);
}

static int qat_comp_alg_init_tfm(struct crypto_acomp *acomp_tfm, int alg)
{
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);
	struct crypto_tfm *tfm = crypto_acomp_tfm(acomp_tfm);
	struct qat_compression_instance *inst;
	int node, ret;

	if (tfm->node == NUMA_NO_NODE)
		node = numa_node_id();
	else
		node = tfm->node;

	memset(ctx, 0, sizeof(*ctx));
	inst = qat_compression_get_instance_node(node, alg);
	if (!inst)
		return -EINVAL;
	ctx->inst = inst;

	ret = qat_comp_build_ctx(inst->accel_dev, ctx->comp_ctx, alg);
	if (ret) {
		qat_compression_put_instance(inst);
		memset(ctx, 0, sizeof(*ctx));
	}

	return ret;
}

static int qat_comp_alg_deflate_init_tfm(struct crypto_acomp *acomp_tfm)
{
	return qat_comp_alg_init_tfm(acomp_tfm, QAT_DEFLATE);
}

static void qat_comp_alg_exit_tfm(struct crypto_acomp *acomp_tfm)
{
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);

	qat_compression_put_instance(ctx->inst);
	memset(ctx, 0, sizeof(*ctx));
}

static int qat_comp_alg_compress_decompress(struct acomp_req *areq, enum direction dir,
					    unsigned int shdr, unsigned int sftr,
					    unsigned int dhdr, unsigned int dftr)
{
	struct qat_compression_req *qat_req = acomp_request_ctx(areq);
	struct crypto_acomp *acomp_tfm = crypto_acomp_reqtfm(areq);
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);
	struct qat_compression_instance *inst = ctx->inst;
	gfp_t f = qat_algs_alloc_flags(&areq->base);
	struct qat_sgl_to_bufl_params params = {0};
	int slen = areq->slen - shdr - sftr;
	int dlen = areq->dlen - dhdr - dftr;
	dma_addr_t sfbuf, dfbuf;
	u8 *req = qat_req->req;
	size_t ovf_buff_sz;
	int ret;

	params.sskip = shdr;
	params.dskip = dhdr;

	if (!areq->src || !slen)
		return -EINVAL;

	if (!areq->dst || !dlen)
		return -EINVAL;

	if (dir == COMPRESSION) {
		params.extra_dst_buff = inst->dc_data->ovf_buff_p;
		ovf_buff_sz = inst->dc_data->ovf_buff_sz;
		params.sz_extra_dst_buff = ovf_buff_sz;
	}

	ret = qat_bl_sgl_to_bufl(ctx->inst->accel_dev, areq->src, areq->dst,
				 &qat_req->buf, &params, f);
	if (unlikely(ret))
		return ret;

	sfbuf = qat_req->buf.blp;
	dfbuf = qat_req->buf.bloutp;
	qat_req->qat_compression_ctx = ctx;
	qat_req->acompress_req = areq;
	qat_req->dir = dir;

	if (dir == COMPRESSION) {
		qat_req->actual_dlen = dlen;
		dlen += ovf_buff_sz;
		qat_comp_create_compression_req(ctx->comp_ctx, req,
						(u64)(__force long)sfbuf, slen,
						(u64)(__force long)dfbuf, dlen,
						(u64)(__force long)qat_req);
	} else {
		qat_comp_create_decompression_req(ctx->comp_ctx, req,
						  (u64)(__force long)sfbuf, slen,
						  (u64)(__force long)dfbuf, dlen,
						  (u64)(__force long)qat_req);
	}

	ret = qat_alg_send_dc_message(qat_req, inst, &areq->base);
	if (ret == -ENOSPC)
		qat_bl_free_bufl(inst->accel_dev, &qat_req->buf);

	return ret;
}

static int qat_comp_alg_compress(struct acomp_req *req)
{
	return qat_comp_alg_compress_decompress(req, COMPRESSION, 0, 0, 0, 0);
}

static int qat_comp_alg_decompress(struct acomp_req *req)
{
	return qat_comp_alg_compress_decompress(req, DECOMPRESSION, 0, 0, 0, 0);
}

static int qat_comp_alg_zstd_decompress(struct acomp_req *req)
{
	struct crypto_acomp *acomp_tfm = crypto_acomp_reqtfm(req);
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);
	struct acomp_req *nreq = acomp_request_ctx(req);
	zstd_frame_header header;
	void *buffer;
	size_t zret;
	int ret;

	buffer = kmap_local_page(sg_page(req->src)) + req->src->offset;
	zret = zstd_get_frame_header(&header, buffer, req->src->length);
	kunmap_local(buffer);
	if (zret) {
		dev_err(&GET_DEV(ctx->inst->accel_dev),
			"ZSTD-compressed data has an incomplete frame header\n");
		return -EINVAL;
	}

	if (header.windowSize > QAT_ZSTD_MAX_BLOCK_SIZE ||
	    header.frameContentSize >= QAT_ZSTD_MAX_CONTENT_SIZE) {
		dev_dbg(&GET_DEV(ctx->inst->accel_dev), "Window size=0x%llx\n",
			header.windowSize);

		memcpy(nreq, req, sizeof(*req));
		acomp_request_set_tfm(nreq, ctx->ftfm);

		ret = crypto_acomp_decompress(nreq);
		req->dlen = nreq->dlen;

		return ret;
	}

	return qat_comp_alg_compress_decompress(req, DECOMPRESSION, 0, 0, 0, 0);
}

static int qat_comp_lz4s_zstd_callback(struct qat_compression_req *qat_req, void *resp,
				       struct qat_callback_params *params)
{
	struct qat_compression_ctx *qat_ctx = qat_req->qat_compression_ctx;
	struct acomp_req *areq = qat_req->acompress_req;
	struct qat_zstd_scratch *scratch;
	struct crypto_acomp_stream *s;
	unsigned int lit_len = 0;
	ZSTD_Sequence *out_seqs;
	void *lz4s, *zstd;
	size_t comp_size;
	ZSTD_CCtx *ctx;
	void *literals;
	int seq_count;
	int ret = 0;

	if (params->produced + QAT_ZSTD_LIT_COPY_LEN > QAT_ZSTD_SCRATCH_SIZE) {
		dev_dbg(&GET_DEV(qat_ctx->inst->accel_dev),
			"LZ4s-ZSTD: produced size (%u) + COPY_SIZE > QAT_ZSTD_SCRATCH_SIZE (%u)\n",
			params->produced, QAT_ZSTD_SCRATCH_SIZE);
		areq->dlen = 0;
		return -E2BIG;
	}

	s = crypto_acomp_lock_stream_bh(&qat_zstd_streams);
	scratch = s->ctx;

	lz4s = scratch->lz4s;
	zstd = lz4s;  /* Output buffer is same as lz4s */
	out_seqs = scratch->out_seqs;
	ctx = scratch->ctx;
	literals = scratch->literals;

	if (likely(!params->plain)) {
		if (likely(sg_nents(areq->dst) == 1)) {
			zstd = sg_virt(areq->dst);
			lz4s = zstd;
		} else {
			memcpy_from_sglist(lz4s, areq->dst, 0, params->produced);
		}

		seq_count = qat_alg_dec_lz4s(out_seqs, QAT_MAX_SEQUENCES, lz4s,
					     params->produced, literals, &lit_len);
		if (seq_count < 0) {
			ret = seq_count;
			comp_size = 0;
			goto out;
		}
	} else {
		out_seqs[0].litLength = areq->slen;
		out_seqs[0].offset = 0;
		out_seqs[0].matchLength = 0;

		seq_count = 1;
	}

	comp_size = zstd_compress_sequences_and_literals(ctx, zstd, params->dlen,
							 out_seqs, seq_count,
							 literals, lit_len,
							 QAT_ZSTD_SCRATCH_SIZE,
							 areq->slen);
	if (zstd_is_error(comp_size)) {
		if (comp_size == ZSTD_error_cannotProduce_uncompressedBlock)
			ret = -E2BIG;
		else
			ret = -EOPNOTSUPP;

		comp_size = 0;
		goto out;
	}

	if (comp_size > params->dlen) {
		dev_dbg(&GET_DEV(qat_ctx->inst->accel_dev),
			"LZ4s-ZSTD: compressed_size (%u) > output buffer size (%u)\n",
			(unsigned int)comp_size, params->dlen);
		ret = -EOVERFLOW;
		goto out;
	}

	if (unlikely(sg_nents(areq->dst) != 1))
		memcpy_to_sglist(areq->dst, 0, zstd, comp_size);

out:
	areq->dlen = comp_size;
	crypto_acomp_unlock_stream_bh(s);

	return ret;
}

static int qat_comp_alg_lz4s_zstd_init_tfm(struct crypto_acomp *acomp_tfm)
{
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);
	struct crypto_tfm *tfm = crypto_acomp_tfm(acomp_tfm);
	int reqsize;
	int ret;

	/* qat_comp_alg_init_tfm() wipes out the ctx */
	ret = qat_comp_alg_init_tfm(acomp_tfm, QAT_LZ4S);
	if (ret)
		return ret;

	ctx->ftfm = crypto_alloc_acomp_node("zstd", 0, CRYPTO_ALG_NEED_FALLBACK,
					    tfm->node);
	if (IS_ERR(ctx->ftfm)) {
		qat_comp_alg_exit_tfm(acomp_tfm);
		return PTR_ERR(ctx->ftfm);
	}

	reqsize = max(sizeof(struct qat_compression_req),
		      sizeof(struct acomp_req) + crypto_acomp_reqsize(ctx->ftfm));

	acomp_tfm->reqsize = reqsize;

	ctx->qat_comp_callback = qat_comp_lz4s_zstd_callback;

	return 0;
}

static int qat_comp_alg_zstd_init_tfm(struct crypto_acomp *acomp_tfm)
{
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);
	struct crypto_tfm *tfm = crypto_acomp_tfm(acomp_tfm);
	int reqsize;
	int ret;

	/* qat_comp_alg_init_tfm() wipes out the ctx */
	ret = qat_comp_alg_init_tfm(acomp_tfm, QAT_ZSTD);
	if (ret)
		return ret;

	ctx->ftfm = crypto_alloc_acomp_node("zstd", 0, CRYPTO_ALG_NEED_FALLBACK,
					    tfm->node);
	if (IS_ERR(ctx->ftfm)) {
		qat_comp_alg_exit_tfm(acomp_tfm);
		return PTR_ERR(ctx->ftfm);
	}

	reqsize = max(sizeof(struct qat_compression_req),
		      sizeof(struct acomp_req) + crypto_acomp_reqsize(ctx->ftfm));

	acomp_tfm->reqsize = reqsize;

	return 0;
}

static void qat_comp_alg_zstd_exit_tfm(struct crypto_acomp *acomp_tfm)
{
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);

	if (ctx->ftfm)
		crypto_free_acomp(ctx->ftfm);

	qat_comp_alg_exit_tfm(acomp_tfm);
}

static int qat_comp_alg_lz4s_zstd_compress(struct acomp_req *req)
{
	struct crypto_acomp *acomp_tfm = crypto_acomp_reqtfm(req);
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);
	struct acomp_req *nreq = acomp_request_ctx(req);
	int ret;

	if (req->slen >= QAT_LZ4S_MIN_INPUT_SIZE && req->dlen >= QAT_LZ4S_MIN_INPUT_SIZE &&
	    req->slen <= QAT_LZ4S_MAX_OUTPUT_SIZE && req->dlen <= QAT_LZ4S_MAX_OUTPUT_SIZE)
		return qat_comp_alg_compress(req);

	memcpy(nreq, req, sizeof(*req));
	acomp_request_set_tfm(nreq, ctx->ftfm);

	ret = crypto_acomp_compress(nreq);
	req->dlen = nreq->dlen;

	return ret;
}

static int qat_comp_alg_sw_decompress(struct acomp_req *req)
{
	struct crypto_acomp *acomp_tfm = crypto_acomp_reqtfm(req);
	struct qat_compression_ctx *ctx = acomp_tfm_ctx(acomp_tfm);
	struct acomp_req *nreq = acomp_request_ctx(req);
	int ret;

	memcpy(nreq, req, sizeof(*req));
	acomp_request_set_tfm(nreq, ctx->ftfm);

	ret = crypto_acomp_decompress(nreq);
	req->dlen = nreq->dlen;

	return ret;
}

static struct acomp_alg qat_acomp_deflate[] = { {
	.base = {
		.cra_name = "deflate",
		.cra_driver_name = "qat_deflate",
		.cra_priority = 4001,
		.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY,
		.cra_ctxsize = sizeof(struct qat_compression_ctx),
		.cra_reqsize = sizeof(struct qat_compression_req),
		.cra_module = THIS_MODULE,
	},
	.init = qat_comp_alg_deflate_init_tfm,
	.exit = qat_comp_alg_exit_tfm,
	.compress = qat_comp_alg_compress,
	.decompress = qat_comp_alg_decompress,
}};

static struct acomp_alg qat_acomp_zstd_lz4s = {
	.base = {
		.cra_name = "zstd",
		.cra_driver_name = "qat_zstd",
		.cra_priority = 4001,
		.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY |
			     CRYPTO_ALG_NEED_FALLBACK,
		.cra_reqsize = sizeof(struct qat_compression_req),
		.cra_ctxsize = sizeof(struct qat_compression_ctx),
		.cra_module = THIS_MODULE,
	},
	.init = qat_comp_alg_lz4s_zstd_init_tfm,
	.exit = qat_comp_alg_zstd_exit_tfm,
	.compress = qat_comp_alg_lz4s_zstd_compress,
	.decompress = qat_comp_alg_sw_decompress,
};

static struct acomp_alg qat_acomp_zstd_native = {
	.base = {
		.cra_name = "zstd",
		.cra_driver_name = "qat_zstd",
		.cra_priority = 4001,
		.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY |
			     CRYPTO_ALG_NEED_FALLBACK,
		.cra_reqsize = sizeof(struct qat_compression_req),
		.cra_ctxsize = sizeof(struct qat_compression_ctx),
		.cra_module = THIS_MODULE,
	},
	.init = qat_comp_alg_zstd_init_tfm,
	.exit = qat_comp_alg_zstd_exit_tfm,
	.compress = qat_comp_alg_compress,
	.decompress = qat_comp_alg_zstd_decompress,
};

static int qat_comp_algs_register_deflate(void)
{
	int ret = 0;

	mutex_lock(&algs_lock);
	if (++active_devs_deflate == 1) {
		ret = crypto_register_acomps(qat_acomp_deflate,
					     ARRAY_SIZE(qat_acomp_deflate));
		if (ret)
			active_devs_deflate--;
	}
	mutex_unlock(&algs_lock);

	return ret;
}

static void qat_comp_algs_unregister_deflate(void)
{
	mutex_lock(&algs_lock);
	if (--active_devs_deflate == 0)
		crypto_unregister_acomps(qat_acomp_deflate, ARRAY_SIZE(qat_acomp_deflate));
	mutex_unlock(&algs_lock);
}

static int qat_comp_algs_register_lz4s(void)
{
	int ret = 0;

	mutex_lock(&algs_lock);
	if (++active_devs_lz4s == 1) {
		ret = crypto_acomp_alloc_streams(&qat_zstd_streams);
		if (ret) {
			active_devs_lz4s--;
			goto unlock;
		}

		ret = crypto_register_acomp(&qat_acomp_zstd_lz4s);
		if (ret) {
			crypto_acomp_free_streams(&qat_zstd_streams);
			active_devs_lz4s--;
		}
	}
unlock:
	mutex_unlock(&algs_lock);

	return ret;
}

static void qat_comp_algs_unregister_lz4s(void)
{
	mutex_lock(&algs_lock);
	if (--active_devs_lz4s == 0) {
		crypto_unregister_acomp(&qat_acomp_zstd_lz4s);
		crypto_acomp_free_streams(&qat_zstd_streams);
	}
	mutex_unlock(&algs_lock);
}

static int qat_comp_algs_register_zstd(void)
{
	int ret = 0;

	mutex_lock(&algs_lock);
	if (++active_devs_zstd == 1) {
		ret = crypto_register_acomp(&qat_acomp_zstd_native);
		if (ret)
			active_devs_zstd--;
	}
	mutex_unlock(&algs_lock);

	return ret;
}

static void qat_comp_algs_unregister_zstd(void)
{
	mutex_lock(&algs_lock);
	if (--active_devs_zstd == 0)
		crypto_unregister_acomp(&qat_acomp_zstd_native);
	mutex_unlock(&algs_lock);
}

int qat_comp_algs_register(u32 caps)
{
	int ret;

	ret = qat_comp_algs_register_deflate();
	if (ret)
		return ret;

	if (caps & ADF_ACCEL_CAPABILITIES_EXT_ZSTD_LZ4S) {
		ret = qat_comp_algs_register_lz4s();
		if (ret)
			goto err_unregister_deflate;
	}

	if (caps & ADF_ACCEL_CAPABILITIES_EXT_ZSTD) {
		ret = qat_comp_algs_register_zstd();
		if (ret)
			goto err_unregister_lz4s;
	}

	return ret;

err_unregister_lz4s:
	if (caps & ADF_ACCEL_CAPABILITIES_EXT_ZSTD_LZ4S)
		qat_comp_algs_unregister_lz4s();
err_unregister_deflate:
	qat_comp_algs_unregister_deflate();

	return ret;
}

void qat_comp_algs_unregister(u32 caps)
{
	qat_comp_algs_unregister_deflate();

	if (caps & ADF_ACCEL_CAPABILITIES_EXT_ZSTD_LZ4S)
		qat_comp_algs_unregister_lz4s();

	if (caps & ADF_ACCEL_CAPABILITIES_EXT_ZSTD)
		qat_comp_algs_unregister_zstd();
}