Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1737889 > unrolled thread

[PATCH 0/5] Squashfs Whitelist and Compression Threshold

Started byDaniel Rosenberg <drosen@google.com>
First post2017-09-23 00:00 +0200
Last post2017-09-23 00:00 +0200
Articles 2 — 1 participant

Back to article view | Back to linux.kernel


Contents

  [PATCH 0/5] Squashfs Whitelist and Compression Threshold Daniel Rosenberg <drosen@google.com> - 2017-09-23 00:00 +0200
    [PATCH 4/5] Squashfs: implement .readpages() Daniel Rosenberg <drosen@google.com> - 2017-09-23 00:00 +0200

#1737889 — [PATCH 0/5] Squashfs Whitelist and Compression Threshold

FromDaniel Rosenberg <drosen@google.com>
Date2017-09-23 00:00 +0200
Subject[PATCH 0/5] Squashfs Whitelist and Compression Threshold
Message-ID<usE9k-2Uu-5@gated-at.bofh.it>
These patches contain several optimizations to Squashfs. ll_rw_block is
replaced with submit_bio. readpages is implemented to support asynchronous
readahead. Uncompressed file reads are optimized, no longer requiring the
entire block to be read if the block doesn't need to be decompressed, which
greatly improves random read speeds in uncompressed files. There is a separate
set of patches to the userspace tools adding a whitelist of files to not
compress, and an ability to set a compression threshold to avoid compressing
files where there isn't a significant advantage made by compressing.

Adrien Schildknecht (5):
  Squashfs: remove the FILE_CACHE option
  Squashfs: refactor page_actor
  Squashfs: replace buffer_head with BIO
  Squashfs: implement .readpages()
  Squashfs: optimize reading uncompressed data

 fs/squashfs/Kconfig          |  28 ---
 fs/squashfs/Makefile         |   3 +-
 fs/squashfs/block.c          | 547 ++++++++++++++++++++++++++++++++-----------
 fs/squashfs/cache.c          |  73 +++---
 fs/squashfs/decompressor.c   |  55 +++--
 fs/squashfs/file.c           | 137 ++++++++---
 fs/squashfs/file_cache.c     |  38 ---
 fs/squashfs/file_direct.c    | 240 +++++++++----------
 fs/squashfs/lz4_wrapper.c    |  29 +--
 fs/squashfs/lzo_wrapper.c    |  40 +---
 fs/squashfs/page_actor.c     | 175 +++++++++-----
 fs/squashfs/page_actor.h     |  84 +++----
 fs/squashfs/squashfs.h       |   9 +-
 fs/squashfs/squashfs_fs_sb.h |   2 +-
 fs/squashfs/super.c          |   7 +
 fs/squashfs/xz_wrapper.c     |  15 +-
 fs/squashfs/zlib_wrapper.c   |  14 +-
 17 files changed, 876 insertions(+), 620 deletions(-)
 delete mode 100644 fs/squashfs/file_cache.c

-- 
2.14.1.821.g8fa685d3b7-goog

[toc] | [next] | [standalone]


#1737892 — [PATCH 4/5] Squashfs: implement .readpages()

FromDaniel Rosenberg <drosen@google.com>
Date2017-09-23 00:00 +0200
Subject[PATCH 4/5] Squashfs: implement .readpages()
Message-ID<usE9l-2Uu-27@gated-at.bofh.it>
In reply to#1737889
From: Adrien Schildknecht <adrien+dev@schischi.me>

Squashfs does not implement .readpages(), so the kernel just repeatedly
calls .readpage().

The readpages function tries to pack as much pages as possible in the
same page actor so that only 1 read request is issued.

Now that the read requests are asynchronous, the kernel can truly
prefetch pages using its readahead algorithm.

Signed-off-by: Adrien Schildknecht <adriens@google.com>
Signed-off-by: Daniel Rosenberg <drosen@google.com>
---
 fs/squashfs/file.c        | 137 ++++++++++++++++++++++++++++++++++------------
 fs/squashfs/file_direct.c |  62 ++++++++++++++-------
 fs/squashfs/squashfs.h    |   5 +-
 3 files changed, 146 insertions(+), 58 deletions(-)

diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
index 13d80947bf9e..bb2e77ee4209 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -47,6 +47,7 @@
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/mutex.h>
+#include <linux/mm_inline.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -438,6 +439,21 @@ static int squashfs_readpage_fragment(struct page *page)
 	return res;
 }
 
