linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* re: [RFC,4/5] squashfs: support multiple decompress stream buffer
@ 2013-10-07  3:41 Phillip Lougher
  2013-10-07  6:09 ` Minchan Kim
  0 siblings, 1 reply; 5+ messages in thread
From: Phillip Lougher @ 2013-10-07  3:41 UTC (permalink / raw)
  To: Minchan Kim; +Cc: linux-kernel, ch0.han, gunho.lee

Hi,

This a partial review, based on the stuff I've managed to review
so far!

1. This is a substantial performance improvement, which is great
    stuff!

    But like the "squashfs: remove cache for normal data page" patch
    it needs to be optional, with the previous behaviour retained as
    default.  Again, without wanting to sound like a broken (vinyl)
    record, this is because as maintainer I get to worry about breaking
    things for existing users of Squashfs when they upgrade their kernel.

    I know from consulting experience, many users of Squashfs are "on the
    edge" of memory and CPU performance, and are using Squashfs to squeeze
    a bit more performance out of a maxed out system.

    In these cases, changing Squashfs so it uses more memory and more
    CPU than previously (and in this patch a lot more memory and CPU as
    it will try and kick off multiple decompressors per core) is a bit
    like robbing Peter to pay Paul, Squashfs may take CPU and memory
    that are needed elsewhere, and used to be available.

    So, basically, users need to be able to explicitly select this.

2. The patch breaks the decompressor interface.  Compressor option
    parsing is implemented in the decompressor init() function, which
    means everytime a new decompressor is dynamically instantiated, we
    need to read and parse the compression options again and again.  This
    is an unnecessary performance degradation.

    Compressor option parsing and reading should be split out of init()
    and into a separate function.

    Compression option parsing and reading is quite obscure, it is a
    late addition to the filesystem format, and had to be squeezed into
    the existing format.  This means it can be difficult to get it right
    as the specification exists only in my head.

    I'll help you here.

Specific comments follow in the patch.

Phillip



>Now squashfs have used for only one stream buffer for decompression
>so it hurts concurrent read performance due to locking lock of getting
>stream buffer.
>
>When file system mount, the number of stream buffer is started from
>num_online_cpus() and grows up to num_online_cpus() * 2M / block_size * 2.
>The rationale is MM does readahead chunk into 2M unit to prevent too much
>memory pin and while one request is waitting, we should request another
>chunk. That's why I multiply by 2.
>
>If it reveals too much memory problem, we can add shrinker routine.
>
>I did test following as
>
>Two 1G file dd read
>
>dd if=test/test1.dat of=/dev/null &
>dd if=test/test2.dat of=/dev/null &
>
>old : 60sec -> new : 30 sec
>
>Signed-off-by: Minchan Kim <minchan@kernel.org>
>
>---
>fs/squashfs/block.c          |    9 ++--
> fs/squashfs/decompressor.c   |  105 ++++++++++++++++++++++++++++++++++++++----
> fs/squashfs/decompressor.h   |   27 +++++------
> fs/squashfs/lzo_wrapper.c    |   12 ++---
> fs/squashfs/squashfs.h       |    3 +-
> fs/squashfs/squashfs_fs_sb.h |    7 ++-
> fs/squashfs/super.c          |   40 ++++++++++++----
> fs/squashfs/xz_wrapper.c     |   20 ++++----
> fs/squashfs/zlib_wrapper.c   |   12 ++---
> 9 files changed, 168 insertions(+), 67 deletions(-)
>
>diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
>index f33c6ef..d41bac8 100644
>--- a/fs/squashfs/block.c
>+++ b/fs/squashfs/block.c
>@@ -78,14 +78,14 @@ static struct buffer_head *get_block_length(struct super_block *sb,
>
>
>
>-int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
>+int squashfs_decompress_block(struct super_block *sb, int compressed,
> 		void **buffer, struct buffer_head **bh, int nr_bh,
> 		int offset, int length, int srclength, int pages)
> {
> 	int k = 0;
>
> 	if (compressed) {
>-		length = squashfs_decompress(msblk, buffer, bh, nr_bh,
>+		length = squashfs_decompress(sb, buffer, bh, nr_bh,
> 				offset, length, srclength, pages);
> 		if (length < 0)
> 			goto out;
>@@ -93,6 +93,7 @@ int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
> 		/*
> 		 * Block is uncompressed.
> 		 */
>+		struct squashfs_sb_info *msblk = sb->s_fs_info;
> 		int bytes, in, avail, pg_offset = 0, page = 0;
>
> 		for (bytes = length; k < nr_bh; k++) {
>@@ -262,8 +263,8 @@ int squashfs_read_metablock(struct super_block *sb, void **buffer, u64 index,
> 	}
> 	ll_rw_block(READ, b - 1, bh + 1);
>
>-	length = squashfs_decompress_block(msblk, compressed, buffer, bh, b,
>-				offset, length, srclength, pages);
>+	length = squashfs_decompress_block(sb, compressed, buffer, bh,
>+				b, offset, length, srclength, pages);
> 	if (length < 0)
> 		goto read_failure;
>
>diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
>index e47453e..ed35b32 100644
>--- a/fs/squashfs/decompressor.c
>+++ b/fs/squashfs/decompressor.c
>@@ -25,6 +25,8 @@
> #include <linux/mutex.h>
> #include <linux/slab.h>
> #include <linux/buffer_head.h>
>+#include <linux/sched.h>
>+#include <linux/wait.h>
>
> #include "squashfs_fs.h"
> #include "squashfs_fs_sb.h"
>@@ -70,6 +72,80 @@ static const struct squashfs_decompressor *decompressor[] = {
> 	&squashfs_unknown_comp_ops
> };
>
>+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
>+	struct squashfs_decomp_strm *stream)
>+{
>+	if (msblk->decompressor)
>+		msblk->decompressor->free(stream->strm);
>+	kfree(stream);
>+}
>+
>+static void *squashfs_get_decomp_strm(struct squashfs_sb_info *msblk)
>+{
>+	struct squashfs_decomp_strm *strm = NULL;
>+	mutex_lock(&msblk->comp_strm_mutex);
>+	if (!list_empty(&msblk->strm_list)) {
>+		strm = list_entry(msblk->strm_list.next,
>+				struct squashfs_decomp_strm, list);
>+		list_del(&strm->list);
>+		msblk->nr_avail_decomp--;
>+		WARN_ON(msblk->nr_avail_decomp < 0);
>+	}
>+	mutex_unlock(&msblk->comp_strm_mutex);
>+	return strm;
>+}
>+
>+static bool full_decomp_strm(struct squashfs_sb_info *msblk)
>+{
>+	/* MM do readahread 2M unit */
>+	int blocks = 2 * 1024 * 1024 / msblk->block_size;
>+	return msblk->nr_avail_decomp > (num_online_cpus() * blocks * 2);
>+}
>+
>+static void squashfs_put_decomp_strm(struct squashfs_sb_info *msblk,
>+					struct squashfs_decomp_strm *strm)
>+{
>+	mutex_lock(&msblk->comp_strm_mutex);
>+	if (full_decomp_strm(msblk)) {
>+		mutex_unlock(&msblk->comp_strm_mutex);
>+		squashfs_decompressor_free(msblk, strm);
>+		return;
>+	}
>+
>+	list_add(&strm->list, &msblk->strm_list);
>+	msblk->nr_avail_decomp++;
>+	mutex_unlock(&msblk->comp_strm_mutex);
>+	wake_up(&msblk->decomp_wait_queue);
>+}
>+
>+int squashfs_decompress(struct super_block *sb, void **buffer,
>+			struct buffer_head **bh, int b, int offset, int length,
>+			int srclength, int pages)
>+{
>+	int ret;
>+	struct squashfs_decomp_strm *strm;
>+	struct squashfs_sb_info *msblk = sb->s_fs_info;
>+	while (1) {
>+		strm = squashfs_get_decomp_strm(msblk);
>+		if (strm)
>+			break;
>+
>+		if (!full_decomp_strm(msblk)) {
>+			strm = squashfs_decompressor_init(sb);

here you call squashfs_decompressor_dynamically to instantiate a new
decompressor,  this needs to read and parse the compression options again.

>+			if (strm)
>+				break;
>+		}
>+
>+		wait_event(msblk->decomp_wait_queue, msblk->nr_avail_decomp);
>+		continue;
>+	}
>+
>+	ret = msblk->decompressor->decompress(msblk, strm->strm, buffer, bh,
>+		b, offset, length, srclength, pages);
>+
>+	squashfs_put_decomp_strm(msblk, strm);
>+	return ret;
>+}
>
> const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
> {
>@@ -82,35 +158,48 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
> 	return decompressor[i];
> }
>
>-
>-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
>+struct squashfs_decomp_strm *squashfs_decompressor_init(struct super_block *sb)
> {
> 	struct squashfs_sb_info *msblk = sb->s_fs_info;
>+	struct squashfs_decomp_strm *decomp_strm = NULL;
> 	void *strm, *buffer = NULL;
> 	int length = 0;
>
>+	decomp_strm = kmalloc(sizeof(struct squashfs_decomp_strm), GFP_KERNEL);
>+	if (!decomp_strm)
>+		return ERR_PTR(-ENOMEM);
> 	/*
> 	 * Read decompressor specific options from file system if present
> 	 */
>-	if (SQUASHFS_COMP_OPTS(flags)) {
>+	if (SQUASHFS_COMP_OPTS(msblk->flags)) {
> 		buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
>-		if (buffer == NULL)
>-			return ERR_PTR(-ENOMEM);
>+		if (buffer == NULL) {
>+			decomp_strm = ERR_PTR(-ENOMEM);
>+			goto finished;
>+		}
>
> 		length = squashfs_read_metablock(sb, &buffer,
> 			sizeof(struct squashfs_super_block), 0, NULL,
> 			PAGE_CACHE_SIZE, 1);
>

This reads the compressor options each decompressor init().  This should
only be done once at mount time.

> 		if (length < 0) {
>-			strm = ERR_PTR(length);
>+			decomp_strm = ERR_PTR(length);
> 			goto finished;
> 		}
> 	}
>


> 	strm = msblk->decompressor->init(msblk, buffer, length);
>+	if (IS_ERR(strm)) {
>+		decomp_strm = strm;
>+		goto finished;
>+	}
>
>-finished:
>+	decomp_strm->strm = strm;
> 	kfree(buffer);
>+	return decomp_strm;
>
>-	return strm;
>+finished:
>+	kfree(decomp_strm);
>+	kfree(buffer);
>+	return decomp_strm;
> }
>diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
>index 330073e..207c810 100644
>--- a/fs/squashfs/decompressor.h
>+++ b/fs/squashfs/decompressor.h
>@@ -26,27 +26,24 @@
> struct squashfs_decompressor {
> 	void	*(*init)(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)
>-{
>-	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);
>-}
>+struct squashfs_decomp_strm {
>+	void *strm;
>+	struct list_head list;
>+};
>+
>+int squashfs_decompress(struct super_block *sb, void **buffer,
>+			struct buffer_head **bh, int b, int offset, int length,
>+			int srclength, int pages);
>+
>+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
>+	struct squashfs_decomp_strm *stream);
>
> #ifdef CONFIG_SQUASHFS_XZ
> extern const struct squashfs_decompressor squashfs_xz_comp_ops;
>diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
>index 00f4dfc..e1bf135 100644
>--- a/fs/squashfs/lzo_wrapper.c
>+++ b/fs/squashfs/lzo_wrapper.c
>@@ -74,17 +74,15 @@ 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]))
>@@ -111,7 +109,6 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
> 		bytes -= avail;
> 	}
>
>-	mutex_unlock(&msblk->read_data_mutex);
> 	return res;
>
> block_release:
>@@ -119,7 +116,6 @@ block_release:
> 		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 405baed..4bb1f90 100644
>--- a/fs/squashfs/squashfs.h
>+++ b/fs/squashfs/squashfs.h
>@@ -52,7 +52,8 @@ 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 struct squashfs_decomp_strm *squashfs_decompressor_init(
>+				struct super_block *);
>
> /* 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..0a209e6 100644
>--- a/fs/squashfs/squashfs_fs_sb.h
>+++ b/fs/squashfs/squashfs_fs_sb.h
>@@ -63,10 +63,10 @@ struct squashfs_sb_info {
> 	__le64					*id_table;
> 	__le64					*fragment_index;
> 	__le64					*xattr_id_table;
>-	struct mutex				read_data_mutex;
>+	struct mutex                            comp_strm_mutex;
> 	struct mutex				meta_index_mutex;
> 	struct meta_index			*meta_index;
>-	void					*stream;
>+	struct list_head			strm_list;
> 	__le64					*inode_lookup_table;
> 	u64					inode_table;
> 	u64					directory_table;
>@@ -76,5 +76,8 @@ struct squashfs_sb_info {
> 	long long				bytes_used;
> 	unsigned int				inodes;
> 	int					xattr_ids;
>+	wait_queue_head_t			decomp_wait_queue;
>+	int					nr_avail_decomp;
>+	unsigned short				flags;
> };
> #endif
>diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
>index 60553a9..70aa9c4 100644
>--- a/fs/squashfs/super.c
>+++ b/fs/squashfs/super.c
>@@ -84,7 +84,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> 	unsigned short flags;
> 	unsigned int fragments;
> 	u64 lookup_table_start, xattr_id_table_start, next_table;
>-	int err;
>+	int err, i;
>
> 	TRACE("Entered squashfs_fill_superblock\n");
>
>@@ -98,7 +98,9 @@ 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);
>+	INIT_LIST_HEAD(&msblk->strm_list);
>+	mutex_init(&msblk->comp_strm_mutex);
>+	init_waitqueue_head(&msblk->decomp_wait_queue);

This should be done via a helper in decompressor.c, i.e. the implementation
shouldn't be visible here.

When I added the decompressor framework, I should have done this.

> 	mutex_init(&msblk->meta_index_mutex);
>
> 	/*
>@@ -176,6 +178,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> 	msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
> 	msblk->inodes = le32_to_cpu(sblk->inodes);
> 	flags = le16_to_cpu(sblk->flags);
>+	msblk->flags = flags;
>

ha, the superblock flags should only be needed at mount time.  After
mount time there shouldn't be anything in flags we need to look at.

You need to do this because flags is needed for the decompressor init
function (COMP_OPTS(flags)) which is now called after mount time.

Once the compressor options parsing is moved back to being only
at mount time, you won't need to store the flags anymore.

> 	TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b));
> 	TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
>@@ -212,11 +215,16 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> 		goto failed_mount;
> 	}
>
>-	msblk->stream = squashfs_decompressor_init(sb, flags);
>-	if (IS_ERR(msblk->stream)) {
>-		err = PTR_ERR(msblk->stream);
>-		msblk->stream = NULL;
>-		goto failed_mount;
>+	/* Allocate mutliple decompressor */
>+	for (i = 0; i < num_online_cpus(); i++) {
>+		struct squashfs_decomp_strm *decomp_strm;
>+		decomp_strm = squashfs_decompressor_init(sb);
>+		if (IS_ERR(decomp_strm)) {
>+			err = PTR_ERR(decomp_strm);
>+			goto failed_mount;
>+		}
>+		list_add(&decomp_strm->list, &msblk->strm_list);
>+		msblk->nr_avail_decomp++;

Again, this is decompressor implementation specific stuff, and should be
done via a helper in decompressor.c.

> 	}
>
> 	/* Handle xattrs */
>@@ -336,7 +344,14 @@ 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);
>+	while (!list_empty(&msblk->strm_list)) {
>+		struct squashfs_decomp_strm *decomp_strm =
>+			list_entry(msblk->strm_list.prev,
>+					struct squashfs_decomp_strm, list);
>+		list_del(&decomp_strm->list);
>+		squashfs_decompressor_free(msblk, decomp_strm);
>+	}
>+	msblk->nr_avail_decomp = 0;

helper in decompressor.c

> 	kfree(msblk->inode_lookup_table);
> 	kfree(msblk->fragment_index);
> 	kfree(msblk->id_table);
>@@ -383,7 +398,14 @@ 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);
>+		while (!list_empty(&sbi->strm_list)) {
>+			struct squashfs_decomp_strm *decomp_strm =
>+				list_entry(sbi->strm_list.prev,
>+					struct squashfs_decomp_strm, list);
>+			list_del(&decomp_strm->list);
>+			squashfs_decompressor_free(sbi, decomp_strm);
>+		}
>+		sbi->nr_avail_decomp = 0;

helper in decompressor.c

> 		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..98b8bb5 100644
>--- a/fs/squashfs/xz_wrapper.c
>+++ b/fs/squashfs/xz_wrapper.c
>@@ -103,15 +103,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;
>@@ -126,7 +124,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
> 			length -= avail;
> 			wait_on_buffer(bh[k]);
> 			if (!buffer_uptodate(bh[k]))
>-				goto release_mutex;
>+				goto out;
>
> 			stream->buf.in = bh[k]->b_data + offset;
> 			stream->buf.in_size = avail;
>@@ -149,20 +147,18 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
>
> 	if (xz_err != XZ_STREAM_END) {
> 		ERROR("xz_dec_run error, data probably corrupt\n");
>-		goto release_mutex;
>+		goto out;
> 	}
>
> 	if (k < b) {
> 		ERROR("xz_uncompress error, input remaining\n");
>-		goto release_mutex;
>+		goto out;
> 	}
>
> 	total += stream->buf.out_pos;
>-	mutex_unlock(&msblk->read_data_mutex);
> 	return total;
>
>-release_mutex:
>-	mutex_unlock(&msblk->read_data_mutex);
>+out:
>
> 	for (; k < b; k++)
> 		put_bh(bh[k]);
>diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
>index 55d918f..82c1a70 100644
>--- a/fs/squashfs/zlib_wrapper.c
>+++ b/fs/squashfs/zlib_wrapper.c
>@@ -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;
>@@ -126,11 +124,9 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
> 	}
>
> 	length = stream->total_out;
>-	mutex_unlock(&msblk->read_data_mutex);
> 	return length;
>
> release_mutex:
>-	mutex_unlock(&msblk->read_data_mutex);
>
> 	for (; k < b; k++)
> 		put_bh(bh[k]);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC,4/5] squashfs: support multiple decompress stream buffer
  2013-10-07  3:41 [RFC,4/5] squashfs: support multiple decompress stream buffer Phillip Lougher
@ 2013-10-07  6:09 ` Minchan Kim
  2013-10-07  6:35   ` Minchan Kim
  0 siblings, 1 reply; 5+ messages in thread
From: Minchan Kim @ 2013-10-07  6:09 UTC (permalink / raw)
  To: Phillip Lougher; +Cc: linux-kernel, ch0.han, gunho.lee

Hello Phillip,

On Mon, Oct 07, 2013 at 04:41:20AM +0100, Phillip Lougher wrote:
> Hi,
> 
> This a partial review, based on the stuff I've managed to review
> so far!
> 
> 1. This is a substantial performance improvement, which is great
>    stuff!

Thanks.

> 
>    But like the "squashfs: remove cache for normal data page" patch
>    it needs to be optional, with the previous behaviour retained as
>    default.  Again, without wanting to sound like a broken (vinyl)

Just FYI, I have a plan to drop "squashfs: remove cache for normal
data page" in next submit as you pointed out it could make regression.
So my plan is that squashfs_readpage uses the cache but squashfs_readpages
will not use the cache.
If you have any concern in my design, please tell me.

>    record, this is because as maintainer I get to worry about breaking
>    things for existing users of Squashfs when they upgrade their kernel.
> 
>    I know from consulting experience, many users of Squashfs are "on the
>    edge" of memory and CPU performance, and are using Squashfs to squeeze
>    a bit more performance out of a maxed out system.
> 
>    In these cases, changing Squashfs so it uses more memory and more
>    CPU than previously (and in this patch a lot more memory and CPU as
>    it will try and kick off multiple decompressors per core) is a bit
>    like robbing Peter to pay Paul, Squashfs may take CPU and memory
>    that are needed elsewhere, and used to be available.
> 
>    So, basically, users need to be able to explicitly select this.

Okay.

> 
> 2. The patch breaks the decompressor interface.  Compressor option
>    parsing is implemented in the decompressor init() function, which
>    means everytime a new decompressor is dynamically instantiated, we
>    need to read and parse the compression options again and again.  This
>    is an unnecessary performance degradation.
> 
>    Compressor option parsing and reading should be split out of init()
>    and into a separate function.

Indeed.

> 
>    Compression option parsing and reading is quite obscure, it is a
>    late addition to the filesystem format, and had to be squeezed into
>    the existing format.  This means it can be difficult to get it right
>    as the specification exists only in my head.

Hmm, I had a question. Please look at below.

> 
>    I'll help you here.
> 
> Specific comments follow in the patch.
> 
> Phillip
> 
> 
> 
> >Now squashfs have used for only one stream buffer for decompression
> >so it hurts concurrent read performance due to locking lock of getting
> >stream buffer.
> >
> >When file system mount, the number of stream buffer is started from
> >num_online_cpus() and grows up to num_online_cpus() * 2M / block_size * 2.
> >The rationale is MM does readahead chunk into 2M unit to prevent too much
> >memory pin and while one request is waitting, we should request another
> >chunk. That's why I multiply by 2.
> >
> >If it reveals too much memory problem, we can add shrinker routine.
> >
> >I did test following as
> >
> >Two 1G file dd read
> >
> >dd if=test/test1.dat of=/dev/null &
> >dd if=test/test2.dat of=/dev/null &
> >
> >old : 60sec -> new : 30 sec
> >
> >Signed-off-by: Minchan Kim <minchan@kernel.org>
> >
> >---
> >fs/squashfs/block.c          |    9 ++--
> >fs/squashfs/decompressor.c   |  105 ++++++++++++++++++++++++++++++++++++++----
> >fs/squashfs/decompressor.h   |   27 +++++------
> >fs/squashfs/lzo_wrapper.c    |   12 ++---
> >fs/squashfs/squashfs.h       |    3 +-
> >fs/squashfs/squashfs_fs_sb.h |    7 ++-
> >fs/squashfs/super.c          |   40 ++++++++++++----
> >fs/squashfs/xz_wrapper.c     |   20 ++++----
> >fs/squashfs/zlib_wrapper.c   |   12 ++---
> >9 files changed, 168 insertions(+), 67 deletions(-)
> >
> >diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
> >index f33c6ef..d41bac8 100644
> >--- a/fs/squashfs/block.c
> >+++ b/fs/squashfs/block.c
> >@@ -78,14 +78,14 @@ static struct buffer_head *get_block_length(struct super_block *sb,
> >
> >
> >
> >-int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
> >+int squashfs_decompress_block(struct super_block *sb, int compressed,
> >		void **buffer, struct buffer_head **bh, int nr_bh,
> >		int offset, int length, int srclength, int pages)
> >{
> >	int k = 0;
> >
> >	if (compressed) {
> >-		length = squashfs_decompress(msblk, buffer, bh, nr_bh,
> >+		length = squashfs_decompress(sb, buffer, bh, nr_bh,
> >				offset, length, srclength, pages);
> >		if (length < 0)
> >			goto out;
> >@@ -93,6 +93,7 @@ int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
> >		/*
> >		 * Block is uncompressed.
> >		 */
> >+		struct squashfs_sb_info *msblk = sb->s_fs_info;
> >		int bytes, in, avail, pg_offset = 0, page = 0;
> >
> >		for (bytes = length; k < nr_bh; k++) {
> >@@ -262,8 +263,8 @@ int squashfs_read_metablock(struct super_block *sb, void **buffer, u64 index,
> >	}
> >	ll_rw_block(READ, b - 1, bh + 1);
> >
> >-	length = squashfs_decompress_block(msblk, compressed, buffer, bh, b,
> >-				offset, length, srclength, pages);
> >+	length = squashfs_decompress_block(sb, compressed, buffer, bh,
> >+				b, offset, length, srclength, pages);
> >	if (length < 0)
> >		goto read_failure;
> >
> >diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
> >index e47453e..ed35b32 100644
> >--- a/fs/squashfs/decompressor.c
> >+++ b/fs/squashfs/decompressor.c
> >@@ -25,6 +25,8 @@
> >#include <linux/mutex.h>
> >#include <linux/slab.h>
> >#include <linux/buffer_head.h>
> >+#include <linux/sched.h>
> >+#include <linux/wait.h>
> >
> >#include "squashfs_fs.h"
> >#include "squashfs_fs_sb.h"
> >@@ -70,6 +72,80 @@ static const struct squashfs_decompressor *decompressor[] = {
> >	&squashfs_unknown_comp_ops
> >};
> >
> >+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
> >+	struct squashfs_decomp_strm *stream)
> >+{
> >+	if (msblk->decompressor)
> >+		msblk->decompressor->free(stream->strm);
> >+	kfree(stream);
> >+}
> >+
> >+static void *squashfs_get_decomp_strm(struct squashfs_sb_info *msblk)
> >+{
> >+	struct squashfs_decomp_strm *strm = NULL;
> >+	mutex_lock(&msblk->comp_strm_mutex);
> >+	if (!list_empty(&msblk->strm_list)) {
> >+		strm = list_entry(msblk->strm_list.next,
> >+				struct squashfs_decomp_strm, list);
> >+		list_del(&strm->list);
> >+		msblk->nr_avail_decomp--;
> >+		WARN_ON(msblk->nr_avail_decomp < 0);
> >+	}
> >+	mutex_unlock(&msblk->comp_strm_mutex);
> >+	return strm;
> >+}
> >+
> >+static bool full_decomp_strm(struct squashfs_sb_info *msblk)
> >+{
> >+	/* MM do readahread 2M unit */
> >+	int blocks = 2 * 1024 * 1024 / msblk->block_size;
> >+	return msblk->nr_avail_decomp > (num_online_cpus() * blocks * 2);
> >+}
> >+
> >+static void squashfs_put_decomp_strm(struct squashfs_sb_info *msblk,
> >+					struct squashfs_decomp_strm *strm)
> >+{
> >+	mutex_lock(&msblk->comp_strm_mutex);
> >+	if (full_decomp_strm(msblk)) {
> >+		mutex_unlock(&msblk->comp_strm_mutex);
> >+		squashfs_decompressor_free(msblk, strm);
> >+		return;
> >+	}
> >+
> >+	list_add(&strm->list, &msblk->strm_list);
> >+	msblk->nr_avail_decomp++;
> >+	mutex_unlock(&msblk->comp_strm_mutex);
> >+	wake_up(&msblk->decomp_wait_queue);
> >+}
> >+
> >+int squashfs_decompress(struct super_block *sb, void **buffer,
> >+			struct buffer_head **bh, int b, int offset, int length,
> >+			int srclength, int pages)
> >+{
> >+	int ret;
> >+	struct squashfs_decomp_strm *strm;
> >+	struct squashfs_sb_info *msblk = sb->s_fs_info;
> >+	while (1) {
> >+		strm = squashfs_get_decomp_strm(msblk);
> >+		if (strm)
> >+			break;
> >+
> >+		if (!full_decomp_strm(msblk)) {
> >+			strm = squashfs_decompressor_init(sb);
> 
> here you call squashfs_decompressor_dynamically to instantiate a new
> decompressor,  this needs to read and parse the compression options again.
> 
> >+			if (strm)
> >+				break;
> >+		}
> >+
> >+		wait_event(msblk->decomp_wait_queue, msblk->nr_avail_decomp);
> >+		continue;
> >+	}
> >+
> >+	ret = msblk->decompressor->decompress(msblk, strm->strm, buffer, bh,
> >+		b, offset, length, srclength, pages);
> >+
> >+	squashfs_put_decomp_strm(msblk, strm);
> >+	return ret;
> >+}
> >
> >const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
> >{
> >@@ -82,35 +158,48 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
> >	return decompressor[i];
> >}
> >
> >-
> >-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
> >+struct squashfs_decomp_strm *squashfs_decompressor_init(struct super_block *sb)
> >{
> >	struct squashfs_sb_info *msblk = sb->s_fs_info;
> >+	struct squashfs_decomp_strm *decomp_strm = NULL;
> >	void *strm, *buffer = NULL;
> >	int length = 0;
> >
> >+	decomp_strm = kmalloc(sizeof(struct squashfs_decomp_strm), GFP_KERNEL);
> >+	if (!decomp_strm)
> >+		return ERR_PTR(-ENOMEM);
> >	/*
> >	 * Read decompressor specific options from file system if present
> >	 */
> >-	if (SQUASHFS_COMP_OPTS(flags)) {
> >+	if (SQUASHFS_COMP_OPTS(msblk->flags)) {
> >		buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
> >-		if (buffer == NULL)
> >-			return ERR_PTR(-ENOMEM);
> >+		if (buffer == NULL) {
> >+			decomp_strm = ERR_PTR(-ENOMEM);
> >+			goto finished;
> >+		}
> >
> >		length = squashfs_read_metablock(sb, &buffer,
> >			sizeof(struct squashfs_super_block), 0, NULL,
> >			PAGE_CACHE_SIZE, 1);
> >
> 
> This reads the compressor options each decompressor init().  This should
> only be done once at mount time.
> 
> >		if (length < 0) {
> >-			strm = ERR_PTR(length);
> >+			decomp_strm = ERR_PTR(length);
> >			goto finished;
> >		}
> >	}
> >
> 
> 
> >	strm = msblk->decompressor->init(msblk, buffer, length);
> >+	if (IS_ERR(strm)) {
> >+		decomp_strm = strm;
> >+		goto finished;
> >+	}
> >
> >-finished:
> >+	decomp_strm->strm = strm;
> >	kfree(buffer);
> >+	return decomp_strm;
> >
> >-	return strm;
> >+finished:
> >+	kfree(decomp_strm);
> >+	kfree(buffer);
> >+	return decomp_strm;
> >}
> >diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
> >index 330073e..207c810 100644
> >--- a/fs/squashfs/decompressor.h
> >+++ b/fs/squashfs/decompressor.h
> >@@ -26,27 +26,24 @@
> >struct squashfs_decompressor {
> >	void	*(*init)(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)
> >-{
> >-	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);
> >-}
> >+struct squashfs_decomp_strm {
> >+	void *strm;
> >+	struct list_head list;
> >+};
> >+
> >+int squashfs_decompress(struct super_block *sb, void **buffer,
> >+			struct buffer_head **bh, int b, int offset, int length,
> >+			int srclength, int pages);
> >+
> >+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
> >+	struct squashfs_decomp_strm *stream);
> >
> >#ifdef CONFIG_SQUASHFS_XZ
> >extern const struct squashfs_decompressor squashfs_xz_comp_ops;
> >diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
> >index 00f4dfc..e1bf135 100644
> >--- a/fs/squashfs/lzo_wrapper.c
> >+++ b/fs/squashfs/lzo_wrapper.c
> >@@ -74,17 +74,15 @@ 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]))
> >@@ -111,7 +109,6 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
> >		bytes -= avail;
> >	}
> >
> >-	mutex_unlock(&msblk->read_data_mutex);
> >	return res;
> >
> >block_release:
> >@@ -119,7 +116,6 @@ block_release:
> >		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 405baed..4bb1f90 100644
> >--- a/fs/squashfs/squashfs.h
> >+++ b/fs/squashfs/squashfs.h
> >@@ -52,7 +52,8 @@ 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 struct squashfs_decomp_strm *squashfs_decompressor_init(
> >+				struct super_block *);
> >
> >/* 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..0a209e6 100644
> >--- a/fs/squashfs/squashfs_fs_sb.h
> >+++ b/fs/squashfs/squashfs_fs_sb.h
> >@@ -63,10 +63,10 @@ struct squashfs_sb_info {
> >	__le64					*id_table;
> >	__le64					*fragment_index;
> >	__le64					*xattr_id_table;
> >-	struct mutex				read_data_mutex;
> >+	struct mutex                            comp_strm_mutex;
> >	struct mutex				meta_index_mutex;
> >	struct meta_index			*meta_index;
> >-	void					*stream;
> >+	struct list_head			strm_list;
> >	__le64					*inode_lookup_table;
> >	u64					inode_table;
> >	u64					directory_table;
> >@@ -76,5 +76,8 @@ struct squashfs_sb_info {
> >	long long				bytes_used;
> >	unsigned int				inodes;
> >	int					xattr_ids;
> >+	wait_queue_head_t			decomp_wait_queue;
> >+	int					nr_avail_decomp;
> >+	unsigned short				flags;
> >};
> >#endif
> >diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
> >index 60553a9..70aa9c4 100644
> >--- a/fs/squashfs/super.c
> >+++ b/fs/squashfs/super.c
> >@@ -84,7 +84,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> >	unsigned short flags;
> >	unsigned int fragments;
> >	u64 lookup_table_start, xattr_id_table_start, next_table;
> >-	int err;
> >+	int err, i;
> >
> >	TRACE("Entered squashfs_fill_superblock\n");
> >
> >@@ -98,7 +98,9 @@ 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);
> >+	INIT_LIST_HEAD(&msblk->strm_list);
> >+	mutex_init(&msblk->comp_strm_mutex);
> >+	init_waitqueue_head(&msblk->decomp_wait_queue);
> 
> This should be done via a helper in decompressor.c, i.e. the implementation
> shouldn't be visible here.
> 
> When I added the decompressor framework, I should have done this.
> 
> >	mutex_init(&msblk->meta_index_mutex);
> >
> >	/*
> >@@ -176,6 +178,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> >	msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
> >	msblk->inodes = le32_to_cpu(sblk->inodes);
> >	flags = le16_to_cpu(sblk->flags);
> >+	msblk->flags = flags;
> >
> 
> ha, the superblock flags should only be needed at mount time.  After
> mount time there shouldn't be anything in flags we need to look at.
> 
> You need to do this because flags is needed for the decompressor init
> function (COMP_OPTS(flags)) which is now called after mount time.
> 
> Once the compressor options parsing is moved back to being only
> at mount time, you won't need to store the flags anymore.

Hmm, I'd like to clarify your point further.
I agree it's unnecessary performance degradation if we read compressor
option from storage whenever we create new stream buffer.
But we need it to create new stream buffer(ex, xz_dec_init) dynamically
so we should keep compressor option into somewhere. It means we should
keep it to somewhere although we remove flags field from msblk.
Am I missing something?

-- 
Kind regards,
Minchan Kim

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC,4/5] squashfs: support multiple decompress stream buffer
  2013-10-07  6:09 ` Minchan Kim
