Commit 105d4ad8 authored by Gao Xiang's avatar Gao Xiang Committed by Greg Kroah-Hartman

staging: erofs: introduce cached decompression

This patch adds an optional choice which can be
enabled by users in order to cache both incomplete
ends of compressed clusters as a complement to
the in-place decompression in order to boost random
read, but it costs more memory than the in-place
decompression only.
Signed-off-by: default avatarGao Xiang <gaoxiang25@huawei.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3883a79a
...@@ -101,3 +101,41 @@ config EROFS_FS_CLUSTER_PAGE_LIMIT ...@@ -101,3 +101,41 @@ config EROFS_FS_CLUSTER_PAGE_LIMIT
than 2. Otherwise, the image cannot be mounted than 2. Otherwise, the image cannot be mounted
correctly on this kernel. correctly on this kernel.
choice
prompt "EROFS VLE Data Decompression mode"
depends on EROFS_FS_ZIP
default EROFS_FS_ZIP_CACHE_BIPOLAR
help
EROFS supports three options for VLE decompression.
"In-place Decompression Only" consumes the minimum memory
with lowest random read.
"Bipolar Cached Decompression" consumes the maximum memory
with highest random read.
If unsure, select "Bipolar Cached Decompression"
config EROFS_FS_ZIP_NO_CACHE
bool "In-place Decompression Only"
help
Read compressed data into page cache and do in-place
decompression directly.
config EROFS_FS_ZIP_CACHE_UNIPOLAR
bool "Unipolar Cached Decompression"
help
For each request, it caches the last compressed page
for further reading.
It still decompresses in place for the rest compressed pages.
config EROFS_FS_ZIP_CACHE_BIPOLAR
bool "Bipolar Cached Decompression"
help
For each request, it caches the both end compressed pages
for further reading.
It still decompresses in place for the rest compressed pages.
Recommended for performance priority.
endchoice
...@@ -58,6 +58,18 @@ struct erofs_fault_info { ...@@ -58,6 +58,18 @@ struct erofs_fault_info {
}; };
#endif #endif
#ifdef CONFIG_EROFS_FS_ZIP_CACHE_BIPOLAR
#define EROFS_FS_ZIP_CACHE_LVL (2)
#elif defined(EROFS_FS_ZIP_CACHE_UNIPOLAR)
#define EROFS_FS_ZIP_CACHE_LVL (1)
#else
#define EROFS_FS_ZIP_CACHE_LVL (0)
#endif
#if (!defined(EROFS_FS_HAS_MANAGED_CACHE) && (EROFS_FS_ZIP_CACHE_LVL > 0))
#define EROFS_FS_HAS_MANAGED_CACHE
#endif
/* EROFS_SUPER_MAGIC_V1 to represent the whole file system */ /* EROFS_SUPER_MAGIC_V1 to represent the whole file system */
#define EROFS_SUPER_MAGIC EROFS_SUPER_MAGIC_V1 #define EROFS_SUPER_MAGIC EROFS_SUPER_MAGIC_V1
...@@ -82,6 +94,11 @@ struct erofs_sb_info { ...@@ -82,6 +94,11 @@ struct erofs_sb_info {
/* the dedicated workstation for compression */ /* the dedicated workstation for compression */
struct radix_tree_root workstn_tree; struct radix_tree_root workstn_tree;
#ifdef EROFS_FS_HAS_MANAGED_CACHE
struct inode *managed_cache;
#endif
#endif #endif
u32 build_time_nsec; u32 build_time_nsec;
...@@ -240,6 +257,15 @@ static inline void erofs_workstation_cleanup_all(struct super_block *sb) ...@@ -240,6 +257,15 @@ static inline void erofs_workstation_cleanup_all(struct super_block *sb)
erofs_shrink_workstation(EROFS_SB(sb), ~0UL, true); erofs_shrink_workstation(EROFS_SB(sb), ~0UL, true);
} }
#ifdef EROFS_FS_HAS_MANAGED_CACHE
#define EROFS_UNALLOCATED_CACHED_PAGE ((void *)0x5F0EF00D)
extern int try_to_free_all_cached_pages(struct erofs_sb_info *sbi,
struct erofs_workgroup *egrp);
extern int try_to_free_cached_page(struct address_space *mapping,
struct page *page);
#endif
#endif #endif
/* we strictly follow PAGE_SIZE and no buffer head yet */ /* we strictly follow PAGE_SIZE and no buffer head yet */
......
...@@ -256,6 +256,63 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -256,6 +256,63 @@ static int parse_options(struct super_block *sb, char *options)
return 0; return 0;
} }
#ifdef EROFS_FS_HAS_MANAGED_CACHE
static const struct address_space_operations managed_cache_aops;
static int managed_cache_releasepage(struct page *page, gfp_t gfp_mask)
{
int ret = 1; /* 0 - busy */
struct address_space *const mapping = page->mapping;
BUG_ON(!PageLocked(page));
BUG_ON(mapping->a_ops != &managed_cache_aops);
if (PagePrivate(page))
ret = try_to_free_cached_page(mapping, page);
return ret;
}
static void managed_cache_invalidatepage(struct page *page,
unsigned int offset, unsigned int length)
{
const unsigned int stop = length + offset;
BUG_ON(!PageLocked(page));
/* Check for overflow */
BUG_ON(stop > PAGE_SIZE || stop < length);
if (offset == 0 && stop == PAGE_SIZE)
while (!managed_cache_releasepage(page, GFP_NOFS))
cond_resched();
}
static const struct address_space_operations managed_cache_aops = {
.releasepage = managed_cache_releasepage,
.invalidatepage = managed_cache_invalidatepage,
};
static struct inode *erofs_init_managed_cache(struct super_block *sb)
{
struct inode *inode = new_inode(sb);
if (unlikely(inode == NULL))
return ERR_PTR(-ENOMEM);
set_nlink(inode, 1);
inode->i_size = OFFSET_MAX;
inode->i_mapping->a_ops = &managed_cache_aops;
mapping_set_gfp_mask(inode->i_mapping,
GFP_NOFS | __GFP_HIGHMEM |
__GFP_MOVABLE | __GFP_NOFAIL);
return inode;
}
#endif
static int erofs_read_super(struct super_block *sb, static int erofs_read_super(struct super_block *sb,
const char *dev_name, void *data, int silent) const char *dev_name, void *data, int silent)
{ {
...@@ -307,6 +364,14 @@ static int erofs_read_super(struct super_block *sb, ...@@ -307,6 +364,14 @@ static int erofs_read_super(struct super_block *sb,
INIT_RADIX_TREE(&sbi->workstn_tree, GFP_ATOMIC); INIT_RADIX_TREE(&sbi->workstn_tree, GFP_ATOMIC);
#endif #endif
#ifdef EROFS_FS_HAS_MANAGED_CACHE
sbi->managed_cache = erofs_init_managed_cache(sb);
if (IS_ERR(sbi->managed_cache)) {
err = PTR_ERR(sbi->managed_cache);
goto err_init_managed_cache;
}
#endif
/* get the root inode */ /* get the root inode */
inode = erofs_iget(sb, ROOT_NID(sbi), true); inode = erofs_iget(sb, ROOT_NID(sbi), true);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
...@@ -361,6 +426,10 @@ static int erofs_read_super(struct super_block *sb, ...@@ -361,6 +426,10 @@ static int erofs_read_super(struct super_block *sb,
if (sb->s_root == NULL) if (sb->s_root == NULL)
iput(inode); iput(inode);
err_iget: err_iget:
#ifdef EROFS_FS_HAS_MANAGED_CACHE
iput(sbi->managed_cache);
err_init_managed_cache:
#endif
err_parseopt: err_parseopt:
err_sbread: err_sbread:
sb->s_fs_info = NULL; sb->s_fs_info = NULL;
...@@ -386,6 +455,10 @@ static void erofs_put_super(struct super_block *sb) ...@@ -386,6 +455,10 @@ static void erofs_put_super(struct super_block *sb)
infoln("unmounted for %s", sbi->dev_name); infoln("unmounted for %s", sbi->dev_name);
__putname(sbi->dev_name); __putname(sbi->dev_name);
#ifdef EROFS_FS_HAS_MANAGED_CACHE
iput(sbi->managed_cache);
#endif
mutex_lock(&sbi->umount_mutex); mutex_lock(&sbi->umount_mutex);
#ifdef CONFIG_EROFS_FS_ZIP #ifdef CONFIG_EROFS_FS_ZIP
......
This diff is collapsed.
...@@ -143,13 +143,28 @@ unsigned long erofs_shrink_workstation(struct erofs_sb_info *sbi, ...@@ -143,13 +143,28 @@ unsigned long erofs_shrink_workstation(struct erofs_sb_info *sbi,
if (cleanup) if (cleanup)
BUG_ON(cnt != 1); BUG_ON(cnt != 1);
#ifndef EROFS_FS_HAS_MANAGED_CACHE
else if (cnt > 1) else if (cnt > 1)
#else
if (!erofs_workgroup_try_to_freeze(grp, 1))
#endif
continue; continue;
if (radix_tree_delete(&sbi->workstn_tree, if (radix_tree_delete(&sbi->workstn_tree,
grp->index) != grp) grp->index) != grp) {
#ifdef EROFS_FS_HAS_MANAGED_CACHE
skip:
erofs_workgroup_unfreeze(grp, 1);
#endif
continue; continue;
}
#ifdef EROFS_FS_HAS_MANAGED_CACHE
if (try_to_free_all_cached_pages(sbi, grp))
goto skip;
erofs_workgroup_unfreeze(grp, 1);
#endif
/* (rarely) grabbed again when freeing */ /* (rarely) grabbed again when freeing */
erofs_workgroup_put(grp); erofs_workgroup_put(grp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment