VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / fs / jffs2 / compr.c
1 /*
2  * JFFS2 -- Journalling Flash File System, Version 2.
3  *
4  * Copyright (C) 2001-2003 Red Hat, Inc.
5  * Created by Arjan van de Ven <arjanv@redhat.com>
6  *
7  * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
8  *                    University of Szeged, Hungary
9  *
10  * For licensing information, see the file 'LICENCE' in this directory.
11  *
12  * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $
13  *
14  */
15
16 #include "compr.h"
17
18 static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
19
20 /* Available compressors are on this list */
21 static LIST_HEAD(jffs2_compressor_list);
22
23 /* Actual compression mode */
24 static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
25
26 void jffs2_set_compression_mode(int mode) 
27 {
28         jffs2_compression_mode = mode;
29 }
30
31 int jffs2_get_compression_mode(void)
32 {
33         return jffs2_compression_mode;
34 }
35
36 /* Statistics for blocks stored without compression */
37 static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
38
39 /* jffs2_compress:
40  * @data: Pointer to uncompressed data
41  * @cdata: Pointer to returned pointer to buffer for compressed data
42  * @datalen: On entry, holds the amount of data available for compression.
43  *      On exit, expected to hold the amount of data actually compressed.
44  * @cdatalen: On entry, holds the amount of space available for compressed
45  *      data. On exit, expected to hold the actual size of the compressed
46  *      data.
47  *
48  * Returns: Lower byte to be stored with data indicating compression type used.
49  * Zero is used to show that the data could not be compressed - the 
50  * compressed version was actually larger than the original.
51  * Upper byte will be used later. (soon)
52  *
53  * If the cdata buffer isn't large enough to hold all the uncompressed data,
54  * jffs2_compress should compress as much as will fit, and should set 
55  * *datalen accordingly to show the amount of data which were compressed.
56  */
57 uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
58                              unsigned char *data_in, unsigned char **cpage_out, 
59                              uint32_t *datalen, uint32_t *cdatalen)
60 {
61         int ret = JFFS2_COMPR_NONE;
62         int compr_ret;
63         struct jffs2_compressor *this, *best=NULL;
64         unsigned char *output_buf = NULL, *tmp_buf;
65         uint32_t orig_slen, orig_dlen;
66         uint32_t best_slen=0, best_dlen=0;
67
68         switch (jffs2_compression_mode) {
69         case JFFS2_COMPR_MODE_NONE:
70                 break;
71         case JFFS2_COMPR_MODE_PRIORITY:
72                 output_buf = kmalloc(*cdatalen,GFP_KERNEL);
73                 if (!output_buf) {
74                         printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
75                         goto out;
76                 }
77                 orig_slen = *datalen;
78                 orig_dlen = *cdatalen;
79                 spin_lock(&jffs2_compressor_list_lock);
80                 list_for_each_entry(this, &jffs2_compressor_list, list) {
81                         /* Skip decompress-only backwards-compatibility and disabled modules */
82                         if ((!this->compress)||(this->disabled))
83                                 continue;
84
85                         this->usecount++;
86                         spin_unlock(&jffs2_compressor_list_lock);
87                         *datalen  = orig_slen;
88                         *cdatalen = orig_dlen;
89                         compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
90                         spin_lock(&jffs2_compressor_list_lock);
91                         this->usecount--;
92                         if (!compr_ret) {
93                                 ret = this->compr;
94                                 this->stat_compr_blocks++;
95                                 this->stat_compr_orig_size += *datalen;
96                                 this->stat_compr_new_size  += *cdatalen;
97                                 break;
98                         }
99                 }
100                 spin_unlock(&jffs2_compressor_list_lock);
101                 if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
102                 break;
103         case JFFS2_COMPR_MODE_SIZE:
104                 orig_slen = *datalen;
105                 orig_dlen = *cdatalen;
106                 spin_lock(&jffs2_compressor_list_lock);
107                 list_for_each_entry(this, &jffs2_compressor_list, list) {
108                         /* Skip decompress-only backwards-compatibility and disabled modules */
109                         if ((!this->compress)||(this->disabled))
110                                 continue;
111                         /* Allocating memory for output buffer if necessary */
112                         if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
113                                 spin_unlock(&jffs2_compressor_list_lock);
114                                 kfree(this->compr_buf);
115                                 spin_lock(&jffs2_compressor_list_lock);
116                                 this->compr_buf_size=0;
117                                 this->compr_buf=NULL;
118                         }
119                         if (!this->compr_buf) {
120                                 spin_unlock(&jffs2_compressor_list_lock);
121                                 tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
122                                 spin_lock(&jffs2_compressor_list_lock);
123                                 if (!tmp_buf) {
124                                         printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
125                                         continue;
126                                 }
127                                 else {
128                                         this->compr_buf = tmp_buf;
129                                         this->compr_buf_size = orig_dlen;
130                                 }
131                         }
132                         this->usecount++;
133                         spin_unlock(&jffs2_compressor_list_lock);
134                         *datalen  = orig_slen;
135                         *cdatalen = orig_dlen;
136                         compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
137                         spin_lock(&jffs2_compressor_list_lock);
138                         this->usecount--;
139                         if (!compr_ret) {
140                                 if ((!best_dlen)||(best_dlen>*cdatalen)) {
141                                         best_dlen = *cdatalen;
142                                         best_slen = *datalen;
143                                         best = this;
144                                 }
145                         }
146                 }
147                 if (best_dlen) {
148                         *cdatalen = best_dlen;
149                         *datalen  = best_slen;
150                         output_buf = best->compr_buf;
151                         best->compr_buf = NULL;
152                         best->compr_buf_size = 0;
153                         best->stat_compr_blocks++;
154                         best->stat_compr_orig_size += best_slen;
155                         best->stat_compr_new_size  += best_dlen;
156                         ret = best->compr;
157                 }
158                 spin_unlock(&jffs2_compressor_list_lock);
159                 break;
160         default:
161                 printk(KERN_ERR "JFFS2: unknow compression mode.\n");
162         }
163  out:
164         if (ret == JFFS2_COMPR_NONE) {
165                 *cpage_out = data_in;
166                 *datalen = *cdatalen;
167                 none_stat_compr_blocks++;
168                 none_stat_compr_size += *datalen;
169         }
170         else {
171                 *cpage_out = output_buf;
172         }
173         return ret;
174 }
175
176 int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
177                      uint16_t comprtype, unsigned char *cdata_in, 
178                      unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
179 {
180         struct jffs2_compressor *this;
181         int ret;
182
183         switch (comprtype & 0xff) {
184         case JFFS2_COMPR_NONE:
185                 /* This should be special-cased elsewhere, but we might as well deal with it */
186                 memcpy(data_out, cdata_in, datalen);
187                 none_stat_decompr_blocks++;
188                 break;
189         case JFFS2_COMPR_ZERO:
190                 memset(data_out, 0, datalen);
191                 break;
192         default:
193                 spin_lock(&jffs2_compressor_list_lock);
194                 list_for_each_entry(this, &jffs2_compressor_list, list) {
195                         if (comprtype == this->compr) {
196                                 this->usecount++;
197                                 spin_unlock(&jffs2_compressor_list_lock);
198                                 ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
199                                 spin_lock(&jffs2_compressor_list_lock);
200                                 if (ret) {
201                                         printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
202                                 }
203                                 else {
204                                         this->stat_decompr_blocks++;
205                                 }
206                                 this->usecount--;
207                                 spin_unlock(&jffs2_compressor_list_lock);
208                                 return ret;
209                         }
210                 }
211                 printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype);
212                 spin_unlock(&jffs2_compressor_list_lock);
213                 return -EIO;
214         }
215         return 0;
216 }
217
218 int jffs2_register_compressor(struct jffs2_compressor *comp)
219 {
220         struct jffs2_compressor *this;
221
222         if (!comp->name) {
223                 printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
224                 return -1;
225         }
226         comp->compr_buf_size=0;
227         comp->compr_buf=NULL;
228         comp->usecount=0;
229         comp->stat_compr_orig_size=0;
230         comp->stat_compr_new_size=0;
231         comp->stat_compr_blocks=0;
232         comp->stat_decompr_blocks=0;
233         D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
234
235         spin_lock(&jffs2_compressor_list_lock);
236
237         list_for_each_entry(this, &jffs2_compressor_list, list) {
238                 if (this->priority < comp->priority) {
239                         list_add(&comp->list, this->list.prev);
240                         goto out;
241                 }
242         }
243         list_add_tail(&comp->list, &jffs2_compressor_list);
244 out:
245         D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
246                 printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
247         })
248
249         spin_unlock(&jffs2_compressor_list_lock);
250
251         return 0;
252 }
253
254 int jffs2_unregister_compressor(struct jffs2_compressor *comp)
255 {
256         D2(struct jffs2_compressor *this;)
257
258         D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
259
260         spin_lock(&jffs2_compressor_list_lock);
261
262         if (comp->usecount) {
263                 spin_unlock(&jffs2_compressor_list_lock);
264                 printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
265                 return -1;
266         }
267         list_del(&comp->list);
268
269         D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
270                 printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
271         })
272         spin_unlock(&jffs2_compressor_list_lock);
273         return 0;
274 }
275
276 #ifdef CONFIG_JFFS2_PROC
277
278 #define JFFS2_STAT_BUF_SIZE 16000
279
280 char *jffs2_list_compressors(void)
281 {
282         struct jffs2_compressor *this;
283         char *buf, *act_buf;
284
285         act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
286         list_for_each_entry(this, &jffs2_compressor_list, list) {
287                 act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
288                 if ((this->disabled)||(!this->compress))
289                         act_buf += sprintf(act_buf,"disabled");
290                 else
291                         act_buf += sprintf(act_buf,"enabled");
292                 act_buf += sprintf(act_buf,"\n");
293         }
294         return buf;
295 }
296
297 char *jffs2_stats(void)
298 {
299         struct jffs2_compressor *this;
300         char *buf, *act_buf;
301
302         act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
303
304         act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
305         act_buf += sprintf(act_buf,"%10s   ","none");
306         act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, 
307                            none_stat_compr_size, none_stat_decompr_blocks);
308         spin_lock(&jffs2_compressor_list_lock);
309         list_for_each_entry(this, &jffs2_compressor_list, list) {
310                 act_buf += sprintf(act_buf,"%10s ",this->name);
311                 if ((this->disabled)||(!this->compress))
312                         act_buf += sprintf(act_buf,"- ");
313                 else
314                         act_buf += sprintf(act_buf,"+ ");
315                 act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks, 
316                                    this->stat_compr_new_size, this->stat_compr_orig_size, 
317                                    this->stat_decompr_blocks);
318                 act_buf += sprintf(act_buf,"\n");
319         }
320         spin_unlock(&jffs2_compressor_list_lock);
321
322         return buf;
323 }
324
325 char *jffs2_get_compression_mode_name(void) 
326 {
327         switch (jffs2_compression_mode) {
328         case JFFS2_COMPR_MODE_NONE:
329                 return "none";
330         case JFFS2_COMPR_MODE_PRIORITY:
331                 return "priority";
332         case JFFS2_COMPR_MODE_SIZE:
333                 return "size";
334         }
335         return "unkown";
336 }
337
338 int jffs2_set_compression_mode_name(const char *name) 
339 {
340         if (!strcmp("none",name)) {
341                 jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
342                 return 0;
343         }
344         if (!strcmp("priority",name)) {
345                 jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
346                 return 0;
347         }
348         if (!strcmp("size",name)) {
349                 jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
350                 return 0;
351         }
352         return 1;
353 }
354
355 static int jffs2_compressor_Xable(const char *name, int disabled)
356 {
357         struct jffs2_compressor *this;
358         spin_lock(&jffs2_compressor_list_lock);
359         list_for_each_entry(this, &jffs2_compressor_list, list) {
360                 if (!strcmp(this->name, name)) {
361                         this->disabled = disabled;
362                         spin_unlock(&jffs2_compressor_list_lock);
363                         return 0;                        
364                 }
365         }
366         spin_unlock(&jffs2_compressor_list_lock);
367         printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
368         return 1;
369 }
370
371 int jffs2_enable_compressor_name(const char *name)
372 {
373         return jffs2_compressor_Xable(name, 0);
374 }
375
376 int jffs2_disable_compressor_name(const char *name)
377 {
378         return jffs2_compressor_Xable(name, 1);
379 }
380
381 int jffs2_set_compressor_priority(const char *name, int priority)
382 {
383         struct jffs2_compressor *this,*comp;
384         spin_lock(&jffs2_compressor_list_lock);
385         list_for_each_entry(this, &jffs2_compressor_list, list) {
386                 if (!strcmp(this->name, name)) {
387                         this->priority = priority;
388                         comp = this;
389                         goto reinsert;
390                 }
391         }
392         spin_unlock(&jffs2_compressor_list_lock);
393         printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);        
394         return 1;
395 reinsert:
396         /* list is sorted in the order of priority, so if
397            we change it we have to reinsert it into the
398            good place */
399         list_del(&comp->list);
400         list_for_each_entry(this, &jffs2_compressor_list, list) {
401                 if (this->priority < comp->priority) {
402                         list_add(&comp->list, this->list.prev);
403                         spin_unlock(&jffs2_compressor_list_lock);
404                         return 0;
405                 }
406         }
407         list_add_tail(&comp->list, &jffs2_compressor_list);
408         spin_unlock(&jffs2_compressor_list_lock);
409         return 0;
410 }
411
412 #endif
413
414 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
415 {
416         if (orig != comprbuf)
417                 kfree(comprbuf);
418 }
419
420 int jffs2_compressors_init(void) 
421 {
422 /* Registering compressors */
423 #ifdef CONFIG_JFFS2_ZLIB
424         jffs2_zlib_init();
425 #endif
426 #ifdef CONFIG_JFFS2_RTIME
427         jffs2_rtime_init();
428 #endif
429 #ifdef CONFIG_JFFS2_RUBIN
430         jffs2_rubinmips_init();
431         jffs2_dynrubin_init();
432 #endif
433 #ifdef CONFIG_JFFS2_LZARI
434         jffs2_lzari_init();
435 #endif
436 #ifdef CONFIG_JFFS2_LZO
437         jffs2_lzo_init();
438 #endif
439 /* Setting default compression mode */
440 #ifdef CONFIG_JFFS2_CMODE_NONE
441         jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
442         D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
443 #else
444 #ifdef CONFIG_JFFS2_CMODE_SIZE
445         jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
446         D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
447 #else
448         D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
449 #endif
450 #endif
451         return 0;
452 }
453
454 int jffs2_compressors_exit(void) 
455 {
456 /* Unregistering compressors */
457 #ifdef CONFIG_JFFS2_LZO
458         jffs2_lzo_exit();
459 #endif
460 #ifdef CONFIG_JFFS2_LZARI
461         jffs2_lzari_exit();
462 #endif
463 #ifdef CONFIG_JFFS2_RUBIN
464         jffs2_dynrubin_exit();
465         jffs2_rubinmips_exit();
466 #endif
467 #ifdef CONFIG_JFFS2_RTIME
468         jffs2_rtime_exit();
469 #endif
470 #ifdef CONFIG_JFFS2_ZLIB
471         jffs2_zlib_exit();
472 #endif
473         return 0;
474 }