summaryrefslogtreecommitdiff
path: root/fs/squashfs
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
committerScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
commit62b8c978ee6b8d135d9e7953221de58000dba986 (patch)
tree683b04b2e627f6710c22c151b23c8cc9a165315e /fs/squashfs
parent78fd82238d0e5716578c326404184a27ba67fd6e (diff)
downloadlinux-fsl-qoriq-62b8c978ee6b8d135d9e7953221de58000dba986.tar.xz
Rewind v3.13-rc3+ (78fd82238d0e5716) to v3.12
Diffstat (limited to 'fs/squashfs')
-rw-r--r--fs/squashfs/Kconfig72
-rw-r--r--fs/squashfs/Makefile5
-rw-r--r--fs/squashfs/block.c36
-rw-r--r--fs/squashfs/cache.c28
-rw-r--r--fs/squashfs/decompressor.c59
-rw-r--r--fs/squashfs/decompressor.h24
-rw-r--r--fs/squashfs/decompressor_multi.c198
-rw-r--r--fs/squashfs/decompressor_multi_percpu.c97
-rw-r--r--fs/squashfs/decompressor_single.c85
-rw-r--r--fs/squashfs/file.c142
-rw-r--r--fs/squashfs/file_cache.c38
-rw-r--r--fs/squashfs/file_direct.c176
-rw-r--r--fs/squashfs/lzo_wrapper.c47
-rw-r--r--fs/squashfs/page_actor.c100
-rw-r--r--fs/squashfs/page_actor.h81
-rw-r--r--fs/squashfs/squashfs.h20
-rw-r--r--fs/squashfs/squashfs_fs_sb.h4
-rw-r--r--fs/squashfs/super.c10
-rw-r--r--fs/squashfs/xz_wrapper.c105
-rw-r--r--fs/squashfs/zlib_wrapper.c64
20 files changed, 243 insertions, 1148 deletions
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
index b6fa865..c70111e 100644
--- a/fs/squashfs/Kconfig
+++ b/fs/squashfs/Kconfig
@@ -25,78 +25,6 @@ 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
- 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
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
index 4132520..110b047 100644
--- a/fs/squashfs/Makefile
+++ b/fs/squashfs/Makefile
@@ -5,11 +5,6 @@
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_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
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 0cea9b9..41d108e 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -36,7 +36,6 @@
#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
@@ -87,16 +86,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, u64 index, int length,
- u64 *next_index, struct squashfs_page_actor *output)
+int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
+ int length, u64 *next_index, int srclength, int pages)
{
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, avail, i;
+ int bytes, compressed, b = 0, k = 0, page = 0, avail;
- bh = kcalloc(((output->length + msblk->devblksize - 1)
+ bh = kcalloc(((srclength + msblk->devblksize - 1)
>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
if (bh == NULL)
return -ENOMEM;
@@ -112,9 +111,9 @@ int squashfs_read_data(struct super_block *sb, u64 index, int length,
*next_index = index + length;
TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
- index, compressed ? "" : "un", length, output->length);
+ index, compressed ? "" : "un", length, srclength);
- if (length < 0 || length > output->length ||
+ if (length < 0 || length > srclength ||
(index + length) > msblk->bytes_used)
goto read_failure;
@@ -146,7 +145,7 @@ int squashfs_read_data(struct super_block *sb, u64 index, int length,
TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
compressed ? "" : "un", length);
- if (length < 0 || length > output->length ||
+ if (length < 0 || length > srclength ||
(index + length) > msblk->bytes_used)
goto block_release;
@@ -159,15 +158,9 @@ int squashfs_read_data(struct super_block *sb, u64 index, int length,
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, bh, b, offset, length,
- output);
+ length = squashfs_decompress(msblk, buffer, bh, b, offset,
+ length, srclength, pages);
if (length < 0)
goto read_failure;
} else {
@@ -175,20 +168,22 @@ int squashfs_read_data(struct super_block *sb, u64 index, int length,
* 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;
+ wait_on_buffer(bh[k]);
+ if (!buffer_uptodate(bh[k]))
+ goto block_release;
while (in) {
if (pg_offset == PAGE_CACHE_SIZE) {
- data = squashfs_next_page(output);
+ page++;
pg_offset = 0;
}
avail = min_t(int, in, PAGE_CACHE_SIZE -
pg_offset);
- memcpy(data + pg_offset, bh[k]->b_data + offset,
- avail);
+ memcpy(buffer[page] + pg_offset,
+ bh[k]->b_data + offset, avail);
in -= avail;
pg_offset += avail;
offset += avail;
@@ -196,7 +191,6 @@ int squashfs_read_data(struct super_block *sb, u64 index, int length,
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 1cb70a0..af0b738 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -56,7 +56,6 @@
#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
@@ -120,8 +119,9 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
entry->error = 0;
spin_unlock(&cache->lock);
- entry->length = squashfs_read_data(sb, block, length,
- &entry->next_index, entry->actor);
+ entry->length = squashfs_read_data(sb, entry->data,
+ block, length, &entry->next_index,
+ cache->block_size, cache->pages);
spin_lock(&cache->lock);
@@ -220,7 +220,6 @@ 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);
@@ -281,13 +280,6 @@ 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;
@@ -418,7 +410,6 @@ 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)
@@ -430,28 +421,19 @@ 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, block, length |
- SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
+ res = squashfs_read_data(sb, data, block, length |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages);
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 ac22fe7..3f6271d 100644
--- a/fs/squashfs/decompressor.c
+++ b/fs/squashfs/decompressor.c
@@ -30,7 +30,6 @@
#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
@@ -38,29 +37,29 @@
*/
static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
- NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
+ NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
};
#ifndef CONFIG_SQUASHFS_LZO
static const struct squashfs_decompressor squashfs_lzo_comp_ops = {
- NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
+ NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
};
#endif
#ifndef CONFIG_SQUASHFS_XZ
static const struct squashfs_decompressor squashfs_xz_comp_ops = {
- NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
+ NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
};
#endif
#ifndef CONFIG_SQUASHFS_ZLIB
static const struct squashfs_decompressor squashfs_zlib_comp_ops = {
- NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
+ NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
};
#endif
static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
- NULL, NULL, NULL, NULL, 0, "unknown", 0
+ NULL, NULL, NULL, 0, "unknown", 0
};
static const struct squashfs_decompressor *decompressor[] = {
@@ -84,11 +83,10 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
}
-static void *get_comp_opts(struct super_block *sb, unsigned short flags)
+void *squashfs_decompressor_init(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;
+ void *strm, *buffer = NULL;
int length = 0;
/*
@@ -96,46 +94,23 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)
*/
if (SQUASHFS_COMP_OPTS(flags)) {
buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
- if (buffer == NULL) {
- comp_opts = ERR_PTR(-ENOMEM);
- goto out;
- }
-
- actor = squashfs_page_actor_init(&buffer, 1, 0);
- if (actor == NULL) {
- comp_opts = ERR_PTR(-ENOMEM);
- goto out;
- }
+ if (buffer == NULL)
+ return ERR_PTR(-ENOMEM);
- length = squashfs_read_data(sb,
- sizeof(struct squashfs_super_block), 0, NULL, actor);
+ length = squashfs_read_data(sb, &buffer,
+ sizeof(struct squashfs_super_block), 0, NULL,
+ PAGE_CACHE_SIZE, 1);
if (length < 0) {
- comp_opts = ERR_PTR(length);
- goto out;
+ strm = ERR_PTR(length);
+ goto finished;
}
}
- comp_opts = squashfs_comp_opts(msblk, buffer, length);
+ strm = msblk->decompressor->init(msblk, buffer, length);
-out:
- kfree(actor);
+finished:
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 stream;
+ return strm;
}
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
index af09853..330073e 100644
--- a/fs/squashfs/decompressor.h
+++ b/fs/squashfs/decompressor.h
@@ -24,22 +24,28 @@
*/
struct squashfs_decompressor {
- void *(*init)(struct squashfs_sb_info *, void *);
- void *(*comp_opts)(struct squashfs_sb_info *, void *, int);
+ void *(*init)(struct squashfs_sb_info *, void *, int);
void (*free)(void *);
- int (*decompress)(struct squashfs_sb_info *, void *,
- struct buffer_head **, int, int, int,
- struct squashfs_page_actor *);
+ int (*decompress)(struct squashfs_sb_info *, void **,
+ struct buffer_head **, int, int, int, int, int);
int id;
char *name;
int supported;
};
-static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk,
- void *buff, int length)
+static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
+ void *s)
{
- return msblk->decompressor->comp_opts ?
- msblk->decompressor->comp_opts(msblk, buff, length) : NULL;
+ 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);
}
#ifdef CONFIG_SQUASHFS_XZ
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c
deleted file mode 100644
index d6008a6..0000000
--- a/fs/squashfs/decompressor_multi.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 2013
- * Minchan Kim <minchan@kernel.org>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-#include <linux/types.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/buffer_head.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/cpumask.h>
-
-#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, 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,
- bh, b, offset, length, output);
- put_decomp_stream(decomp_stream, stream);
- if (res < 0)
- ERROR("%s decompression failed, data probably corrupt\n",
- msblk->decompressor->name);
- return res;
-}
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c
deleted file mode 100644
index 23a9c28..0000000
--- a/fs/squashfs/decompressor_multi_percpu.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/percpu.h>
-#include <linux/buffer_head.h>
-
-#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, 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, bh, b,
- offset, length, output);
- 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();
-}
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c
deleted file mode 100644
index a6c7592..0000000
--- a/fs/squashfs/decompressor_single.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/types.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/buffer_head.h>
-
-#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, 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, bh, b,
- offset, length, output);
- 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/file.c b/fs/squashfs/file.c
index e5c9689..8ca62c2 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -370,15 +370,77 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)
return le32_to_cpu(size);
}
-/* Copy data into page cache */
-void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
- int bytes, int offset)
+
+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 bytes, i, offset = 0, sparse = 0;
+ struct squashfs_cache_entry *buffer = NULL;
void *pageaddr;
- int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
- int start_index = page->index & ~mask, end_index = start_index | mask;
+
+ 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;
+ }
/*
* Loop copying datablock into pages. As the datablock likely covers
@@ -389,7 +451,7 @@ void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
for (i = start_index; i <= end_index && bytes > 0; i++,
bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
struct page *push_page;
- int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0;
+ int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
@@ -413,75 +475,11 @@ 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;
-}
-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;
+ if (!sparse)
+ squashfs_cache_put(buffer);
- 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
deleted file mode 100644
index f2310d2..0000000
--- a/fs/squashfs/file_cache.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/fs.h>
-#include <linux/vfs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/pagemap.h>
-#include <linux/mutex.h>
-
-#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/file_direct.c b/fs/squashfs/file_direct.c
deleted file mode 100644
index 62a0de6..0000000
--- a/fs/squashfs/file_direct.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/fs.h>
-#include <linux/vfs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/pagemap.h>
-#include <linux/mutex.h>
-
-#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);
- if (res < 0)
- goto mark_errored;
-
- 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] == NULL || 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/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
index 244b9fb..00f4dfc 100644
--- a/fs/squashfs/lzo_wrapper.c
+++ b/fs/squashfs/lzo_wrapper.c
@@ -31,14 +31,13 @@
#include "squashfs_fs_sb.h"
#include "squashfs.h"
#include "decompressor.h"
-#include "page_actor.h"
struct squashfs_lzo {
void *input;
void *output;
};
-static void *lzo_init(struct squashfs_sb_info *msblk, void *buff)
+static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len)
{
int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
@@ -75,16 +74,22 @@ static void lzo_free(void *strm)
}
-static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
- struct buffer_head **bh, int b, int offset, int length,
- struct squashfs_page_actor *output)
+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)
{
- struct squashfs_lzo *stream = strm;
- void *buff = stream->input, *data;
+ struct squashfs_lzo *stream = msblk->stream;
+ void *buff = stream->input;
int avail, i, bytes = length, res;
- size_t out_len = output->length;
+ 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;
@@ -99,24 +104,24 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
goto failed;
res = bytes = (int)out_len;
- 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);
- }
+ 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;
}
- squashfs_finish_page(output);
+ 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/page_actor.c b/fs/squashfs/page_actor.c
deleted file mode 100644
index 5a1c11f..0000000
--- a/fs/squashfs/page_actor.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/pagemap.h>
-#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
deleted file mode 100644
index 26dd820..0000000
--- a/fs/squashfs/page_actor.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#ifndef PAGE_ACTOR_H
-#define PAGE_ACTOR_H
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#ifndef CONFIG_SQUASHFS_FILE_DIRECT
-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 */
-}
-#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
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 9e1bb79..d126651 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 *, u64, int, u64 *,
- struct squashfs_page_actor *);
+extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
+ int, int);
/* cache.c */
extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -48,14 +48,7 @@ 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_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 *, struct buffer_head **,
- int, int, int, struct squashfs_page_actor *);
-extern int squashfs_max_decompressors(void);
+extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
/* export.c */
extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
@@ -66,13 +59,6 @@ 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,
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 1da565c..52934a2 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -50,7 +50,6 @@ 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 {
@@ -64,9 +63,10 @@ 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;
- struct squashfs_stream *stream;
+ void *stream;
__le64 *inode_lookup_table;
u64 inode_table;
u64 directory_table;
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 202df63..60553a9 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -98,6 +98,7 @@ 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);
/*
@@ -205,14 +206,13 @@ 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",
- squashfs_max_decompressors(), msblk->block_size);
+ msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
if (msblk->read_page == NULL) {
ERROR("Failed to allocate read_page block\n");
goto failed_mount;
}
- msblk->stream = squashfs_decompressor_setup(sb, flags);
+ msblk->stream = squashfs_decompressor_init(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_destroy(msblk);
+ squashfs_decompressor_free(msblk, msblk->stream);
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_destroy(sbi);
+ squashfs_decompressor_free(sbi, sbi->stream);
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 c609624..1760b7d1 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -32,70 +32,44 @@
#include "squashfs_fs_sb.h"
#include "squashfs.h"
#include "decompressor.h"
-#include "page_actor.h"
struct squashfs_xz {
struct xz_dec *state;
struct xz_buf buf;
};
-struct disk_comp_opts {
+struct comp_opts {
__le32 dictionary_size;
__le32 flags;
};
-struct comp_opts {
- int dict_size;
-};
-
-static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
- void *buff, int len)
+static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
+ int len)
{
- 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;
- }
+ struct comp_opts *comp_opts = buff;
+ struct squashfs_xz *stream;
+ int dict_size = msblk->block_size;
+ int err, n;
if (comp_opts) {
/* check compressor options are the expected length */
if (len < sizeof(*comp_opts)) {
err = -EIO;
- goto out;
+ goto failed;
}
- opts->dict_size = le32_to_cpu(comp_opts->dictionary_size);
+ dict_size = le32_to_cpu(comp_opts->dictionary_size);
/* the dictionary size should be 2^n or 2^n+2^(n+1) */
- n = ffs(opts->dict_size) - 1;
- if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) +
+ n = ffs(dict_size) - 1;
+ if (dict_size != (1 << n) && dict_size != (1 << n) +
(1 << (n + 1))) {
err = -EIO;
- goto out;
+ goto failed;
}
- } else
- /* use defaults */
- opts->dict_size = max_t(int, msblk->block_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;
+ dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE);
stream = kmalloc(sizeof(*stream), GFP_KERNEL);
if (stream == NULL) {
@@ -103,7 +77,7 @@ static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff)
goto failed;
}
- stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size);
+ stream->state = xz_dec_init(XZ_PREALLOC, dict_size);
if (stream->state == NULL) {
kfree(stream);
err = -ENOMEM;
@@ -129,37 +103,42 @@ static void squashfs_xz_free(void *strm)
}
-static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
- struct buffer_head **bh, int b, int offset, int length,
- struct squashfs_page_actor *output)
+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)
{
enum xz_ret xz_err;
- int avail, total = 0, k = 0;
- struct squashfs_xz *stream = strm;
+ int avail, total = 0, k = 0, page = 0;
+ struct squashfs_xz *stream = msblk->stream;
+
+ mutex_lock(&msblk->read_data_mutex);
xz_dec_reset(stream->state);
stream->buf.in_pos = 0;
stream->buf.in_size = 0;
stream->buf.out_pos = 0;
stream->buf.out_size = PAGE_CACHE_SIZE;
- stream->buf.out = squashfs_first_page(output);
+ stream->buf.out = buffer[page++];
do {
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;
offset = 0;
}
- 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;
- }
+ 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;
}
xz_err = xz_dec_run(stream->state, &stream->buf);
@@ -168,14 +147,23 @@ 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) {
+ 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;
+ }
- if (xz_err != XZ_STREAM_END || k < b)
- goto out;
+ total += stream->buf.out_pos;
+ mutex_unlock(&msblk->read_data_mutex);
+ return total;
- return total + stream->buf.out_pos;
+release_mutex:
+ mutex_unlock(&msblk->read_data_mutex);
-out:
for (; k < b; k++)
put_bh(bh[k]);
@@ -184,7 +172,6 @@ out:
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 8727cab..55d918f 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -32,9 +32,8 @@
#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)
+static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
{
z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
if (stream == NULL)
@@ -62,37 +61,44 @@ static void zlib_free(void *strm)
}
-static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
- struct buffer_head **bh, int b, int offset, int length,
- struct squashfs_page_actor *output)
+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)
{
- int zlib_err, zlib_init = 0, k = 0;
- z_stream *stream = strm;
+ int zlib_err, zlib_init = 0;
+ int k = 0, page = 0;
+ z_stream *stream = msblk->stream;
+
+ mutex_lock(&msblk->read_data_mutex);
- stream->avail_out = PAGE_CACHE_SIZE;
- stream->next_out = squashfs_first_page(output);
+ stream->avail_out = 0;
stream->avail_in = 0;
do {
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;
}
- if (stream->avail_out == 0) {
- stream->next_out = squashfs_next_page(output);
- if (stream->next_out != NULL)
- stream->avail_out = PAGE_CACHE_SIZE;
+ if (stream->avail_out == 0 && page < pages) {
+ stream->next_out = buffer[page++];
+ stream->avail_out = PAGE_CACHE_SIZE;
}
if (!zlib_init) {
zlib_err = zlib_inflateInit(stream);
if (zlib_err != Z_OK) {
- squashfs_finish_page(output);
- goto out;
+ ERROR("zlib_inflateInit returned unexpected "
+ "result 0x%x, srclength %d\n",
+ zlib_err, srclength);
+ goto release_mutex;
}
zlib_init = 1;
}
@@ -103,21 +109,29 @@ 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;
+ if (zlib_err != Z_STREAM_END) {
+ ERROR("zlib_inflate error, data probably corrupt\n");
+ goto release_mutex;
+ }
zlib_err = zlib_inflateEnd(stream);
- if (zlib_err != Z_OK)
- goto out;
+ 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 (k < b)
- goto out;
+ length = stream->total_out;
+ mutex_unlock(&msblk->read_data_mutex);
+ return length;
- return stream->total_out;
+release_mutex:
+ mutex_unlock(&msblk->read_data_mutex);
-out:
for (; k < b; k++)
put_bh(bh[k]);