+static int squashfs_readpages_fragment(struct page *page,
+	struct list_head *readahead_pages, struct address_space *mapping)
+{
+	if (!page) {
+		page = lru_to_page(readahead_pages);
+		list_del(&page->lru);
+		if (add_to_page_cache_lru(page, mapping, page->index,
+			mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+			put_page(page);
+			return 0;
+		}
+	}
+	return squashfs_readpage_fragment(page);
+}
+
 static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
 {
 	struct inode *inode = page->mapping->host;
@@ -450,54 +466,105 @@ static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
 	return 0;
 }
 
-static int squashfs_readpage(struct file *file, struct page *page)
+static int squashfs_readpages_sparse(struct page *page,
+	struct list_head *readahead_pages, int index, int file_end,
+	struct address_space *mapping)
 {
-	struct inode *inode = page->mapping->host;
+	if (!page) {
+		page = lru_to_page(readahead_pages);
+		list_del(&page->lru);
+		if (add_to_page_cache_lru(page, mapping, page->index,
+			mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+			put_page(page);
+			return 0;
+		}
+	}
+	return squashfs_readpage_sparse(page, index, file_end);
+}
+
+static int __squashfs_readpages(struct file *file, struct page *page,
+	struct list_head *readahead_pages, unsigned int nr_pages,
+	struct address_space *mapping)
+{
+	struct inode *inode = mapping->host;
 	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-	int index = page->index >> (msblk->block_log - PAGE_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);
+	do {
+		struct page *cur_page = page ? page
+					     : lru_to_page(readahead_pages);
+		int page_index = cur_page->index;
+		int index = page_index >> (msblk->block_log - PAGE_SHIFT);
+
+		if (page_index >= ((i_size_read(inode) + PAGE_SIZE - 1) >>
+						PAGE_SHIFT))
+			return 1;
+
+		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)
+				return -1;
+
+			if (bsize == 0) {
+				res = squashfs_readpages_sparse(page,
+					readahead_pages, index, file_end,
+					mapping);
+			} else {
+				res = squashfs_readpages_block(page,
+					readahead_pages, &nr_pages, mapping,
+					page_index, block, bsize);
+			}
+		} else {
+			res = squashfs_readpages_fragment(page,
+				readahead_pages, mapping);
+		}
+		if (res)
+			return 0;
+		page = NULL;
+	} while (readahead_pages && !list_empty(readahead_pages));
+
+	return 0;
+}
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+	int ret;
 
-	if (page->index >= ((i_size_read(inode) + PAGE_SIZE - 1) >>
-					PAGE_SHIFT))
-		goto out;
+	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+	      page->index, squashfs_i(page->mapping->host)->start);
 
-	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;
+	get_page(page);
 
-		if (bsize == 0)
-			res = squashfs_readpage_sparse(page, index, file_end);
+	ret = __squashfs_readpages(file, page, NULL, 1, page->mapping);
+	if (ret) {
+		flush_dcache_page(page);
+		if (ret < 0)
+			SetPageError(page);
 		else
-			res = squashfs_readpage_block(page, block, bsize);
-	} else
-		res = squashfs_readpage_fragment(page);
-
-	if (!res)
-		return 0;
-
-error_out:
-	SetPageError(page);
-out:
-	pageaddr = kmap_atomic(page);
-	memset(pageaddr, 0, PAGE_SIZE);
-	kunmap_atomic(pageaddr);
-	flush_dcache_page(page);
-	if (!PageError(page))
-		SetPageUptodate(page);
-	unlock_page(page);
+			SetPageUptodate(page);
+		zero_user_segment(page, 0, PAGE_SIZE);
+		unlock_page(page);
+		put_page(page);
+	}
 
 	return 0;
 }
 