@ 2013-10-07  6:35   ` Minchan Kim
  2013-10-08  1:25     ` Minchan Kim
  0 siblings, 1 reply; 5+ messages in thread
From: Minchan Kim @ 2013-10-07  6:35 UTC (permalink / raw)
  To: Phillip Lougher; +Cc: linux-kernel, ch0.han, gunho.lee

On Mon, Oct 07, 2013 at 03:09:51PM +0900, Minchan Kim wrote:
> Hello Phillip,
> 
> On Mon, Oct 07, 2013 at 04:41:20AM +0100, Phillip Lougher wrote:
> > Hi,
> > 
> > This a partial review, based on the stuff I've managed to review
> > so far!
> > 
> > 1. This is a substantial performance improvement, which is great
> >    stuff!
> 
> Thanks.
> 
> > 
> >    But like the "squashfs: remove cache for normal data page" patch
> >    it needs to be optional, with the previous behaviour retained as
> >    default.  Again, without wanting to sound like a broken (vinyl)
> 
> Just FYI, I have a plan to drop "squashfs: remove cache for normal
> data page" in next submit as you pointed out it could make regression.
> So my plan is that squashfs_readpage uses the cache but squashfs_readpages
> will not use the cache.
> If you have any concern in my design, please tell me.
> 
> >    record, this is because as maintainer I get to worry about breaking
> >    things for existing users of Squashfs when they upgrade their kernel.
> > 
> >    I know from consulting experience, many users of Squashfs are "on the
> >    edge" of memory and CPU performance, and are using Squashfs to squeeze
> >    a bit more performance out of a maxed out system.
> > 
> >    In these cases, changing Squashfs so it uses more memory and more
> >    CPU than previously (and in this patch a lot more memory and CPU as
> >    it will try and kick off multiple decompressors per core) is a bit
> >    like robbing Peter to pay Paul, Squashfs may take CPU and memory
> >    that are needed elsewhere, and used to be available.
> > 
> >    So, basically, users need to be able to explicitly select this.
> 
> Okay.
> 
> > 
> > 2. The patch breaks the decompressor interface.  Compressor option
> >    parsing is implemented in the decompressor init() function, which
> >    means everytime a new decompressor is dynamically instantiated, we
> >    need to read and parse the compression options again and again.  This
> >    is an unnecessary performance degradation.
> > 
> >    Compressor option parsing and reading should be split out of init()
> >    and into a separate function.
> 
> Indeed.
> 
> > 
> >    Compression option parsing and reading is quite obscure, it is a
> >    late addition to the filesystem format, and had to be squeezed into
> >    the existing format.  This means it can be difficult to get it right
> >    as the specification exists only in my head.
> 
> Hmm, I had a question. Please look at below.
> 
> > 
> >    I'll help you here.
> > 
> > Specific comments follow in the patch.
> > 
> > Phillip
> > 
> > 
> > 
> > >Now squashfs have used for only one stream buffer for decompression
> > >so it hurts concurrent read performance due to locking lock of getting
> > >stream buffer.
> > >
> > >When file system mount, the number of stream buffer is started from
> > >num_online_cpus() and grows up to num_online_cpus() * 2M / block_size * 2.
> > >The rationale is MM does readahead chunk into 2M unit to prevent too much
> > >memory pin and while one request is waitting, we should request another
> > >chunk. That's why I multiply by 2.
> > >
> > >If it reveals too much memory problem, we can add shrinker routine.
> > >
> > >I did test following as
> > >
> > >Two 1G file dd read
> > >
> > >dd if=test/test1.dat of=/dev/null &
> > >dd if=test/test2.dat of=/dev/null &
> > >
> > >old : 60sec -> new : 30 sec
> > >
> > >Signed-off-by: Minchan Kim <minchan@kernel.org>
> > >
> > >---
> > >fs/squashfs/block.c          |    9 ++--
> > >fs/squashfs/decompressor.c   |  105 ++++++++++++++++++++++++++++++++++++++----
> > >fs/squashfs/decompressor.h   |   27 +++++------
> > >fs/squashfs/lzo_wrapper.c    |   12 ++---
> > >fs/squashfs/squashfs.h       |    3 +-
> > >fs/squashfs/squashfs_fs_sb.h |    7 ++-
> > >fs/squashfs/super.c          |   40 ++++++++++++----
> > >fs/squashfs/xz_wrapper.c     |   20 ++++----
> > >fs/squashfs/zlib_wrapper.c   |   12 ++---
> > >9 files changed, 168 insertions(+), 67 deletions(-)
> > >
> > >diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
> > >index f33c6ef..d41bac8 100644
> > >--- a/fs/squashfs/block.c
> > >+++ b/fs/squashfs/block.c
> > >@@ -78,14 +78,14 @@ static struct buffer_head *get_block_length(struct super_block *sb,
> > >
> > >
> > >
> > >-int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
> > >+int squashfs_decompress_block(struct super_block *sb, int compressed,
> > >		void **buffer, struct buffer_head **bh, int nr_bh,
> > >		int offset, int length, int srclength, int pages)
> > >{
> > >	int k = 0;
> > >
> > >	if (compressed) {
> > >-		length = squashfs_decompress(msblk, buffer, bh, nr_bh,
> > >+		length = squashfs_decompress(sb, buffer, bh, nr_bh,
> > >				offset, length, srclength, pages);
> > >		if (length < 0)
> > >			goto out;
> > >@@ -93,6 +93,7 @@ int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
> > >		/*
> > >		 * Block is uncompressed.
> > >		 */
> > >+		struct squashfs_sb_info *msblk = sb->s_fs_info;
> > >		int bytes, in, avail, pg_offset = 0, page = 0;
> > >
> > >		for (bytes = length; k < nr_bh; k++) {
> > >@@ -262,8 +263,8 @@ int squashfs_read_metablock(struct super_block *sb, void **buffer, u64 index,
> > >	}
> > >	ll_rw_block(READ, b - 1, bh + 1);
> > >
> > >-	length = squashfs_decompress_block(msblk, compressed, buffer, bh, b,
> > >-				offset, length, srclength, pages);
> > >+	length = squashfs_decompress_block(sb, compressed, buffer, bh,
> > >+				b, offset, length, srclength, pages);
> > >	if (length < 0)
> > >		goto read_failure;
> > >
> > >diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
> > >index e47453e..ed35b32 100644
> > >--- a/fs/squashfs/decompressor.c
> > >+++ b/fs/squashfs/decompressor.c
> > >@@ -25,6 +25,8 @@
> > >#include <linux/mutex.h>
> > >#include <linux/slab.h>
> > >#include <linux/buffer_head.h>
> > >+#include <linux/sched.h>
> > >+#include <linux/wait.h>
> > >
> > >#include "squashfs_fs.h"
> > >#include "squashfs_fs_sb.h"
> > >@@ -70,6 +72,80 @@ static const struct squashfs_decompressor *decompressor[] = {
> > >	&squashfs_unknown_comp_ops
> > >};
> > >
> > >+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
> > >+	struct squashfs_decomp_strm *stream)
> > >+{
> > >+	if (msblk->decompressor)
> > >+		msblk->decompressor->free(stream->strm);
> > >+	kfree(stream);
> > >+}
> > >+
> > >+static void *squashfs_get_decomp_strm(struct squashfs_sb_info *msblk)
> > >+{
> > >+	struct squashfs_decomp_strm *strm = NULL;
> > >+	mutex_lock(&msblk->comp_strm_mutex);
> > >+	if (!list_empty(&msblk->strm_list)) {
> > >+		strm = list_entry(msblk->strm_list.next,
> > >+				struct squashfs_decomp_strm, list);
> > >+		list_del(&strm->list);
> > >+		msblk->nr_avail_decomp--;
> > >+		WARN_ON(msblk->nr_avail_decomp < 0);
> > >+	}
> > >+	mutex_unlock(&msblk->comp_strm_mutex);
> > >+	return strm;
> > >+}
> > >+
> > >+static bool full_decomp_strm(struct squashfs_sb_info *msblk)
> > >+{
> > >+	/* MM do readahread 2M unit */
> > >+	int blocks = 2 * 1024 * 1024 / msblk->block_size;
> > >+	return msblk->nr_avail_decomp > (num_online_cpus() * blocks * 2);
> > >+}
> > >+
> > >+static void squashfs_put_decomp_strm(struct squashfs_sb_info *msblk,
> > >+					struct squashfs_decomp_strm *strm)
> > >+{
> > >+	mutex_lock(&msblk->comp_strm_mutex);
> > >+	if (full_decomp_strm(msblk)) {
> > >+		mutex_unlock(&msblk->comp_strm_mutex);
> > >+		squashfs_decompressor_free(msblk, strm);
> > >+		return;
> > >+	}
> > >+
> > >+	list_add(&strm->list, &msblk->strm_list);
> > >+	msblk->nr_avail_decomp++;
> > >+	mutex_unlock(&msblk->comp_strm_mutex);
> > >+	wake_up(&msblk->decomp_wait_queue);
> > >+}
> > >+
> > >+int squashfs_decompress(struct super_block *sb, void **buffer,
> > >+			struct buffer_head **bh, int b, int offset, int length,
> > >+			int srclength, int pages)
> > >+{
> > >+	int ret;
> > >+	struct squashfs_decomp_strm *strm;
> > >+	struct squashfs_sb_info *msblk = sb->s_fs_info;
> > >+	while (1) {
> > >+		strm = squashfs_get_decomp_strm(msblk);
> > >+		if (strm)
> > >+			break;
> > >+
> > >+		if (!full_decomp_strm(msblk)) {
> > >+			strm = squashfs_decompressor_init(sb);
> > 
> > here you call squashfs_decompressor_dynamically to instantiate a new
> > decompressor,  this needs to read and parse the compression options again.
> > 
> > >+			if (strm)
> > >+				break;
> > >+		}
> > >+
> > >+		wait_event(msblk->decomp_wait_queue, msblk->nr_avail_decomp);
> > >+		continue;
> > >+	}
> > >+
> > >+	ret = msblk->decompressor->decompress(msblk, strm->strm, buffer, bh,
> > >+		b, offset, length, srclength, pages);
> > >+
> > >+	squashfs_put_decomp_strm(msblk, strm);
> > >+	return ret;
> > >+}
> > >
> > >const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
> > >{
> > >@@ -82,35 +158,48 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
> > >	return decompressor[i];
> > >}
> > >
> > >-
> > >-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
> > >+struct squashfs_decomp_strm *squashfs_decompressor_init(struct super_block *sb)
> > >{
> > >	struct squashfs_sb_info *msblk = sb->s_fs_info;
> > >+	struct squashfs_decomp_strm *decomp_strm = NULL;
> > >	void *strm, *buffer = NULL;
> > >	int length = 0;
> > >
> > >+	decomp_strm = kmalloc(sizeof(struct squashfs_decomp_strm), GFP_KERNEL);
> > >+	if (!decomp_strm)
> > >+		return ERR_PTR(-ENOMEM);
> > >	/*
> > >	 * Read decompressor specific options from file system if present
> > >	 */
> > >-	if (SQUASHFS_COMP_OPTS(flags)) {
> > >+	if (SQUASHFS_COMP_OPTS(msblk->flags)) {
> > >		buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
> > >-		if (buffer == NULL)
> > >-			return ERR_PTR(-ENOMEM);
> > >+		if (buffer == NULL) {
> > >+			decomp_strm = ERR_PTR(-ENOMEM);
> > >+			goto finished;
> > >+		}
> > >
> > >		length = squashfs_read_metablock(sb, &buffer,
> > >			sizeof(struct squashfs_super_block), 0, NULL,
> > >			PAGE_CACHE_SIZE, 1);
> > >
> > 
> > This reads the compressor options each decompressor init().  This should
> > only be done once at mount time.
> > 
> > >		if (length < 0) {
> > >-			strm = ERR_PTR(length);
> > >+			decomp_strm = ERR_PTR(length);
> > >			goto finished;
> > >		}
> > >	}
> > >
> > 
> > 
> > >	strm = msblk->decompressor->init(msblk, buffer, length);
> > >+	if (IS_ERR(strm)) {
> > >+		decomp_strm = strm;
> > >+		goto finished;
> > >+	}
> > >
> > >-finished:
> > >+	decomp_strm->strm = strm;
> > >	kfree(buffer);
> > >+	return decomp_strm;
> > >
> > >-	return strm;
> > >+finished:
> > >+	kfree(decomp_strm);
> > >+	kfree(buffer);
> > >+	return decomp_strm;
> > >}
> > >diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
> > >index 330073e..207c810 100644
> > >--- a/fs/squashfs/decompressor.h
> > >+++ b/fs/squashfs/decompressor.h
> > >@@ -26,27 +26,24 @@
> > >struct squashfs_decompressor {
> > >	void	*(*init)(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)
> > >-{
> > >-	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);
> > >-}
> > >+struct squashfs_decomp_strm {
> > >+	void *strm;
> > >+	struct list_head list;
> > >+};
> > >+
> > >+int squashfs_decompress(struct super_block *sb, void **buffer,
> > >+			struct buffer_head **bh, int b, int offset, int length,
> > >+			int srclength, int pages);
> > >+
> > >+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
> > >+	struct squashfs_decomp_strm *stream);
> > >
> > >#ifdef CONFIG_SQUASHFS_XZ
> > >extern const struct squashfs_decompressor squashfs_xz_comp_ops;
> > >diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
> > >index 00f4dfc..e1bf135 100644
> > >--- a/fs/squashfs/lzo_wrapper.c
> > >+++ b/fs/squashfs/lzo_wrapper.c
> > >@@ -74,17 +74,15 @@ 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]))
> > >@@ -111,7 +109,6 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
> > >		bytes -= avail;
> > >	}
> > >
> > >-	mutex_unlock(&msblk->read_data_mutex);
> > >	return res;
> > >
> > >block_release:
> > >@@ -119,7 +116,6 @@ block_release:
> > >		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 405baed..4bb1f90 100644
> > >--- a/fs/squashfs/squashfs.h
> > >+++ b/fs/squashfs/squashfs.h
> > >@@ -52,7 +52,8 @@ 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 struct squashfs_decomp_strm *squashfs_decompressor_init(
> > >+				struct super_block *);
> > >
> > >/* 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..0a209e6 100644
> > >--- a/fs/squashfs/squashfs_fs_sb.h
> > >+++ b/fs/squashfs/squashfs_fs_sb.h
> > >@@ -63,10 +63,10 @@ struct squashfs_sb_info {
> > >	__le64					*id_table;
> > >	__le64					*fragment_index;
> > >	__le64					*xattr_id_table;
> > >-	struct mutex				read_data_mutex;
> > >+	struct mutex                            comp_strm_mutex;
> > >	struct mutex				meta_index_mutex;
> > >	struct meta_index			*meta_index;
> > >-	void					*stream;
> > >+	struct list_head			strm_list;
> > >	__le64					*inode_lookup_table;
> > >	u64					inode_table;
> > >	u64					directory_table;
> > >@@ -76,5 +76,8 @@ struct squashfs_sb_info {
> > >	long long				bytes_used;
> > >	unsigned int				inodes;
> > >	int					xattr_ids;
> > >+	wait_queue_head_t			decomp_wait_queue;
> > >+	int					nr_avail_decomp;
> > >+	unsigned short				flags;
> > >};
> > >#endif
> > >diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
> > >index 60553a9..70aa9c4 100644
> > >--- a/fs/squashfs/super.c
> > >+++ b/fs/squashfs/super.c
> > >@@ -84,7 +84,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> > >	unsigned short flags;
> > >	unsigned int fragments;
> > >	u64 lookup_table_start, xattr_id_table_start, next_table;
> > >-	int err;
> > >+	int err, i;
> > >
> > >	TRACE("Entered squashfs_fill_superblock\n");
> > >
> > >@@ -98,7 +98,9 @@ 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);
> > >+	INIT_LIST_HEAD(&msblk->strm_list);
> > >+	mutex_init(&msblk->comp_strm_mutex);
> > >+	init_waitqueue_head(&msblk->decomp_wait_queue);
> > 
> > This should be done via a helper in decompressor.c, i.e. the implementation
> > shouldn't be visible here.
> > 
> > When I added the decompressor framework, I should have done this.
> > 
> > >	mutex_init(&msblk->meta_index_mutex);
> > >
> > >	/*
> > >@@ -176,6 +178,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> > >	msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
> > >	msblk->inodes = le32_to_cpu(sblk->inodes);
> > >	flags = le16_to_cpu(sblk->flags);
> > >+	msblk->flags = flags;
> > >
> > 
> > ha, the superblock flags should only be needed at mount time.  After
> > mount time there shouldn't be anything in flags we need to look at.
> > 
> > You need to do this because flags is needed for the decompressor init
> > function (COMP_OPTS(flags)) which is now called after mount time.
> > 
> > Once the compressor options parsing is moved back to being only
> > at mount time, you won't need to store the flags anymore.
> 
> Hmm, I'd like to clarify your point further.
> I agree it's unnecessary performance degradation if we read compressor
> option from storage whenever we create new stream buffer.
> But we need it to create new stream buffer(ex, xz_dec_init) dynamically
> so we should keep compressor option into somewhere. It means we should
> keep it to somewhere although we remove flags field from msblk.
> Am I missing something?

I should have been more specific.
My concern is that we could remove the flags from msblk but we need comp_opts
to create new comp buffer dynamically so we should keep comp_opts into somewhere.
So, I think best place to keep it is struct squashfs_sb_info like this.

struct squashfs_sb_info {
        const struct squashfs_decompressor      *decompressor;
        void                                    *comp_opts;
        ...
};

What do you think about it?
-- 
Kind regards,
Minchan Kim

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC,4/5] squashfs: support multiple decompress stream buffer
  2013-10-07  6:35   ` Minchan Kim
@ 2013-10-08  1:25     ` Minchan Kim
  0 siblings, 0 replies; 5+ messages in thread
From: Minchan Kim @ 2013-10-08  1:25 UTC (permalink / raw)
  To: Phillip Lougher; +Cc: linux-kernel, ch0.han, gunho.lee

On Mon, Oct 07, 2013 at 03:35:03PM +0900, Minchan Kim wrote:

< snip >

> > > 
> > > ha, the superblock flags should only be needed at mount time.  After
> > > mount time there shouldn't be anything in flags we need to look at.
> > > 
> > > You need to do this because flags is needed for the decompressor init
> > > function (COMP_OPTS(flags)) which is now called after mount time.
> > > 
> > > Once the compressor options parsing is moved back to being only
> > > at mount time, you won't need to store the flags anymore.
> > 
> > Hmm, I'd like to clarify your point further.
> > I agree it's unnecessary performance degradation if we read compressor
> > option from storage whenever we create new stream buffer.
> > But we need it to create new stream buffer(ex, xz_dec_init) dynamically
> > so we should keep compressor option into somewhere. It means we should
> > keep it to somewhere although we remove flags field from msblk.
> > Am I missing something?
> 
> I should have been more specific.
> My concern is that we could remove the flags from msblk but we need comp_opts
> to create new comp buffer dynamically so we should keep comp_opts into somewhere.
> So, I think best place to keep it is struct squashfs_sb_info like this.
> 
> struct squashfs_sb_info {
>         const struct squashfs_decompressor      *decompressor;
>         void                                    *comp_opts;
>         ...
> };
> 
> What do you think about it?

So, this is my thought. Could you review this? I'd like to rebase other patches
on this.


>From 04b050357f4d6bf1dec8b324c3ab70b0cd113e6c Mon Sep 17 00:00:00 2001
From: Minchan Kim <minchan@kernel.org>
Date: Mon, 7 Oct 2013 16:54:31 +0900
Subject: [PATCH] squashfs: separate decompressor option parse logic and
 stream allocation

Now, when we mount, squashfs parses(ie, read block on storage)
decompressor option and creates stream buffer. Both functions are
abstracted by "initialization" phase of decompressor all at once.

But decompressor option parsing could be done only mounted time
while stream buffer allocation could be done dynamically in future
so this patch try to separate them.

This patch adds new decompressor functions (alloc and destroy)
and changes init function's semantic.

Old :

init : parse decompressor option + create stream buffer

New :

init : parse decompressor option and keep it on memory.
alloc: create stream buffer
destroy: free decompressor option which is allocated in init phase.

Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 fs/squashfs/decompressor.c   |   30 ++++++++++-------
 fs/squashfs/decompressor.h   |   10 +++++-
 fs/squashfs/lzo_wrapper.c    |   14 +++++++-
 fs/squashfs/squashfs.h       |    1 +
 fs/squashfs/squashfs_fs_sb.h |    1 +
 fs/squashfs/super.c          |    9 ++++-
 fs/squashfs/xz_wrapper.c     |   76 +++++++++++++++++++++++++++++++-----------
 fs/squashfs/zlib_wrapper.c   |   14 +++++++-
 8 files changed, 119 insertions(+), 36 deletions(-)

diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
index 3f6271d..b0004c5 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, 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, 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, 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, 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, NULL, 0, "unknown", 0
 };
 
 static const struct squashfs_decompressor *decompressor[] = {
@@ -82,11 +82,11 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
 	return decompressor[i];
 }
 
-
 void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
 {
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	void *strm, *buffer = NULL;
+	int err;
+	void *buffer = NULL;
 	int length = 0;
 
 	/*
@@ -102,15 +102,21 @@ void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
 			PAGE_CACHE_SIZE, 1);
 
 		if (length < 0) {
-			strm = ERR_PTR(length);
-			goto finished;
+			kfree(buffer);
+			return ERR_PTR(length);
 		}
 	}
 
-	strm = msblk->decompressor->init(msblk, buffer, length);
-
-finished:
+	err = msblk->decompressor->init(msblk, buffer, length);
 	kfree(buffer);
 
+	return ERR_PTR(err);
+
+}
+
+void *squashfs_decompressor_alloc(struct super_block *sb)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	void *strm = msblk->decompressor->alloc(msblk);
 	return strm;
 }
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
index 330073e..0c108dd 100644
--- a/fs/squashfs/decompressor.h
+++ b/fs/squashfs/decompressor.h
@@ -24,15 +24,23 @@
  */
 
 struct squashfs_decompressor {
-	void	*(*init)(struct squashfs_sb_info *, void *, int);
+	int	(*init)(struct squashfs_sb_info *, void *, int);
+	void	*(*alloc)(struct squashfs_sb_info *);
 	void	(*free)(void *);
 	int	(*decompress)(struct squashfs_sb_info *, void **,
 		struct buffer_head **, int, int, int, int, int);
+	void	(*destroy)(struct squashfs_sb_info *);
 	int	id;
 	char	*name;
 	int	supported;
 };
 
