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