+static int squashfs_readpages(struct file *file, struct address_space *mapping,
+			      struct list_head *pages, unsigned int nr_pages)
+{
+	TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n",
+		nr_pages, lru_to_page(pages)->index);
+	__squashfs_readpages(file, NULL, pages, nr_pages, mapping);
+	return 0;
+}
+
 
 const struct address_space_operations squashfs_aops = {
-	.readpage = squashfs_readpage
+	.readpage = squashfs_readpage,
+	.readpages = squashfs_readpages,
 };
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
index 74b8f381320b..a978811de327 100644
--- a/fs/squashfs/file_direct.c
+++ b/fs/squashfs/file_direct.c
@@ -13,6 +13,7 @@
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/mutex.h>
+#include <linux/mm_inline.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -45,23 +46,40 @@ static void release_actor_pages(struct page **page, int pages, int error)
  * page cache pages appropriately within the decompressor
  */
 static struct squashfs_page_actor *actor_from_page_cache(
-	struct page *target_page, int start_index, int nr_pages)
+	unsigned int actor_pages, struct page *target_page,
+	struct list_head *rpages, unsigned int *nr_pages, int start_index,
+	struct address_space *mapping)
 {
-	int i, n;
 	struct page **page;
 	struct squashfs_page_actor *actor;
+	int i, n;
+	gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL);
 
-	page = kmalloc_array(nr_pages, sizeof(void *), GFP_KERNEL);
+	page = kmalloc_array(actor_pages, sizeof(void *), GFP_KERNEL);
 	if (!page)
 		return NULL;
 
-	/* Try to grab all the pages covered by the SquashFS block */
-	for (i = 0, n = start_index; i < nr_pages; i++, n++) {
-		if (target_page->index == n) {
+	for (i = 0, n = start_index; i < actor_pages; i++, n++) {
+		if (target_page == NULL && rpages && !list_empty(rpages)) {
+			struct page *cur_page = lru_to_page(rpages);
+
+			if (cur_page->index < start_index + actor_pages) {
+				list_del(&cur_page->lru);
+				--(*nr_pages);
+				if (add_to_page_cache_lru(cur_page, mapping,
+							  cur_page->index, gfp))
+					put_page(cur_page);
+				else
+					target_page = cur_page;
+			} else
+				rpages = NULL;
+		}
+
+		if (target_page && target_page->index == n) {
 			page[i] = target_page;
+			target_page = NULL;
 		} else {
-			page[i] = grab_cache_page_nowait(target_page->mapping,
-							 n);
+			page[i] = grab_cache_page_nowait(mapping, n);
 			if (page[i] == NULL)
 				continue;
 		}
@@ -73,40 +91,42 @@ static struct squashfs_page_actor *actor_from_page_cache(
 		}
 	}
 
-	actor = squashfs_page_actor_init(page, nr_pages, 0,
+	actor = squashfs_page_actor_init(page, actor_pages, 0,
 			release_actor_pages);
 	if (!actor) {
-		release_actor_pages(page, nr_pages, -ENOMEM);
+		release_actor_pages(page, actor_pages, -ENOMEM);
 		kfree(page);
 		return NULL;
 	}
 	return actor;
 }
 
-/* Read separately compressed datablock directly into page cache */
-int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
+int squashfs_readpages_block(struct page *target_page,
+			     struct list_head *readahead_pages,
+			     unsigned int *nr_pages,
+			     struct address_space *mapping,
+			     int page_index, u64 block, int bsize)
 
 {
-	struct inode *inode = target_page->mapping->host;
+	struct squashfs_page_actor *actor;
+	struct inode *inode = mapping->host;
 	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-
 	int file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
 	int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
-	int start_index = target_page->index & ~mask;
+	int start_index = page_index & ~mask;
 	int end_index = start_index | mask;
-	int pages, res = -ENOMEM;
-	struct squashfs_page_actor *actor;
+	int actor_pages, res;
 
 	if (end_index > file_end)
 		end_index = file_end;
-	pages = end_index - start_index + 1;
-
-	actor = actor_from_page_cache(target_page, start_index, pages);
+	actor_pages = end_index - start_index + 1;
 
+	actor = actor_from_page_cache(actor_pages, target_page,
+				      readahead_pages, nr_pages, start_index,
+				      mapping);
 	if (!actor)
 		return -ENOMEM;
 
-	get_page(target_page);
 	res = squashfs_read_data_async(inode->i_sb, block, bsize, NULL,
 				       actor);
 	return res < 0 ? res : 0;
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 66d7194563b6..f4faab52a879 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -74,8 +74,9 @@ extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
 void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
 				int);
 
-/* file_xxx.c */
-extern int squashfs_readpage_block(struct page *, u64, int);
+/* file_direct.c */
+extern int squashfs_readpages_block(struct page *, struct list_head *,
+	unsigned int *, struct address_space *, int, u64, int);
 
 /* id.c */
 extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
-- 
2.14.1.821.g8fa685d3b7-goog

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web