* [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress
[not found] <CGME20240819083433epcas1p3861b773a5b21eea6f0332036a71bb5d7@epcas1p3.samsung.com>
@ 2024-08-19 8:34 ` Yeongjin Gil
2024-08-20 1:20 ` Chao Yu
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Yeongjin Gil @ 2024-08-19 8:34 UTC (permalink / raw)
To: jaegeuk, chao, daehojeong
Cc: linux-f2fs-devel, linux-kernel, Jaewook Kim, Sungjong Seo
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: 5fdb322ff2c2 ("f2fs: add F2FS_IOC_DECOMPRESS_FILE and F2FS_IOC_COMPRESS_FILE")
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Reviewed-by: Sunmin Jeong <s_min.jeong@samsung.com>
Tested-by: Jaewook Kim <jw5454.kim@samsung.com>
Signed-off-by: Yeongjin Gil <youngjin.gil@samsung.com>
---
v2:
- Rename function and enum value for readability
---
fs/f2fs/compress.c | 36 ++++++++++++++++++++++++++++--------
fs/f2fs/f2fs.h | 12 ++++++++++++
fs/f2fs/file.c | 39 +++++++++++++++++++++------------------
3 files changed, 61 insertions(+), 26 deletions(-)
diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 990b93689b46..f55d54bb12f4 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -945,7 +945,7 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
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,
dn->ofs_in_node + i);
@@ -956,8 +956,8 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
return count;
}
-static int __f2fs_cluster_blocks(struct inode *inode,
- unsigned int cluster_idx, bool compr_blks)
+static int __f2fs_cluster_blocks(struct inode *inode, unsigned int cluster_idx,
+ enum cluster_check_type type)
{
struct dnode_of_data dn;
unsigned int start_idx = cluster_idx <<
@@ -978,10 +978,12 @@ static int __f2fs_cluster_blocks(struct inode *inode,
}
if (dn.data_blkaddr == COMPRESS_ADDR) {
- if (compr_blks)
- ret = __f2fs_get_cluster_blocks(inode, &dn);
- else
+ if (type == CLUSTER_COMPR_BLKS)
+ ret = 1 + __f2fs_get_cluster_blocks(inode, &dn);
+ else if (type == CLUSTER_IS_COMPR)
ret = 1;
+ } else if (type == CLUSTER_RAW_BLKS) {
+ ret = __f2fs_get_cluster_blocks(inode, &dn);
}
fail:
f2fs_put_dnode(&dn);
@@ -991,7 +993,16 @@ static int __f2fs_cluster_blocks(struct inode *inode,
/* return # of compressed blocks in compressed cluster */
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 */
@@ -999,7 +1010,16 @@ int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index)
{
return __f2fs_cluster_blocks(inode,
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)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 51fd5063a69c..6b5a3b692c08 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -4302,6 +4302,11 @@ static inline bool f2fs_meta_inode_gc_required(struct inode *inode)
* compress.c
*/
#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);
struct page *f2fs_compress_control_page(struct page *page);
int f2fs_prepare_compress_overwrite(struct inode *inode,
@@ -4328,6 +4333,7 @@ int f2fs_write_multi_pages(struct compress_ctx *cc,
struct writeback_control *wbc,
enum iostat_type io_type);
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,
pgoff_t fofs, block_t blkaddr,
unsigned int llen, unsigned int c_len);
@@ -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,
nid_t ino) { }
#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(
struct inode *inode,
pgoff_t fofs, block_t blkaddr,
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 270c32e3385f..0362d7ad21cc 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -4220,9 +4220,8 @@ static int f2fs_ioc_decompress_file(struct file *filp)
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
- pgoff_t page_idx = 0, last_idx;
- int cluster_size = fi->i_cluster_size;
- int count, ret;
+ pgoff_t page_idx = 0, last_idx, cluster_idx;
+ int ret;
if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
@@ -4257,10 +4256,15 @@ static int f2fs_ioc_decompress_file(struct file *filp)
goto out;
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;
- while (count && count >= cluster_size) {
- ret = redirty_blocks(inode, page_idx, cluster_size);
+ ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
if (ret < 0)
break;
@@ -4270,9 +4274,6 @@ static int f2fs_ioc_decompress_file(struct file *filp)
break;
}
- count -= cluster_size;
- page_idx += cluster_size;
-
cond_resched();
if (fatal_signal_pending(current)) {
ret = -EINTR;
@@ -4299,9 +4300,9 @@ static int f2fs_ioc_compress_file(struct file *filp)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- pgoff_t page_idx = 0, last_idx;
- int cluster_size = F2FS_I(inode)->i_cluster_size;
- int count, ret;
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ pgoff_t page_idx = 0, last_idx, cluster_idx;
+ int ret;
if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
@@ -4335,10 +4336,15 @@ static int f2fs_ioc_compress_file(struct file *filp)
set_inode_flag(inode, FI_ENABLE_COMPRESS);
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
+ last_idx >>= fi->i_log_cluster_size;
- count = last_idx - page_idx;
- while (count && count >= cluster_size) {
- ret = redirty_blocks(inode, page_idx, cluster_size);
+ for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
+ page_idx = cluster_idx << fi->i_log_cluster_size;
+
+ if (f2fs_is_sparse_cluster(inode, page_idx))
+ continue;
+
+ ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
if (ret < 0)
break;
@@ -4348,9 +4354,6 @@ static int f2fs_ioc_compress_file(struct file *filp)
break;
}
- count -= cluster_size;
- page_idx += cluster_size;
-
cond_resched();
if (fatal_signal_pending(current)) {
ret = -EINTR;
--
2.40.1
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress
2024-08-19 8:34 ` [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress Yeongjin Gil
@ 2024-08-20 1:20 ` Chao Yu
2024-08-30 20:51 ` patchwork-bot+f2fs--- via Linux-f2fs-devel
2024-12-12 6:31 ` Dan Carpenter
2 siblings, 0 replies; 6+ messages in thread
From: Chao Yu @ 2024-08-20 1:20 UTC (permalink / raw)
To: Yeongjin Gil, jaegeuk, daehojeong
Cc: Sungjong Seo, Jaewook Kim, linux-kernel, linux-f2fs-devel
On 2024/8/19 16:34, Yeongjin Gil wrote:
> 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: 5fdb322ff2c2 ("f2fs: add F2FS_IOC_DECOMPRESS_FILE and F2FS_IOC_COMPRESS_FILE")
> Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
> Reviewed-by: Sunmin Jeong <s_min.jeong@samsung.com>
> Tested-by: Jaewook Kim <jw5454.kim@samsung.com>
> Signed-off-by: Yeongjin Gil <youngjin.gil@samsung.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress
2024-08-19 8:34 ` [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress Yeongjin Gil
2024-08-20 1:20 ` Chao Yu
@ 2024-08-30 20:51 ` patchwork-bot+f2fs--- via Linux-f2fs-devel
2024-12-12 6:31 ` Dan Carpenter
2 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+f2fs--- via Linux-f2fs-devel @ 2024-08-30 20:51 UTC (permalink / raw)
To: Yeongjin Gil
Cc: daehojeong, linux-kernel, linux-f2fs-devel, jw5454.kim, jaegeuk,
sj1557.seo
Hello:
This patch was applied to jaegeuk/f2fs.git (dev)
by Jaegeuk Kim <jaegeuk@kernel.org>:
On Mon, 19 Aug 2024 17:34:30 +0900 you wrote:
> 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:
>
> [...]
Here is the summary with links:
- [f2fs-dev,v2] f2fs: compress: don't redirty sparse cluster during {, de}compress
https://git.kernel.org/jaegeuk/f2fs/c/f785cec298c9
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress
2024-08-19 8:34 ` [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress Yeongjin Gil
2024-08-20 1:20 ` Chao Yu
2024-08-30 20:51 ` patchwork-bot+f2fs--- via Linux-f2fs-devel
@ 2024-12-12 6:31 ` Dan Carpenter
2024-12-12 16:11 ` Jaegeuk Kim via Linux-f2fs-devel
2 siblings, 1 reply; 6+ messages in thread
From: Dan Carpenter @ 2024-12-12 6:31 UTC (permalink / raw)
To: Yeongjin Gil
Cc: daehojeong, linux-kernel, linux-f2fs-devel, jaegeuk, Sungjong Seo
On Mon, Aug 19, 2024 at 05:34:30PM +0900, Yeongjin Gil wrote:
> 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.
>
We were discussing how to detect these sorts of things in the future.
Presumably a user found this by chance? Xfstests has two tests which deal
with compression tests/f2fs/002 and tests/f2fs/007. But it feels like
xfstests is not really the right place for this sort of thing, it would
be better as part of some sort of fuzz testing.
What do you think?
regards,
dan carpenter
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress
2024-12-12 6:31 ` Dan Carpenter
@ 2024-12-12 16:11 ` Jaegeuk Kim via Linux-f2fs-devel
2024-12-16 15:38 ` Chao Yu via Linux-f2fs-devel
0 siblings, 1 reply; 6+ messages in thread
From: Jaegeuk Kim via Linux-f2fs-devel @ 2024-12-12 16:11 UTC (permalink / raw)
To: Dan Carpenter; +Cc: daehojeong, linux-kernel, linux-f2fs-devel, Sungjong Seo
On 12/12, Dan Carpenter wrote:
> On Mon, Aug 19, 2024 at 05:34:30PM +0900, Yeongjin Gil wrote:
> > 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.
> >
>
> We were discussing how to detect these sorts of things in the future.
> Presumably a user found this by chance? Xfstests has two tests which deal
> with compression tests/f2fs/002 and tests/f2fs/007. But it feels like
> xfstests is not really the right place for this sort of thing, it would
> be better as part of some sort of fuzz testing.
>
> What do you think?
Yeah, agreed that we must have tests to catch this. One way may be creating
some basic disk images having some possible valid layout to see f2fs can
work as intended. I feel we can put it in xfstests as wel?
Chao, thoughts?
>
> regards,
> dan carpenter
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress
2024-12-12 16:11 ` Jaegeuk Kim via Linux-f2fs-devel
@ 2024-12-16 15:38 ` Chao Yu via Linux-f2fs-devel
0 siblings, 0 replies; 6+ messages in thread
From: Chao Yu via Linux-f2fs-devel @ 2024-12-16 15:38 UTC (permalink / raw)
To: Jaegeuk Kim, Dan Carpenter
Cc: daehojeong, linux-kernel, linux-f2fs-devel, Sungjong Seo
On 2024/12/13 0:11, Jaegeuk Kim wrote:
> On 12/12, Dan Carpenter wrote:
>> On Mon, Aug 19, 2024 at 05:34:30PM +0900, Yeongjin Gil wrote:
>>> 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.
>>>
>>
>> We were discussing how to detect these sorts of things in the future.
>> Presumably a user found this by chance? Xfstests has two tests which deal
>> with compression tests/f2fs/002 and tests/f2fs/007. But it feels like
>> xfstests is not really the right place for this sort of thing, it would
>> be better as part of some sort of fuzz testing.
>>
>> What do you think?
>
> Yeah, agreed that we must have tests to catch this. One way may be creating
> some basic disk images having some possible valid layout to see f2fs can
> work as intended. I feel we can put it in xfstests as wel?
> > Chao, thoughts?
Hi Jaegeuk, Dan,
I'd like to continue to add regression testcases like f2fs/[003-008]
to xfstests, so that, we can make sure last change of f2fs won't cause
any regression by checking w/ those testcases.
Recently, Sheng Yong has introduced a new tool named inject.f2fs [1], and
then, built an auto testsuit in f2fs-tools based on inject.f2fs [2], w/
this testsuit, we can construct image by injecting specified fields, and
check the image w/ fsck to see whether result is as expected.
IMO, it's fine to add new testcases into testsuit to check whether f2fs'
behavior is as expected on constructed image w/ valid data layout.
So, regression testcase goes into xfstests, and fuzz/common image testcase
goes into testsuit of f2fs-tools? what do you think?
[1] "f2fs-tools: introduce inject.f2fs" https://lore.kernel.org/linux-f2fs-devel/20240704025740.527171-1-shengyong@oppo.com/
[2] "f2fs-tools: add testcases" https://lore.kernel.org/linux-f2fs-devel/20241029120956.4186731-1-shengyong@oppo.com/
Thanks,
>
>>
>> regards,
>> dan carpenter
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-12-16 15:39 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20240819083433epcas1p3861b773a5b21eea6f0332036a71bb5d7@epcas1p3.samsung.com>
2024-08-19 8:34 ` [f2fs-dev] [PATCH v2] f2fs: compress: don't redirty sparse cluster during {, de}compress Yeongjin Gil
2024-08-20 1:20 ` Chao Yu
2024-08-30 20:51 ` patchwork-bot+f2fs--- via Linux-f2fs-devel
2024-12-12 6:31 ` Dan Carpenter
2024-12-12 16:11 ` Jaegeuk Kim via Linux-f2fs-devel
2024-12-16 15:38 ` Chao Yu via Linux-f2fs-devel
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).