+static inline void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+	if (msblk->decompressor)
+		msblk->decompressor->destroy(msblk);
+}
+
 static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
 	void *s)
 {
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
index 00f4dfc..f1ad2df 100644
--- a/fs/squashfs/lzo_wrapper.c
+++ b/fs/squashfs/lzo_wrapper.c
@@ -37,7 +37,17 @@ struct squashfs_lzo {
 	void	*output;
 };
 
-static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len)
+static int lzo_init(struct squashfs_sb_info *msblk, void *buffer, int len)
+{
+	return 0;
+}
+
+static void lzo_destroy(struct squashfs_sb_info *msblk)
+{
+
+}
+
+static void *lzo_alloc(struct squashfs_sb_info *msblk)
 {
 	int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
 
@@ -127,8 +137,10 @@ failed:
 
 const struct squashfs_decompressor squashfs_lzo_comp_ops = {
 	.init = lzo_init,
+	.alloc = lzo_alloc,
 	.free = lzo_free,
 	.decompress = lzo_uncompress,
+	.destroy = lzo_destroy,
 	.id = LZO_COMPRESSION,
 	.name = "lzo",
 	.supported = 1
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index d126651..6fc329f 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -49,6 +49,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_init(struct super_block *, unsigned short);
+extern void *squashfs_decompressor_alloc(struct super_block *);
 
 /* 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..a553ec4 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -54,6 +54,7 @@ struct squashfs_cache_entry {
 
 struct squashfs_sb_info {
 	const struct squashfs_decompressor	*decompressor;
+	void 					*decomp_opt;
 	int					devblksize;
 	int					devblksize_log2;
 	struct squashfs_cache			*block_cache;
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 60553a9..47180b9 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -212,7 +212,12 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 		goto failed_mount;
 	}
 
-	msblk->stream = squashfs_decompressor_init(sb, flags);
+	if (squashfs_decompressor_init(sb, flags)) {
+		ERROR("Failed to initialize decompressor\n");
+		goto failed_mount;
+	}
+
+	msblk->stream = squashfs_decompressor_alloc(sb);
 	if (IS_ERR(msblk->stream)) {
 		err = PTR_ERR(msblk->stream);
 		msblk->stream = NULL;
@@ -337,6 +342,7 @@ failed_mount:
 	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);
@@ -384,6 +390,7 @@ static void squashfs_put_super(struct super_block *sb)
 		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..60f30d7e2 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -38,38 +38,72 @@ struct squashfs_xz {
 	struct xz_buf buf;
 };
 
-struct comp_opts {
+struct comp_opts_ondisk {
 	__le32 dictionary_size;
 	__le32 flags;
 };
 
-static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
+struct comp_opts {
+	int dict_size;
+};
+
+static int squashfs_xz_init(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;
+	int err = 0;
+	int dict_size, n;
+	struct comp_opts_ondisk *comp_opts_ondisk = buff;
+	struct comp_opts *comp_opts = NULL;
+	/* check compressor options are the expected length */
+	if (len < sizeof(*comp_opts_ondisk)) {
+		err = -EIO;
+		goto failed;
+	}
 
-	if (comp_opts) {
-		/* check compressor options are the expected length */
-		if (len < sizeof(*comp_opts)) {
-			err = -EIO;
-			goto failed;
-		}
+	comp_opts = kmalloc(sizeof(struct comp_opts), GFP_KERNEL);
+	if (!comp_opts) {
+		err = -ENOMEM;
+		goto failed;
+	}
 
-		dict_size = le32_to_cpu(comp_opts->dictionary_size);
+	dict_size = le32_to_cpu(comp_opts_ondisk->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) +
-						(1 << (n + 1))) {
-			err = -EIO;
-			goto failed;
-		}
+	/* 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) +
+					(1 << (n + 1))) {
+		err = -EIO;
+		goto failed;
 	}
 
 	dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE);
+	comp_opts->dict_size = dict_size;
+	msblk->decomp_opt = comp_opts;
+	return 0;
+failed:
+	kfree(comp_opts);
+	return err;
+}
+
+static void squashfs_xz_destroy(struct squashfs_sb_info *msblk)
+{
+	if (msblk->decomp_opt)
+		kfree(msblk->decomp_opt);
+}
+
+static void *squashfs_xz_alloc(struct squashfs_sb_info *msblk)
+{
+	struct squashfs_xz *stream;
+	struct comp_opts *comp_opts = (struct comp_opts *)msblk->decomp_opt;
+	int err;
+	int dict_size;
+
+	if (comp_opts) {
+		dict_size = comp_opts->dict_size;
+	} else {
+		dict_size = max_t(int, msblk->block_size,
+				SQUASHFS_METADATA_SIZE);
+	}
 
 	stream = kmalloc(sizeof(*stream), GFP_KERNEL);
 	if (stream == NULL) {
@@ -172,8 +206,10 @@ release_mutex:
 
 const struct squashfs_decompressor squashfs_xz_comp_ops = {
 	.init = squashfs_xz_init,
+	.alloc = squashfs_xz_alloc,
 	.free = squashfs_xz_free,
 	.decompress = squashfs_xz_uncompress,
+	.destroy = squashfs_xz_destroy,
 	.id = XZ_COMPRESSION,
 	.name = "xz",
 	.supported = 1
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index 55d918f..e46e24d 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -33,7 +33,17 @@
 #include "squashfs.h"
 #include "decompressor.h"
 
-static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
+static int zlib_init(struct squashfs_sb_info *dummy, void *buffer, int len)
+{
+	return 0;
+}
+
+static void zlib_destroy(struct squashfs_sb_info *dummy)
+{
+
+}
+
+static void *zlib_alloc(struct squashfs_sb_info *dummy)
 {
 	z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
 	if (stream == NULL)
@@ -140,8 +150,10 @@ release_mutex:
 
 const struct squashfs_decompressor squashfs_zlib_comp_ops = {
 	.init = zlib_init,
+	.alloc = zlib_alloc,
 	.free = zlib_free,
 	.decompress = zlib_uncompress,
+	.destroy = zlib_destroy,
 	.id = ZLIB_COMPRESSION,
 	.name = "zlib",
 	.supported = 1
-- 
1.7.9.5

-- 
Kind regards,
Minchan Kim

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC 4/5] squashfs: support multiple decompress stream buffer
  2013-09-16  7:08 [RFC 0/5] squashfs enhance Minchan Kim
@ 2013-09-16  7:08 ` Minchan Kim
  0 siblings, 0 replies; 5+ messages in thread
From: Minchan Kim @ 2013-09-16  7:08 UTC (permalink / raw)
  To: Phillip Lougher; +Cc: linux-kernel, ch0.han, gunho.lee, Minchan Kim

Now squashfs have used for only one stream buffer for decompression
so it hurts concurrent read performance due to locking lock of getting
stream buffer.

When file system mount, the number of stream buffer is started from
num_online_cpus() and grows up to num_online_cpus() * 2M / block_size * 2.
The rationale is MM does readahead chunk into 2M unit to prevent too much
memory pin and while one request is waitting, we should request another
chunk. That's why I multiply by 2.

If it reveals too much memory problem, we can add shrinker routine.

I did test following as

Two 1G file dd read

dd if=test/test1.dat of=/dev/null &
dd if=test/test2.dat of=/dev/null &

old : 60sec -> new : 30 sec

Signed-off-by: Minchan Kim <minchan@kernel.org>
---
 fs/squashfs/block.c          |    9 ++--
 fs/squashfs/decompressor.c   |  105 ++++++++++++++++++++++++++++++++++++++----
 fs/squashfs/decompressor.h   |   27 +++++------
 fs/squashfs/lzo_wrapper.c    |   12 ++---
 fs/squashfs/squashfs.h       |    3 +-
 fs/squashfs/squashfs_fs_sb.h |    7 ++-
 fs/squashfs/super.c          |   40 ++++++++++++----
 fs/squashfs/xz_wrapper.c     |   20 ++++----
 fs/squashfs/zlib_wrapper.c   |   12 ++---
 9 files changed, 168 insertions(+), 67 deletions(-)

diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index f33c6ef..d41bac8 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -78,14 +78,14 @@ static struct buffer_head *get_block_length(struct super_block *sb,
 
 
 
-int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
+int squashfs_decompress_block(struct super_block *sb, int compressed,
 		void **buffer, struct buffer_head **bh, int nr_bh,
 		int offset, int length, int srclength, int pages)
 {
 	int k = 0;
 
 	if (compressed) {
-		length = squashfs_decompress(msblk, buffer, bh, nr_bh,
+		length = squashfs_decompress(sb, buffer, bh, nr_bh,
 				offset, length, srclength, pages);
 		if (length < 0)
 			goto out;
@@ -93,6 +93,7 @@ int squashfs_decompress_block(struct squashfs_sb_info *msblk, int compressed,
 		/*
 		 * Block is uncompressed.
 		 */
+		struct squashfs_sb_info *msblk = sb->s_fs_info;
 		int bytes, in, avail, pg_offset = 0, page = 0;
 
 		for (bytes = length; k < nr_bh; k++) {
@@ -262,8 +263,8 @@ int squashfs_read_metablock(struct super_block *sb, void **buffer, u64 index,
 	}
 	ll_rw_block(READ, b - 1, bh + 1);
 
-	length = squashfs_decompress_block(msblk, compressed, buffer, bh, b,
-				offset, length, srclength, pages);
+	length = squashfs_decompress_block(sb, compressed, buffer, bh,
+				b, offset, length, srclength, pages);
 	if (length < 0)
 		goto read_failure;
 
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
index e47453e..ed35b32 100644
--- a/fs/squashfs/decompressor.c
+++ b/fs/squashfs/decompressor.c
@@ -25,6 +25,8 @@
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/buffer_head.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -70,6 +72,80 @@ static const struct squashfs_decompressor *decompressor[] = {
 	&squashfs_unknown_comp_ops
 };
 
+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
+	struct squashfs_decomp_strm *stream)
+{
+	if (msblk->decompressor)
+		msblk->decompressor->free(stream->strm);
+	kfree(stream);
+}
+
+static void *squashfs_get_decomp_strm(struct squashfs_sb_info *msblk)
+{
+	struct squashfs_decomp_strm *strm = NULL;
+	mutex_lock(&msblk->comp_strm_mutex);
+	if (!list_empty(&msblk->strm_list)) {
+		strm = list_entry(msblk->strm_list.next,
+				struct squashfs_decomp_strm, list);
+		list_del(&strm->list);
+		msblk->nr_avail_decomp--;
+		WARN_ON(msblk->nr_avail_decomp < 0);
+	}
+	mutex_unlock(&msblk->comp_strm_mutex);
+	return strm;
+}
+
+static bool full_decomp_strm(struct squashfs_sb_info *msblk)
+{
+	/* MM do readahread 2M unit */
+	int blocks = 2 * 1024 * 1024 / msblk->block_size;
+	return msblk->nr_avail_decomp > (num_online_cpus() * blocks * 2);
+}
+
+static void squashfs_put_decomp_strm(struct squashfs_sb_info *msblk,
+					struct squashfs_decomp_strm *strm)
+{
+	mutex_lock(&msblk->comp_strm_mutex);
+	if (full_decomp_strm(msblk)) {
+		mutex_unlock(&msblk->comp_strm_mutex);
+		squashfs_decompressor_free(msblk, strm);
+		return;
+	}
+
+	list_add(&strm->list, &msblk->strm_list);
+	msblk->nr_avail_decomp++;
+	mutex_unlock(&msblk->comp_strm_mutex);
+	wake_up(&msblk->decomp_wait_queue);
+}
+
+int squashfs_decompress(struct super_block *sb, void **buffer,
+			struct buffer_head **bh, int b, int offset, int length,
+			int srclength, int pages)
+{
+	int ret;
+	struct squashfs_decomp_strm *strm;
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	while (1) {
+		strm = squashfs_get_decomp_strm(msblk);
+		if (strm)
+			break;
+
+		if (!full_decomp_strm(msblk)) {
+			strm = squashfs_decompressor_init(sb);
+			if (strm)
+				break;
+		}
+
+		wait_event(msblk->decomp_wait_queue, msblk->nr_avail_decomp);
+		continue;
+	}
+
+	ret = msblk->decompressor->decompress(msblk, strm->strm, buffer, bh,
+		b, offset, length, srclength, pages);
+
+	squashfs_put_decomp_strm(msblk, strm);
+	return ret;
+}
 
 const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
 {
@@ -82,35 +158,48 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
 	return decompressor[i];
 }
 
-
-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
+struct squashfs_decomp_strm *squashfs_decompressor_init(struct super_block *sb)
 {
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	struct squashfs_decomp_strm *decomp_strm = NULL;
 	void *strm, *buffer = NULL;
 	int length = 0;
 
+	decomp_strm = kmalloc(sizeof(struct squashfs_decomp_strm), GFP_KERNEL);
+	if (!decomp_strm)
+		return ERR_PTR(-ENOMEM);
 	/*
 	 * Read decompressor specific options from file system if present
 	 */
-	if (SQUASHFS_COMP_OPTS(flags)) {
+	if (SQUASHFS_COMP_OPTS(msblk->flags)) {
 		buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
-		if (buffer == NULL)
-			return ERR_PTR(-ENOMEM);
+		if (buffer == NULL) {
+			decomp_strm = ERR_PTR(-ENOMEM);
+			goto finished;
+		}
 
 		length = squashfs_read_metablock(sb, &buffer,
 			sizeof(struct squashfs_super_block), 0, NULL,
 			PAGE_CACHE_SIZE, 1);
 
 		if (length < 0) {
-			strm = ERR_PTR(length);
+			decomp_strm = ERR_PTR(length);
 			goto finished;
 		}
 	}
 
 	strm = msblk->decompressor->init(msblk, buffer, length);
+	if (IS_ERR(strm)) {
+		decomp_strm = strm;
+		goto finished;
+	}
 
-finished:
+	decomp_strm->strm = strm;
 	kfree(buffer);
+	return decomp_strm;
 
-	return strm;
+finished:
+	kfree(decomp_strm);
+	kfree(buffer);
+	return decomp_strm;
 }
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
index 330073e..207c810 100644
--- a/fs/squashfs/decompressor.h
+++ b/fs/squashfs/decompressor.h
@@ -26,27 +26,24 @@
 struct squashfs_decompressor {
 	void	*(*init)(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)
-{
-	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);
-}
+struct squashfs_decomp_strm {
+	void *strm;
+	struct list_head list;
+};
+
+int squashfs_decompress(struct super_block *sb, void **buffer,
+			struct buffer_head **bh, int b, int offset, int length,
+			int srclength, int pages);
+
+void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
+	struct squashfs_decomp_strm *stream);
 
 #ifdef CONFIG_SQUASHFS_XZ
 extern const struct squashfs_decompressor squashfs_xz_comp_ops;
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
index 00f4dfc..e1bf135 100644
--- a/fs/squashfs/lzo_wrapper.c
+++ b/fs/squashfs/lzo_wrapper.c
@@ -74,17 +74,15 @@ 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]))
@@ -111,7 +109,6 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 		bytes -= avail;
 	}
 
