]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * JFFS2 -- Journalling Flash File System, Version 2. | |
3 | * | |
c00c310e | 4 | * Copyright © 2001-2007 Red Hat, Inc. |
6088c058 | 5 | * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> |
c00c310e | 6 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, |
ef53cb02 | 7 | * University of Szeged, Hungary |
1da177e4 | 8 | * |
6088c058 DW |
9 | * Created by Arjan van de Ven <arjan@infradead.org> |
10 | * | |
1da177e4 LT |
11 | * For licensing information, see the file 'LICENCE' in this directory. |
12 | * | |
1da177e4 LT |
13 | */ |
14 | ||
15 | #include "compr.h" | |
16 | ||
17 | static DEFINE_SPINLOCK(jffs2_compressor_list_lock); | |
18 | ||
19 | /* Available compressors are on this list */ | |
20 | static LIST_HEAD(jffs2_compressor_list); | |
21 | ||
22 | /* Actual compression mode */ | |
23 | static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; | |
24 | ||
25 | /* Statistics for blocks stored without compression */ | |
26 | static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; | |
27 | ||
3b23c1f5 RP |
28 | |
29 | /* | |
30 | * Return 1 to use this compression | |
31 | */ | |
32 | static int jffs2_is_best_compression(struct jffs2_compressor *this, | |
33 | struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) | |
34 | { | |
35 | switch (jffs2_compression_mode) { | |
36 | case JFFS2_COMPR_MODE_SIZE: | |
37 | if (bestsize > size) | |
38 | return 1; | |
39 | return 0; | |
40 | case JFFS2_COMPR_MODE_FAVOURLZO: | |
41 | if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) | |
42 | return 1; | |
43 | if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) | |
44 | return 1; | |
45 | if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) | |
46 | return 1; | |
47 | if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) | |
48 | return 1; | |
49 | ||
50 | return 0; | |
51 | } | |
52 | /* Shouldn't happen */ | |
53 | return 0; | |
54 | } | |
55 | ||
123005f3 AS |
56 | /* |
57 | * jffs2_selected_compress: | |
58 | * @compr: Explicit compression type to use (ie, JFFS2_COMPR_ZLIB). | |
59 | * If 0, just take the first available compression mode. | |
60 | * @data_in: Pointer to uncompressed data | |
61 | * @cpage_out: Pointer to returned pointer to buffer for compressed data | |
62 | * @datalen: On entry, holds the amount of data available for compression. | |
63 | * On exit, expected to hold the amount of data actually compressed. | |
64 | * @cdatalen: On entry, holds the amount of space available for compressed | |
65 | * data. On exit, expected to hold the actual size of the compressed | |
66 | * data. | |
67 | * | |
68 | * Returns: the compression type used. Zero is used to show that the data | |
69 | * could not be compressed; probably because we couldn't find the requested | |
70 | * compression mode. | |
71 | */ | |
72 | static int jffs2_selected_compress(u8 compr, unsigned char *data_in, | |
73 | unsigned char **cpage_out, u32 *datalen, u32 *cdatalen) | |
74 | { | |
75 | struct jffs2_compressor *this; | |
76 | int err, ret = JFFS2_COMPR_NONE; | |
77 | uint32_t orig_slen, orig_dlen; | |
78 | char *output_buf; | |
79 | ||
80 | output_buf = kmalloc(*cdatalen, GFP_KERNEL); | |
81 | if (!output_buf) { | |
82 | printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); | |
83 | return ret; | |
84 | } | |
85 | orig_slen = *datalen; | |
86 | orig_dlen = *cdatalen; | |
87 | spin_lock(&jffs2_compressor_list_lock); | |
88 | list_for_each_entry(this, &jffs2_compressor_list, list) { | |
89 | /* Skip decompress-only and disabled modules */ | |
90 | if (!this->compress || this->disabled) | |
91 | continue; | |
92 | ||
93 | /* Skip if not the desired compression type */ | |
94 | if (compr && (compr != this->compr)) | |
95 | continue; | |
96 | ||
97 | /* | |
98 | * Either compression type was unspecified, or we found our | |
99 | * compressor; either way, we're good to go. | |
100 | */ | |
101 | this->usecount++; | |
102 | spin_unlock(&jffs2_compressor_list_lock); | |
103 | ||
104 | *datalen = orig_slen; | |
105 | *cdatalen = orig_dlen; | |
106 | err = this->compress(data_in, output_buf, datalen, cdatalen); | |
107 | ||
108 | spin_lock(&jffs2_compressor_list_lock); | |
109 | this->usecount--; | |
110 | if (!err) { | |
111 | /* Success */ | |
112 | ret = this->compr; | |
113 | this->stat_compr_blocks++; | |
114 | this->stat_compr_orig_size += *datalen; | |
115 | this->stat_compr_new_size += *cdatalen; | |
116 | break; | |
117 | } | |
118 | } | |
119 | spin_unlock(&jffs2_compressor_list_lock); | |
120 | if (ret == JFFS2_COMPR_NONE) | |
121 | kfree(output_buf); | |
122 | else | |
123 | *cpage_out = output_buf; | |
124 | ||
125 | return ret; | |
126 | } | |
127 | ||
1da177e4 | 128 | /* jffs2_compress: |
faa5c2a1 GU |
129 | * @data_in: Pointer to uncompressed data |
130 | * @cpage_out: Pointer to returned pointer to buffer for compressed data | |
1da177e4 LT |
131 | * @datalen: On entry, holds the amount of data available for compression. |
132 | * On exit, expected to hold the amount of data actually compressed. | |
133 | * @cdatalen: On entry, holds the amount of space available for compressed | |
134 | * data. On exit, expected to hold the actual size of the compressed | |
135 | * data. | |
136 | * | |
137 | * Returns: Lower byte to be stored with data indicating compression type used. | |
182ec4ee | 138 | * Zero is used to show that the data could not be compressed - the |
1da177e4 LT |
139 | * compressed version was actually larger than the original. |
140 | * Upper byte will be used later. (soon) | |
141 | * | |
142 | * If the cdata buffer isn't large enough to hold all the uncompressed data, | |
182ec4ee | 143 | * jffs2_compress should compress as much as will fit, and should set |
1da177e4 LT |
144 | * *datalen accordingly to show the amount of data which were compressed. |
145 | */ | |
146 | uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | |
ef53cb02 DW |
147 | unsigned char *data_in, unsigned char **cpage_out, |
148 | uint32_t *datalen, uint32_t *cdatalen) | |
1da177e4 LT |
149 | { |
150 | int ret = JFFS2_COMPR_NONE; | |
92abc475 | 151 | int mode, compr_ret; |
ef53cb02 DW |
152 | struct jffs2_compressor *this, *best=NULL; |
153 | unsigned char *output_buf = NULL, *tmp_buf; | |
154 | uint32_t orig_slen, orig_dlen; | |
155 | uint32_t best_slen=0, best_dlen=0; | |
1da177e4 | 156 | |
92abc475 AS |
157 | if (c->mount_opts.override_compr) |
158 | mode = c->mount_opts.compr; | |
159 | else | |
160 | mode = jffs2_compression_mode; | |
161 | ||
162 | switch (mode) { | |
ef53cb02 DW |
163 | case JFFS2_COMPR_MODE_NONE: |
164 | break; | |
165 | case JFFS2_COMPR_MODE_PRIORITY: | |
123005f3 AS |
166 | ret = jffs2_selected_compress(0, data_in, cpage_out, datalen, |
167 | cdatalen); | |
ef53cb02 DW |
168 | break; |
169 | case JFFS2_COMPR_MODE_SIZE: | |
3b23c1f5 | 170 | case JFFS2_COMPR_MODE_FAVOURLZO: |
ef53cb02 DW |
171 | orig_slen = *datalen; |
172 | orig_dlen = *cdatalen; | |
173 | spin_lock(&jffs2_compressor_list_lock); | |
174 | list_for_each_entry(this, &jffs2_compressor_list, list) { | |
175 | /* Skip decompress-only backwards-compatibility and disabled modules */ | |
176 | if ((!this->compress)||(this->disabled)) | |
177 | continue; | |
178 | /* Allocating memory for output buffer if necessary */ | |
3b23c1f5 | 179 | if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) { |
ef53cb02 DW |
180 | spin_unlock(&jffs2_compressor_list_lock); |
181 | kfree(this->compr_buf); | |
182 | spin_lock(&jffs2_compressor_list_lock); | |
183 | this->compr_buf_size=0; | |
184 | this->compr_buf=NULL; | |
185 | } | |
186 | if (!this->compr_buf) { | |
187 | spin_unlock(&jffs2_compressor_list_lock); | |
3b23c1f5 | 188 | tmp_buf = kmalloc(orig_slen, GFP_KERNEL); |
ef53cb02 DW |
189 | spin_lock(&jffs2_compressor_list_lock); |
190 | if (!tmp_buf) { | |
3b23c1f5 | 191 | printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen); |
ef53cb02 DW |
192 | continue; |
193 | } | |
194 | else { | |
195 | this->compr_buf = tmp_buf; | |
3b23c1f5 | 196 | this->compr_buf_size = orig_slen; |
ef53cb02 DW |
197 | } |
198 | } | |
199 | this->usecount++; | |
200 | spin_unlock(&jffs2_compressor_list_lock); | |
201 | *datalen = orig_slen; | |
202 | *cdatalen = orig_dlen; | |
088bd455 | 203 | compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen); |
ef53cb02 DW |
204 | spin_lock(&jffs2_compressor_list_lock); |
205 | this->usecount--; | |
206 | if (!compr_ret) { | |
3b23c1f5 RP |
207 | if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) |
208 | && (*cdatalen < *datalen)) { | |
ef53cb02 DW |
209 | best_dlen = *cdatalen; |
210 | best_slen = *datalen; | |
211 | best = this; | |
212 | } | |
213 | } | |
214 | } | |
215 | if (best_dlen) { | |
216 | *cdatalen = best_dlen; | |
217 | *datalen = best_slen; | |
218 | output_buf = best->compr_buf; | |
219 | best->compr_buf = NULL; | |
220 | best->compr_buf_size = 0; | |
221 | best->stat_compr_blocks++; | |
222 | best->stat_compr_orig_size += best_slen; | |
223 | best->stat_compr_new_size += best_dlen; | |
224 | ret = best->compr; | |
123005f3 | 225 | *cpage_out = output_buf; |
ef53cb02 DW |
226 | } |
227 | spin_unlock(&jffs2_compressor_list_lock); | |
228 | break; | |
123005f3 AS |
229 | case JFFS2_COMPR_MODE_FORCELZO: |
230 | ret = jffs2_selected_compress(JFFS2_COMPR_LZO, data_in, | |
231 | cpage_out, datalen, cdatalen); | |
232 | break; | |
233 | case JFFS2_COMPR_MODE_FORCEZLIB: | |
234 | ret = jffs2_selected_compress(JFFS2_COMPR_ZLIB, data_in, | |
235 | cpage_out, datalen, cdatalen); | |
236 | break; | |
ef53cb02 | 237 | default: |
af901ca1 | 238 | printk(KERN_ERR "JFFS2: unknown compression mode.\n"); |
ef53cb02 | 239 | } |
123005f3 | 240 | |
ef53cb02 DW |
241 | if (ret == JFFS2_COMPR_NONE) { |
242 | *cpage_out = data_in; | |
243 | *datalen = *cdatalen; | |
244 | none_stat_compr_blocks++; | |
245 | none_stat_compr_size += *datalen; | |
246 | } | |
1da177e4 LT |
247 | return ret; |
248 | } | |
249 | ||
250 | int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | |
182ec4ee | 251 | uint16_t comprtype, unsigned char *cdata_in, |
1da177e4 LT |
252 | unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) |
253 | { | |
ef53cb02 DW |
254 | struct jffs2_compressor *this; |
255 | int ret; | |
1da177e4 LT |
256 | |
257 | /* Older code had a bug where it would write non-zero 'usercompr' | |
258 | fields. Deal with it. */ | |
259 | if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) | |
260 | comprtype &= 0xff; | |
261 | ||
262 | switch (comprtype & 0xff) { | |
263 | case JFFS2_COMPR_NONE: | |
264 | /* This should be special-cased elsewhere, but we might as well deal with it */ | |
265 | memcpy(data_out, cdata_in, datalen); | |
ef53cb02 | 266 | none_stat_decompr_blocks++; |
1da177e4 LT |
267 | break; |
268 | case JFFS2_COMPR_ZERO: | |
269 | memset(data_out, 0, datalen); | |
270 | break; | |
271 | default: | |
ef53cb02 DW |
272 | spin_lock(&jffs2_compressor_list_lock); |
273 | list_for_each_entry(this, &jffs2_compressor_list, list) { | |
274 | if (comprtype == this->compr) { | |
275 | this->usecount++; | |
276 | spin_unlock(&jffs2_compressor_list_lock); | |
088bd455 | 277 | ret = this->decompress(cdata_in, data_out, cdatalen, datalen); |
ef53cb02 DW |
278 | spin_lock(&jffs2_compressor_list_lock); |
279 | if (ret) { | |
280 | printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); | |
281 | } | |
282 | else { | |
283 | this->stat_decompr_blocks++; | |
284 | } | |
285 | this->usecount--; | |
286 | spin_unlock(&jffs2_compressor_list_lock); | |
287 | return ret; | |
288 | } | |
289 | } | |
1da177e4 | 290 | printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); |
ef53cb02 | 291 | spin_unlock(&jffs2_compressor_list_lock); |
1da177e4 LT |
292 | return -EIO; |
293 | } | |
294 | return 0; | |
295 | } | |
296 | ||
297 | int jffs2_register_compressor(struct jffs2_compressor *comp) | |
298 | { | |
ef53cb02 | 299 | struct jffs2_compressor *this; |
1da177e4 | 300 | |
ef53cb02 DW |
301 | if (!comp->name) { |
302 | printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); | |
303 | return -1; | |
304 | } | |
305 | comp->compr_buf_size=0; | |
306 | comp->compr_buf=NULL; | |
307 | comp->usecount=0; | |
308 | comp->stat_compr_orig_size=0; | |
309 | comp->stat_compr_new_size=0; | |
310 | comp->stat_compr_blocks=0; | |
311 | comp->stat_decompr_blocks=0; | |
9c261b33 | 312 | jffs2_dbg(1, "Registering JFFS2 compressor \"%s\"\n", comp->name); |
1da177e4 | 313 | |
ef53cb02 | 314 | spin_lock(&jffs2_compressor_list_lock); |
1da177e4 | 315 | |
ef53cb02 DW |
316 | list_for_each_entry(this, &jffs2_compressor_list, list) { |
317 | if (this->priority < comp->priority) { | |
318 | list_add(&comp->list, this->list.prev); | |
319 | goto out; | |
320 | } | |
321 | } | |
322 | list_add_tail(&comp->list, &jffs2_compressor_list); | |
1da177e4 | 323 | out: |
ef53cb02 DW |
324 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { |
325 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); | |
326 | }) | |
1da177e4 | 327 | |
ef53cb02 | 328 | spin_unlock(&jffs2_compressor_list_lock); |
1da177e4 | 329 | |
ef53cb02 | 330 | return 0; |
1da177e4 LT |
331 | } |
332 | ||
333 | int jffs2_unregister_compressor(struct jffs2_compressor *comp) | |
334 | { | |
9c261b33 | 335 | D2(struct jffs2_compressor *this); |
1da177e4 | 336 | |
9c261b33 | 337 | jffs2_dbg(1, "Unregistering JFFS2 compressor \"%s\"\n", comp->name); |
1da177e4 | 338 | |
ef53cb02 | 339 | spin_lock(&jffs2_compressor_list_lock); |
1da177e4 | 340 | |
ef53cb02 DW |
341 | if (comp->usecount) { |
342 | spin_unlock(&jffs2_compressor_list_lock); | |
4de03478 | 343 | printk(KERN_WARNING "JFFS2: Compressor module is in use. Unregister failed.\n"); |
ef53cb02 DW |
344 | return -1; |
345 | } | |
346 | list_del(&comp->list); | |
1da177e4 | 347 | |
ef53cb02 DW |
348 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { |
349 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); | |
350 | }) | |
351 | spin_unlock(&jffs2_compressor_list_lock); | |
352 | return 0; | |
1da177e4 LT |
353 | } |
354 | ||
1da177e4 LT |
355 | void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) |
356 | { | |
ef53cb02 DW |
357 | if (orig != comprbuf) |
358 | kfree(comprbuf); | |
1da177e4 LT |
359 | } |
360 | ||
7d2beb13 | 361 | int __init jffs2_compressors_init(void) |
1da177e4 LT |
362 | { |
363 | /* Registering compressors */ | |
364 | #ifdef CONFIG_JFFS2_ZLIB | |
ef53cb02 | 365 | jffs2_zlib_init(); |
1da177e4 LT |
366 | #endif |
367 | #ifdef CONFIG_JFFS2_RTIME | |
ef53cb02 | 368 | jffs2_rtime_init(); |
1da177e4 LT |
369 | #endif |
370 | #ifdef CONFIG_JFFS2_RUBIN | |
ef53cb02 DW |
371 | jffs2_rubinmips_init(); |
372 | jffs2_dynrubin_init(); | |
1da177e4 | 373 | #endif |
c799aca3 RP |
374 | #ifdef CONFIG_JFFS2_LZO |
375 | jffs2_lzo_init(); | |
376 | #endif | |
1da177e4 LT |
377 | /* Setting default compression mode */ |
378 | #ifdef CONFIG_JFFS2_CMODE_NONE | |
ef53cb02 | 379 | jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; |
9c261b33 | 380 | jffs2_dbg(1, "JFFS2: default compression mode: none\n"); |
1da177e4 LT |
381 | #else |
382 | #ifdef CONFIG_JFFS2_CMODE_SIZE | |
ef53cb02 | 383 | jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; |
9c261b33 | 384 | jffs2_dbg(1, "JFFS2: default compression mode: size\n"); |
3b23c1f5 RP |
385 | #else |
386 | #ifdef CONFIG_JFFS2_CMODE_FAVOURLZO | |
387 | jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; | |
9c261b33 | 388 | jffs2_dbg(1, "JFFS2: default compression mode: favourlzo\n"); |
1da177e4 | 389 | #else |
9c261b33 | 390 | jffs2_dbg(1, "JFFS2: default compression mode: priority\n"); |
1da177e4 | 391 | #endif |
3b23c1f5 | 392 | #endif |
1da177e4 | 393 | #endif |
ef53cb02 | 394 | return 0; |
1da177e4 LT |
395 | } |
396 | ||
3bcc86f5 | 397 | int jffs2_compressors_exit(void) |
1da177e4 LT |
398 | { |
399 | /* Unregistering compressors */ | |
c799aca3 RP |
400 | #ifdef CONFIG_JFFS2_LZO |
401 | jffs2_lzo_exit(); | |
402 | #endif | |
1da177e4 | 403 | #ifdef CONFIG_JFFS2_RUBIN |
ef53cb02 DW |
404 | jffs2_dynrubin_exit(); |
405 | jffs2_rubinmips_exit(); | |
1da177e4 LT |
406 | #endif |
407 | #ifdef CONFIG_JFFS2_RTIME | |
ef53cb02 | 408 | jffs2_rtime_exit(); |
1da177e4 LT |
409 | #endif |
410 | #ifdef CONFIG_JFFS2_ZLIB | |
ef53cb02 | 411 | jffs2_zlib_exit(); |
1da177e4 | 412 | #endif |
ef53cb02 | 413 | return 0; |
1da177e4 | 414 | } |