1 // SPDX-License-Identifier: GPL-2.0-or-later 2 3 #include <linux/kernel.h> 4 #include <linux/string.h> 5 #include <linux/err.h> 6 #include <linux/slab.h> 7 #include <linux/wait.h> 8 #include <linux/sched.h> 9 #include <linux/cpu.h> 10 #include <linux/crypto.h> 11 #include <linux/vmalloc.h> 12 13 #include "zcomp.h" 14 15 #include "backend_lzo.h" 16 #include "backend_lzorle.h" 17 #include "backend_lz4.h" 18 #include "backend_lz4hc.h" 19 20 static const struct zcomp_ops *backends[] = { 21 #if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZO) 22 &backend_lzorle, 23 &backend_lzo, 24 #endif 25 #if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZ4) 26 &backend_lz4, 27 #endif 28 #if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZ4HC) 29 &backend_lz4hc, 30 #endif 31 NULL 32 }; 33 34 static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm) 35 { 36 if (zstrm->ctx) 37 comp->ops->destroy_ctx(zstrm->ctx); 38 vfree(zstrm->buffer); 39 zstrm->ctx = NULL; 40 zstrm->buffer = NULL; 41 } 42 43 static int zcomp_strm_init(struct zcomp *comp, struct zcomp_strm *zstrm) 44 { 45 zstrm->ctx = comp->ops->create_ctx(); 46 47 /* 48 * allocate 2 pages. 1 for compressed data, plus 1 extra for the 49 * case when compressed size is larger than the original one 50 */ 51 zstrm->buffer = vzalloc(2 * PAGE_SIZE); 52 if (!zstrm->ctx || !zstrm->buffer) { 53 zcomp_strm_free(comp, zstrm); 54 return -ENOMEM; 55 } 56 return 0; 57 } 58 59 static const struct zcomp_ops *lookup_backend_ops(const char *comp) 60 { 61 int i = 0; 62 63 while (backends[i]) { 64 if (sysfs_streq(comp, backends[i]->name)) 65 break; 66 i++; 67 } 68 return backends[i]; 69 } 70 71 bool zcomp_available_algorithm(const char *comp) 72 { 73 return lookup_backend_ops(comp) != NULL; 74 } 75 76 /* show available compressors */ 77 ssize_t zcomp_available_show(const char *comp, char *buf) 78 { 79 ssize_t sz = 0; 80 int i; 81 82 for (i = 0; i < ARRAY_SIZE(backends) - 1; i++) { 83 if (!strcmp(comp, backends[i]->name)) { 84 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, 85 "[%s] ", backends[i]->name); 86 } else { 87 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, 88 "%s ", backends[i]->name); 89 } 90 } 91 92 sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); 93 return sz; 94 } 95 96 struct zcomp_strm *zcomp_stream_get(struct zcomp *comp) 97 { 98 local_lock(&comp->stream->lock); 99 return this_cpu_ptr(comp->stream); 100 } 101 102 void zcomp_stream_put(struct zcomp *comp) 103 { 104 local_unlock(&comp->stream->lock); 105 } 106 107 int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, 108 const void *src, unsigned int *dst_len) 109 { 110 /* The dst buffer should always be 2 * PAGE_SIZE */ 111 size_t dlen = 2 * PAGE_SIZE; 112 int ret; 113 114 ret = comp->ops->compress(zstrm->ctx, src, PAGE_SIZE, 115 zstrm->buffer, &dlen); 116 if (!ret) 117 *dst_len = dlen; 118 return ret; 119 } 120 121 int zcomp_decompress(struct zcomp *comp, struct zcomp_strm *zstrm, 122 const void *src, unsigned int src_len, void *dst) 123 { 124 return comp->ops->decompress(zstrm->ctx, src, src_len, 125 dst, PAGE_SIZE); 126 } 127 128 int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node) 129 { 130 struct zcomp *comp = hlist_entry(node, struct zcomp, node); 131 struct zcomp_strm *zstrm; 132 int ret; 133 134 zstrm = per_cpu_ptr(comp->stream, cpu); 135 local_lock_init(&zstrm->lock); 136 137 ret = zcomp_strm_init(comp, zstrm); 138 if (ret) 139 pr_err("Can't allocate a compression stream\n"); 140 return ret; 141 } 142 143 int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node) 144 { 145 struct zcomp *comp = hlist_entry(node, struct zcomp, node); 146 struct zcomp_strm *zstrm; 147 148 zstrm = per_cpu_ptr(comp->stream, cpu); 149 zcomp_strm_free(comp, zstrm); 150 return 0; 151 } 152 153 static int zcomp_init(struct zcomp *comp) 154 { 155 int ret; 156 157 comp->stream = alloc_percpu(struct zcomp_strm); 158 if (!comp->stream) 159 return -ENOMEM; 160 161 ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node); 162 if (ret < 0) 163 goto cleanup; 164 return 0; 165 166 cleanup: 167 free_percpu(comp->stream); 168 return ret; 169 } 170 171 void zcomp_destroy(struct zcomp *comp) 172 { 173 cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node); 174 free_percpu(comp->stream); 175 kfree(comp); 176 } 177 178 struct zcomp *zcomp_create(const char *alg) 179 { 180 struct zcomp *comp; 181 int error; 182 183 comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); 184 if (!comp) 185 return ERR_PTR(-ENOMEM); 186 187 comp->ops = lookup_backend_ops(alg); 188 if (!comp->ops) { 189 kfree(comp); 190 return ERR_PTR(-EINVAL); 191 } 192 193 error = zcomp_init(comp); 194 if (error) { 195 kfree(comp); 196 return ERR_PTR(error); 197 } 198 return comp; 199 } 200