1 // SPDX-License-Identifier: GPL-2.0-or-later 2 3 #define pr_fmt(fmt) "zstd: " fmt 4 5 #include <linux/kernel.h> 6 #include <linux/slab.h> 7 #include <linux/vmalloc.h> 8 #include <linux/zstd.h> 9 10 #include "backend_zstd.h" 11 12 struct zstd_ctx { 13 zstd_cctx *cctx; 14 zstd_dctx *dctx; 15 void *cctx_mem; 16 void *dctx_mem; 17 }; 18 19 struct zstd_params { 20 zstd_custom_mem custom_mem; 21 zstd_cdict *cdict; 22 zstd_ddict *ddict; 23 zstd_parameters cprm; 24 }; 25 26 /* 27 * For C/D dictionaries we need to provide zstd with zstd_custom_mem, 28 * which zstd uses internally to allocate/free memory when needed. 29 */ 30 static void *zstd_custom_alloc(void *opaque, size_t size) 31 { 32 return kvzalloc(size, GFP_NOIO | __GFP_NOWARN); 33 } 34 35 static void zstd_custom_free(void *opaque, void *address) 36 { 37 kvfree(address); 38 } 39 40 static void zstd_release_params(struct zcomp_params *params) 41 { 42 struct zstd_params *zp = params->drv_data; 43 44 params->drv_data = NULL; 45 if (!zp) 46 return; 47 48 zstd_free_cdict(zp->cdict); 49 zstd_free_ddict(zp->ddict); 50 kfree(zp); 51 } 52 53 static int zstd_setup_params(struct zcomp_params *params) 54 { 55 zstd_compression_parameters prm; 56 struct zstd_params *zp; 57 58 zp = kzalloc_obj(*zp); 59 if (!zp) 60 return -ENOMEM; 61 62 params->drv_data = zp; 63 if (params->level == ZCOMP_PARAM_NOT_SET) { 64 params->level = zstd_default_clevel(); 65 } else if (params->level < zstd_min_clevel() || 66 params->level > zstd_max_clevel()) { 67 pr_err("invalid compression level %d\n", params->level); 68 goto error; 69 } 70 71 zp->cprm = zstd_get_params(params->level, PAGE_SIZE); 72 73 zp->custom_mem.customAlloc = zstd_custom_alloc; 74 zp->custom_mem.customFree = zstd_custom_free; 75 76 prm = zstd_get_cparams(params->level, PAGE_SIZE, 77 params->dict_sz); 78 79 zp->cdict = zstd_create_cdict_byreference(params->dict, 80 params->dict_sz, 81 prm, 82 zp->custom_mem); 83 if (!zp->cdict) 84 goto error; 85 86 zp->ddict = zstd_create_ddict_byreference(params->dict, 87 params->dict_sz, 88 zp->custom_mem); 89 if (!zp->ddict) 90 goto error; 91 92 return 0; 93 94 error: 95 return -EINVAL; 96 } 97 98 static void zstd_destroy(struct zcomp_ctx *ctx) 99 { 100 struct zstd_ctx *zctx = ctx->context; 101 102 if (!zctx) 103 return; 104 105 /* 106 * If ->cctx_mem and ->dctx_mem were allocated then we didn't use 107 * C/D dictionary and ->cctx / ->dctx were "embedded" into these 108 * buffers. 109 * 110 * If otherwise then we need to explicitly release ->cctx / ->dctx. 111 */ 112 if (zctx->cctx_mem) 113 vfree(zctx->cctx_mem); 114 else 115 zstd_free_cctx(zctx->cctx); 116 117 if (zctx->dctx_mem) 118 vfree(zctx->dctx_mem); 119 else 120 zstd_free_dctx(zctx->dctx); 121 122 kfree(zctx); 123 } 124 125 static int zstd_create(struct zcomp_params *params, struct zcomp_ctx *ctx) 126 { 127 struct zstd_ctx *zctx; 128 zstd_parameters prm; 129 size_t sz; 130 131 zctx = kzalloc_obj(*zctx); 132 if (!zctx) 133 return -ENOMEM; 134 135 ctx->context = zctx; 136 if (params->dict_sz == 0) { 137 prm = zstd_get_params(params->level, PAGE_SIZE); 138 sz = zstd_cctx_workspace_bound(&prm.cParams); 139 zctx->cctx_mem = vzalloc(sz); 140 if (!zctx->cctx_mem) 141 goto error; 142 143 zctx->cctx = zstd_init_cctx(zctx->cctx_mem, sz); 144 if (!zctx->cctx) 145 goto error; 146 147 sz = zstd_dctx_workspace_bound(); 148 zctx->dctx_mem = vzalloc(sz); 149 if (!zctx->dctx_mem) 150 goto error; 151 152 zctx->dctx = zstd_init_dctx(zctx->dctx_mem, sz); 153 if (!zctx->dctx) 154 goto error; 155 } else { 156 struct zstd_params *zp = params->drv_data; 157 158 zctx->cctx = zstd_create_cctx_advanced(zp->custom_mem); 159 if (!zctx->cctx) 160 goto error; 161 162 zctx->dctx = zstd_create_dctx_advanced(zp->custom_mem); 163 if (!zctx->dctx) 164 goto error; 165 } 166 167 return 0; 168 169 error: 170 zstd_destroy(ctx); 171 return -EINVAL; 172 } 173 174 static int zstd_compress(struct zcomp_params *params, struct zcomp_ctx *ctx, 175 struct zcomp_req *req) 176 { 177 struct zstd_params *zp = params->drv_data; 178 struct zstd_ctx *zctx = ctx->context; 179 size_t ret; 180 181 if (params->dict_sz == 0) 182 ret = zstd_compress_cctx(zctx->cctx, req->dst, req->dst_len, 183 req->src, req->src_len, &zp->cprm); 184 else 185 ret = zstd_compress_using_cdict(zctx->cctx, req->dst, 186 req->dst_len, req->src, 187 req->src_len, 188 zp->cdict); 189 if (zstd_is_error(ret)) 190 return -EINVAL; 191 req->dst_len = ret; 192 return 0; 193 } 194 195 static int zstd_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx, 196 struct zcomp_req *req) 197 { 198 struct zstd_params *zp = params->drv_data; 199 struct zstd_ctx *zctx = ctx->context; 200 size_t ret; 201 202 if (params->dict_sz == 0) 203 ret = zstd_decompress_dctx(zctx->dctx, req->dst, req->dst_len, 204 req->src, req->src_len); 205 else 206 ret = zstd_decompress_using_ddict(zctx->dctx, req->dst, 207 req->dst_len, req->src, 208 req->src_len, zp->ddict); 209 if (zstd_is_error(ret)) 210 return -EINVAL; 211 return 0; 212 } 213 214 const struct zcomp_ops backend_zstd = { 215 .compress = zstd_compress, 216 .decompress = zstd_decompress, 217 .create_ctx = zstd_create, 218 .destroy_ctx = zstd_destroy, 219 .setup_params = zstd_setup_params, 220 .release_params = zstd_release_params, 221 .name = "zstd", 222 }; 223