Commit f785cec2 authored by Yeongjin Gil's avatar Yeongjin Gil Committed by Jaegeuk Kim

f2fs: compress: don't redirty sparse cluster during {,de}compress

In f2fs_do_write_data_page, when the data block is NULL_ADDR, it skips
writepage considering that it has been already truncated.
This results in an infinite loop as the PAGECACHE_TAG_TOWRITE tag is not
cleared during the writeback process for a compressed file including
NULL_ADDR in compress_mode=user.

This is the reproduction process:

1. dd if=/dev/zero bs=4096 count=1024 seek=1024 of=testfile
2. f2fs_io compress testfile
3. dd if=/dev/zero bs=4096 count=1 conv=notrunc of=testfile
4. f2fs_io decompress testfile

To prevent the problem, let's check whether the cluster is fully
allocated before redirty its pages.

Fixes: 5fdb322f ("f2fs: add F2FS_IOC_DECOMPRESS_FILE and F2FS_IOC_COMPRESS_FILE")
Reviewed-by: default avatarSungjong Seo <sj1557.seo@samsung.com>
Reviewed-by: default avatarSunmin Jeong <s_min.jeong@samsung.com>
Tested-by: default avatarJaewook Kim <jw5454.kim@samsung.com>
Signed-off-by: default avatarYeongjin Gil <youngjin.gil@samsung.com>
Reviewed-by: default avatarChao Yu <chao@kernel.org>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 43aec4d0
...@@ -945,7 +945,7 @@ static int __f2fs_get_cluster_blocks(struct inode *inode, ...@@ -945,7 +945,7 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
unsigned int cluster_size = F2FS_I(inode)->i_cluster_size; unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
int count, i; int count, i;
for (i = 1, count = 1; i < cluster_size; i++) { for (i = 0, count = 0; i < cluster_size; i++) {
block_t blkaddr = data_blkaddr(dn->inode, dn->node_page, block_t blkaddr = data_blkaddr(dn->inode, dn->node_page,
dn->ofs_in_node + i); dn->ofs_in_node + i);
...@@ -956,8 +956,8 @@ static int __f2fs_get_cluster_blocks(struct inode *inode, ...@@ -956,8 +956,8 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
return count; return count;
} }
static int __f2fs_cluster_blocks(struct inode *inode, static int __f2fs_cluster_blocks(struct inode *inode, unsigned int cluster_idx,
unsigned int cluster_idx, bool compr_blks) enum cluster_check_type type)
{ {
struct dnode_of_data dn; struct dnode_of_data dn;
unsigned int start_idx = cluster_idx << unsigned int start_idx = cluster_idx <<
...@@ -978,10 +978,12 @@ static int __f2fs_cluster_blocks(struct inode *inode, ...@@ -978,10 +978,12 @@ static int __f2fs_cluster_blocks(struct inode *inode,
} }
if (dn.data_blkaddr == COMPRESS_ADDR) { if (dn.data_blkaddr == COMPRESS_ADDR) {
if (compr_blks) if (type == CLUSTER_COMPR_BLKS)
ret = __f2fs_get_cluster_blocks(inode, &dn); ret = 1 + __f2fs_get_cluster_blocks(inode, &dn);
else else if (type == CLUSTER_IS_COMPR)
ret = 1; ret = 1;
} else if (type == CLUSTER_RAW_BLKS) {
ret = __f2fs_get_cluster_blocks(inode, &dn);
} }
fail: fail:
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
...@@ -991,7 +993,16 @@ static int __f2fs_cluster_blocks(struct inode *inode, ...@@ -991,7 +993,16 @@ static int __f2fs_cluster_blocks(struct inode *inode,
/* return # of compressed blocks in compressed cluster */ /* return # of compressed blocks in compressed cluster */
static int f2fs_compressed_blocks(struct compress_ctx *cc) static int f2fs_compressed_blocks(struct compress_ctx *cc)
{ {
return __f2fs_cluster_blocks(cc->inode, cc->cluster_idx, true); return __f2fs_cluster_blocks(cc->inode, cc->cluster_idx,
CLUSTER_COMPR_BLKS);
}
/* return # of raw blocks in non-compressed cluster */
static int f2fs_decompressed_blocks(struct inode *inode,
unsigned int cluster_idx)
{
return __f2fs_cluster_blocks(inode, cluster_idx,
CLUSTER_RAW_BLKS);
} }
/* return whether cluster is compressed one or not */ /* return whether cluster is compressed one or not */
...@@ -999,7 +1010,16 @@ int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index) ...@@ -999,7 +1010,16 @@ int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index)
{ {
return __f2fs_cluster_blocks(inode, return __f2fs_cluster_blocks(inode,
index >> F2FS_I(inode)->i_log_cluster_size, index >> F2FS_I(inode)->i_log_cluster_size,
false); CLUSTER_IS_COMPR);
}
/* return whether cluster contains non raw blocks or not */
bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index)
{
unsigned int cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size;
return f2fs_decompressed_blocks(inode, cluster_idx) !=
F2FS_I(inode)->i_cluster_size;
} }
static bool cluster_may_compress(struct compress_ctx *cc) static bool cluster_may_compress(struct compress_ctx *cc)
......
...@@ -4302,6 +4302,11 @@ static inline bool f2fs_meta_inode_gc_required(struct inode *inode) ...@@ -4302,6 +4302,11 @@ static inline bool f2fs_meta_inode_gc_required(struct inode *inode)
* compress.c * compress.c
*/ */
#ifdef CONFIG_F2FS_FS_COMPRESSION #ifdef CONFIG_F2FS_FS_COMPRESSION
enum cluster_check_type {
CLUSTER_IS_COMPR, /* check only if compressed cluster */
CLUSTER_COMPR_BLKS, /* return # of compressed blocks in a cluster */
CLUSTER_RAW_BLKS /* return # of raw blocks in a cluster */
};
bool f2fs_is_compressed_page(struct page *page); bool f2fs_is_compressed_page(struct page *page);
struct page *f2fs_compress_control_page(struct page *page); struct page *f2fs_compress_control_page(struct page *page);
int f2fs_prepare_compress_overwrite(struct inode *inode, int f2fs_prepare_compress_overwrite(struct inode *inode,
...@@ -4328,6 +4333,7 @@ int f2fs_write_multi_pages(struct compress_ctx *cc, ...@@ -4328,6 +4333,7 @@ int f2fs_write_multi_pages(struct compress_ctx *cc,
struct writeback_control *wbc, struct writeback_control *wbc,
enum iostat_type io_type); enum iostat_type io_type);
int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index); int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index);
bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index);
void f2fs_update_read_extent_tree_range_compressed(struct inode *inode, void f2fs_update_read_extent_tree_range_compressed(struct inode *inode,
pgoff_t fofs, block_t blkaddr, pgoff_t fofs, block_t blkaddr,
unsigned int llen, unsigned int c_len); unsigned int llen, unsigned int c_len);
...@@ -4414,6 +4420,12 @@ static inline bool f2fs_load_compressed_page(struct f2fs_sb_info *sbi, ...@@ -4414,6 +4420,12 @@ static inline bool f2fs_load_compressed_page(struct f2fs_sb_info *sbi,
static inline void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi, static inline void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi,
nid_t ino) { } nid_t ino) { }
#define inc_compr_inode_stat(inode) do { } while (0) #define inc_compr_inode_stat(inode) do { } while (0)
static inline int f2fs_is_compressed_cluster(
struct inode *inode,
pgoff_t index) { return 0; }
static inline bool f2fs_is_sparse_cluster(
struct inode *inode,
pgoff_t index) { return true; }
static inline void f2fs_update_read_extent_tree_range_compressed( static inline void f2fs_update_read_extent_tree_range_compressed(
struct inode *inode, struct inode *inode,
pgoff_t fofs, block_t blkaddr, pgoff_t fofs, block_t blkaddr,
......
...@@ -4236,9 +4236,8 @@ static int f2fs_ioc_decompress_file(struct file *filp) ...@@ -4236,9 +4236,8 @@ static int f2fs_ioc_decompress_file(struct file *filp)
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
pgoff_t page_idx = 0, last_idx; pgoff_t page_idx = 0, last_idx, cluster_idx;
int cluster_size = fi->i_cluster_size; int ret;
int count, ret;
if (!f2fs_sb_has_compression(sbi) || if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER) F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
...@@ -4273,10 +4272,15 @@ static int f2fs_ioc_decompress_file(struct file *filp) ...@@ -4273,10 +4272,15 @@ static int f2fs_ioc_decompress_file(struct file *filp)
goto out; goto out;
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
last_idx >>= fi->i_log_cluster_size;
for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
page_idx = cluster_idx << fi->i_log_cluster_size;
if (!f2fs_is_compressed_cluster(inode, page_idx))
continue;
count = last_idx - page_idx; ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
while (count && count >= cluster_size) {
ret = redirty_blocks(inode, page_idx, cluster_size);
if (ret < 0) if (ret < 0)
break; break;
...@@ -4286,9 +4290,6 @@ static int f2fs_ioc_decompress_file(struct file *filp) ...@@ -4286,9 +4290,6 @@ static int f2fs_ioc_decompress_file(struct file *filp)
break; break;
} }
count -= cluster_size;
page_idx += cluster_size;
cond_resched(); cond_resched();
if (fatal_signal_pending(current)) { if (fatal_signal_pending(current)) {
ret = -EINTR; ret = -EINTR;
...@@ -4315,9 +4316,9 @@ static int f2fs_ioc_compress_file(struct file *filp) ...@@ -4315,9 +4316,9 @@ static int f2fs_ioc_compress_file(struct file *filp)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t page_idx = 0, last_idx; struct f2fs_inode_info *fi = F2FS_I(inode);
int cluster_size = F2FS_I(inode)->i_cluster_size; pgoff_t page_idx = 0, last_idx, cluster_idx;
int count, ret; int ret;
if (!f2fs_sb_has_compression(sbi) || if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER) F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
...@@ -4351,10 +4352,15 @@ static int f2fs_ioc_compress_file(struct file *filp) ...@@ -4351,10 +4352,15 @@ static int f2fs_ioc_compress_file(struct file *filp)
set_inode_flag(inode, FI_ENABLE_COMPRESS); set_inode_flag(inode, FI_ENABLE_COMPRESS);
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
last_idx >>= fi->i_log_cluster_size;
count = last_idx - page_idx; for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
while (count && count >= cluster_size) { page_idx = cluster_idx << fi->i_log_cluster_size;
ret = redirty_blocks(inode, page_idx, cluster_size);
if (f2fs_is_sparse_cluster(inode, page_idx))
continue;
ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
if (ret < 0) if (ret < 0)
break; break;
...@@ -4364,9 +4370,6 @@ static int f2fs_ioc_compress_file(struct file *filp) ...@@ -4364,9 +4370,6 @@ static int f2fs_ioc_compress_file(struct file *filp)
break; break;
} }
count -= cluster_size;
page_idx += cluster_size;
cond_resched(); cond_resched();
if (fatal_signal_pending(current)) { if (fatal_signal_pending(current)) {
ret = -EINTR; ret = -EINTR;
......
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