-	mutex_unlock(&msblk->read_data_mutex);
 	return res;
 
 block_release:
@@ -119,7 +116,6 @@ block_release:
 		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 405baed..4bb1f90 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -52,7 +52,8 @@ 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 struct squashfs_decomp_strm *squashfs_decompressor_init(
+				struct super_block *);
 
 /* 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..0a209e6 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -63,10 +63,10 @@ struct squashfs_sb_info {
 	__le64					*id_table;
 	__le64					*fragment_index;
 	__le64					*xattr_id_table;
-	struct mutex				read_data_mutex;
+	struct mutex                            comp_strm_mutex;
 	struct mutex				meta_index_mutex;
 	struct meta_index			*meta_index;
-	void					*stream;
+	struct list_head			strm_list;
 	__le64					*inode_lookup_table;
 	u64					inode_table;
 	u64					directory_table;
@@ -76,5 +76,8 @@ struct squashfs_sb_info {
 	long long				bytes_used;
 	unsigned int				inodes;
 	int					xattr_ids;
+	wait_queue_head_t			decomp_wait_queue;
+	int					nr_avail_decomp;
+	unsigned short				flags;
 };
 #endif
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 60553a9..70aa9c4 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -84,7 +84,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	unsigned short flags;
 	unsigned int fragments;
 	u64 lookup_table_start, xattr_id_table_start, next_table;
-	int err;
+	int err, i;
 
 	TRACE("Entered squashfs_fill_superblock\n");
 
@@ -98,7 +98,9 @@ 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);
+	INIT_LIST_HEAD(&msblk->strm_list);
+	mutex_init(&msblk->comp_strm_mutex);
+	init_waitqueue_head(&msblk->decomp_wait_queue);
 	mutex_init(&msblk->meta_index_mutex);
 
 	/*
@@ -176,6 +178,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
 	msblk->inodes = le32_to_cpu(sblk->inodes);
 	flags = le16_to_cpu(sblk->flags);
+	msblk->flags = flags;
 
 	TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b));
 	TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
@@ -212,11 +215,16 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 		goto failed_mount;
 	}
 
-	msblk->stream = squashfs_decompressor_init(sb, flags);
-	if (IS_ERR(msblk->stream)) {
-		err = PTR_ERR(msblk->stream);
-		msblk->stream = NULL;
-		goto failed_mount;
+	/* Allocate mutliple decompressor */
+	for (i = 0; i < num_online_cpus(); i++) {
+		struct squashfs_decomp_strm *decomp_strm;
+		decomp_strm = squashfs_decompressor_init(sb);
+		if (IS_ERR(decomp_strm)) {
+			err = PTR_ERR(decomp_strm);
+			goto failed_mount;
+		}
+		list_add(&decomp_strm->list, &msblk->strm_list);
+		msblk->nr_avail_decomp++;
 	}
 
 	/* Handle xattrs */
