diff --git a/packages/linux/patches/3.12.1/linux-994.0001-Squashfs-Refactor-decompressor-interface-and-code.patch b/packages/linux/patches/3.12.1/linux-994.0001-Squashfs-Refactor-decompressor-interface-and-code.patch new file mode 100644 index 0000000000..16ba1e6854 --- /dev/null +++ b/packages/linux/patches/3.12.1/linux-994.0001-Squashfs-Refactor-decompressor-interface-and-code.patch @@ -0,0 +1,732 @@ +From 9508c6b90b3f57ecea4e7a896cf8325400fc0c6e Mon Sep 17 00:00:00 2001 +From: Phillip Lougher +Date: Wed, 13 Nov 2013 02:56:26 +0000 +Subject: [PATCH 1/7] Squashfs: Refactor decompressor interface and code + +The decompressor interface and code was written from +the point of view of single-threaded operation. In doing +so it mixed a lot of single-threaded implementation specific +aspects into the decompressor code and elsewhere which makes it +difficult to seamlessly support multiple different decompressor +implementations. + +This patch does the following: + +1. It removes compressor_options parsing from the decompressor + init() function. This allows the decompressor init() function + to be dynamically called to instantiate multiple decompressors, + without the compressor options needing to be read and parsed each + time. + +2. It moves threading and all sleeping operations out of the + decompressors. In doing so, it makes the decompressors + non-blocking wrappers which only deal with interfacing with + the decompressor implementation. + +3. It splits decompressor.[ch] into decompressor generic functions + in decompressor.[ch], and moves the single threaded + decompressor implementation into decompressor_single.c. + +The result of this patch is Squashfs should now be able to +support multiple decompressors by adding new decompressor_xxx.c +files with specialised implementations of the functions in +decompressor_single.c + +Signed-off-by: Phillip Lougher +Reviewed-by: Minchan Kim +--- + fs/squashfs/Makefile | 2 +- + fs/squashfs/block.c | 11 +++-- + fs/squashfs/decompressor.c | 47 +++++++++++++------- + fs/squashfs/decompressor.h | 21 +++------ + fs/squashfs/decompressor_single.c | 86 +++++++++++++++++++++++++++++++++++ + fs/squashfs/lzo_wrapper.c | 24 +++------- + fs/squashfs/squashfs.h | 9 +++- + fs/squashfs/squashfs_fs_sb.h | 3 +- + fs/squashfs/super.c | 10 ++--- + fs/squashfs/xz_wrapper.c | 89 ++++++++++++++++++++----------------- + fs/squashfs/zlib_wrapper.c | 50 +++++++-------------- + 11 files changed, 216 insertions(+), 136 deletions(-) + create mode 100644 fs/squashfs/decompressor_single.c + +diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile +index 110b047..c223c84 100644 +--- a/fs/squashfs/Makefile ++++ b/fs/squashfs/Makefile +@@ -4,7 +4,7 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o +-squashfs-y += namei.o super.o symlink.o decompressor.o ++squashfs-y += namei.o super.o symlink.o decompressor.o decompressor_single.o + squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o + squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o + squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o +diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c +index 41d108e..4dd4025 100644 +--- a/fs/squashfs/block.c ++++ b/fs/squashfs/block.c +@@ -93,7 +93,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + struct buffer_head **bh; + int offset = index & ((1 << msblk->devblksize_log2) - 1); + u64 cur_index = index >> msblk->devblksize_log2; +- int bytes, compressed, b = 0, k = 0, page = 0, avail; ++ int bytes, compressed, b = 0, k = 0, page = 0, avail, i; + + bh = kcalloc(((srclength + msblk->devblksize - 1) + >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); +@@ -158,6 +158,12 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + ll_rw_block(READ, b - 1, bh + 1); + } + ++ for (i = 0; i < b; i++) { ++ wait_on_buffer(bh[i]); ++ if (!buffer_uptodate(bh[i])) ++ goto block_release; ++ } ++ + if (compressed) { + length = squashfs_decompress(msblk, buffer, bh, b, offset, + length, srclength, pages); +@@ -172,9 +178,6 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + for (bytes = length; k < b; k++) { + in = min(bytes, msblk->devblksize - offset); + bytes -= in; +- wait_on_buffer(bh[k]); +- if (!buffer_uptodate(bh[k])) +- goto block_release; + while (in) { + if (pg_offset == PAGE_CACHE_SIZE) { + page++; +diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c +index 3f6271d..234291f 100644 +--- a/fs/squashfs/decompressor.c ++++ b/fs/squashfs/decompressor.c +@@ -37,29 +37,29 @@ + */ + + static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = { +- NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 ++ NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 + }; + + #ifndef CONFIG_SQUASHFS_LZO + static const struct squashfs_decompressor squashfs_lzo_comp_ops = { +- NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 ++ NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 + }; + #endif + + #ifndef CONFIG_SQUASHFS_XZ + static const struct squashfs_decompressor squashfs_xz_comp_ops = { +- NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0 ++ NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0 + }; + #endif + + #ifndef CONFIG_SQUASHFS_ZLIB + static const struct squashfs_decompressor squashfs_zlib_comp_ops = { +- NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0 ++ NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0 + }; + #endif + + static const struct squashfs_decompressor squashfs_unknown_comp_ops = { +- NULL, NULL, NULL, 0, "unknown", 0 ++ NULL, NULL, NULL, NULL, 0, "unknown", 0 + }; + + static const struct squashfs_decompressor *decompressor[] = { +@@ -83,10 +83,10 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id) + } + + +-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags) ++static void *get_comp_opts(struct super_block *sb, unsigned short flags) + { + struct squashfs_sb_info *msblk = sb->s_fs_info; +- void *strm, *buffer = NULL; ++ void *buffer = NULL, *comp_opts; + int length = 0; + + /* +@@ -94,23 +94,40 @@ void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags) + */ + if (SQUASHFS_COMP_OPTS(flags)) { + buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); +- if (buffer == NULL) +- return ERR_PTR(-ENOMEM); ++ if (buffer == NULL) { ++ comp_opts = ERR_PTR(-ENOMEM); ++ goto out; ++ } + + length = squashfs_read_data(sb, &buffer, + sizeof(struct squashfs_super_block), 0, NULL, +- PAGE_CACHE_SIZE, 1); ++ PAGE_CACHE_SIZE, 1); + + if (length < 0) { +- strm = ERR_PTR(length); +- goto finished; ++ comp_opts = ERR_PTR(length); ++ goto out; + } + } + +- strm = msblk->decompressor->init(msblk, buffer, length); ++ comp_opts = squashfs_comp_opts(msblk, buffer, length); + +-finished: ++out: + kfree(buffer); ++ return comp_opts; ++} ++ ++ ++void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags) ++{ ++ struct squashfs_sb_info *msblk = sb->s_fs_info; ++ void *stream, *comp_opts = get_comp_opts(sb, flags); ++ ++ if (IS_ERR(comp_opts)) ++ return comp_opts; ++ ++ stream = squashfs_decompressor_create(msblk, comp_opts); ++ if (IS_ERR(stream)) ++ kfree(comp_opts); + +- return strm; ++ return stream; + } +diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h +index 330073e..6cdb20a 100644 +--- a/fs/squashfs/decompressor.h ++++ b/fs/squashfs/decompressor.h +@@ -24,28 +24,21 @@ + */ + + struct squashfs_decompressor { +- void *(*init)(struct squashfs_sb_info *, void *, int); ++ void *(*init)(struct squashfs_sb_info *, void *); ++ void *(*comp_opts)(struct squashfs_sb_info *, void *, int); + void (*free)(void *); +- int (*decompress)(struct squashfs_sb_info *, void **, ++ int (*decompress)(struct squashfs_sb_info *, void *, void **, + struct buffer_head **, int, int, int, int, int); + int id; + char *name; + int supported; + }; + +-static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk, +- void *s) ++static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk, ++ void *buff, int length) + { +- if (msblk->decompressor) +- msblk->decompressor->free(s); +-} +- +-static inline int squashfs_decompress(struct squashfs_sb_info *msblk, +- void **buffer, struct buffer_head **bh, int b, int offset, int length, +- int srclength, int pages) +-{ +- return msblk->decompressor->decompress(msblk, buffer, bh, b, offset, +- length, srclength, pages); ++ return msblk->decompressor->comp_opts ? ++ msblk->decompressor->comp_opts(msblk, buff, length) : NULL; + } + + #ifdef CONFIG_SQUASHFS_XZ +diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c +new file mode 100644 +index 0000000..f857cf6 +--- /dev/null ++++ b/fs/squashfs/decompressor_single.c +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (c) 2013 ++ * Phillip Lougher ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "squashfs_fs.h" ++#include "squashfs_fs_sb.h" ++#include "decompressor.h" ++#include "squashfs.h" ++ ++/* ++ * This file implements single-threaded decompression in the ++ * decompressor framework ++ */ ++ ++struct squashfs_stream { ++ void *stream; ++ struct mutex mutex; ++}; ++ ++void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, ++ void *comp_opts) ++{ ++ struct squashfs_stream *stream; ++ int err = -ENOMEM; ++ ++ stream = kmalloc(sizeof(*stream), GFP_KERNEL); ++ if (stream == NULL) ++ goto out; ++ ++ stream->stream = msblk->decompressor->init(msblk, comp_opts); ++ if (IS_ERR(stream->stream)) { ++ err = PTR_ERR(stream->stream); ++ goto out; ++ } ++ ++ kfree(comp_opts); ++ mutex_init(&stream->mutex); ++ return stream; ++ ++out: ++ kfree(stream); ++ return ERR_PTR(err); ++} ++ ++void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_stream *stream = msblk->stream; ++ ++ if (stream) { ++ msblk->decompressor->free(stream->stream); ++ kfree(stream); ++ } ++} ++ ++int squashfs_decompress(struct squashfs_sb_info *msblk, ++ void **buffer, struct buffer_head **bh, int b, int offset, int length, ++ int srclength, int pages) ++{ ++ int res; ++ struct squashfs_stream *stream = msblk->stream; ++ ++ mutex_lock(&stream->mutex); ++ res = msblk->decompressor->decompress(msblk, stream->stream, buffer, ++ bh, b, offset, length, srclength, pages); ++ mutex_unlock(&stream->mutex); ++ ++ if (res < 0) ++ ERROR("%s decompression failed, data probably corrupt\n", ++ msblk->decompressor->name); ++ ++ return res; ++} ++ ++int squashfs_max_decompressors(void) ++{ ++ return 1; ++} +diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c +index 00f4dfc..75c3b57 100644 +--- a/fs/squashfs/lzo_wrapper.c ++++ b/fs/squashfs/lzo_wrapper.c +@@ -37,7 +37,7 @@ struct squashfs_lzo { + void *output; + }; + +-static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len) ++static void *lzo_init(struct squashfs_sb_info *msblk, void *buff) + { + int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); + +@@ -74,22 +74,16 @@ static void lzo_free(void *strm) + } + + +-static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, +- struct buffer_head **bh, int b, int offset, int length, int srclength, +- int pages) ++static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, ++ void **buffer, struct buffer_head **bh, int b, int offset, int length, ++ int srclength, int pages) + { +- struct squashfs_lzo *stream = msblk->stream; ++ struct squashfs_lzo *stream = strm; + void *buff = stream->input; + int avail, i, bytes = length, res; + size_t out_len = srclength; + +- mutex_lock(&msblk->read_data_mutex); +- + for (i = 0; i < b; i++) { +- wait_on_buffer(bh[i]); +- if (!buffer_uptodate(bh[i])) +- goto block_release; +- + avail = min(bytes, msblk->devblksize - offset); + memcpy(buff, bh[i]->b_data + offset, avail); + buff += avail; +@@ -111,17 +105,9 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, + bytes -= avail; + } + +- mutex_unlock(&msblk->read_data_mutex); + return res; + +-block_release: +- for (; i < b; i++) +- put_bh(bh[i]); +- + failed: +- mutex_unlock(&msblk->read_data_mutex); +- +- ERROR("lzo decompression failed, data probably corrupt\n"); + return -EIO; + } + +diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h +index d126651..2e2751d 100644 +--- a/fs/squashfs/squashfs.h ++++ b/fs/squashfs/squashfs.h +@@ -48,7 +48,14 @@ extern void *squashfs_read_table(struct super_block *, u64, int); + + /* decompressor.c */ + extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); +-extern void *squashfs_decompressor_init(struct super_block *, unsigned short); ++extern void *squashfs_decompressor_setup(struct super_block *, unsigned short); ++ ++/* decompressor_xxx.c */ ++extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *); ++extern void squashfs_decompressor_destroy(struct squashfs_sb_info *); ++extern int squashfs_decompress(struct squashfs_sb_info *, void **, ++ struct buffer_head **, int, int, int, int, int); ++extern int squashfs_max_decompressors(void); + + /* export.c */ + extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64, +diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h +index 52934a2..9cdcf41 100644 +--- a/fs/squashfs/squashfs_fs_sb.h ++++ b/fs/squashfs/squashfs_fs_sb.h +@@ -63,10 +63,9 @@ struct squashfs_sb_info { + __le64 *id_table; + __le64 *fragment_index; + __le64 *xattr_id_table; +- struct mutex read_data_mutex; + struct mutex meta_index_mutex; + struct meta_index *meta_index; +- void *stream; ++ struct squashfs_stream *stream; + __le64 *inode_lookup_table; + u64 inode_table; + u64 directory_table; +diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c +index 60553a9..202df63 100644 +--- a/fs/squashfs/super.c ++++ b/fs/squashfs/super.c +@@ -98,7 +98,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) + msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE); + msblk->devblksize_log2 = ffz(~msblk->devblksize); + +- mutex_init(&msblk->read_data_mutex); + mutex_init(&msblk->meta_index_mutex); + + /* +@@ -206,13 +205,14 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) + goto failed_mount; + + /* Allocate read_page block */ +- msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size); ++ msblk->read_page = squashfs_cache_init("data", ++ squashfs_max_decompressors(), msblk->block_size); + if (msblk->read_page == NULL) { + ERROR("Failed to allocate read_page block\n"); + goto failed_mount; + } + +- msblk->stream = squashfs_decompressor_init(sb, flags); ++ msblk->stream = squashfs_decompressor_setup(sb, flags); + if (IS_ERR(msblk->stream)) { + err = PTR_ERR(msblk->stream); + msblk->stream = NULL; +@@ -336,7 +336,7 @@ failed_mount: + squashfs_cache_delete(msblk->block_cache); + squashfs_cache_delete(msblk->fragment_cache); + squashfs_cache_delete(msblk->read_page); +- squashfs_decompressor_free(msblk, msblk->stream); ++ squashfs_decompressor_destroy(msblk); + kfree(msblk->inode_lookup_table); + kfree(msblk->fragment_index); + kfree(msblk->id_table); +@@ -383,7 +383,7 @@ static void squashfs_put_super(struct super_block *sb) + squashfs_cache_delete(sbi->block_cache); + squashfs_cache_delete(sbi->fragment_cache); + squashfs_cache_delete(sbi->read_page); +- squashfs_decompressor_free(sbi, sbi->stream); ++ squashfs_decompressor_destroy(sbi); + kfree(sbi->id_table); + kfree(sbi->fragment_index); + kfree(sbi->meta_index); +diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c +index 1760b7d1..5d1d07c 100644 +--- a/fs/squashfs/xz_wrapper.c ++++ b/fs/squashfs/xz_wrapper.c +@@ -38,38 +38,63 @@ struct squashfs_xz { + struct xz_buf buf; + }; + +-struct comp_opts { ++struct disk_comp_opts { + __le32 dictionary_size; + __le32 flags; + }; + +-static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff, +- int len) ++struct comp_opts { ++ int dict_size; ++}; ++ ++static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, ++ void *buff, int len) + { +- struct comp_opts *comp_opts = buff; +- struct squashfs_xz *stream; +- int dict_size = msblk->block_size; +- int err, n; ++ struct disk_comp_opts *comp_opts = buff; ++ struct comp_opts *opts; ++ int err = 0, n; ++ ++ opts = kmalloc(sizeof(*opts), GFP_KERNEL); ++ if (opts == NULL) { ++ err = -ENOMEM; ++ goto out2; ++ } + + if (comp_opts) { + /* check compressor options are the expected length */ + if (len < sizeof(*comp_opts)) { + err = -EIO; +- goto failed; ++ goto out; + } + +- dict_size = le32_to_cpu(comp_opts->dictionary_size); ++ opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); + + /* the dictionary size should be 2^n or 2^n+2^(n+1) */ +- n = ffs(dict_size) - 1; +- if (dict_size != (1 << n) && dict_size != (1 << n) + ++ n = ffs(opts->dict_size) - 1; ++ if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + + (1 << (n + 1))) { + err = -EIO; +- goto failed; ++ goto out; + } +- } ++ } else ++ /* use defaults */ ++ opts->dict_size = max_t(int, msblk->block_size, ++ SQUASHFS_METADATA_SIZE); + +- dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE); ++ return opts; ++ ++out: ++ kfree(opts); ++out2: ++ return ERR_PTR(err); ++} ++ ++ ++static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) ++{ ++ struct comp_opts *comp_opts = buff; ++ struct squashfs_xz *stream; ++ int err; + + stream = kmalloc(sizeof(*stream), GFP_KERNEL); + if (stream == NULL) { +@@ -77,7 +102,7 @@ static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff, + goto failed; + } + +- stream->state = xz_dec_init(XZ_PREALLOC, dict_size); ++ stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); + if (stream->state == NULL) { + kfree(stream); + err = -ENOMEM; +@@ -103,15 +128,13 @@ static void squashfs_xz_free(void *strm) + } + + +-static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, +- struct buffer_head **bh, int b, int offset, int length, int srclength, +- int pages) ++static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, ++ void **buffer, struct buffer_head **bh, int b, int offset, int length, ++ int srclength, int pages) + { + enum xz_ret xz_err; + int avail, total = 0, k = 0, page = 0; +- struct squashfs_xz *stream = msblk->stream; +- +- mutex_lock(&msblk->read_data_mutex); ++ struct squashfs_xz *stream = strm; + + xz_dec_reset(stream->state); + stream->buf.in_pos = 0; +@@ -124,10 +147,6 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, + if (stream->buf.in_pos == stream->buf.in_size && k < b) { + avail = min(length, msblk->devblksize - offset); + length -= avail; +- wait_on_buffer(bh[k]); +- if (!buffer_uptodate(bh[k])) +- goto release_mutex; +- + stream->buf.in = bh[k]->b_data + offset; + stream->buf.in_size = avail; + stream->buf.in_pos = 0; +@@ -147,23 +166,12 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, + put_bh(bh[k++]); + } while (xz_err == XZ_OK); + +- if (xz_err != XZ_STREAM_END) { +- ERROR("xz_dec_run error, data probably corrupt\n"); +- goto release_mutex; +- } +- +- if (k < b) { +- ERROR("xz_uncompress error, input remaining\n"); +- goto release_mutex; +- } +- +- total += stream->buf.out_pos; +- mutex_unlock(&msblk->read_data_mutex); +- return total; ++ if (xz_err != XZ_STREAM_END || k < b) ++ goto out; + +-release_mutex: +- mutex_unlock(&msblk->read_data_mutex); ++ return total + stream->buf.out_pos; + ++out: + for (; k < b; k++) + put_bh(bh[k]); + +@@ -172,6 +180,7 @@ release_mutex: + + const struct squashfs_decompressor squashfs_xz_comp_ops = { + .init = squashfs_xz_init, ++ .comp_opts = squashfs_xz_comp_opts, + .free = squashfs_xz_free, + .decompress = squashfs_xz_uncompress, + .id = XZ_COMPRESSION, +diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c +index 55d918f..bb04902 100644 +--- a/fs/squashfs/zlib_wrapper.c ++++ b/fs/squashfs/zlib_wrapper.c +@@ -33,7 +33,7 @@ + #include "squashfs.h" + #include "decompressor.h" + +-static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len) ++static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) + { + z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); + if (stream == NULL) +@@ -61,15 +61,13 @@ static void zlib_free(void *strm) + } + + +-static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, +- struct buffer_head **bh, int b, int offset, int length, int srclength, +- int pages) ++static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, ++ void **buffer, struct buffer_head **bh, int b, int offset, int length, ++ int srclength, int pages) + { + int zlib_err, zlib_init = 0; + int k = 0, page = 0; +- z_stream *stream = msblk->stream; +- +- mutex_lock(&msblk->read_data_mutex); ++ z_stream *stream = strm; + + stream->avail_out = 0; + stream->avail_in = 0; +@@ -78,10 +76,6 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, + if (stream->avail_in == 0 && k < b) { + int avail = min(length, msblk->devblksize - offset); + length -= avail; +- wait_on_buffer(bh[k]); +- if (!buffer_uptodate(bh[k])) +- goto release_mutex; +- + stream->next_in = bh[k]->b_data + offset; + stream->avail_in = avail; + offset = 0; +@@ -94,12 +88,8 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, + + if (!zlib_init) { + zlib_err = zlib_inflateInit(stream); +- if (zlib_err != Z_OK) { +- ERROR("zlib_inflateInit returned unexpected " +- "result 0x%x, srclength %d\n", +- zlib_err, srclength); +- goto release_mutex; +- } ++ if (zlib_err != Z_OK) ++ goto out; + zlib_init = 1; + } + +@@ -109,29 +99,19 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, + put_bh(bh[k++]); + } while (zlib_err == Z_OK); + +- if (zlib_err != Z_STREAM_END) { +- ERROR("zlib_inflate error, data probably corrupt\n"); +- goto release_mutex; +- } ++ if (zlib_err != Z_STREAM_END) ++ goto out; + + zlib_err = zlib_inflateEnd(stream); +- if (zlib_err != Z_OK) { +- ERROR("zlib_inflate error, data probably corrupt\n"); +- goto release_mutex; +- } +- +- if (k < b) { +- ERROR("zlib_uncompress error, data remaining\n"); +- goto release_mutex; +- } ++ if (zlib_err != Z_OK) ++ goto out; + +- length = stream->total_out; +- mutex_unlock(&msblk->read_data_mutex); +- return length; ++ if (k < b) ++ goto out; + +-release_mutex: +- mutex_unlock(&msblk->read_data_mutex); ++ return stream->total_out; + ++out: + for (; k < b; k++) + put_bh(bh[k]); + +-- +1.7.10.4 + diff --git a/packages/linux/patches/3.12.1/linux-994.0002-squashfs-Enhance-parallel-I-O.patch b/packages/linux/patches/3.12.1/linux-994.0002-squashfs-Enhance-parallel-I-O.patch new file mode 100644 index 0000000000..6efc7d1b46 --- /dev/null +++ b/packages/linux/patches/3.12.1/linux-994.0002-squashfs-Enhance-parallel-I-O.patch @@ -0,0 +1,285 @@ +From cd59c2ec5f37a2bc1315c9324aab6c21d43ffa1a Mon Sep 17 00:00:00 2001 +From: Minchan Kim +Date: Mon, 28 Oct 2013 14:26:30 +0900 +Subject: [PATCH 2/7] squashfs: Enhance parallel I/O + +Now squashfs have used for only one stream buffer for decompression +so it hurts parallel read performance so this patch supports +multiple decompressor to enhance performance parallel I/O. + +Four 1G file dd read on KVM machine which has 2 CPU and 4G memory. + +dd if=test/test1.dat of=/dev/null & +dd if=test/test2.dat of=/dev/null & +dd if=test/test3.dat of=/dev/null & +dd if=test/test4.dat of=/dev/null & + +old : 1m39s -> new : 9s + +* From v1 + * Change comp_strm with decomp_strm - Phillip + * Change/add comments - Phillip + +Signed-off-by: Minchan Kim +Signed-off-by: Phillip Lougher +--- + fs/squashfs/Kconfig | 13 +++ + fs/squashfs/Makefile | 9 +- + fs/squashfs/decompressor_multi.c | 200 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 221 insertions(+), 1 deletion(-) + create mode 100644 fs/squashfs/decompressor_multi.c + +diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig +index c70111e..1c6d340 100644 +--- a/fs/squashfs/Kconfig ++++ b/fs/squashfs/Kconfig +@@ -63,6 +63,19 @@ config SQUASHFS_LZO + + If unsure, say N. + ++config SQUASHFS_MULTI_DECOMPRESSOR ++ bool "Use multiple decompressors for handling parallel I/O" ++ depends on SQUASHFS ++ help ++ By default Squashfs uses a single decompressor but it gives ++ poor performance on parallel I/O workloads when using multiple CPU ++ machines due to waiting on decompressor availability. ++ ++ If you have a parallel I/O workload and your system has enough memory, ++ using this option may improve overall I/O performance. ++ ++ If unsure, say N. ++ + config SQUASHFS_XZ + bool "Include support for XZ compressed file systems" + depends on SQUASHFS +diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile +index c223c84..dfebc3b 100644 +--- a/fs/squashfs/Makefile ++++ b/fs/squashfs/Makefile +@@ -4,8 +4,15 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o +-squashfs-y += namei.o super.o symlink.o decompressor.o decompressor_single.o ++squashfs-y += namei.o super.o symlink.o decompressor.o ++ + squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o + squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o + squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o + squashfs-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o ++ ++ifdef CONFIG_SQUASHFS_MULTI_DECOMPRESSOR ++ squashfs-y += decompressor_multi.o ++else ++ squashfs-y += decompressor_single.o ++endif +diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c +new file mode 100644 +index 0000000..462731d +--- /dev/null ++++ b/fs/squashfs/decompressor_multi.c +@@ -0,0 +1,200 @@ ++/* ++ * Copyright (c) 2013 ++ * Minchan Kim ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs_fs.h" ++#include "squashfs_fs_sb.h" ++#include "decompressor.h" ++#include "squashfs.h" ++ ++/* ++ * This file implements multi-threaded decompression in the ++ * decompressor framework ++ */ ++ ++ ++/* ++ * The reason that multiply two is that a CPU can request new I/O ++ * while it is waiting previous request. ++ */ ++#define MAX_DECOMPRESSOR (num_online_cpus() * 2) ++ ++ ++int squashfs_max_decompressors(void) ++{ ++ return MAX_DECOMPRESSOR; ++} ++ ++ ++struct squashfs_stream { ++ void *comp_opts; ++ struct list_head strm_list; ++ struct mutex mutex; ++ int avail_decomp; ++ wait_queue_head_t wait; ++}; ++ ++ ++struct decomp_stream { ++ void *stream; ++ struct list_head list; ++}; ++ ++ ++static void put_decomp_stream(struct decomp_stream *decomp_strm, ++ struct squashfs_stream *stream) ++{ ++ mutex_lock(&stream->mutex); ++ list_add(&decomp_strm->list, &stream->strm_list); ++ mutex_unlock(&stream->mutex); ++ wake_up(&stream->wait); ++} ++ ++void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, ++ void *comp_opts) ++{ ++ struct squashfs_stream *stream; ++ struct decomp_stream *decomp_strm = NULL; ++ int err = -ENOMEM; ++ ++ stream = kzalloc(sizeof(*stream), GFP_KERNEL); ++ if (!stream) ++ goto out; ++ ++ stream->comp_opts = comp_opts; ++ mutex_init(&stream->mutex); ++ INIT_LIST_HEAD(&stream->strm_list); ++ init_waitqueue_head(&stream->wait); ++ ++ /* ++ * We should have a decompressor at least as default ++ * so if we fail to allocate new decompressor dynamically, ++ * we could always fall back to default decompressor and ++ * file system works. ++ */ ++ decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); ++ if (!decomp_strm) ++ goto out; ++ ++ decomp_strm->stream = msblk->decompressor->init(msblk, ++ stream->comp_opts); ++ if (IS_ERR(decomp_strm->stream)) { ++ err = PTR_ERR(decomp_strm->stream); ++ goto out; ++ } ++ ++ list_add(&decomp_strm->list, &stream->strm_list); ++ stream->avail_decomp = 1; ++ return stream; ++ ++out: ++ kfree(decomp_strm); ++ kfree(stream); ++ return ERR_PTR(err); ++} ++ ++ ++void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_stream *stream = msblk->stream; ++ if (stream) { ++ struct decomp_stream *decomp_strm; ++ ++ while (!list_empty(&stream->strm_list)) { ++ decomp_strm = list_entry(stream->strm_list.prev, ++ struct decomp_stream, list); ++ list_del(&decomp_strm->list); ++ msblk->decompressor->free(decomp_strm->stream); ++ kfree(decomp_strm); ++ stream->avail_decomp--; ++ } ++ } ++ ++ WARN_ON(stream->avail_decomp); ++ kfree(stream->comp_opts); ++ kfree(stream); ++} ++ ++ ++static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk, ++ struct squashfs_stream *stream) ++{ ++ struct decomp_stream *decomp_strm; ++ ++ while (1) { ++ mutex_lock(&stream->mutex); ++ ++ /* There is available decomp_stream */ ++ if (!list_empty(&stream->strm_list)) { ++ decomp_strm = list_entry(stream->strm_list.prev, ++ struct decomp_stream, list); ++ list_del(&decomp_strm->list); ++ mutex_unlock(&stream->mutex); ++ break; ++ } ++ ++ /* ++ * If there is no available decomp and already full, ++ * let's wait for releasing decomp from other users. ++ */ ++ if (stream->avail_decomp >= MAX_DECOMPRESSOR) ++ goto wait; ++ ++ /* Let's allocate new decomp */ ++ decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); ++ if (!decomp_strm) ++ goto wait; ++ ++ decomp_strm->stream = msblk->decompressor->init(msblk, ++ stream->comp_opts); ++ if (IS_ERR(decomp_strm->stream)) { ++ kfree(decomp_strm); ++ goto wait; ++ } ++ ++ stream->avail_decomp++; ++ WARN_ON(stream->avail_decomp > MAX_DECOMPRESSOR); ++ ++ mutex_unlock(&stream->mutex); ++ break; ++wait: ++ /* ++ * If system memory is tough, let's for other's ++ * releasing instead of hurting VM because it could ++ * make page cache thrashing. ++ */ ++ mutex_unlock(&stream->mutex); ++ wait_event(stream->wait, ++ !list_empty(&stream->strm_list)); ++ } ++ ++ return decomp_strm; ++} ++ ++ ++int squashfs_decompress(struct squashfs_sb_info *msblk, ++ void **buffer, struct buffer_head **bh, int b, int offset, int length, ++ int srclength, int pages) ++{ ++ int res; ++ struct squashfs_stream *stream = msblk->stream; ++ struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); ++ res = msblk->decompressor->decompress(msblk, decomp_stream->stream, ++ buffer, bh, b, offset, length, srclength, pages); ++ put_decomp_stream(decomp_stream, stream); ++ if (res < 0) ++ ERROR("%s decompression failed, data probably corrupt\n", ++ msblk->decompressor->name); ++ return res; ++} +-- +1.7.10.4 + diff --git a/packages/linux/patches/3.12.1/linux-994.0003-Squashfs-add-multi-threaded-decompression-using-perc.patch b/packages/linux/patches/3.12.1/linux-994.0003-Squashfs-add-multi-threaded-decompression-using-perc.patch new file mode 100644 index 0000000000..5a7dd8daf4 --- /dev/null +++ b/packages/linux/patches/3.12.1/linux-994.0003-Squashfs-add-multi-threaded-decompression-using-perc.patch @@ -0,0 +1,231 @@ +From d208383d640727b70cd6689bc17e67e9b5ebf4ff Mon Sep 17 00:00:00 2001 +From: Phillip Lougher +Date: Mon, 18 Nov 2013 02:31:36 +0000 +Subject: [PATCH 3/7] Squashfs: add multi-threaded decompression using percpu + variable + +Add a multi-threaded decompression implementation which uses +percpu variables. + +Using percpu variables has advantages and disadvantages over +implementations which do not use percpu variables. + +Advantages: + * the nature of percpu variables ensures decompression is + load-balanced across the multiple cores. + * simplicity. + +Disadvantages: it limits decompression to one thread per core. + +Signed-off-by: Phillip Lougher +--- + fs/squashfs/Kconfig | 57 ++++++++++++++---- + fs/squashfs/Makefile | 10 +--- + fs/squashfs/decompressor_multi_percpu.c | 98 +++++++++++++++++++++++++++++++ + 3 files changed, 145 insertions(+), 20 deletions(-) + create mode 100644 fs/squashfs/decompressor_multi_percpu.c + +diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig +index 1c6d340..159bd66 100644 +--- a/fs/squashfs/Kconfig ++++ b/fs/squashfs/Kconfig +@@ -25,6 +25,50 @@ config SQUASHFS + + If unsure, say N. + ++choice ++ prompt "Decompressor parallelisation options" ++ depends on SQUASHFS ++ help ++ Squashfs now supports three parallelisation options for ++ decompression. Each one exhibits various trade-offs between ++ decompression performance and CPU and memory usage. ++ ++ If in doubt, select "Single threaded compression" ++ ++config SQUASHFS_DECOMP_SINGLE ++ bool "Single threaded compression" ++ help ++ Traditionally Squashfs has used single-threaded decompression. ++ Only one block (data or metadata) can be decompressed at any ++ one time. This limits CPU and memory usage to a minimum. ++ ++config SQUASHFS_DECOMP_MULTI ++ bool "Use multiple decompressors for parallel I/O" ++ help ++ By default Squashfs uses a single decompressor but it gives ++ poor performance on parallel I/O workloads when using multiple CPU ++ machines due to waiting on decompressor availability. ++ ++ If you have a parallel I/O workload and your system has enough memory, ++ using this option may improve overall I/O performance. ++ ++ This decompressor implementation uses up to two parallel ++ decompressors per core. It dynamically allocates decompressors ++ on a demand basis. ++ ++config SQUASHFS_DECOMP_MULTI_PERCPU ++ bool "Use percpu multiple decompressors for parallel I/O" ++ help ++ By default Squashfs uses a single decompressor but it gives ++ poor performance on parallel I/O workloads when using multiple CPU ++ machines due to waiting on decompressor availability. ++ ++ This decompressor implementation uses a maximum of one ++ decompressor per core. It uses percpu variables to ensure ++ decompression is load-balanced across the cores. ++ ++endchoice ++ + config SQUASHFS_XATTR + bool "Squashfs XATTR support" + depends on SQUASHFS +@@ -63,19 +107,6 @@ config SQUASHFS_LZO + + If unsure, say N. + +-config SQUASHFS_MULTI_DECOMPRESSOR +- bool "Use multiple decompressors for handling parallel I/O" +- depends on SQUASHFS +- help +- By default Squashfs uses a single decompressor but it gives +- poor performance on parallel I/O workloads when using multiple CPU +- machines due to waiting on decompressor availability. +- +- If you have a parallel I/O workload and your system has enough memory, +- using this option may improve overall I/O performance. +- +- If unsure, say N. +- + config SQUASHFS_XZ + bool "Include support for XZ compressed file systems" + depends on SQUASHFS +diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile +index dfebc3b..5833b96 100644 +--- a/fs/squashfs/Makefile ++++ b/fs/squashfs/Makefile +@@ -5,14 +5,10 @@ + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o + squashfs-y += namei.o super.o symlink.o decompressor.o +- ++squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o ++squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o ++squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o + squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o + squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o + squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o + squashfs-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o +- +-ifdef CONFIG_SQUASHFS_MULTI_DECOMPRESSOR +- squashfs-y += decompressor_multi.o +-else +- squashfs-y += decompressor_single.o +-endif +diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c +new file mode 100644 +index 0000000..0e7b679 +--- /dev/null ++++ b/fs/squashfs/decompressor_multi_percpu.c +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (c) 2013 ++ * Phillip Lougher ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "squashfs_fs.h" ++#include "squashfs_fs_sb.h" ++#include "decompressor.h" ++#include "squashfs.h" ++ ++/* ++ * This file implements multi-threaded decompression using percpu ++ * variables, one thread per cpu core. ++ */ ++ ++struct squashfs_stream { ++ void *stream; ++}; ++ ++void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, ++ void *comp_opts) ++{ ++ struct squashfs_stream *stream; ++ struct squashfs_stream __percpu *percpu; ++ int err, cpu; ++ ++ percpu = alloc_percpu(struct squashfs_stream); ++ if (percpu == NULL) ++ return ERR_PTR(-ENOMEM); ++ ++ for_each_possible_cpu(cpu) { ++ stream = per_cpu_ptr(percpu, cpu); ++ stream->stream = msblk->decompressor->init(msblk, comp_opts); ++ if (IS_ERR(stream->stream)) { ++ err = PTR_ERR(stream->stream); ++ goto out; ++ } ++ } ++ ++ kfree(comp_opts); ++ return (__force void *) percpu; ++ ++out: ++ for_each_possible_cpu(cpu) { ++ stream = per_cpu_ptr(percpu, cpu); ++ if (!IS_ERR_OR_NULL(stream->stream)) ++ msblk->decompressor->free(stream->stream); ++ } ++ free_percpu(percpu); ++ return ERR_PTR(err); ++} ++ ++void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_stream __percpu *percpu = ++ (struct squashfs_stream __percpu *) msblk->stream; ++ struct squashfs_stream *stream; ++ int cpu; ++ ++ if (msblk->stream) { ++ for_each_possible_cpu(cpu) { ++ stream = per_cpu_ptr(percpu, cpu); ++ msblk->decompressor->free(stream->stream); ++ } ++ free_percpu(percpu); ++ } ++} ++ ++int squashfs_decompress(struct squashfs_sb_info *msblk, ++ void **buffer, struct buffer_head **bh, int b, int offset, int length, ++ int srclength, int pages) ++{ ++ struct squashfs_stream __percpu *percpu = ++ (struct squashfs_stream __percpu *) msblk->stream; ++ struct squashfs_stream *stream = get_cpu_ptr(percpu); ++ int res = msblk->decompressor->decompress(msblk, stream->stream, buffer, ++ bh, b, offset, length, srclength, pages); ++ put_cpu_ptr(stream); ++ ++ if (res < 0) ++ ERROR("%s decompression failed, data probably corrupt\n", ++ msblk->decompressor->name); ++ ++ return res; ++} ++ ++int squashfs_max_decompressors(void) ++{ ++ return num_possible_cpus(); ++} +-- +1.7.10.4 + diff --git a/packages/linux/patches/3.12.1/linux-994.0004-Squashfs-Generalise-paging-handling-in-the-decompres.patch b/packages/linux/patches/3.12.1/linux-994.0004-Squashfs-Generalise-paging-handling-in-the-decompres.patch new file mode 100644 index 0000000000..c1a12247d0 --- /dev/null +++ b/packages/linux/patches/3.12.1/linux-994.0004-Squashfs-Generalise-paging-handling-in-the-decompres.patch @@ -0,0 +1,636 @@ +From 846b730e99518a1c9945afcb2afbe4d08a02ed80 Mon Sep 17 00:00:00 2001 +From: Phillip Lougher +Date: Mon, 18 Nov 2013 02:59:12 +0000 +Subject: [PATCH 4/7] Squashfs: Generalise paging handling in the + decompressors + +Further generalise the decompressors by adding a page handler +abstraction. This adds helpers to allow the decompressors +to access and process the output buffers in an implementation +independant manner. + +This allows different types of output buffer to be passed +to the decompressors, with the implementation specific +aspects handled at decompression time, but without the +knowledge being held in the decompressor wrapper code. + +This will allow the decompressors to handle Squashfs +cache buffers, and page cache pages. + +This patch adds the abstraction and an implementation for +the caches. + +Signed-off-by: Phillip Lougher +Reviewed-by: Minchan Kim +--- + fs/squashfs/block.c | 27 +++++++++-------- + fs/squashfs/cache.c | 28 ++++++++++++++---- + fs/squashfs/decompressor.c | 14 +++++++-- + fs/squashfs/decompressor.h | 5 ++-- + fs/squashfs/decompressor_multi.c | 7 ++--- + fs/squashfs/decompressor_multi_percpu.c | 9 +++--- + fs/squashfs/decompressor_single.c | 9 +++--- + fs/squashfs/lzo_wrapper.c | 27 +++++++++++------ + fs/squashfs/page_actor.h | 49 +++++++++++++++++++++++++++++++ + fs/squashfs/squashfs.h | 8 ++--- + fs/squashfs/squashfs_fs_sb.h | 1 + + fs/squashfs/xz_wrapper.c | 22 ++++++++------ + fs/squashfs/zlib_wrapper.c | 24 +++++++++------ + 13 files changed, 163 insertions(+), 67 deletions(-) + create mode 100644 fs/squashfs/page_actor.h + +diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c +index 4dd4025..0cea9b9 100644 +--- a/fs/squashfs/block.c ++++ b/fs/squashfs/block.c +@@ -36,6 +36,7 @@ + #include "squashfs_fs_sb.h" + #include "squashfs.h" + #include "decompressor.h" ++#include "page_actor.h" + + /* + * Read the metadata block length, this is stored in the first two +@@ -86,16 +87,16 @@ static struct buffer_head *get_block_length(struct super_block *sb, + * generated a larger block - this does occasionally happen with compression + * algorithms). + */ +-int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, +- int length, u64 *next_index, int srclength, int pages) ++int squashfs_read_data(struct super_block *sb, u64 index, int length, ++ u64 *next_index, struct squashfs_page_actor *output) + { + struct squashfs_sb_info *msblk = sb->s_fs_info; + struct buffer_head **bh; + int offset = index & ((1 << msblk->devblksize_log2) - 1); + u64 cur_index = index >> msblk->devblksize_log2; +- int bytes, compressed, b = 0, k = 0, page = 0, avail, i; ++ int bytes, compressed, b = 0, k = 0, avail, i; + +- bh = kcalloc(((srclength + msblk->devblksize - 1) ++ bh = kcalloc(((output->length + msblk->devblksize - 1) + >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); + if (bh == NULL) + return -ENOMEM; +@@ -111,9 +112,9 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + *next_index = index + length; + + TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", +- index, compressed ? "" : "un", length, srclength); ++ index, compressed ? "" : "un", length, output->length); + +- if (length < 0 || length > srclength || ++ if (length < 0 || length > output->length || + (index + length) > msblk->bytes_used) + goto read_failure; + +@@ -145,7 +146,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + TRACE("Block @ 0x%llx, %scompressed size %d\n", index, + compressed ? "" : "un", length); + +- if (length < 0 || length > srclength || ++ if (length < 0 || length > output->length || + (index + length) > msblk->bytes_used) + goto block_release; + +@@ -165,8 +166,8 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + } + + if (compressed) { +- length = squashfs_decompress(msblk, buffer, bh, b, offset, +- length, srclength, pages); ++ length = squashfs_decompress(msblk, bh, b, offset, length, ++ output); + if (length < 0) + goto read_failure; + } else { +@@ -174,19 +175,20 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + * Block is uncompressed. + */ + int in, pg_offset = 0; ++ void *data = squashfs_first_page(output); + + for (bytes = length; k < b; k++) { + in = min(bytes, msblk->devblksize - offset); + bytes -= in; + while (in) { + if (pg_offset == PAGE_CACHE_SIZE) { +- page++; ++ data = squashfs_next_page(output); + pg_offset = 0; + } + avail = min_t(int, in, PAGE_CACHE_SIZE - + pg_offset); +- memcpy(buffer[page] + pg_offset, +- bh[k]->b_data + offset, avail); ++ memcpy(data + pg_offset, bh[k]->b_data + offset, ++ avail); + in -= avail; + pg_offset += avail; + offset += avail; +@@ -194,6 +196,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, + offset = 0; + put_bh(bh[k]); + } ++ squashfs_finish_page(output); + } + + kfree(bh); +diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c +index af0b738..1cb70a0 100644 +--- a/fs/squashfs/cache.c ++++ b/fs/squashfs/cache.c +@@ -56,6 +56,7 @@ + #include "squashfs_fs.h" + #include "squashfs_fs_sb.h" + #include "squashfs.h" ++#include "page_actor.h" + + /* + * Look-up block in cache, and increment usage count. If not in cache, read +@@ -119,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb, + entry->error = 0; + spin_unlock(&cache->lock); + +- entry->length = squashfs_read_data(sb, entry->data, +- block, length, &entry->next_index, +- cache->block_size, cache->pages); ++ entry->length = squashfs_read_data(sb, block, length, ++ &entry->next_index, entry->actor); + + spin_lock(&cache->lock); + +@@ -220,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache) + kfree(cache->entry[i].data[j]); + kfree(cache->entry[i].data); + } ++ kfree(cache->entry[i].actor); + } + + kfree(cache->entry); +@@ -280,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries, + goto cleanup; + } + } ++ ++ entry->actor = squashfs_page_actor_init(entry->data, ++ cache->pages, 0); ++ if (entry->actor == NULL) { ++ ERROR("Failed to allocate %s cache entry\n", name); ++ goto cleanup; ++ } + } + + return cache; +@@ -410,6 +418,7 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length) + int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + int i, res; + void *table, *buffer, **data; ++ struct squashfs_page_actor *actor; + + table = buffer = kmalloc(length, GFP_KERNEL); + if (table == NULL) +@@ -421,19 +430,28 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length) + goto failed; + } + ++ actor = squashfs_page_actor_init(data, pages, length); ++ if (actor == NULL) { ++ res = -ENOMEM; ++ goto failed2; ++ } ++ + for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) + data[i] = buffer; + +- res = squashfs_read_data(sb, data, block, length | +- SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages); ++ res = squashfs_read_data(sb, block, length | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor); + + kfree(data); ++ kfree(actor); + + if (res < 0) + goto failed; + + return table; + ++failed2: ++ kfree(data); + failed: + kfree(table); + return ERR_PTR(res); +diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c +index 234291f..ac22fe7 100644 +--- a/fs/squashfs/decompressor.c ++++ b/fs/squashfs/decompressor.c +@@ -30,6 +30,7 @@ + #include "squashfs_fs_sb.h" + #include "decompressor.h" + #include "squashfs.h" ++#include "page_actor.h" + + /* + * This file (and decompressor.h) implements a decompressor framework for +@@ -87,6 +88,7 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags) + { + struct squashfs_sb_info *msblk = sb->s_fs_info; + void *buffer = NULL, *comp_opts; ++ struct squashfs_page_actor *actor = NULL; + int length = 0; + + /* +@@ -99,9 +101,14 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags) + goto out; + } + +- length = squashfs_read_data(sb, &buffer, +- sizeof(struct squashfs_super_block), 0, NULL, +- PAGE_CACHE_SIZE, 1); ++ actor = squashfs_page_actor_init(&buffer, 1, 0); ++ if (actor == NULL) { ++ comp_opts = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ length = squashfs_read_data(sb, ++ sizeof(struct squashfs_super_block), 0, NULL, actor); + + if (length < 0) { + comp_opts = ERR_PTR(length); +@@ -112,6 +119,7 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags) + comp_opts = squashfs_comp_opts(msblk, buffer, length); + + out: ++ kfree(actor); + kfree(buffer); + return comp_opts; + } +diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h +index 6cdb20a..af09853 100644 +--- a/fs/squashfs/decompressor.h ++++ b/fs/squashfs/decompressor.h +@@ -27,8 +27,9 @@ struct squashfs_decompressor { + void *(*init)(struct squashfs_sb_info *, void *); + void *(*comp_opts)(struct squashfs_sb_info *, void *, int); + void (*free)(void *); +- int (*decompress)(struct squashfs_sb_info *, void *, void **, +- struct buffer_head **, int, int, int, int, int); ++ int (*decompress)(struct squashfs_sb_info *, void *, ++ struct buffer_head **, int, int, int, ++ struct squashfs_page_actor *); + int id; + char *name; + int supported; +diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c +index 462731d..ae54675 100644 +--- a/fs/squashfs/decompressor_multi.c ++++ b/fs/squashfs/decompressor_multi.c +@@ -183,15 +183,14 @@ wait: + } + + +-int squashfs_decompress(struct squashfs_sb_info *msblk, +- void **buffer, struct buffer_head **bh, int b, int offset, int length, +- int srclength, int pages) ++int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, ++ int b, int offset, int length, struct squashfs_page_actor *output) + { + int res; + struct squashfs_stream *stream = msblk->stream; + struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); + res = msblk->decompressor->decompress(msblk, decomp_stream->stream, +- buffer, bh, b, offset, length, srclength, pages); ++ bh, b, offset, length, output); + put_decomp_stream(decomp_stream, stream); + if (res < 0) + ERROR("%s decompression failed, data probably corrupt\n", +diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c +index 0e7b679..23a9c28 100644 +--- a/fs/squashfs/decompressor_multi_percpu.c ++++ b/fs/squashfs/decompressor_multi_percpu.c +@@ -74,15 +74,14 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) + } + } + +-int squashfs_decompress(struct squashfs_sb_info *msblk, +- void **buffer, struct buffer_head **bh, int b, int offset, int length, +- int srclength, int pages) ++int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, ++ int b, int offset, int length, struct squashfs_page_actor *output) + { + struct squashfs_stream __percpu *percpu = + (struct squashfs_stream __percpu *) msblk->stream; + struct squashfs_stream *stream = get_cpu_ptr(percpu); +- int res = msblk->decompressor->decompress(msblk, stream->stream, buffer, +- bh, b, offset, length, srclength, pages); ++ int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, ++ offset, length, output); + put_cpu_ptr(stream); + + if (res < 0) +diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c +index f857cf6..a6c7592 100644 +--- a/fs/squashfs/decompressor_single.c ++++ b/fs/squashfs/decompressor_single.c +@@ -61,16 +61,15 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) + } + } + +-int squashfs_decompress(struct squashfs_sb_info *msblk, +- void **buffer, struct buffer_head **bh, int b, int offset, int length, +- int srclength, int pages) ++int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, ++ int b, int offset, int length, struct squashfs_page_actor *output) + { + int res; + struct squashfs_stream *stream = msblk->stream; + + mutex_lock(&stream->mutex); +- res = msblk->decompressor->decompress(msblk, stream->stream, buffer, +- bh, b, offset, length, srclength, pages); ++ res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, ++ offset, length, output); + mutex_unlock(&stream->mutex); + + if (res < 0) +diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c +index 75c3b57..244b9fb 100644 +--- a/fs/squashfs/lzo_wrapper.c ++++ b/fs/squashfs/lzo_wrapper.c +@@ -31,6 +31,7 @@ + #include "squashfs_fs_sb.h" + #include "squashfs.h" + #include "decompressor.h" ++#include "page_actor.h" + + struct squashfs_lzo { + void *input; +@@ -75,13 +76,13 @@ static void lzo_free(void *strm) + + + static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, +- void **buffer, struct buffer_head **bh, int b, int offset, int length, +- int srclength, int pages) ++ struct buffer_head **bh, int b, int offset, int length, ++ struct squashfs_page_actor *output) + { + struct squashfs_lzo *stream = strm; +- void *buff = stream->input; ++ void *buff = stream->input, *data; + int avail, i, bytes = length, res; +- size_t out_len = srclength; ++ size_t out_len = output->length; + + for (i = 0; i < b; i++) { + avail = min(bytes, msblk->devblksize - offset); +@@ -98,12 +99,20 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, + goto failed; + + res = bytes = (int)out_len; +- for (i = 0, buff = stream->output; bytes && i < pages; i++) { +- avail = min_t(int, bytes, PAGE_CACHE_SIZE); +- memcpy(buffer[i], buff, avail); +- buff += avail; +- bytes -= avail; ++ data = squashfs_first_page(output); ++ buff = stream->output; ++ while (data) { ++ if (bytes <= PAGE_CACHE_SIZE) { ++ memcpy(data, buff, bytes); ++ break; ++ } else { ++ memcpy(data, buff, PAGE_CACHE_SIZE); ++ buff += PAGE_CACHE_SIZE; ++ bytes -= PAGE_CACHE_SIZE; ++ data = squashfs_next_page(output); ++ } + } ++ squashfs_finish_page(output); + + return res; + +diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h +new file mode 100644 +index 0000000..5b0ba5a +--- /dev/null ++++ b/fs/squashfs/page_actor.h +@@ -0,0 +1,49 @@ ++#ifndef PAGE_ACTOR_H ++#define PAGE_ACTOR_H ++/* ++ * Copyright (c) 2013 ++ * Phillip Lougher ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++struct squashfs_page_actor { ++ void **page; ++ int pages; ++ int length; ++ int next_page; ++}; ++ ++static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page, ++ int pages, int length) ++{ ++ struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); ++ ++ if (actor == NULL) ++ return NULL; ++ ++ actor->length = length ? : pages * PAGE_CACHE_SIZE; ++ actor->page = page; ++ actor->pages = pages; ++ actor->next_page = 0; ++ return actor; ++} ++ ++static inline void *squashfs_first_page(struct squashfs_page_actor *actor) ++{ ++ actor->next_page = 1; ++ return actor->page[0]; ++} ++ ++static inline void *squashfs_next_page(struct squashfs_page_actor *actor) ++{ ++ return actor->next_page == actor->pages ? NULL : ++ actor->page[actor->next_page++]; ++} ++ ++static inline void squashfs_finish_page(struct squashfs_page_actor *actor) ++{ ++ /* empty */ ++} ++#endif +diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h +index 2e2751d..6a97e63 100644 +--- a/fs/squashfs/squashfs.h ++++ b/fs/squashfs/squashfs.h +@@ -28,8 +28,8 @@ + #define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) + + /* block.c */ +-extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *, +- int, int); ++extern int squashfs_read_data(struct super_block *, u64, int, u64 *, ++ struct squashfs_page_actor *); + + /* cache.c */ + extern struct squashfs_cache *squashfs_cache_init(char *, int, int); +@@ -53,8 +53,8 @@ extern void *squashfs_decompressor_setup(struct super_block *, unsigned short); + /* decompressor_xxx.c */ + extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *); + extern void squashfs_decompressor_destroy(struct squashfs_sb_info *); +-extern int squashfs_decompress(struct squashfs_sb_info *, void **, +- struct buffer_head **, int, int, int, int, int); ++extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **, ++ int, int, int, struct squashfs_page_actor *); + extern int squashfs_max_decompressors(void); + + /* export.c */ +diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h +index 9cdcf41..1da565c 100644 +--- a/fs/squashfs/squashfs_fs_sb.h ++++ b/fs/squashfs/squashfs_fs_sb.h +@@ -50,6 +50,7 @@ struct squashfs_cache_entry { + wait_queue_head_t wait_queue; + struct squashfs_cache *cache; + void **data; ++ struct squashfs_page_actor *actor; + }; + + struct squashfs_sb_info { +diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c +index 5d1d07c..c609624 100644 +--- a/fs/squashfs/xz_wrapper.c ++++ b/fs/squashfs/xz_wrapper.c +@@ -32,6 +32,7 @@ + #include "squashfs_fs_sb.h" + #include "squashfs.h" + #include "decompressor.h" ++#include "page_actor.h" + + struct squashfs_xz { + struct xz_dec *state; +@@ -129,11 +130,11 @@ static void squashfs_xz_free(void *strm) + + + static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, +- void **buffer, struct buffer_head **bh, int b, int offset, int length, +- int srclength, int pages) ++ struct buffer_head **bh, int b, int offset, int length, ++ struct squashfs_page_actor *output) + { + enum xz_ret xz_err; +- int avail, total = 0, k = 0, page = 0; ++ int avail, total = 0, k = 0; + struct squashfs_xz *stream = strm; + + xz_dec_reset(stream->state); +@@ -141,7 +142,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, + stream->buf.in_size = 0; + stream->buf.out_pos = 0; + stream->buf.out_size = PAGE_CACHE_SIZE; +- stream->buf.out = buffer[page++]; ++ stream->buf.out = squashfs_first_page(output); + + do { + if (stream->buf.in_pos == stream->buf.in_size && k < b) { +@@ -153,11 +154,12 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, + offset = 0; + } + +- if (stream->buf.out_pos == stream->buf.out_size +- && page < pages) { +- stream->buf.out = buffer[page++]; +- stream->buf.out_pos = 0; +- total += PAGE_CACHE_SIZE; ++ if (stream->buf.out_pos == stream->buf.out_size) { ++ stream->buf.out = squashfs_next_page(output); ++ if (stream->buf.out != NULL) { ++ stream->buf.out_pos = 0; ++ total += PAGE_CACHE_SIZE; ++ } + } + + xz_err = xz_dec_run(stream->state, &stream->buf); +@@ -166,6 +168,8 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, + put_bh(bh[k++]); + } while (xz_err == XZ_OK); + ++ squashfs_finish_page(output); ++ + if (xz_err != XZ_STREAM_END || k < b) + goto out; + +diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c +index bb04902..8727cab 100644 +--- a/fs/squashfs/zlib_wrapper.c ++++ b/fs/squashfs/zlib_wrapper.c +@@ -32,6 +32,7 @@ + #include "squashfs_fs_sb.h" + #include "squashfs.h" + #include "decompressor.h" ++#include "page_actor.h" + + static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) + { +@@ -62,14 +63,14 @@ static void zlib_free(void *strm) + + + static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, +- void **buffer, struct buffer_head **bh, int b, int offset, int length, +- int srclength, int pages) ++ struct buffer_head **bh, int b, int offset, int length, ++ struct squashfs_page_actor *output) + { +- int zlib_err, zlib_init = 0; +- int k = 0, page = 0; ++ int zlib_err, zlib_init = 0, k = 0; + z_stream *stream = strm; + +- stream->avail_out = 0; ++ stream->avail_out = PAGE_CACHE_SIZE; ++ stream->next_out = squashfs_first_page(output); + stream->avail_in = 0; + + do { +@@ -81,15 +82,18 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, + offset = 0; + } + +- if (stream->avail_out == 0 && page < pages) { +- stream->next_out = buffer[page++]; +- stream->avail_out = PAGE_CACHE_SIZE; ++ if (stream->avail_out == 0) { ++ stream->next_out = squashfs_next_page(output); ++ if (stream->next_out != NULL) ++ stream->avail_out = PAGE_CACHE_SIZE; + } + + if (!zlib_init) { + zlib_err = zlib_inflateInit(stream); +- if (zlib_err != Z_OK) ++ if (zlib_err != Z_OK) { ++ squashfs_finish_page(output); + goto out; ++ } + zlib_init = 1; + } + +@@ -99,6 +103,8 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, + put_bh(bh[k++]); + } while (zlib_err == Z_OK); + ++ squashfs_finish_page(output); ++ + if (zlib_err != Z_STREAM_END) + goto out; + +-- +1.7.10.4 + diff --git a/packages/linux/patches/3.12.1/linux-994.0005-Squashfs-Restructure-squashfs_readpage.patch b/packages/linux/patches/3.12.1/linux-994.0005-Squashfs-Restructure-squashfs_readpage.patch new file mode 100644 index 0000000000..08435a10b9 --- /dev/null +++ b/packages/linux/patches/3.12.1/linux-994.0005-Squashfs-Restructure-squashfs_readpage.patch @@ -0,0 +1,275 @@ +From 5f55dbc0c5c466a9cdfa4da7ac1bfe351c7fc52a Mon Sep 17 00:00:00 2001 +From: Phillip Lougher +Date: Thu, 31 Oct 2013 19:24:27 +0000 +Subject: [PATCH 5/7] Squashfs: Restructure squashfs_readpage() + +Restructure squashfs_readpage() splitting it into separate +functions for datablocks, fragments and sparse blocks. + +Move the memcpying (from squashfs cache entry) implementation of +squashfs_readpage_block into file_cache.c + +This allows different implementations to be supported. + +Signed-off-by: Phillip Lougher +Reviewed-by: Minchan Kim +--- + fs/squashfs/Makefile | 2 +- + fs/squashfs/file.c | 142 +++++++++++++++++++++++----------------------- + fs/squashfs/file_cache.c | 38 +++++++++++++ + fs/squashfs/squashfs.h | 7 +++ + 4 files changed, 118 insertions(+), 71 deletions(-) + create mode 100644 fs/squashfs/file_cache.c + +diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile +index 5833b96..e01ba11 100644 +--- a/fs/squashfs/Makefile ++++ b/fs/squashfs/Makefile +@@ -4,7 +4,7 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o +-squashfs-y += namei.o super.o symlink.o decompressor.o ++squashfs-y += namei.o super.o symlink.o decompressor.o file_cache.o + squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o + squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o + squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o +diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c +index 8ca62c2..e5c9689 100644 +--- a/fs/squashfs/file.c ++++ b/fs/squashfs/file.c +@@ -370,77 +370,15 @@ static int read_blocklist(struct inode *inode, int index, u64 *block) + return le32_to_cpu(size); + } + +- +-static int squashfs_readpage(struct file *file, struct page *page) ++/* Copy data into page cache */ ++void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer, ++ int bytes, int offset) + { + struct inode *inode = page->mapping->host; + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; +- int bytes, i, offset = 0, sparse = 0; +- struct squashfs_cache_entry *buffer = NULL; + void *pageaddr; +- +- int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; +- int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); +- int start_index = page->index & ~mask; +- int end_index = start_index | mask; +- int file_end = i_size_read(inode) >> msblk->block_log; +- +- TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", +- page->index, squashfs_i(inode)->start); +- +- if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> +- PAGE_CACHE_SHIFT)) +- goto out; +- +- if (index < file_end || squashfs_i(inode)->fragment_block == +- SQUASHFS_INVALID_BLK) { +- /* +- * Reading a datablock from disk. Need to read block list +- * to get location and block size. +- */ +- u64 block = 0; +- int bsize = read_blocklist(inode, index, &block); +- if (bsize < 0) +- goto error_out; +- +- if (bsize == 0) { /* hole */ +- bytes = index == file_end ? +- (i_size_read(inode) & (msblk->block_size - 1)) : +- msblk->block_size; +- sparse = 1; +- } else { +- /* +- * Read and decompress datablock. +- */ +- buffer = squashfs_get_datablock(inode->i_sb, +- block, bsize); +- if (buffer->error) { +- ERROR("Unable to read page, block %llx, size %x" +- "\n", block, bsize); +- squashfs_cache_put(buffer); +- goto error_out; +- } +- bytes = buffer->length; +- } +- } else { +- /* +- * Datablock is stored inside a fragment (tail-end packed +- * block). +- */ +- buffer = squashfs_get_fragment(inode->i_sb, +- squashfs_i(inode)->fragment_block, +- squashfs_i(inode)->fragment_size); +- +- if (buffer->error) { +- ERROR("Unable to read page, block %llx, size %x\n", +- squashfs_i(inode)->fragment_block, +- squashfs_i(inode)->fragment_size); +- squashfs_cache_put(buffer); +- goto error_out; +- } +- bytes = i_size_read(inode) & (msblk->block_size - 1); +- offset = squashfs_i(inode)->fragment_offset; +- } ++ int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; ++ int start_index = page->index & ~mask, end_index = start_index | mask; + + /* + * Loop copying datablock into pages. As the datablock likely covers +@@ -451,7 +389,7 @@ static int squashfs_readpage(struct file *file, struct page *page) + for (i = start_index; i <= end_index && bytes > 0; i++, + bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { + struct page *push_page; +- int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE); ++ int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0; + + TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); + +@@ -475,11 +413,75 @@ skip_page: + if (i != page->index) + page_cache_release(push_page); + } ++} ++ ++/* Read datablock stored packed inside a fragment (tail-end packed block) */ ++static int squashfs_readpage_fragment(struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, ++ squashfs_i(inode)->fragment_block, ++ squashfs_i(inode)->fragment_size); ++ int res = buffer->error; ++ ++ if (res) ++ ERROR("Unable to read page, block %llx, size %x\n", ++ squashfs_i(inode)->fragment_block, ++ squashfs_i(inode)->fragment_size); ++ else ++ squashfs_copy_cache(page, buffer, i_size_read(inode) & ++ (msblk->block_size - 1), ++ squashfs_i(inode)->fragment_offset); ++ ++ squashfs_cache_put(buffer); ++ return res; ++} + +- if (!sparse) +- squashfs_cache_put(buffer); ++static int squashfs_readpage_sparse(struct page *page, int index, int file_end) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ int bytes = index == file_end ? ++ (i_size_read(inode) & (msblk->block_size - 1)) : ++ msblk->block_size; + ++ squashfs_copy_cache(page, NULL, bytes, 0); + return 0; ++} ++ ++static int squashfs_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); ++ int file_end = i_size_read(inode) >> msblk->block_log; ++ int res; ++ void *pageaddr; ++ ++ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", ++ page->index, squashfs_i(inode)->start); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) ++ goto out; ++ ++ if (index < file_end || squashfs_i(inode)->fragment_block == ++ SQUASHFS_INVALID_BLK) { ++ u64 block = 0; ++ int bsize = read_blocklist(inode, index, &block); ++ if (bsize < 0) ++ goto error_out; ++ ++ if (bsize == 0) ++ res = squashfs_readpage_sparse(page, index, file_end); ++ else ++ res = squashfs_readpage_block(page, block, bsize); ++ } else ++ res = squashfs_readpage_fragment(page); ++ ++ if (!res) ++ return 0; + + error_out: + SetPageError(page); +diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c +new file mode 100644 +index 0000000..f2310d2 +--- /dev/null ++++ b/fs/squashfs/file_cache.c +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) 2013 ++ * Phillip Lougher ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs_fs.h" ++#include "squashfs_fs_sb.h" ++#include "squashfs_fs_i.h" ++#include "squashfs.h" ++ ++/* Read separately compressed datablock and memcopy into page cache */ ++int squashfs_readpage_block(struct page *page, u64 block, int bsize) ++{ ++ struct inode *i = page->mapping->host; ++ struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, ++ block, bsize); ++ int res = buffer->error; ++ ++ if (res) ++ ERROR("Unable to read page, block %llx, size %x\n", block, ++ bsize); ++ else ++ squashfs_copy_cache(page, buffer, buffer->length, 0); ++ ++ squashfs_cache_put(buffer); ++ return res; ++} +diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h +index 6a97e63..9e1bb79 100644 +--- a/fs/squashfs/squashfs.h ++++ b/fs/squashfs/squashfs.h +@@ -66,6 +66,13 @@ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *); + extern __le64 *squashfs_read_fragment_index_table(struct super_block *, + u64, u64, unsigned int); + ++/* file.c */ ++void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int, ++ int); ++ ++/* file_xxx.c */ ++extern int squashfs_readpage_block(struct page *, u64, int); ++ + /* id.c */ + extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); + extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64, +-- +1.7.10.4 + diff --git a/packages/linux/patches/3.12.1/linux-994.0006-Squashfs-Directly-decompress-into-the-page-cache-for.patch b/packages/linux/patches/3.12.1/linux-994.0006-Squashfs-Directly-decompress-into-the-page-cache-for.patch new file mode 100644 index 0000000000..c5fa0ebf01 --- /dev/null +++ b/packages/linux/patches/3.12.1/linux-994.0006-Squashfs-Directly-decompress-into-the-page-cache-for.patch @@ -0,0 +1,442 @@ +From 0d455c12c6428647547bacccaaced3cae0f35570 Mon Sep 17 00:00:00 2001 +From: Phillip Lougher +Date: Wed, 13 Nov 2013 02:04:19 +0000 +Subject: [PATCH 6/7] Squashfs: Directly decompress into the page cache for + file data + +This introduces an implementation of squashfs_readpage_block() +that directly decompresses into the page cache. + +This uses the previously added page handler abstraction to push +down the necessary kmap_atomic/kunmap_atomic operations on the +page cache buffers into the decompressors. This enables +direct copying into the page cache without using the slow +kmap/kunmap calls. + +The code detects when multiple threads are racing in +squashfs_readpage() to decompress the same block, and avoids +this regression by falling back to using an intermediate +buffer. + +This patch enhances the performance of Squashfs significantly +when multiple processes are accessing the filesystem simultaneously +because it not only reduces memcopying, but it more importantly +eliminates the lock contention on the intermediate buffer. + +Using single-thread decompression. + + dd if=file1 of=/dev/null bs=4096 & + dd if=file2 of=/dev/null bs=4096 & + dd if=file3 of=/dev/null bs=4096 & + dd if=file4 of=/dev/null bs=4096 + +Before: + +629145600 bytes (629 MB) copied, 45.8046 s, 13.7 MB/s + +After: + +629145600 bytes (629 MB) copied, 9.29414 s, 67.7 MB/s + +Signed-off-by: Phillip Lougher +Reviewed-by: Minchan Kim +--- + fs/squashfs/Kconfig | 28 ++++++++ + fs/squashfs/Makefile | 4 +- + fs/squashfs/file_direct.c | 173 +++++++++++++++++++++++++++++++++++++++++++++ + fs/squashfs/page_actor.c | 100 ++++++++++++++++++++++++++ + fs/squashfs/page_actor.h | 32 +++++++++ + 5 files changed, 336 insertions(+), 1 deletion(-) + create mode 100644 fs/squashfs/file_direct.c + create mode 100644 fs/squashfs/page_actor.c + +diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig +index 159bd66..b6fa865 100644 +--- a/fs/squashfs/Kconfig ++++ b/fs/squashfs/Kconfig +@@ -26,6 +26,34 @@ config SQUASHFS + If unsure, say N. + + choice ++ prompt "File decompression options" ++ depends on SQUASHFS ++ help ++ Squashfs now supports two options for decompressing file ++ data. Traditionally Squashfs has decompressed into an ++ intermediate buffer and then memcopied it into the page cache. ++ Squashfs now supports the ability to decompress directly into ++ the page cache. ++ ++ If unsure, select "Decompress file data into an intermediate buffer" ++ ++config SQUASHFS_FILE_CACHE ++ bool "Decompress file data into an intermediate buffer" ++ help ++ Decompress file data into an intermediate buffer and then ++ memcopy it into the page cache. ++ ++config SQUASHFS_FILE_DIRECT ++ bool "Decompress files directly into the page cache" ++ help ++ Directly decompress file data into the page cache. ++ Doing so can significantly improve performance because ++ it eliminates a memcpy and it also removes the lock contention ++ on the single buffer. ++ ++endchoice ++ ++choice + prompt "Decompressor parallelisation options" + depends on SQUASHFS + help +diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile +index e01ba11..4132520 100644 +--- a/fs/squashfs/Makefile ++++ b/fs/squashfs/Makefile +@@ -4,7 +4,9 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o +-squashfs-y += namei.o super.o symlink.o decompressor.o file_cache.o ++squashfs-y += namei.o super.o symlink.o decompressor.o ++squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o ++squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o + squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o + squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o + squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o +diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c +new file mode 100644 +index 0000000..2943b2b +--- /dev/null ++++ b/fs/squashfs/file_direct.c +@@ -0,0 +1,173 @@ ++/* ++ * Copyright (c) 2013 ++ * Phillip Lougher ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs_fs.h" ++#include "squashfs_fs_sb.h" ++#include "squashfs_fs_i.h" ++#include "squashfs.h" ++#include "page_actor.h" ++ ++static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, ++ int pages, struct page **page); ++ ++/* Read separately compressed datablock directly into page cache */ ++int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) ++ ++{ ++ struct inode *inode = target_page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ ++ int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; ++ int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; ++ int start_index = target_page->index & ~mask; ++ int end_index = start_index | mask; ++ int i, n, pages, missing_pages, bytes, res = -ENOMEM; ++ struct page **page; ++ struct squashfs_page_actor *actor; ++ void *pageaddr; ++ ++ if (end_index > file_end) ++ end_index = file_end; ++ ++ pages = end_index - start_index + 1; ++ ++ page = kmalloc(sizeof(void *) * pages, GFP_KERNEL); ++ if (page == NULL) ++ return res; ++ ++ /* ++ * Create a "page actor" which will kmap and kunmap the ++ * page cache pages appropriately within the decompressor ++ */ ++ actor = squashfs_page_actor_init_special(page, pages, 0); ++ if (actor == NULL) ++ goto out; ++ ++ /* Try to grab all the pages covered by the Squashfs block */ ++ for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) { ++ page[i] = (n == target_page->index) ? target_page : ++ grab_cache_page_nowait(target_page->mapping, n); ++ ++ if (page[i] == NULL) { ++ missing_pages++; ++ continue; ++ } ++ ++ if (PageUptodate(page[i])) { ++ unlock_page(page[i]); ++ page_cache_release(page[i]); ++ page[i] = NULL; ++ missing_pages++; ++ } ++ } ++ ++ if (missing_pages) { ++ /* ++ * Couldn't get one or more pages, this page has either ++ * been VM reclaimed, but others are still in the page cache ++ * and uptodate, or we're racing with another thread in ++ * squashfs_readpage also trying to grab them. Fall back to ++ * using an intermediate buffer. ++ */ ++ res = squashfs_read_cache(target_page, block, bsize, pages, ++ page); ++ goto out; ++ } ++ ++ /* Decompress directly into the page cache buffers */ ++ res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); ++ if (res < 0) ++ goto mark_errored; ++ ++ /* Last page may have trailing bytes not filled */ ++ bytes = res % PAGE_CACHE_SIZE; ++ if (bytes) { ++ pageaddr = kmap_atomic(page[pages - 1]); ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap_atomic(pageaddr); ++ } ++ ++ /* Mark pages as uptodate, unlock and release */ ++ for (i = 0; i < pages; i++) { ++ flush_dcache_page(page[i]); ++ SetPageUptodate(page[i]); ++ unlock_page(page[i]); ++ if (page[i] != target_page) ++ page_cache_release(page[i]); ++ } ++ ++ kfree(actor); ++ kfree(page); ++ ++ return 0; ++ ++mark_errored: ++ /* Decompression failed, mark pages as errored. Target_page is ++ * dealt with by the caller ++ */ ++ for (i = 0; i < pages; i++) { ++ if (page[i] == target_page) ++ continue; ++ flush_dcache_page(page[i]); ++ SetPageError(page[i]); ++ unlock_page(page[i]); ++ page_cache_release(page[i]); ++ } ++ ++out: ++ kfree(actor); ++ kfree(page); ++ return res; ++} ++ ++ ++static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, ++ int pages, struct page **page) ++{ ++ struct inode *i = target_page->mapping->host; ++ struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, ++ block, bsize); ++ int bytes = buffer->length, res = buffer->error, n, offset = 0; ++ void *pageaddr; ++ ++ if (res) { ++ ERROR("Unable to read page, block %llx, size %x\n", block, ++ bsize); ++ goto out; ++ } ++ ++ for (n = 0; n < pages && bytes > 0; n++, ++ bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { ++ int avail = min_t(int, bytes, PAGE_CACHE_SIZE); ++ ++ if (page[n] == NULL) ++ continue; ++ ++ pageaddr = kmap_atomic(page[n]); ++ squashfs_copy_data(pageaddr, buffer, offset, avail); ++ memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); ++ kunmap_atomic(pageaddr); ++ flush_dcache_page(page[n]); ++ SetPageUptodate(page[n]); ++ unlock_page(page[n]); ++ if (page[n] != target_page) ++ page_cache_release(page[n]); ++ } ++ ++out: ++ squashfs_cache_put(buffer); ++ return res; ++} +diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c +new file mode 100644 +index 0000000..5a1c11f +--- /dev/null ++++ b/fs/squashfs/page_actor.c +@@ -0,0 +1,100 @@ ++/* ++ * Copyright (c) 2013 ++ * Phillip Lougher ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include ++#include ++#include ++#include "page_actor.h" ++ ++/* ++ * This file contains implementations of page_actor for decompressing into ++ * an intermediate buffer, and for decompressing directly into the ++ * page cache. ++ * ++ * Calling code should avoid sleeping between calls to squashfs_first_page() ++ * and squashfs_finish_page(). ++ */ ++ ++/* Implementation of page_actor for decompressing into intermediate buffer */ ++static void *cache_first_page(struct squashfs_page_actor *actor) ++{ ++ actor->next_page = 1; ++ return actor->buffer[0]; ++} ++ ++static void *cache_next_page(struct squashfs_page_actor *actor) ++{ ++ if (actor->next_page == actor->pages) ++ return NULL; ++ ++ return actor->buffer[actor->next_page++]; ++} ++ ++static void cache_finish_page(struct squashfs_page_actor *actor) ++{ ++ /* empty */ ++} ++ ++struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, ++ int pages, int length) ++{ ++ struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); ++ ++ if (actor == NULL) ++ return NULL; ++ ++ actor->length = length ? : pages * PAGE_CACHE_SIZE; ++ actor->buffer = buffer; ++ actor->pages = pages; ++ actor->next_page = 0; ++ actor->squashfs_first_page = cache_first_page; ++ actor->squashfs_next_page = cache_next_page; ++ actor->squashfs_finish_page = cache_finish_page; ++ return actor; ++} ++ ++/* Implementation of page_actor for decompressing directly into page cache. */ ++static void *direct_first_page(struct squashfs_page_actor *actor) ++{ ++ actor->next_page = 1; ++ return actor->pageaddr = kmap_atomic(actor->page[0]); ++} ++ ++static void *direct_next_page(struct squashfs_page_actor *actor) ++{ ++ if (actor->pageaddr) ++ kunmap_atomic(actor->pageaddr); ++ ++ return actor->pageaddr = actor->next_page == actor->pages ? NULL : ++ kmap_atomic(actor->page[actor->next_page++]); ++} ++ ++static void direct_finish_page(struct squashfs_page_actor *actor) ++{ ++ if (actor->pageaddr) ++ kunmap_atomic(actor->pageaddr); ++} ++ ++struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page, ++ int pages, int length) ++{ ++ struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); ++ ++ if (actor == NULL) ++ return NULL; ++ ++ actor->length = length ? : pages * PAGE_CACHE_SIZE; ++ actor->page = page; ++ actor->pages = pages; ++ actor->next_page = 0; ++ actor->pageaddr = NULL; ++ actor->squashfs_first_page = direct_first_page; ++ actor->squashfs_next_page = direct_next_page; ++ actor->squashfs_finish_page = direct_finish_page; ++ return actor; ++} +diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h +index 5b0ba5a..26dd820 100644 +--- a/fs/squashfs/page_actor.h ++++ b/fs/squashfs/page_actor.h +@@ -8,6 +8,7 @@ + * the COPYING file in the top-level directory. + */ + ++#ifndef CONFIG_SQUASHFS_FILE_DIRECT + struct squashfs_page_actor { + void **page; + int pages; +@@ -46,4 +47,35 @@ static inline void squashfs_finish_page(struct squashfs_page_actor *actor) + { + /* empty */ + } ++#else ++struct squashfs_page_actor { ++ union { ++ void **buffer; ++ struct page **page; ++ }; ++ void *pageaddr; ++ void *(*squashfs_first_page)(struct squashfs_page_actor *); ++ void *(*squashfs_next_page)(struct squashfs_page_actor *); ++ void (*squashfs_finish_page)(struct squashfs_page_actor *); ++ int pages; ++ int length; ++ int next_page; ++}; ++ ++extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int); ++extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page ++ **, int, int); ++static inline void *squashfs_first_page(struct squashfs_page_actor *actor) ++{ ++ return actor->squashfs_first_page(actor); ++} ++static inline void *squashfs_next_page(struct squashfs_page_actor *actor) ++{ ++ return actor->squashfs_next_page(actor); ++} ++static inline void squashfs_finish_page(struct squashfs_page_actor *actor) ++{ ++ actor->squashfs_finish_page(actor); ++} ++#endif + #endif +-- +1.7.10.4 + diff --git a/packages/linux/patches/3.12.1/linux-994.0007-Squashfs-Check-stream-is-not-NULL-in-decompressor_mu.patch b/packages/linux/patches/3.12.1/linux-994.0007-Squashfs-Check-stream-is-not-NULL-in-decompressor_mu.patch new file mode 100644 index 0000000000..49d8c1edb7 --- /dev/null +++ b/packages/linux/patches/3.12.1/linux-994.0007-Squashfs-Check-stream-is-not-NULL-in-decompressor_mu.patch @@ -0,0 +1,38 @@ +From ed4f381ec15e5f11724cdbc68cffd2c22d1eaebd Mon Sep 17 00:00:00 2001 +From: Phillip Lougher +Date: Sun, 10 Nov 2013 00:02:29 +0000 +Subject: [PATCH 7/7] Squashfs: Check stream is not NULL in + decompressor_multi.c + +Fix static checker complaint that stream is not checked in +squashfs_decompressor_destroy(). + +Reported-by: Dan Carpenter +Signed-off-by: Phillip Lougher +Reviewed-by: Minchan Kim +--- + fs/squashfs/decompressor_multi.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c +index ae54675..d6008a6 100644 +--- a/fs/squashfs/decompressor_multi.c ++++ b/fs/squashfs/decompressor_multi.c +@@ -119,11 +119,10 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) + kfree(decomp_strm); + stream->avail_decomp--; + } ++ WARN_ON(stream->avail_decomp); ++ kfree(stream->comp_opts); ++ kfree(stream); + } +- +- WARN_ON(stream->avail_decomp); +- kfree(stream->comp_opts); +- kfree(stream); + } + + +-- +1.7.10.4 + diff --git a/projects/ARCTIC_MC/linux/linux.x86_64.conf b/projects/ARCTIC_MC/linux/linux.x86_64.conf index f61c8ea7c7..606e092c8f 100644 --- a/projects/ARCTIC_MC/linux/linux.x86_64.conf +++ b/projects/ARCTIC_MC/linux/linux.x86_64.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 3.12.0 Kernel Configuration +# Linux/x86_64 3.12.1 Kernel Configuration # CONFIG_64BIT=y CONFIG_X86_64=y @@ -3579,6 +3579,11 @@ CONFIG_HFSPLUS_FS=m # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y # CONFIG_SQUASHFS_XATTR is not set CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y diff --git a/projects/ATV/linux/linux.i386.conf b/projects/ATV/linux/linux.i386.conf index 152116633f..77e148e412 100644 --- a/projects/ATV/linux/linux.i386.conf +++ b/projects/ATV/linux/linux.i386.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/i386 3.12.0 Kernel Configuration +# Linux/i386 3.12.1 Kernel Configuration # # CONFIG_64BIT is not set CONFIG_X86_32=y @@ -3052,6 +3052,11 @@ CONFIG_HFSPLUS_FS=y # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y # CONFIG_SQUASHFS_XATTR is not set CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y diff --git a/projects/Generic/linux/linux.i386.conf b/projects/Generic/linux/linux.i386.conf index 21aa8918f5..f724c2a380 100644 --- a/projects/Generic/linux/linux.i386.conf +++ b/projects/Generic/linux/linux.i386.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/i386 3.12.0 Kernel Configuration +# Linux/i386 3.12.1 Kernel Configuration # # CONFIG_64BIT is not set CONFIG_X86_32=y @@ -3611,6 +3611,11 @@ CONFIG_HFSPLUS_FS=m # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y # CONFIG_SQUASHFS_XATTR is not set CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y diff --git a/projects/Generic/linux/linux.x86_64.conf b/projects/Generic/linux/linux.x86_64.conf index b830f25441..53a0800645 100644 --- a/projects/Generic/linux/linux.x86_64.conf +++ b/projects/Generic/linux/linux.x86_64.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 3.12.0 Kernel Configuration +# Linux/x86_64 3.12.1 Kernel Configuration # CONFIG_64BIT=y CONFIG_X86_64=y @@ -3578,6 +3578,11 @@ CONFIG_HFSPLUS_FS=m # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y # CONFIG_SQUASHFS_XATTR is not set CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y diff --git a/projects/RPi/linux/linux.arm.conf b/projects/RPi/linux/linux.arm.conf index d49662b10c..73facbc59d 100644 --- a/projects/RPi/linux/linux.arm.conf +++ b/projects/RPi/linux/linux.arm.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 3.12.0 Kernel Configuration +# Linux/arm 3.12.1 Kernel Configuration # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -2542,6 +2542,11 @@ CONFIG_HFSPLUS_FS=y # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y # CONFIG_SQUASHFS_XATTR is not set CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y diff --git a/projects/Virtual/linux/linux.i386.conf b/projects/Virtual/linux/linux.i386.conf index 8131390a11..c3a961e092 100644 --- a/projects/Virtual/linux/linux.i386.conf +++ b/projects/Virtual/linux/linux.i386.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/i386 3.12.0 Kernel Configuration +# Linux/i386 3.12.1 Kernel Configuration # # CONFIG_64BIT is not set CONFIG_X86_32=y @@ -3611,6 +3611,11 @@ CONFIG_HFSPLUS_FS=m # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y # CONFIG_SQUASHFS_XATTR is not set CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y diff --git a/projects/Virtual/linux/linux.x86_64.conf b/projects/Virtual/linux/linux.x86_64.conf index e451dbb56d..b2621bb122 100644 --- a/projects/Virtual/linux/linux.x86_64.conf +++ b/projects/Virtual/linux/linux.x86_64.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 3.12.0 Kernel Configuration +# Linux/x86_64 3.12.1 Kernel Configuration # CONFIG_64BIT=y CONFIG_X86_64=y @@ -3578,6 +3578,11 @@ CONFIG_HFSPLUS_FS=m # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y # CONFIG_SQUASHFS_XATTR is not set CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y