1 /* 2 * Copyright (C) 2014 Sergey Senozhatsky. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/string.h> 12 #include <linux/err.h> 13 #include <linux/slab.h> 14 #include <linux/wait.h> 15 #include <linux/sched.h> 16 #include <linux/cpu.h> 17 18 #include "zcomp.h" 19 #include "zcomp_lzo.h" 20 #ifdef CONFIG_ZRAM_LZ4_COMPRESS 21 #include "zcomp_lz4.h" 22 #endif 23 24 static struct zcomp_backend *backends[] = { 25 &zcomp_lzo, 26 #ifdef CONFIG_ZRAM_LZ4_COMPRESS 27 &zcomp_lz4, 28 #endif 29 NULL 30 }; 31 32 static struct zcomp_backend *find_backend(const char *compress) 33 { 34 int i = 0; 35 while (backends[i]) { 36 if (sysfs_streq(compress, backends[i]->name)) 37 break; 38 i++; 39 } 40 return backends[i]; 41 } 42 43 static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm) 44 { 45 if (zstrm->private) 46 comp->backend->destroy(zstrm->private); 47 free_pages((unsigned long)zstrm->buffer, 1); 48 kfree(zstrm); 49 } 50 51 /* 52 * allocate new zcomp_strm structure with ->private initialized by 53 * backend, return NULL on error 54 */ 55 static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp, gfp_t flags) 56 { 57 struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), flags); 58 if (!zstrm) 59 return NULL; 60 61 zstrm->private = comp->backend->create(flags); 62 /* 63 * allocate 2 pages. 1 for compressed data, plus 1 extra for the 64 * case when compressed size is larger than the original one 65 */ 66 zstrm->buffer = (void *)__get_free_pages(flags | __GFP_ZERO, 1); 67 if (!zstrm->private || !zstrm->buffer) { 68 zcomp_strm_free(comp, zstrm); 69 zstrm = NULL; 70 } 71 return zstrm; 72 } 73 74 /* show available compressors */ 75 ssize_t zcomp_available_show(const char *comp, char *buf) 76 { 77 ssize_t sz = 0; 78 int i = 0; 79 80 while (backends[i]) { 81 if (!strcmp(comp, backends[i]->name)) 82 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, 83 "[%s] ", backends[i]->name); 84 else 85 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, 86 "%s ", backends[i]->name); 87 i++; 88 } 89 sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); 90 return sz; 91 } 92 93 bool zcomp_available_algorithm(const char *comp) 94 { 95 return find_backend(comp) != NULL; 96 } 97 98 struct zcomp_strm *zcomp_strm_find(struct zcomp *comp) 99 { 100 return *get_cpu_ptr(comp->stream); 101 } 102 103 void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm) 104 { 105 put_cpu_ptr(comp->stream); 106 } 107 108 int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, 109 const unsigned char *src, size_t *dst_len) 110 { 111 return comp->backend->compress(src, zstrm->buffer, dst_len, 112 zstrm->private); 113 } 114 115 int zcomp_decompress(struct zcomp *comp, const unsigned char *src, 116 size_t src_len, unsigned char *dst) 117 { 118 return comp->backend->decompress(src, src_len, dst); 119 } 120 121 static int __zcomp_cpu_notifier(struct zcomp *comp, 122 unsigned long action, unsigned long cpu) 123 { 124 struct zcomp_strm *zstrm; 125 126 switch (action) { 127 case CPU_UP_PREPARE: 128 if (WARN_ON(*per_cpu_ptr(comp->stream, cpu))) 129 break; 130 zstrm = zcomp_strm_alloc(comp, GFP_KERNEL); 131 if (IS_ERR_OR_NULL(zstrm)) { 132 pr_err("Can't allocate a compression stream\n"); 133 return NOTIFY_BAD; 134 } 135 *per_cpu_ptr(comp->stream, cpu) = zstrm; 136 break; 137 case CPU_DEAD: 138 case CPU_UP_CANCELED: 139 zstrm = *per_cpu_ptr(comp->stream, cpu); 140 if (!IS_ERR_OR_NULL(zstrm)) 141 zcomp_strm_free(comp, zstrm); 142 *per_cpu_ptr(comp->stream, cpu) = NULL; 143 break; 144 default: 145 break; 146 } 147 return NOTIFY_OK; 148 } 149 150 static int zcomp_cpu_notifier(struct notifier_block *nb, 151 unsigned long action, void *pcpu) 152 { 153 unsigned long cpu = (unsigned long)pcpu; 154 struct zcomp *comp = container_of(nb, typeof(*comp), notifier); 155 156 return __zcomp_cpu_notifier(comp, action, cpu); 157 } 158 159 static int zcomp_init(struct zcomp *comp) 160 { 161 unsigned long cpu; 162 int ret; 163 164 comp->notifier.notifier_call = zcomp_cpu_notifier; 165 166 comp->stream = alloc_percpu(struct zcomp_strm *); 167 if (!comp->stream) 168 return -ENOMEM; 169 170 cpu_notifier_register_begin(); 171 for_each_online_cpu(cpu) { 172 ret = __zcomp_cpu_notifier(comp, CPU_UP_PREPARE, cpu); 173 if (ret == NOTIFY_BAD) 174 goto cleanup; 175 } 176 __register_cpu_notifier(&comp->notifier); 177 cpu_notifier_register_done(); 178 return 0; 179 180 cleanup: 181 for_each_online_cpu(cpu) 182 __zcomp_cpu_notifier(comp, CPU_UP_CANCELED, cpu); 183 cpu_notifier_register_done(); 184 return -ENOMEM; 185 } 186 187 void zcomp_destroy(struct zcomp *comp) 188 { 189 unsigned long cpu; 190 191 cpu_notifier_register_begin(); 192 for_each_online_cpu(cpu) 193 __zcomp_cpu_notifier(comp, CPU_UP_CANCELED, cpu); 194 __unregister_cpu_notifier(&comp->notifier); 195 cpu_notifier_register_done(); 196 197 free_percpu(comp->stream); 198 kfree(comp); 199 } 200 201 /* 202 * search available compressors for requested algorithm. 203 * allocate new zcomp and initialize it. return compressing 204 * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) 205 * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in 206 * case of allocation error, or any other error potentially 207 * returned by zcomp_init(). 208 */ 209 struct zcomp *zcomp_create(const char *compress) 210 { 211 struct zcomp *comp; 212 struct zcomp_backend *backend; 213 int error; 214 215 backend = find_backend(compress); 216 if (!backend) 217 return ERR_PTR(-EINVAL); 218 219 comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); 220 if (!comp) 221 return ERR_PTR(-ENOMEM); 222 223 comp->backend = backend; 224 error = zcomp_init(comp); 225 if (error) { 226 kfree(comp); 227 return ERR_PTR(error); 228 } 229 return comp; 230 } 231