@@ -336,7 +344,14 @@ 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);
+	while (!list_empty(&msblk->strm_list)) {
+		struct squashfs_decomp_strm *decomp_strm =
+			list_entry(msblk->strm_list.prev,
+					struct squashfs_decomp_strm, list);
+		list_del(&decomp_strm->list);
+		squashfs_decompressor_free(msblk, decomp_strm);
+	}
+	msblk->nr_avail_decomp = 0;
 	kfree(msblk->inode_lookup_table);
 	kfree(msblk->fragment_index);
 	kfree(msblk->id_table);
@@ -383,7 +398,14 @@ 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);
+		while (!list_empty(&sbi->strm_list)) {
+			struct squashfs_decomp_strm *decomp_strm =
+				list_entry(sbi->strm_list.prev,
+					struct squashfs_decomp_strm, list);
+			list_del(&decomp_strm->list);
+			squashfs_decompressor_free(sbi, decomp_strm);
+		}
+		sbi->nr_avail_decomp = 0;
 		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..98b8bb5 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -103,15 +103,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;
@@ -126,7 +124,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 			length -= avail;
 			wait_on_buffer(bh[k]);
 			if (!buffer_uptodate(bh[k]))
-				goto release_mutex;
+				goto out;
 
 			stream->buf.in = bh[k]->b_data + offset;
 			stream->buf.in_size = avail;
