// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include #include #include "backend_zstd.h" struct zstd_ctx { zstd_cctx *cctx; zstd_dctx *dctx; void *cctx_mem; void *dctx_mem; }; struct zstd_params { zstd_custom_mem custom_mem; zstd_cdict *cdict; zstd_ddict *ddict; zstd_parameters cprm; }; /* * For C/D dictionaries we need to provide zstd with zstd_custom_mem, * which zstd uses internally to allocate/free memory when needed. * * This means that allocator.customAlloc() can be called from zcomp_compress() * under local-lock (per-CPU compression stream), in which case we must use * GFP_ATOMIC. * * Another complication here is that we can be configured as a swap device. */ static void *zstd_custom_alloc(void *opaque, size_t size) { if (!preemptible()) return kvzalloc(size, GFP_ATOMIC); return kvzalloc(size, __GFP_KSWAPD_RECLAIM | __GFP_NOWARN); } static void zstd_custom_free(void *opaque, void *address) { kvfree(address); } static void zstd_release_params(struct zcomp_params *params) { struct zstd_params *zp = params->drv_data; params->drv_data = NULL; if (!zp) return; zstd_free_cdict(zp->cdict); zstd_free_ddict(zp->ddict); kfree(zp); } static int zstd_setup_params(struct zcomp_params *params) { zstd_compression_parameters prm; struct zstd_params *zp; zp = kzalloc(sizeof(*zp), GFP_KERNEL); if (!zp) return -ENOMEM; params->drv_data = zp; if (params->level == ZCOMP_PARAM_NO_LEVEL) params->level = zstd_default_clevel(); zp->cprm = zstd_get_params(params->level, PAGE_SIZE); zp->custom_mem.customAlloc = zstd_custom_alloc; zp->custom_mem.customFree = zstd_custom_free; prm = zstd_get_cparams(params->level, PAGE_SIZE, params->dict_sz); zp->cdict = zstd_create_cdict_byreference(params->dict, params->dict_sz, prm, zp->custom_mem); if (!zp->cdict) goto error; zp->ddict = zstd_create_ddict_byreference(params->dict, params->dict_sz, zp->custom_mem); if (!zp->ddict) goto error; return 0; error: zstd_release_params(params); return -EINVAL; } static void zstd_destroy(struct zcomp_ctx *ctx) { struct zstd_ctx *zctx = ctx->context; if (!zctx) return; /* * If ->cctx_mem and ->dctx_mem were allocated then we didn't use * C/D dictionary and ->cctx / ->dctx were "embedded" into these * buffers. * * If otherwise then we need to explicitly release ->cctx / ->dctx. */ if (zctx->cctx_mem) vfree(zctx->cctx_mem); else zstd_free_cctx(zctx->cctx); if (zctx->dctx_mem) vfree(zctx->dctx_mem); else zstd_free_dctx(zctx->dctx); kfree(zctx); } static int zstd_create(struct zcomp_params *params, struct zcomp_ctx *ctx) { struct zstd_ctx *zctx; zstd_parameters prm; size_t sz; zctx = kzalloc(sizeof(*zctx), GFP_KERNEL); if (!zctx) return -ENOMEM; ctx->context = zctx; if (params->dict_sz == 0) { prm = zstd_get_params(params->level, PAGE_SIZE); sz = zstd_cctx_workspace_bound(&prm.cParams); zctx->cctx_mem = vzalloc(sz); if (!zctx->cctx_mem) goto error; zctx->cctx = zstd_init_cctx(zctx->cctx_mem, sz); if (!zctx->cctx) goto error; sz = zstd_dctx_workspace_bound(); zctx->dctx_mem = vzalloc(sz); if (!zctx->dctx_mem) goto error; zctx->dctx = zstd_init_dctx(zctx->dctx_mem, sz); if (!zctx->dctx) goto error; } else { struct zstd_params *zp = params->drv_data; zctx->cctx = zstd_create_cctx_advanced(zp->custom_mem); if (!zctx->cctx) goto error; zctx->dctx = zstd_create_dctx_advanced(zp->custom_mem); if (!zctx->dctx) goto error; } return 0; error: zstd_release_params(params); zstd_destroy(ctx); return -EINVAL; } static int zstd_compress(struct zcomp_params *params, struct zcomp_ctx *ctx, struct zcomp_req *req) { struct zstd_params *zp = params->drv_data; struct zstd_ctx *zctx = ctx->context; size_t ret; if (params->dict_sz == 0) ret = zstd_compress_cctx(zctx->cctx, req->dst, req->dst_len, req->src, req->src_len, &zp->cprm); else ret = zstd_compress_using_cdict(zctx->cctx, req->dst, req->dst_len, req->src, req->src_len, zp->cdict); if (zstd_is_error(ret)) return -EINVAL; req->dst_len = ret; return 0; } static int zstd_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx, struct zcomp_req *req) { struct zstd_params *zp = params->drv_data; struct zstd_ctx *zctx = ctx->context; size_t ret; if (params->dict_sz == 0) ret = zstd_decompress_dctx(zctx->dctx, req->dst, req->dst_len, req->src, req->src_len); else ret = zstd_decompress_using_ddict(zctx->dctx, req->dst, req->dst_len, req->src, req->src_len, zp->ddict); if (zstd_is_error(ret)) return -EINVAL; return 0; } const struct zcomp_ops backend_zstd = { .compress = zstd_compress, .decompress = zstd_decompress, .create_ctx = zstd_create, .destroy_ctx = zstd_destroy, .setup_params = zstd_setup_params, .release_params = zstd_release_params, .name = "zstd", };