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) { | |
da320f05 | 82 | pr_warn("JFFS2: No memory for compressor allocation. Compression failed.\n"); |
123005f3 AS |
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) { | |
da320f05 JP |
191 | pr_warn("JFFS2: No memory for compressor allocation. (%d bytes)\n", |
192 | orig_slen); | |
ef53cb02 DW |
193 | continue; |
194 | } | |
195 | else { | |
196 | this->compr_buf = tmp_buf; | |
3b23c1f5 | 197 | this->compr_buf_size = orig_slen; |
ef53cb02 DW |
198 | } |
199 | } | |
200 | this->usecount++; | |
201 | spin_unlock(&jffs2_compressor_list_lock); | |
202 | *datalen = orig_slen; | |
203 | *cdatalen = orig_dlen; | |
088bd455 | 204 | compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen); |
ef53cb02 DW |
205 | spin_lock(&jffs2_compressor_list_lock); |
206 | this->usecount--; | |
207 | if (!compr_ret) { | |
3b23c1f5 RP |
208 | if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) |
209 | && (*cdatalen < *datalen)) { | |
ef53cb02 DW |
210 | best_dlen = *cdatalen; |
211 | best_slen = *datalen; | |
212 | best = this; | |
213 | } | |
214 | } | |
215 | } | |
216 | if (best_dlen) { | |
217 | *cdatalen = best_dlen; | |
218 | *datalen = best_slen; | |
219 | output_buf = best->compr_buf; | |
220 | best->compr_buf = NULL; | |
221 | best->compr_buf_size = 0; | |
222 | best->stat_compr_blocks++; | |
223 | best->stat_compr_orig_size += best_slen; | |
224 | best->stat_compr_new_size += best_dlen; | |
225 | ret = best->compr; | |
123005f3 | 226 | *cpage_out = output_buf; |
ef53cb02 DW |
227 | } |
228 | spin_unlock(&jffs2_compressor_list_lock); | |
229 | break; | |
123005f3 AS |
230 | case JFFS2_COMPR_MODE_FORCELZO: |
231 | ret = jffs2_selected_compress(JFFS2_COMPR_LZO, data_in, | |
232 | cpage_out, datalen, cdatalen); | |
233 | break; | |
234 | case JFFS2_COMPR_MODE_FORCEZLIB: | |
235 | ret = jffs2_selected_compress(JFFS2_COMPR_ZLIB, data_in, | |
236 | cpage_out, datalen, cdatalen); | |
237 | break; | |
ef53cb02 | 238 | default: |
da320f05 | 239 | pr_err("JFFS2: unknown compression mode\n"); |
ef53cb02 | 240 | } |
123005f3 | 241 | |
ef53cb02 DW |
242 | if (ret == JFFS2_COMPR_NONE) { |
243 | *cpage_out = data_in; | |
244 | *datalen = *cdatalen; | |
245 | none_stat_compr_blocks++; | |
246 | none_stat_compr_size += *datalen; | |
247 | } | |
1da177e4 LT |
248 | return ret; |
249 | } | |
250 | ||
251 | int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | |
182ec4ee | 252 | uint16_t comprtype, unsigned char *cdata_in, |
1da177e4 LT |
253 | unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) |
254 | { | |
ef53cb02 DW |
255 | struct jffs2_compressor *this; |
256 | int ret; | |
1da177e4 LT |
257 | |
258 | /* Older code had a bug where it would write non-zero 'usercompr' | |
259 | fields. Deal with it. */ | |
260 | if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) | |
261 | comprtype &= 0xff; | |
262 | ||
263 | switch (comprtype & 0xff) { | |
264 | case JFFS2_COMPR_NONE: | |
265 | /* This should be special-cased elsewhere, but we might as well deal with it */ | |
266 | memcpy(data_out, cdata_in, datalen); | |
ef53cb02 | 267 | none_stat_decompr_blocks++; |
1da177e4 LT |
268 | break; |
269 | case JFFS2_COMPR_ZERO: | |
270 | memset(data_out, 0, datalen); | |
271 | break; | |
272 | default: | |
ef53cb02 DW |
273 | spin_lock(&jffs2_compressor_list_lock); |
274 | list_for_each_entry(this, &jffs2_compressor_list, list) { | |
275 | if (comprtype == this->compr) { | |
276 | this->usecount++; | |
277 | spin_unlock(&jffs2_compressor_list_lock); | |
088bd455 | 278 | ret = this->decompress(cdata_in, data_out, cdatalen, datalen); |
ef53cb02 DW |
279 | spin_lock(&jffs2_compressor_list_lock); |
280 | if (ret) { | |
da320f05 JP |
281 | pr_warn("Decompressor \"%s\" returned %d\n", |
282 | this->name, ret); | |
ef53cb02 DW |
283 | } |
284 | else { | |
285 | this->stat_decompr_blocks++; | |
286 | } | |
287 | this->usecount--; | |
288 | spin_unlock(&jffs2_compressor_list_lock); | |
289 | return ret; | |
290 | } | |
291 | } | |
da320f05 JP |
292 | pr_warn("JFFS2 compression type 0x%02x not available\n", |
293 | comprtype); | |
ef53cb02 | 294 | spin_unlock(&jffs2_compressor_list_lock); |
1da177e4 LT |
295 | return -EIO; |
296 | } | |
297 | return 0; | |
298 | } | |
299 | ||
300 | int jffs2_register_compressor(struct jffs2_compressor *comp) | |
301 | { | |
ef53cb02 | 302 | struct jffs2_compressor *this; |
1da177e4 | 303 | |
ef53cb02 | 304 | if (!comp->name) { |
da320f05 | 305 | pr_warn("NULL compressor name at registering JFFS2 compressor. Failed.\n"); |
ef53cb02 DW |
306 | return -1; |
307 | } | |
308 | comp->compr_buf_size=0; | |
309 | comp->compr_buf=NULL; | |
310 | comp->usecount=0; | |
311 | comp->stat_compr_orig_size=0; | |
312 | comp->stat_compr_new_size=0; | |
313 | comp->stat_compr_blocks=0; | |
314 | comp->stat_decompr_blocks=0; | |
9c261b33 | 315 | jffs2_dbg(1, "Registering JFFS2 compressor \"%s\"\n", comp->name); |
1da177e4 | 316 | |
ef53cb02 | 317 | spin_lock(&jffs2_compressor_list_lock); |
1da177e4 | 318 | |
ef53cb02 DW |
319 | list_for_each_entry(this, &jffs2_compressor_list, list) { |
320 | if (this->priority < comp->priority) { | |
321 | list_add(&comp->list, this->list.prev); | |
322 | goto out; | |
323 | } | |
324 | } | |
325 | list_add_tail(&comp->list, &jffs2_compressor_list); | |
1da177e4 | 326 | out: |
ef53cb02 DW |
327 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { |
328 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); | |
329 | }) | |
1da177e4 | 330 | |
ef53cb02 | 331 | spin_unlock(&jffs2_compressor_list_lock); |
1da177e4 | 332 | |
ef53cb02 | 333 | return 0; |
1da177e4 LT |
334 | } |
335 | ||
336 | int jffs2_unregister_compressor(struct jffs2_compressor *comp) | |
337 | { | |
9c261b33 | 338 | D2(struct jffs2_compressor *this); |
1da177e4 | 339 | |
9c261b33 | 340 | jffs2_dbg(1, "Unregistering JFFS2 compressor \"%s\"\n", comp->name); |
1da177e4 | 341 | |
ef53cb02 | 342 | spin_lock(&jffs2_compressor_list_lock); |
1da177e4 | 343 | |
ef53cb02 DW |
344 | if (comp->usecount) { |
345 | spin_unlock(&jffs2_compressor_list_lock); | |
da320f05 | 346 | pr_warn("JFFS2: Compressor module is in use. Unregister failed.\n"); |
ef53cb02 DW |
347 | return -1; |
348 | } | |
349 | list_del(&comp->list); | |
1da177e4 | 350 | |
ef53cb02 DW |
351 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { |
352 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); | |
353 | }) | |
354 | spin_unlock(&jffs2_compressor_list_lock); | |
355 | return 0; | |
1da177e4 LT |
356 | } |
357 | ||
1da177e4 LT |
358 | void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) |
359 | { | |
ef53cb02 DW |
360 | if (orig != comprbuf) |
361 | kfree(comprbuf); | |
1da177e4 LT |
362 | } |
363 | ||
7d2beb13 | 364 | int __init jffs2_compressors_init(void) |
1da177e4 LT |
365 | { |
366 | /* Registering compressors */ | |
367 | #ifdef CONFIG_JFFS2_ZLIB | |
ef53cb02 | 368 | jffs2_zlib_init(); |
1da177e4 LT |
369 | #endif |
370 | #ifdef CONFIG_JFFS2_RTIME | |
ef53cb02 | 371 | jffs2_rtime_init(); |
1da177e4 LT |
372 | #endif |
373 | #ifdef CONFIG_JFFS2_RUBIN | |
ef53cb02 DW |
374 | jffs2_rubinmips_init(); |
375 | jffs2_dynrubin_init(); | |
1da177e4 | 376 | #endif |
c799aca3 RP |
377 | #ifdef CONFIG_JFFS2_LZO |
378 | jffs2_lzo_init(); | |
379 | #endif | |
1da177e4 LT |
380 | /* Setting default compression mode */ |
381 | #ifdef CONFIG_JFFS2_CMODE_NONE | |
ef53cb02 | 382 | jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; |
9c261b33 | 383 | jffs2_dbg(1, "JFFS2: default compression mode: none\n"); |
1da177e4 LT |
384 | #else |
385 | #ifdef CONFIG_JFFS2_CMODE_SIZE | |
ef53cb02 | 386 | jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; |
9c261b33 | 387 | jffs2_dbg(1, "JFFS2: default compression mode: size\n"); |
3b23c1f5 RP |
388 | #else |
389 | #ifdef CONFIG_JFFS2_CMODE_FAVOURLZO | |
390 | jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; | |
9c261b33 | 391 | jffs2_dbg(1, "JFFS2: default compression mode: favourlzo\n"); |
1da177e4 | 392 | #else |
9c261b33 | 393 | jffs2_dbg(1, "JFFS2: default compression mode: priority\n"); |
1da177e4 | 394 | #endif |
3b23c1f5 | 395 | #endif |
1da177e4 | 396 | #endif |
ef53cb02 | 397 | return 0; |
1da177e4 LT |
398 | } |
399 | ||
3bcc86f5 | 400 | int jffs2_compressors_exit(void) |
1da177e4 LT |
401 | { |
402 | /* Unregistering compressors */ | |
c799aca3 RP |
403 | #ifdef CONFIG_JFFS2_LZO |
404 | jffs2_lzo_exit(); | |
405 | #endif | |
1da177e4 | 406 | #ifdef CONFIG_JFFS2_RUBIN |
ef53cb02 DW |
407 | jffs2_dynrubin_exit(); |
408 | jffs2_rubinmips_exit(); | |
1da177e4 LT |
409 | #endif |
410 | #ifdef CONFIG_JFFS2_RTIME | |
ef53cb02 | 411 | jffs2_rtime_exit(); |
1da177e4 LT |
412 | #endif |
413 | #ifdef CONFIG_JFFS2_ZLIB | |
ef53cb02 | 414 | jffs2_zlib_exit(); |
1da177e4 | 415 | #endif |
ef53cb02 | 416 | return 0; |
1da177e4 | 417 | } |