@@ -149,20 +147,18 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 
 	if (xz_err != XZ_STREAM_END) {
 		ERROR("xz_dec_run error, data probably corrupt\n");
-		goto release_mutex;
+		goto out;
 	}
 
 	if (k < b) {
 		ERROR("xz_uncompress error, input remaining\n");
-		goto release_mutex;
+		goto out;
 	}
 
 	total += stream->buf.out_pos;
-	mutex_unlock(&msblk->read_data_mutex);
 	return total;
 
-release_mutex:
-	mutex_unlock(&msblk->read_data_mutex);
+out:
 
 	for (; k < b; k++)
 		put_bh(bh[k]);
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index 55d918f..82c1a70 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -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;
@@ -126,11 +124,9 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 	}
 
 	length = stream->total_out;
-	mutex_unlock(&msblk->read_data_mutex);
 	return length;
 
 release_mutex:
-	mutex_unlock(&msblk->read_data_mutex);
 
 	for (; k < b; k++)
 		put_bh(bh[k]);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-10-08  1:24 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-07  3:41 [RFC,4/5] squashfs: support multiple decompress stream buffer Phillip Lougher
2013-10-07  6:09 ` Minchan Kim
2013-10-07  6:35   ` Minchan Kim
2013-10-08  1:25     ` Minchan Kim
  -- strict thread matches above, loose matches on Subject: below --
2013-09-16  7:08 [RFC 0/5] squashfs enhance Minchan Kim
2013-09-16  7:08 ` [RFC 4/5] squashfs: support multiple decompress stream buffer Minchan Kim

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).