]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/block/zram/zcomp.c
d5919031ca8ba5498cf473671c65033a0d0041c6
[karo-tx-linux.git] / drivers / block / zram / zcomp.c
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/slab.h>
13 #include <linux/wait.h>
14 #include <linux/sched.h>
15
16 #include "zcomp.h"
17 #include "zcomp_lzo.h"
18 #ifdef CONFIG_ZRAM_LZ4_COMPRESS
19 #include "zcomp_lz4.h"
20 #endif
21
22 /*
23  * single zcomp_strm backend
24  */
25 struct zcomp_strm_single {
26         struct mutex strm_lock;
27         struct zcomp_strm *zstrm;
28 };
29
30 /*
31  * multi zcomp_strm backend
32  */
33 struct zcomp_strm_multi {
34         /* protect strm list */
35         spinlock_t strm_lock;
36         /* max possible number of zstrm streams */
37         int max_strm;
38         /* number of available zstrm streams */
39         int avail_strm;
40         /* list of available strms */
41         struct list_head idle_strm;
42         wait_queue_head_t strm_wait;
43 };
44
45 static struct zcomp_backend *backends[] = {
46         &zcomp_lzo,
47 #ifdef CONFIG_ZRAM_LZ4_COMPRESS
48         &zcomp_lz4,
49 #endif
50         NULL
51 };
52
53 static struct zcomp_backend *find_backend(const char *compress)
54 {
55         int i = 0;
56         while (backends[i]) {
57                 if (sysfs_streq(compress, backends[i]->name))
58                         break;
59                 i++;
60         }
61         return backends[i];
62 }
63
64 static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
65 {
66         if (zstrm->private)
67                 comp->backend->destroy(zstrm->private);
68         free_pages((unsigned long)zstrm->buffer, 1);
69         kfree(zstrm);
70 }
71
72 /*
73  * allocate new zcomp_strm structure with ->private initialized by
74  * backend, return NULL on error
75  */
76 static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
77 {
78         struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL);
79         if (!zstrm)
80                 return NULL;
81
82         zstrm->private = comp->backend->create();
83         /*
84          * allocate 2 pages. 1 for compressed data, plus 1 extra for the
85          * case when compressed size is larger than the original one
86          */
87         zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
88         if (!zstrm->private || !zstrm->buffer) {
89                 zcomp_strm_free(comp, zstrm);
90                 zstrm = NULL;
91         }
92         return zstrm;
93 }
94
95 /*
96  * get idle zcomp_strm or wait until other process release
97  * (zcomp_strm_release()) one for us
98  */
99 static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp)
100 {
101         struct zcomp_strm_multi *zs = comp->stream;
102         struct zcomp_strm *zstrm;
103
104         while (1) {
105                 spin_lock(&zs->strm_lock);
106                 if (!list_empty(&zs->idle_strm)) {
107                         zstrm = list_entry(zs->idle_strm.next,
108                                         struct zcomp_strm, list);
109                         list_del(&zstrm->list);
110                         spin_unlock(&zs->strm_lock);
111                         return zstrm;
112                 }
113                 /* zstrm streams limit reached, wait for idle stream */
114                 if (zs->avail_strm >= zs->max_strm) {
115                         spin_unlock(&zs->strm_lock);
116                         wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
117                         continue;
118                 }
119                 /* allocate new zstrm stream */
120                 zs->avail_strm++;
121                 spin_unlock(&zs->strm_lock);
122
123                 zstrm = zcomp_strm_alloc(comp);
124                 if (!zstrm) {
125                         spin_lock(&zs->strm_lock);
126                         zs->avail_strm--;
127                         spin_unlock(&zs->strm_lock);
128                         wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
129                         continue;
130                 }
131                 break;
132         }
133         return zstrm;
134 }
135
136 /* add stream back to idle list and wake up waiter or free the stream */
137 static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstrm)
138 {
139         struct zcomp_strm_multi *zs = comp->stream;
140
141         spin_lock(&zs->strm_lock);
142         if (zs->avail_strm <= zs->max_strm) {
143                 list_add(&zstrm->list, &zs->idle_strm);
144                 spin_unlock(&zs->strm_lock);
145                 wake_up(&zs->strm_wait);
146                 return;
147         }
148
149         zs->avail_strm--;
150         spin_unlock(&zs->strm_lock);
151         zcomp_strm_free(comp, zstrm);
152 }
153
154 /* change max_strm limit */
155 static int zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm)
156 {
157         struct zcomp_strm_multi *zs = comp->stream;
158         struct zcomp_strm *zstrm;
159
160         spin_lock(&zs->strm_lock);
161         zs->max_strm = num_strm;
162         /*
163          * if user has lowered the limit and there are idle streams,
164          * immediately free as much streams (and memory) as we can.
165          */
166         while (zs->avail_strm > num_strm && !list_empty(&zs->idle_strm)) {
167                 zstrm = list_entry(zs->idle_strm.next,
168                                 struct zcomp_strm, list);
169                 list_del(&zstrm->list);
170                 zcomp_strm_free(comp, zstrm);
171                 zs->avail_strm--;
172         }
173         spin_unlock(&zs->strm_lock);
174         return 0;
175 }
176
177 static void zcomp_strm_multi_destroy(struct zcomp *comp)
178 {
179         struct zcomp_strm_multi *zs = comp->stream;
180         struct zcomp_strm *zstrm;
181
182         while (!list_empty(&zs->idle_strm)) {
183                 zstrm = list_entry(zs->idle_strm.next,
184                                 struct zcomp_strm, list);
185                 list_del(&zstrm->list);
186                 zcomp_strm_free(comp, zstrm);
187         }
188         kfree(zs);
189 }
190
191 static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm)
192 {
193         struct zcomp_strm *zstrm;
194         struct zcomp_strm_multi *zs;
195
196         comp->destroy = zcomp_strm_multi_destroy;
197         comp->strm_find = zcomp_strm_multi_find;
198         comp->strm_release = zcomp_strm_multi_release;
199         comp->set_max_streams = zcomp_strm_multi_set_max_streams;
200         zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL);
201         if (!zs)
202                 return -ENOMEM;
203
204         comp->stream = zs;
205         spin_lock_init(&zs->strm_lock);
206         INIT_LIST_HEAD(&zs->idle_strm);
207         init_waitqueue_head(&zs->strm_wait);
208         zs->max_strm = max_strm;
209         zs->avail_strm = 1;
210
211         zstrm = zcomp_strm_alloc(comp);
212         if (!zstrm) {
213                 kfree(zs);
214                 return -ENOMEM;
215         }
216         list_add(&zstrm->list, &zs->idle_strm);
217         return 0;
218 }
219
220 static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp)
221 {
222         struct zcomp_strm_single *zs = comp->stream;
223         mutex_lock(&zs->strm_lock);
224         return zs->zstrm;
225 }
226
227 static void zcomp_strm_single_release(struct zcomp *comp,
228                 struct zcomp_strm *zstrm)
229 {
230         struct zcomp_strm_single *zs = comp->stream;
231         mutex_unlock(&zs->strm_lock);
232 }
233
234 static int zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm)
235 {
236         /* zcomp_strm_single support only max_comp_streams == 1 */
237         return -ENOTSUPP;
238 }
239
240 static void zcomp_strm_single_destroy(struct zcomp *comp)
241 {
242         struct zcomp_strm_single *zs = comp->stream;
243         zcomp_strm_free(comp, zs->zstrm);
244         kfree(zs);
245 }
246
247 static int zcomp_strm_single_create(struct zcomp *comp)
248 {
249         struct zcomp_strm_single *zs;
250
251         comp->destroy = zcomp_strm_single_destroy;
252         comp->strm_find = zcomp_strm_single_find;
253         comp->strm_release = zcomp_strm_single_release;
254         comp->set_max_streams = zcomp_strm_single_set_max_streams;
255         zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL);
256         if (!zs)
257                 return -ENOMEM;
258
259         comp->stream = zs;
260         mutex_init(&zs->strm_lock);
261         zs->zstrm = zcomp_strm_alloc(comp);
262         if (!zs->zstrm) {
263                 kfree(zs);
264                 return -ENOMEM;
265         }
266         return 0;
267 }
268
269 /* show available compressors */
270 ssize_t zcomp_available_show(const char *comp, char *buf)
271 {
272         ssize_t sz = 0;
273         int i = 0;
274
275         while (backends[i]) {
276                 if (sysfs_streq(comp, backends[i]->name))
277                         sz += sprintf(buf + sz, "[%s] ", backends[i]->name);
278                 else
279                         sz += sprintf(buf + sz, "%s ", backends[i]->name);
280                 i++;
281         }
282         sz += sprintf(buf + sz, "\n");
283         return sz;
284 }
285
286 int zcomp_set_max_streams(struct zcomp *comp, int num_strm)
287 {
288         return comp->set_max_streams(comp, num_strm);
289 }
290
291 struct zcomp_strm *zcomp_strm_find(struct zcomp *comp)
292 {
293         return comp->strm_find(comp);
294 }
295
296 void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm)
297 {
298         comp->strm_release(comp, zstrm);
299 }
300
301 int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
302                 const unsigned char *src, size_t *dst_len)
303 {
304         return comp->backend->compress(src, zstrm->buffer, dst_len,
305                         zstrm->private);
306 }
307
308 int zcomp_decompress(struct zcomp *comp, const unsigned char *src,
309                 size_t src_len, unsigned char *dst)
310 {
311         return comp->backend->decompress(src, src_len, dst);
312 }
313
314 void zcomp_destroy(struct zcomp *comp)
315 {
316         comp->destroy(comp);
317         kfree(comp);
318 }
319
320 /*
321  * search available compressors for requested algorithm.
322  * allocate new zcomp and initialize it. return NULL
323  * if requested algorithm is not supported or in case
324  * of init error
325  */
326 struct zcomp *zcomp_create(const char *compress, int max_strm)
327 {
328         struct zcomp *comp;
329         struct zcomp_backend *backend;
330
331         backend = find_backend(compress);
332         if (!backend)
333                 return NULL;
334
335         comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
336         if (!comp)
337                 return NULL;
338
339         comp->backend = backend;
340         if (max_strm > 1)
341                 zcomp_strm_multi_create(comp, max_strm);
342         else
343                 zcomp_strm_single_create(comp);
344         if (!comp->stream) {
345                 kfree(comp);
346                 return NULL;
347         }
348         return comp;
349 }