1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2014 Sergey Senozhatsky. 4 */ 5 6#include <linux/kernel.h> 7#include <linux/string.h> 8#include <linux/err.h> 9#include <linux/slab.h> 10#include <linux/wait.h> 11#include <linux/sched.h> 12#include <linux/cpu.h> 13#include <linux/crypto.h> 14 15#include "zcomp.h" 16 17static const char * const backends[] = { 18 "lzo", 19 "lzo-rle", 20#if IS_ENABLED(CONFIG_CRYPTO_LZ4) 21 "lz4", 22#endif 23#if IS_ENABLED(CONFIG_CRYPTO_LZ4HC) 24 "lz4hc", 25#endif 26#if IS_ENABLED(CONFIG_CRYPTO_842) 27 "842", 28#endif 29#if IS_ENABLED(CONFIG_CRYPTO_ZSTD) 30 "zstd", 31#endif 32}; 33 34static void zcomp_strm_free(struct zcomp_strm *zstrm) 35{ 36 if (!IS_ERR_OR_NULL(zstrm->tfm)) 37 crypto_free_comp(zstrm->tfm); 38 free_pages((unsigned long)zstrm->buffer, 1); 39 zstrm->tfm = NULL; 40 zstrm->buffer = NULL; 41} 42 43/* 44 * Initialize zcomp_strm structure with ->tfm initialized by backend, and 45 * ->buffer. Return a negative value on error. 46 */ 47static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp) 48{ 49 zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0); 50 /* 51 * allocate 2 pages. 1 for compressed data, plus 1 extra for the 52 * case when compressed size is larger than the original one 53 */ 54 zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); 55 if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) { 56 zcomp_strm_free(zstrm); 57 return -ENOMEM; 58 } 59 return 0; 60} 61 62bool zcomp_available_algorithm(const char *comp) 63{ 64 /* 65 * Crypto does not ignore a trailing new line symbol, 66 * so make sure you don't supply a string containing 67 * one. 68 * This also means that we permit zcomp initialisation 69 * with any compressing algorithm known to crypto api. 70 */ 71 return crypto_has_comp(comp, 0, 0) == 1; 72} 73 74/* show available compressors */ 75ssize_t zcomp_available_show(const char *comp, char *buf) 76{ 77 bool known_algorithm = false; 78 ssize_t sz = 0; 79 int i; 80 81 for (i = 0; i < ARRAY_SIZE(backends); i++) { 82 if (!strcmp(comp, backends[i])) { 83 known_algorithm = true; 84 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, 85 "[%s] ", backends[i]); 86 } else { 87 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, 88 "%s ", backends[i]); 89 } 90 } 91 92 /* 93 * Out-of-tree module known to crypto api or a missing 94 * entry in `backends'. 95 */ 96 if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1) 97 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, 98 "[%s] ", comp); 99 100 sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); 101 return sz; 102} 103 104struct zcomp_strm *zcomp_stream_get(struct zcomp *comp) 105{ 106 local_lock(&comp->stream->lock); 107 return this_cpu_ptr(comp->stream); 108} 109 110void zcomp_stream_put(struct zcomp *comp) 111{ 112 local_unlock(&comp->stream->lock); 113} 114 115int zcomp_compress(struct zcomp_strm *zstrm, 116 const void *src, unsigned int *dst_len) 117{ 118 /* 119 * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized 120 * because sometimes we can endup having a bigger compressed data 121 * due to various reasons: for example compression algorithms tend 122 * to add some padding to the compressed buffer. Speaking of padding, 123 * comp algorithm `842' pads the compressed length to multiple of 8 124 * and returns -ENOSP when the dst memory is not big enough, which 125 * is not something that ZRAM wants to see. We can handle the 126 * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we 127 * receive -ERRNO from the compressing backend we can't help it 128 * anymore. To make `842' happy we need to tell the exact size of 129 * the dst buffer, zram_drv will take care of the fact that 130 * compressed buffer is too big. 131 */ 132 *dst_len = PAGE_SIZE * 2; 133 134 return crypto_comp_compress(zstrm->tfm, 135 src, PAGE_SIZE, 136 zstrm->buffer, dst_len); 137} 138 139int zcomp_decompress(struct zcomp_strm *zstrm, 140 const void *src, unsigned int src_len, void *dst) 141{ 142 unsigned int dst_len = PAGE_SIZE; 143 144 return crypto_comp_decompress(zstrm->tfm, 145 src, src_len, 146 dst, &dst_len); 147} 148 149int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node) 150{ 151 struct zcomp *comp = hlist_entry(node, struct zcomp, node); 152 struct zcomp_strm *zstrm; 153 int ret; 154 155 zstrm = per_cpu_ptr(comp->stream, cpu); 156 local_lock_init(&zstrm->lock); 157 158 ret = zcomp_strm_init(zstrm, comp); 159 if (ret) 160 pr_err("Can't allocate a compression stream\n"); 161 return ret; 162} 163 164int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node) 165{ 166 struct zcomp *comp = hlist_entry(node, struct zcomp, node); 167 struct zcomp_strm *zstrm; 168 169 zstrm = per_cpu_ptr(comp->stream, cpu); 170 zcomp_strm_free(zstrm); 171 return 0; 172} 173 174static int zcomp_init(struct zcomp *comp) 175{ 176 int ret; 177 178 comp->stream = alloc_percpu(struct zcomp_strm); 179 if (!comp->stream) 180 return -ENOMEM; 181 182 ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node); 183 if (ret < 0) 184 goto cleanup; 185 return 0; 186 187cleanup: 188 free_percpu(comp->stream); 189 return ret; 190} 191 192void zcomp_destroy(struct zcomp *comp) 193{ 194 cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node); 195 free_percpu(comp->stream); 196 kfree(comp); 197} 198 199/* 200 * search available compressors for requested algorithm. 201 * allocate new zcomp and initialize it. return compressing 202 * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) 203 * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in 204 * case of allocation error, or any other error potentially 205 * returned by zcomp_init(). 206 */ 207struct zcomp *zcomp_create(const char *compress) 208{ 209 struct zcomp *comp; 210 int error; 211 212 /* 213 * Crypto API will execute /sbin/modprobe if the compression module 214 * is not loaded yet. We must do it here, otherwise we are about to 215 * call /sbin/modprobe under CPU hot-plug lock. 216 */ 217 if (!zcomp_available_algorithm(compress)) 218 return ERR_PTR(-EINVAL); 219 220 comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); 221 if (!comp) 222 return ERR_PTR(-ENOMEM); 223 224 comp->name = compress; 225 error = zcomp_init(comp); 226 if (error) { 227 kfree(comp); 228 return ERR_PTR(error); 229 } 230 return comp; 231} 232