173e7d81aSSergey Senozhatsky // SPDX-License-Identifier: GPL-2.0-or-later 273e7d81aSSergey Senozhatsky 373e7d81aSSergey Senozhatsky #include <linux/kernel.h> 473e7d81aSSergey Senozhatsky #include <linux/slab.h> 573e7d81aSSergey Senozhatsky #include <linux/vmalloc.h> 673e7d81aSSergey Senozhatsky #include <linux/zstd.h> 773e7d81aSSergey Senozhatsky 873e7d81aSSergey Senozhatsky #include "backend_zstd.h" 973e7d81aSSergey Senozhatsky 1073e7d81aSSergey Senozhatsky struct zstd_ctx { 1173e7d81aSSergey Senozhatsky zstd_cctx *cctx; 1273e7d81aSSergey Senozhatsky zstd_dctx *dctx; 1373e7d81aSSergey Senozhatsky void *cctx_mem; 1473e7d81aSSergey Senozhatsky void *dctx_mem; 1573e7d81aSSergey Senozhatsky }; 1673e7d81aSSergey Senozhatsky 17b8f03cb7SSergey Senozhatsky struct zstd_params { 18*6a559ecdSSergey Senozhatsky zstd_custom_mem custom_mem; 19*6a559ecdSSergey Senozhatsky zstd_cdict *cdict; 20*6a559ecdSSergey Senozhatsky zstd_ddict *ddict; 21b8f03cb7SSergey Senozhatsky zstd_parameters cprm; 22b8f03cb7SSergey Senozhatsky }; 23b8f03cb7SSergey Senozhatsky 24*6a559ecdSSergey Senozhatsky /* 25*6a559ecdSSergey Senozhatsky * For C/D dictionaries we need to provide zstd with zstd_custom_mem, 26*6a559ecdSSergey Senozhatsky * which zstd uses internally to allocate/free memory when needed. 27*6a559ecdSSergey Senozhatsky * 28*6a559ecdSSergey Senozhatsky * This means that allocator.customAlloc() can be called from zcomp_compress() 29*6a559ecdSSergey Senozhatsky * under local-lock (per-CPU compression stream), in which case we must use 30*6a559ecdSSergey Senozhatsky * GFP_ATOMIC. 31*6a559ecdSSergey Senozhatsky * 32*6a559ecdSSergey Senozhatsky * Another complication here is that we can be configured as a swap device. 33*6a559ecdSSergey Senozhatsky */ 34*6a559ecdSSergey Senozhatsky static void *zstd_custom_alloc(void *opaque, size_t size) 35*6a559ecdSSergey Senozhatsky { 36*6a559ecdSSergey Senozhatsky if (!preemptible()) 37*6a559ecdSSergey Senozhatsky return kvzalloc(size, GFP_ATOMIC); 38*6a559ecdSSergey Senozhatsky 39*6a559ecdSSergey Senozhatsky return kvzalloc(size, __GFP_KSWAPD_RECLAIM | __GFP_NOWARN); 40*6a559ecdSSergey Senozhatsky } 41*6a559ecdSSergey Senozhatsky 42*6a559ecdSSergey Senozhatsky static void zstd_custom_free(void *opaque, void *address) 43*6a559ecdSSergey Senozhatsky { 44*6a559ecdSSergey Senozhatsky kvfree(address); 45*6a559ecdSSergey Senozhatsky } 46*6a559ecdSSergey Senozhatsky 47b8f03cb7SSergey Senozhatsky static void zstd_release_params(struct zcomp_params *params) 48b8f03cb7SSergey Senozhatsky { 49*6a559ecdSSergey Senozhatsky struct zstd_params *zp = params->drv_data; 50*6a559ecdSSergey Senozhatsky 51*6a559ecdSSergey Senozhatsky params->drv_data = NULL; 52*6a559ecdSSergey Senozhatsky if (!zp) 53*6a559ecdSSergey Senozhatsky return; 54*6a559ecdSSergey Senozhatsky 55*6a559ecdSSergey Senozhatsky zstd_free_cdict(zp->cdict); 56*6a559ecdSSergey Senozhatsky zstd_free_ddict(zp->ddict); 57*6a559ecdSSergey Senozhatsky kfree(zp); 58b8f03cb7SSergey Senozhatsky } 59b8f03cb7SSergey Senozhatsky 60b8f03cb7SSergey Senozhatsky static int zstd_setup_params(struct zcomp_params *params) 61b8f03cb7SSergey Senozhatsky { 62*6a559ecdSSergey Senozhatsky zstd_compression_parameters prm; 63b8f03cb7SSergey Senozhatsky struct zstd_params *zp; 64b8f03cb7SSergey Senozhatsky 65b8f03cb7SSergey Senozhatsky zp = kzalloc(sizeof(*zp), GFP_KERNEL); 66b8f03cb7SSergey Senozhatsky if (!zp) 67b8f03cb7SSergey Senozhatsky return -ENOMEM; 68b8f03cb7SSergey Senozhatsky 69*6a559ecdSSergey Senozhatsky params->drv_data = zp; 70b8f03cb7SSergey Senozhatsky if (params->level == ZCOMP_PARAM_NO_LEVEL) 71b8f03cb7SSergey Senozhatsky params->level = zstd_default_clevel(); 72b8f03cb7SSergey Senozhatsky 73b8f03cb7SSergey Senozhatsky zp->cprm = zstd_get_params(params->level, PAGE_SIZE); 74*6a559ecdSSergey Senozhatsky 75*6a559ecdSSergey Senozhatsky zp->custom_mem.customAlloc = zstd_custom_alloc; 76*6a559ecdSSergey Senozhatsky zp->custom_mem.customFree = zstd_custom_free; 77*6a559ecdSSergey Senozhatsky 78*6a559ecdSSergey Senozhatsky prm = zstd_get_cparams(params->level, PAGE_SIZE, 79*6a559ecdSSergey Senozhatsky params->dict_sz); 80*6a559ecdSSergey Senozhatsky 81*6a559ecdSSergey Senozhatsky zp->cdict = zstd_create_cdict_byreference(params->dict, 82*6a559ecdSSergey Senozhatsky params->dict_sz, 83*6a559ecdSSergey Senozhatsky prm, 84*6a559ecdSSergey Senozhatsky zp->custom_mem); 85*6a559ecdSSergey Senozhatsky if (!zp->cdict) 86*6a559ecdSSergey Senozhatsky goto error; 87*6a559ecdSSergey Senozhatsky 88*6a559ecdSSergey Senozhatsky zp->ddict = zstd_create_ddict_byreference(params->dict, 89*6a559ecdSSergey Senozhatsky params->dict_sz, 90*6a559ecdSSergey Senozhatsky zp->custom_mem); 91*6a559ecdSSergey Senozhatsky if (!zp->ddict) 92*6a559ecdSSergey Senozhatsky goto error; 93b8f03cb7SSergey Senozhatsky 94b8f03cb7SSergey Senozhatsky return 0; 95*6a559ecdSSergey Senozhatsky 96*6a559ecdSSergey Senozhatsky error: 97*6a559ecdSSergey Senozhatsky zstd_release_params(params); 98*6a559ecdSSergey Senozhatsky return -EINVAL; 99b8f03cb7SSergey Senozhatsky } 100b8f03cb7SSergey Senozhatsky 1016a81bdfeSSergey Senozhatsky static void zstd_destroy(struct zcomp_ctx *ctx) 10273e7d81aSSergey Senozhatsky { 1036a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx = ctx->context; 1046a81bdfeSSergey Senozhatsky 1056a81bdfeSSergey Senozhatsky if (!zctx) 1066a81bdfeSSergey Senozhatsky return; 10773e7d81aSSergey Senozhatsky 108*6a559ecdSSergey Senozhatsky /* 109*6a559ecdSSergey Senozhatsky * If ->cctx_mem and ->dctx_mem were allocated then we didn't use 110*6a559ecdSSergey Senozhatsky * C/D dictionary and ->cctx / ->dctx were "embedded" into these 111*6a559ecdSSergey Senozhatsky * buffers. 112*6a559ecdSSergey Senozhatsky * 113*6a559ecdSSergey Senozhatsky * If otherwise then we need to explicitly release ->cctx / ->dctx. 114*6a559ecdSSergey Senozhatsky */ 115*6a559ecdSSergey Senozhatsky if (zctx->cctx_mem) 11673e7d81aSSergey Senozhatsky vfree(zctx->cctx_mem); 117*6a559ecdSSergey Senozhatsky else 118*6a559ecdSSergey Senozhatsky zstd_free_cctx(zctx->cctx); 119*6a559ecdSSergey Senozhatsky 120*6a559ecdSSergey Senozhatsky if (zctx->dctx_mem) 12173e7d81aSSergey Senozhatsky vfree(zctx->dctx_mem); 122*6a559ecdSSergey Senozhatsky else 123*6a559ecdSSergey Senozhatsky zstd_free_dctx(zctx->dctx); 124*6a559ecdSSergey Senozhatsky 12573e7d81aSSergey Senozhatsky kfree(zctx); 12673e7d81aSSergey Senozhatsky } 12773e7d81aSSergey Senozhatsky 1286a81bdfeSSergey Senozhatsky static int zstd_create(struct zcomp_params *params, struct zcomp_ctx *ctx) 12973e7d81aSSergey Senozhatsky { 1306a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx; 131f2bac7adSSergey Senozhatsky zstd_parameters prm; 13273e7d81aSSergey Senozhatsky size_t sz; 13373e7d81aSSergey Senozhatsky 1346a81bdfeSSergey Senozhatsky zctx = kzalloc(sizeof(*zctx), GFP_KERNEL); 1356a81bdfeSSergey Senozhatsky if (!zctx) 1366a81bdfeSSergey Senozhatsky return -ENOMEM; 13773e7d81aSSergey Senozhatsky 1386a81bdfeSSergey Senozhatsky ctx->context = zctx; 139*6a559ecdSSergey Senozhatsky if (params->dict_sz == 0) { 140b8f03cb7SSergey Senozhatsky prm = zstd_get_params(params->level, PAGE_SIZE); 141f2bac7adSSergey Senozhatsky sz = zstd_cctx_workspace_bound(&prm.cParams); 1426a81bdfeSSergey Senozhatsky zctx->cctx_mem = vzalloc(sz); 1436a81bdfeSSergey Senozhatsky if (!zctx->cctx_mem) 14473e7d81aSSergey Senozhatsky goto error; 14573e7d81aSSergey Senozhatsky 1466a81bdfeSSergey Senozhatsky zctx->cctx = zstd_init_cctx(zctx->cctx_mem, sz); 1476a81bdfeSSergey Senozhatsky if (!zctx->cctx) 14873e7d81aSSergey Senozhatsky goto error; 14973e7d81aSSergey Senozhatsky 15073e7d81aSSergey Senozhatsky sz = zstd_dctx_workspace_bound(); 1516a81bdfeSSergey Senozhatsky zctx->dctx_mem = vzalloc(sz); 1526a81bdfeSSergey Senozhatsky if (!zctx->dctx_mem) 15373e7d81aSSergey Senozhatsky goto error; 15473e7d81aSSergey Senozhatsky 1556a81bdfeSSergey Senozhatsky zctx->dctx = zstd_init_dctx(zctx->dctx_mem, sz); 1566a81bdfeSSergey Senozhatsky if (!zctx->dctx) 15773e7d81aSSergey Senozhatsky goto error; 158*6a559ecdSSergey Senozhatsky } else { 159*6a559ecdSSergey Senozhatsky struct zstd_params *zp = params->drv_data; 160*6a559ecdSSergey Senozhatsky 161*6a559ecdSSergey Senozhatsky zctx->cctx = zstd_create_cctx_advanced(zp->custom_mem); 162*6a559ecdSSergey Senozhatsky if (!zctx->cctx) 163*6a559ecdSSergey Senozhatsky goto error; 164*6a559ecdSSergey Senozhatsky 165*6a559ecdSSergey Senozhatsky zctx->dctx = zstd_create_dctx_advanced(zp->custom_mem); 166*6a559ecdSSergey Senozhatsky if (!zctx->dctx) 167*6a559ecdSSergey Senozhatsky goto error; 168*6a559ecdSSergey Senozhatsky } 16973e7d81aSSergey Senozhatsky 1706a81bdfeSSergey Senozhatsky return 0; 17173e7d81aSSergey Senozhatsky 17273e7d81aSSergey Senozhatsky error: 173*6a559ecdSSergey Senozhatsky zstd_release_params(params); 17473e7d81aSSergey Senozhatsky zstd_destroy(ctx); 1756a81bdfeSSergey Senozhatsky return -EINVAL; 17673e7d81aSSergey Senozhatsky } 17773e7d81aSSergey Senozhatsky 178b8f03cb7SSergey Senozhatsky static int zstd_compress(struct zcomp_params *params, struct zcomp_ctx *ctx, 179b8f03cb7SSergey Senozhatsky struct zcomp_req *req) 18073e7d81aSSergey Senozhatsky { 181b8f03cb7SSergey Senozhatsky struct zstd_params *zp = params->drv_data; 1826a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx = ctx->context; 18373e7d81aSSergey Senozhatsky size_t ret; 18473e7d81aSSergey Senozhatsky 185*6a559ecdSSergey Senozhatsky if (params->dict_sz == 0) 18652c7b4e2SSergey Senozhatsky ret = zstd_compress_cctx(zctx->cctx, req->dst, req->dst_len, 187b8f03cb7SSergey Senozhatsky req->src, req->src_len, &zp->cprm); 188*6a559ecdSSergey Senozhatsky else 189*6a559ecdSSergey Senozhatsky ret = zstd_compress_using_cdict(zctx->cctx, req->dst, 190*6a559ecdSSergey Senozhatsky req->dst_len, req->src, 191*6a559ecdSSergey Senozhatsky req->src_len, 192*6a559ecdSSergey Senozhatsky zp->cdict); 19373e7d81aSSergey Senozhatsky if (zstd_is_error(ret)) 19473e7d81aSSergey Senozhatsky return -EINVAL; 19552c7b4e2SSergey Senozhatsky req->dst_len = ret; 19673e7d81aSSergey Senozhatsky return 0; 19773e7d81aSSergey Senozhatsky } 19873e7d81aSSergey Senozhatsky 199b8f03cb7SSergey Senozhatsky static int zstd_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx, 200b8f03cb7SSergey Senozhatsky struct zcomp_req *req) 20173e7d81aSSergey Senozhatsky { 202*6a559ecdSSergey Senozhatsky struct zstd_params *zp = params->drv_data; 2036a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx = ctx->context; 20473e7d81aSSergey Senozhatsky size_t ret; 20573e7d81aSSergey Senozhatsky 206*6a559ecdSSergey Senozhatsky if (params->dict_sz == 0) 20752c7b4e2SSergey Senozhatsky ret = zstd_decompress_dctx(zctx->dctx, req->dst, req->dst_len, 20852c7b4e2SSergey Senozhatsky req->src, req->src_len); 209*6a559ecdSSergey Senozhatsky else 210*6a559ecdSSergey Senozhatsky ret = zstd_decompress_using_ddict(zctx->dctx, req->dst, 211*6a559ecdSSergey Senozhatsky req->dst_len, req->src, 212*6a559ecdSSergey Senozhatsky req->src_len, zp->ddict); 21373e7d81aSSergey Senozhatsky if (zstd_is_error(ret)) 21473e7d81aSSergey Senozhatsky return -EINVAL; 21573e7d81aSSergey Senozhatsky return 0; 21673e7d81aSSergey Senozhatsky } 21773e7d81aSSergey Senozhatsky 21873e7d81aSSergey Senozhatsky const struct zcomp_ops backend_zstd = { 21973e7d81aSSergey Senozhatsky .compress = zstd_compress, 22073e7d81aSSergey Senozhatsky .decompress = zstd_decompress, 22173e7d81aSSergey Senozhatsky .create_ctx = zstd_create, 22273e7d81aSSergey Senozhatsky .destroy_ctx = zstd_destroy, 223b8f03cb7SSergey Senozhatsky .setup_params = zstd_setup_params, 224b8f03cb7SSergey Senozhatsky .release_params = zstd_release_params, 22573e7d81aSSergey Senozhatsky .name = "zstd", 22673e7d81aSSergey Senozhatsky }; 227