2020-12-30 07:58:26

by Chao Yu

[permalink] [raw]
Subject: [PATCH v2] f2fs: fix to keep isolation of atomic write

ThreadA ThreadB
- f2fs_ioc_start_atomic_write
- write
- f2fs_ioc_commit_atomic_write
- f2fs_commit_inmem_pages
- f2fs_drop_inmem_pages
- f2fs_drop_inmem_pages
- __revoke_inmem_pages
- f2fs_vm_page_mkwrite
- set_page_dirty
- tag ATOMIC_WRITTEN_PAGE and add page
to inmem_pages list
- clear_inode_flag(FI_ATOMIC_FILE)
- f2fs_vm_page_mkwrite
- set_page_dirty
- f2fs_update_dirty_page
- f2fs_trace_pid
- tag inmem page private to pid
- truncate
- f2fs_invalidate_page
- set page->mapping to NULL
then it will cause panic once we
access page->mapping

The root cause is we missed to keep isolation of atomic write in the case
of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
lock to avoid this issue.

Signed-off-by: Chao Yu <[email protected]>
---
v2:
- use i_mmap_sem to avoid mkwrite racing with below flows:
* f2fs_ioc_start_atomic_write
* f2fs_drop_inmem_pages
* f2fs_commit_inmem_pages

fs/f2fs/file.c | 3 +++
fs/f2fs/segment.c | 7 +++++++
2 files changed, 10 insertions(+)

diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 4e6d4b9120a8..a48ec650d691 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
goto out;

down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
+ down_write(&F2FS_I(inode)->i_mmap_sem);

/*
* Should wait end_io to count F2FS_WB_CP_DATA correctly by
@@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
inode->i_ino, get_dirty_pages(inode));
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret) {
+ up_write(&F2FS_I(inode)->i_mmap_sem);
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
goto out;
}
@@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
/* add inode in inmem_list first and set atomic_file */
set_inode_flag(inode, FI_ATOMIC_FILE);
clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
+ up_write(&F2FS_I(inode)->i_mmap_sem);
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);

f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index d8570b0359f5..dab870d9faf6 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);

+ down_write(&F2FS_I(inode)->i_mmap_sem);
+
while (!list_empty(&fi->inmem_pages)) {
mutex_lock(&fi->inmem_lock);
__revoke_inmem_pages(inode, &fi->inmem_pages,
@@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
sbi->atomic_files--;
}
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
+
+ up_write(&F2FS_I(inode)->i_mmap_sem);
}

void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
@@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
f2fs_balance_fs(sbi, true);

down_write(&fi->i_gc_rwsem[WRITE]);
+ down_write(&F2FS_I(inode)->i_mmap_sem);

f2fs_lock_op(sbi);
set_inode_flag(inode, FI_ATOMIC_COMMIT);
@@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
clear_inode_flag(inode, FI_ATOMIC_COMMIT);

f2fs_unlock_op(sbi);
+
+ up_write(&F2FS_I(inode)->i_mmap_sem);
up_write(&fi->i_gc_rwsem[WRITE]);

return err;
--
2.29.2


2021-01-06 22:31:04

by Jaegeuk Kim

[permalink] [raw]
Subject: Re: [PATCH v2] f2fs: fix to keep isolation of atomic write

Hi Chao,

With a quick test, this patch causes down_write failure resulting in blocking
process. I didn't dig in the bug so, please check the code again. :P

On 12/30, Chao Yu wrote:
> ThreadA ThreadB
> - f2fs_ioc_start_atomic_write
> - write
> - f2fs_ioc_commit_atomic_write
> - f2fs_commit_inmem_pages
> - f2fs_drop_inmem_pages
> - f2fs_drop_inmem_pages
> - __revoke_inmem_pages
> - f2fs_vm_page_mkwrite
> - set_page_dirty
> - tag ATOMIC_WRITTEN_PAGE and add page
> to inmem_pages list
> - clear_inode_flag(FI_ATOMIC_FILE)
> - f2fs_vm_page_mkwrite
> - set_page_dirty
> - f2fs_update_dirty_page
> - f2fs_trace_pid
> - tag inmem page private to pid
> - truncate
> - f2fs_invalidate_page
> - set page->mapping to NULL
> then it will cause panic once we
> access page->mapping
>
> The root cause is we missed to keep isolation of atomic write in the case
> of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
> lock to avoid this issue.
>
> Signed-off-by: Chao Yu <[email protected]>
> ---
> v2:
> - use i_mmap_sem to avoid mkwrite racing with below flows:
> * f2fs_ioc_start_atomic_write
> * f2fs_drop_inmem_pages
> * f2fs_commit_inmem_pages
>
> fs/f2fs/file.c | 3 +++
> fs/f2fs/segment.c | 7 +++++++
> 2 files changed, 10 insertions(+)
>
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 4e6d4b9120a8..a48ec650d691 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> goto out;
>
> down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> + down_write(&F2FS_I(inode)->i_mmap_sem);
>
> /*
> * Should wait end_io to count F2FS_WB_CP_DATA correctly by
> @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> inode->i_ino, get_dirty_pages(inode));
> ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
> if (ret) {
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> goto out;
> }
> @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> /* add inode in inmem_list first and set atomic_file */
> set_inode_flag(inode, FI_ATOMIC_FILE);
> clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>
> f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> index d8570b0359f5..dab870d9faf6 100644
> --- a/fs/f2fs/segment.c
> +++ b/fs/f2fs/segment.c
> @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> struct f2fs_inode_info *fi = F2FS_I(inode);
>
> + down_write(&F2FS_I(inode)->i_mmap_sem);
> +
> while (!list_empty(&fi->inmem_pages)) {
> mutex_lock(&fi->inmem_lock);
> __revoke_inmem_pages(inode, &fi->inmem_pages,
> @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> sbi->atomic_files--;
> }
> spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
> +
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> }
>
> void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
> @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> f2fs_balance_fs(sbi, true);
>
> down_write(&fi->i_gc_rwsem[WRITE]);
> + down_write(&F2FS_I(inode)->i_mmap_sem);
>
> f2fs_lock_op(sbi);
> set_inode_flag(inode, FI_ATOMIC_COMMIT);
> @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> clear_inode_flag(inode, FI_ATOMIC_COMMIT);
>
> f2fs_unlock_op(sbi);
> +
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> up_write(&fi->i_gc_rwsem[WRITE]);
>
> return err;
> --
> 2.29.2

2021-01-06 23:01:25

by Jaegeuk Kim

[permalink] [raw]
Subject: Re: [f2fs-dev] [PATCH v2] f2fs: fix to keep isolation of atomic write

On 01/06, Jaegeuk Kim wrote:
> Hi Chao,
>
> With a quick test, this patch causes down_write failure resulting in blocking
> process. I didn't dig in the bug so, please check the code again. :P

nvm. I can see it works now.

>
> On 12/30, Chao Yu wrote:
> > ThreadA ThreadB
> > - f2fs_ioc_start_atomic_write
> > - write
> > - f2fs_ioc_commit_atomic_write
> > - f2fs_commit_inmem_pages
> > - f2fs_drop_inmem_pages
> > - f2fs_drop_inmem_pages
> > - __revoke_inmem_pages
> > - f2fs_vm_page_mkwrite
> > - set_page_dirty
> > - tag ATOMIC_WRITTEN_PAGE and add page
> > to inmem_pages list
> > - clear_inode_flag(FI_ATOMIC_FILE)
> > - f2fs_vm_page_mkwrite
> > - set_page_dirty
> > - f2fs_update_dirty_page
> > - f2fs_trace_pid
> > - tag inmem page private to pid
> > - truncate
> > - f2fs_invalidate_page
> > - set page->mapping to NULL
> > then it will cause panic once we
> > access page->mapping
> >
> > The root cause is we missed to keep isolation of atomic write in the case
> > of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
> > lock to avoid this issue.
> >
> > Signed-off-by: Chao Yu <[email protected]>
> > ---
> > v2:
> > - use i_mmap_sem to avoid mkwrite racing with below flows:
> > * f2fs_ioc_start_atomic_write
> > * f2fs_drop_inmem_pages
> > * f2fs_commit_inmem_pages
> >
> > fs/f2fs/file.c | 3 +++
> > fs/f2fs/segment.c | 7 +++++++
> > 2 files changed, 10 insertions(+)
> >
> > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > index 4e6d4b9120a8..a48ec650d691 100644
> > --- a/fs/f2fs/file.c
> > +++ b/fs/f2fs/file.c
> > @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > goto out;
> >
> > down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > + down_write(&F2FS_I(inode)->i_mmap_sem);
> >
> > /*
> > * Should wait end_io to count F2FS_WB_CP_DATA correctly by
> > @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > inode->i_ino, get_dirty_pages(inode));
> > ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
> > if (ret) {
> > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > goto out;
> > }
> > @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > /* add inode in inmem_list first and set atomic_file */
> > set_inode_flag(inode, FI_ATOMIC_FILE);
> > clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
> > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> >
> > f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> > index d8570b0359f5..dab870d9faf6 100644
> > --- a/fs/f2fs/segment.c
> > +++ b/fs/f2fs/segment.c
> > @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > struct f2fs_inode_info *fi = F2FS_I(inode);
> >
> > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > +
> > while (!list_empty(&fi->inmem_pages)) {
> > mutex_lock(&fi->inmem_lock);
> > __revoke_inmem_pages(inode, &fi->inmem_pages,
> > @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > sbi->atomic_files--;
> > }
> > spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
> > +
> > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > }
> >
> > void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
> > @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > f2fs_balance_fs(sbi, true);
> >
> > down_write(&fi->i_gc_rwsem[WRITE]);
> > + down_write(&F2FS_I(inode)->i_mmap_sem);
> >
> > f2fs_lock_op(sbi);
> > set_inode_flag(inode, FI_ATOMIC_COMMIT);
> > @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > clear_inode_flag(inode, FI_ATOMIC_COMMIT);
> >
> > f2fs_unlock_op(sbi);
> > +
> > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > up_write(&fi->i_gc_rwsem[WRITE]);
> >
> > return err;
> > --
> > 2.29.2
>
>
> _______________________________________________
> Linux-f2fs-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

2021-01-11 16:34:20

by Jaegeuk Kim

[permalink] [raw]
Subject: Re: [f2fs-dev] [PATCH v2] f2fs: fix to keep isolation of atomic write

On 01/06, Jaegeuk Kim wrote:
> On 01/06, Jaegeuk Kim wrote:
> > Hi Chao,
> >
> > With a quick test, this patch causes down_write failure resulting in blocking
> > process. I didn't dig in the bug so, please check the code again. :P
>
> nvm. I can see it works now.

Hmm, this gives a huge perf regression when running sqlite. :(
We may need to check the lock coverage. Thoughts?

>
> >
> > On 12/30, Chao Yu wrote:
> > > ThreadA ThreadB
> > > - f2fs_ioc_start_atomic_write
> > > - write
> > > - f2fs_ioc_commit_atomic_write
> > > - f2fs_commit_inmem_pages
> > > - f2fs_drop_inmem_pages
> > > - f2fs_drop_inmem_pages
> > > - __revoke_inmem_pages
> > > - f2fs_vm_page_mkwrite
> > > - set_page_dirty
> > > - tag ATOMIC_WRITTEN_PAGE and add page
> > > to inmem_pages list
> > > - clear_inode_flag(FI_ATOMIC_FILE)
> > > - f2fs_vm_page_mkwrite
> > > - set_page_dirty
> > > - f2fs_update_dirty_page
> > > - f2fs_trace_pid
> > > - tag inmem page private to pid
> > > - truncate
> > > - f2fs_invalidate_page
> > > - set page->mapping to NULL
> > > then it will cause panic once we
> > > access page->mapping
> > >
> > > The root cause is we missed to keep isolation of atomic write in the case
> > > of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
> > > lock to avoid this issue.
> > >
> > > Signed-off-by: Chao Yu <[email protected]>
> > > ---
> > > v2:
> > > - use i_mmap_sem to avoid mkwrite racing with below flows:
> > > * f2fs_ioc_start_atomic_write
> > > * f2fs_drop_inmem_pages
> > > * f2fs_commit_inmem_pages
> > >
> > > fs/f2fs/file.c | 3 +++
> > > fs/f2fs/segment.c | 7 +++++++
> > > 2 files changed, 10 insertions(+)
> > >
> > > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > > index 4e6d4b9120a8..a48ec650d691 100644
> > > --- a/fs/f2fs/file.c
> > > +++ b/fs/f2fs/file.c
> > > @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > goto out;
> > >
> > > down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > >
> > > /*
> > > * Should wait end_io to count F2FS_WB_CP_DATA correctly by
> > > @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > inode->i_ino, get_dirty_pages(inode));
> > > ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
> > > if (ret) {
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > goto out;
> > > }
> > > @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > /* add inode in inmem_list first and set atomic_file */
> > > set_inode_flag(inode, FI_ATOMIC_FILE);
> > > clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > >
> > > f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> > > index d8570b0359f5..dab870d9faf6 100644
> > > --- a/fs/f2fs/segment.c
> > > +++ b/fs/f2fs/segment.c
> > > @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > > struct f2fs_inode_info *fi = F2FS_I(inode);
> > >
> > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > +
> > > while (!list_empty(&fi->inmem_pages)) {
> > > mutex_lock(&fi->inmem_lock);
> > > __revoke_inmem_pages(inode, &fi->inmem_pages,
> > > @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > sbi->atomic_files--;
> > > }
> > > spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
> > > +
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > }
> > >
> > > void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
> > > @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > f2fs_balance_fs(sbi, true);
> > >
> > > down_write(&fi->i_gc_rwsem[WRITE]);
> > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > >
> > > f2fs_lock_op(sbi);
> > > set_inode_flag(inode, FI_ATOMIC_COMMIT);
> > > @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > clear_inode_flag(inode, FI_ATOMIC_COMMIT);
> > >
> > > f2fs_unlock_op(sbi);
> > > +
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > up_write(&fi->i_gc_rwsem[WRITE]);
> > >
> > > return err;
> > > --
> > > 2.29.2
> >
> >
> > _______________________________________________
> > Linux-f2fs-devel mailing list
> > [email protected]
> > https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
>
>
> _______________________________________________
> Linux-f2fs-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

2021-01-12 11:13:11

by Chao Yu

[permalink] [raw]
Subject: Re: [f2fs-dev] [PATCH v2] f2fs: fix to keep isolation of atomic write

On 2021/1/12 0:32, Jaegeuk Kim wrote:
> On 01/06, Jaegeuk Kim wrote:
>> On 01/06, Jaegeuk Kim wrote:
>>> Hi Chao,
>>>
>>> With a quick test, this patch causes down_write failure resulting in blocking
>>> process. I didn't dig in the bug so, please check the code again. :P
>>
>> nvm. I can see it works now.
>
> Hmm, this gives a huge perf regression when running sqlite. :(
> We may need to check the lock coverage. Thoughts?

I added i_mmap_sem lock only, so it can cause atomic_{start,commit,finish}
race with mmap and truncation operations in additionally.

I'd like to know what's your sqlite testcase?

Thanks,

>
>>
>>>
>>> On 12/30, Chao Yu wrote:
>>>> ThreadA ThreadB
>>>> - f2fs_ioc_start_atomic_write
>>>> - write
>>>> - f2fs_ioc_commit_atomic_write
>>>> - f2fs_commit_inmem_pages
>>>> - f2fs_drop_inmem_pages
>>>> - f2fs_drop_inmem_pages
>>>> - __revoke_inmem_pages
>>>> - f2fs_vm_page_mkwrite
>>>> - set_page_dirty
>>>> - tag ATOMIC_WRITTEN_PAGE and add page
>>>> to inmem_pages list
>>>> - clear_inode_flag(FI_ATOMIC_FILE)
>>>> - f2fs_vm_page_mkwrite
>>>> - set_page_dirty
>>>> - f2fs_update_dirty_page
>>>> - f2fs_trace_pid
>>>> - tag inmem page private to pid
>>>> - truncate
>>>> - f2fs_invalidate_page
>>>> - set page->mapping to NULL
>>>> then it will cause panic once we
>>>> access page->mapping
>>>>
>>>> The root cause is we missed to keep isolation of atomic write in the case
>>>> of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
>>>> lock to avoid this issue.
>>>>
>>>> Signed-off-by: Chao Yu <[email protected]>
>>>> ---
>>>> v2:
>>>> - use i_mmap_sem to avoid mkwrite racing with below flows:
>>>> * f2fs_ioc_start_atomic_write
>>>> * f2fs_drop_inmem_pages
>>>> * f2fs_commit_inmem_pages
>>>>
>>>> fs/f2fs/file.c | 3 +++
>>>> fs/f2fs/segment.c | 7 +++++++
>>>> 2 files changed, 10 insertions(+)
>>>>
>>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>>> index 4e6d4b9120a8..a48ec650d691 100644
>>>> --- a/fs/f2fs/file.c
>>>> +++ b/fs/f2fs/file.c
>>>> @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>> goto out;
>>>>
>>>> down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>
>>>> /*
>>>> * Should wait end_io to count F2FS_WB_CP_DATA correctly by
>>>> @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>> inode->i_ino, get_dirty_pages(inode));
>>>> ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
>>>> if (ret) {
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>> goto out;
>>>> }
>>>> @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>> /* add inode in inmem_list first and set atomic_file */
>>>> set_inode_flag(inode, FI_ATOMIC_FILE);
>>>> clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>>
>>>> f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
>>>> index d8570b0359f5..dab870d9faf6 100644
>>>> --- a/fs/f2fs/segment.c
>>>> +++ b/fs/f2fs/segment.c
>>>> @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>>> struct f2fs_inode_info *fi = F2FS_I(inode);
>>>>
>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>> +
>>>> while (!list_empty(&fi->inmem_pages)) {
>>>> mutex_lock(&fi->inmem_lock);
>>>> __revoke_inmem_pages(inode, &fi->inmem_pages,
>>>> @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>> sbi->atomic_files--;
>>>> }
>>>> spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
>>>> +
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> }
>>>>
>>>> void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
>>>> @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>> f2fs_balance_fs(sbi, true);
>>>>
>>>> down_write(&fi->i_gc_rwsem[WRITE]);
>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>
>>>> f2fs_lock_op(sbi);
>>>> set_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>> @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>> clear_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>>
>>>> f2fs_unlock_op(sbi);
>>>> +
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> up_write(&fi->i_gc_rwsem[WRITE]);
>>>>
>>>> return err;
>>>> --
>>>> 2.29.2
>>>
>>>
>>> _______________________________________________
>>> Linux-f2fs-devel mailing list
>>> [email protected]
>>> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
>>
>>
>> _______________________________________________
>> Linux-f2fs-devel mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
> .
>

2021-01-13 03:16:26

by Jaegeuk Kim

[permalink] [raw]
Subject: Re: [f2fs-dev] [PATCH v2] f2fs: fix to keep isolation of atomic write

On 01/12, Chao Yu wrote:
> On 2021/1/12 0:32, Jaegeuk Kim wrote:
> > On 01/06, Jaegeuk Kim wrote:
> > > On 01/06, Jaegeuk Kim wrote:
> > > > Hi Chao,
> > > >
> > > > With a quick test, this patch causes down_write failure resulting in blocking
> > > > process. I didn't dig in the bug so, please check the code again. :P
> > >
> > > nvm. I can see it works now.
> >
> > Hmm, this gives a huge perf regression when running sqlite. :(
> > We may need to check the lock coverage. Thoughts?
>
> I added i_mmap_sem lock only, so it can cause atomic_{start,commit,finish}
> race with mmap and truncation operations in additionally.
>
> I'd like to know what's your sqlite testcase?

Nothing special. Just generating multiple sqlite transactions to the same db.

>
> Thanks,
>
> >
> > >
> > > >
> > > > On 12/30, Chao Yu wrote:
> > > > > ThreadA ThreadB
> > > > > - f2fs_ioc_start_atomic_write
> > > > > - write
> > > > > - f2fs_ioc_commit_atomic_write
> > > > > - f2fs_commit_inmem_pages
> > > > > - f2fs_drop_inmem_pages
> > > > > - f2fs_drop_inmem_pages
> > > > > - __revoke_inmem_pages
> > > > > - f2fs_vm_page_mkwrite
> > > > > - set_page_dirty
> > > > > - tag ATOMIC_WRITTEN_PAGE and add page
> > > > > to inmem_pages list
> > > > > - clear_inode_flag(FI_ATOMIC_FILE)
> > > > > - f2fs_vm_page_mkwrite
> > > > > - set_page_dirty
> > > > > - f2fs_update_dirty_page
> > > > > - f2fs_trace_pid
> > > > > - tag inmem page private to pid
> > > > > - truncate
> > > > > - f2fs_invalidate_page
> > > > > - set page->mapping to NULL
> > > > > then it will cause panic once we
> > > > > access page->mapping
> > > > >
> > > > > The root cause is we missed to keep isolation of atomic write in the case
> > > > > of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
> > > > > lock to avoid this issue.
> > > > >
> > > > > Signed-off-by: Chao Yu <[email protected]>
> > > > > ---
> > > > > v2:
> > > > > - use i_mmap_sem to avoid mkwrite racing with below flows:
> > > > > * f2fs_ioc_start_atomic_write
> > > > > * f2fs_drop_inmem_pages
> > > > > * f2fs_commit_inmem_pages
> > > > >
> > > > > fs/f2fs/file.c | 3 +++
> > > > > fs/f2fs/segment.c | 7 +++++++
> > > > > 2 files changed, 10 insertions(+)
> > > > >
> > > > > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > > > > index 4e6d4b9120a8..a48ec650d691 100644
> > > > > --- a/fs/f2fs/file.c
> > > > > +++ b/fs/f2fs/file.c
> > > > > @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > > > goto out;
> > > > > down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > /*
> > > > > * Should wait end_io to count F2FS_WB_CP_DATA correctly by
> > > > > @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > > > inode->i_ino, get_dirty_pages(inode));
> > > > > ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
> > > > > if (ret) {
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > > > goto out;
> > > > > }
> > > > > @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > > > /* add inode in inmem_list first and set atomic_file */
> > > > > set_inode_flag(inode, FI_ATOMIC_FILE);
> > > > > clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > > > f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > > > > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> > > > > index d8570b0359f5..dab870d9faf6 100644
> > > > > --- a/fs/f2fs/segment.c
> > > > > +++ b/fs/f2fs/segment.c
> > > > > @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > > > struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > > > > struct f2fs_inode_info *fi = F2FS_I(inode);
> > > > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > +
> > > > > while (!list_empty(&fi->inmem_pages)) {
> > > > > mutex_lock(&fi->inmem_lock);
> > > > > __revoke_inmem_pages(inode, &fi->inmem_pages,
> > > > > @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > > > sbi->atomic_files--;
> > > > > }
> > > > > spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
> > > > > +
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > }
> > > > > void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
> > > > > @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > > > f2fs_balance_fs(sbi, true);
> > > > > down_write(&fi->i_gc_rwsem[WRITE]);
> > > > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > f2fs_lock_op(sbi);
> > > > > set_inode_flag(inode, FI_ATOMIC_COMMIT);
> > > > > @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > > > clear_inode_flag(inode, FI_ATOMIC_COMMIT);
> > > > > f2fs_unlock_op(sbi);
> > > > > +
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > up_write(&fi->i_gc_rwsem[WRITE]);
> > > > > return err;
> > > > > --
> > > > > 2.29.2
> > > >
> > > >
> > > > _______________________________________________
> > > > Linux-f2fs-devel mailing list
> > > > [email protected]
> > > > https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
> > >
> > >
> > > _______________________________________________
> > > Linux-f2fs-devel mailing list
> > > [email protected]
> > > https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
> > .
> >

2021-01-13 03:37:13

by Chao Yu

[permalink] [raw]
Subject: Re: [f2fs-dev] [PATCH v2] f2fs: fix to keep isolation of atomic write

On 2021/1/13 6:32, Jaegeuk Kim wrote:
> On 01/12, Chao Yu wrote:
>> On 2021/1/12 0:32, Jaegeuk Kim wrote:
>>> On 01/06, Jaegeuk Kim wrote:
>>>> On 01/06, Jaegeuk Kim wrote:
>>>>> Hi Chao,
>>>>>
>>>>> With a quick test, this patch causes down_write failure resulting in blocking
>>>>> process. I didn't dig in the bug so, please check the code again. :P
>>>>
>>>> nvm. I can see it works now.
>>>
>>> Hmm, this gives a huge perf regression when running sqlite. :(
>>> We may need to check the lock coverage. Thoughts?
>>
>> I added i_mmap_sem lock only, so it can cause atomic_{start,commit,finish}
>> race with mmap and truncation operations in additionally.
>>
>> I'd like to know what's your sqlite testcase?
>
> Nothing special. Just generating multiple sqlite transactions to the same db.

I doubt that start/commit flow race with ->release/->flush interface can cause
lower concurrency?

f2fs_ioc_start_atomic_write or ->release or ->flush
f2fs_ioc_commit_atomic_write
- f2fs_drop_inmem_pages
down_write(&F2FS_I(inode)->i_mmap_sem);
down_write(&F2FS_I(inode)->i_mmap_sem);

How about trying this testcase again after removing i_mmap_sem lock in
f2fs_drop_inmem_pages()?

Thanks,

>
>>
>> Thanks,
>>
>>>
>>>>
>>>>>
>>>>> On 12/30, Chao Yu wrote:
>>>>>> ThreadA ThreadB
>>>>>> - f2fs_ioc_start_atomic_write
>>>>>> - write
>>>>>> - f2fs_ioc_commit_atomic_write
>>>>>> - f2fs_commit_inmem_pages
>>>>>> - f2fs_drop_inmem_pages
>>>>>> - f2fs_drop_inmem_pages
>>>>>> - __revoke_inmem_pages
>>>>>> - f2fs_vm_page_mkwrite
>>>>>> - set_page_dirty
>>>>>> - tag ATOMIC_WRITTEN_PAGE and add page
>>>>>> to inmem_pages list
>>>>>> - clear_inode_flag(FI_ATOMIC_FILE)
>>>>>> - f2fs_vm_page_mkwrite
>>>>>> - set_page_dirty
>>>>>> - f2fs_update_dirty_page
>>>>>> - f2fs_trace_pid
>>>>>> - tag inmem page private to pid
>>>>>> - truncate
>>>>>> - f2fs_invalidate_page
>>>>>> - set page->mapping to NULL
>>>>>> then it will cause panic once we
>>>>>> access page->mapping
>>>>>>
>>>>>> The root cause is we missed to keep isolation of atomic write in the case
>>>>>> of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
>>>>>> lock to avoid this issue.
>>>>>>
>>>>>> Signed-off-by: Chao Yu <[email protected]>
>>>>>> ---
>>>>>> v2:
>>>>>> - use i_mmap_sem to avoid mkwrite racing with below flows:
>>>>>> * f2fs_ioc_start_atomic_write
>>>>>> * f2fs_drop_inmem_pages
>>>>>> * f2fs_commit_inmem_pages
>>>>>>
>>>>>> fs/f2fs/file.c | 3 +++
>>>>>> fs/f2fs/segment.c | 7 +++++++
>>>>>> 2 files changed, 10 insertions(+)
>>>>>>
>>>>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>>>>> index 4e6d4b9120a8..a48ec650d691 100644
>>>>>> --- a/fs/f2fs/file.c
>>>>>> +++ b/fs/f2fs/file.c
>>>>>> @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>>>> goto out;
>>>>>> down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> /*
>>>>>> * Should wait end_io to count F2FS_WB_CP_DATA correctly by
>>>>>> @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>>>> inode->i_ino, get_dirty_pages(inode));
>>>>>> ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
>>>>>> if (ret) {
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>>>> goto out;
>>>>>> }
>>>>>> @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>>>> /* add inode in inmem_list first and set atomic_file */
>>>>>> set_inode_flag(inode, FI_ATOMIC_FILE);
>>>>>> clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>>>> f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>>>>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
>>>>>> index d8570b0359f5..dab870d9faf6 100644
>>>>>> --- a/fs/f2fs/segment.c
>>>>>> +++ b/fs/f2fs/segment.c
>>>>>> @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>>>> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>>>>> struct f2fs_inode_info *fi = F2FS_I(inode);
>>>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> +
>>>>>> while (!list_empty(&fi->inmem_pages)) {
>>>>>> mutex_lock(&fi->inmem_lock);
>>>>>> __revoke_inmem_pages(inode, &fi->inmem_pages,
>>>>>> @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>>>> sbi->atomic_files--;
>>>>>> }
>>>>>> spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
>>>>>> +
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> }
>>>>>> void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
>>>>>> @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>>>> f2fs_balance_fs(sbi, true);
>>>>>> down_write(&fi->i_gc_rwsem[WRITE]);
>>>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> f2fs_lock_op(sbi);
>>>>>> set_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>>>> @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>>>> clear_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>>>> f2fs_unlock_op(sbi);
>>>>>> +
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> up_write(&fi->i_gc_rwsem[WRITE]);
>>>>>> return err;
>>>>>> --
>>>>>> 2.29.2
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Linux-f2fs-devel mailing list
>>>>> [email protected]
>>>>> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
>>>>
>>>>
>>>> _______________________________________________
>>>> Linux-f2fs-devel mailing list
>>>> [email protected]
>>>> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
>>> .
>>>
> .
>

2021-01-14 21:56:11

by Jaegeuk Kim

[permalink] [raw]
Subject: Re: [PATCH v2] f2fs: fix to keep isolation of atomic write

On 12/30, Chao Yu wrote:
> ThreadA ThreadB
> - f2fs_ioc_start_atomic_write
> - write
> - f2fs_ioc_commit_atomic_write
> - f2fs_commit_inmem_pages
> - f2fs_drop_inmem_pages
> - f2fs_drop_inmem_pages
> - __revoke_inmem_pages
> - f2fs_vm_page_mkwrite
> - set_page_dirty
> - tag ATOMIC_WRITTEN_PAGE and add page
> to inmem_pages list
> - clear_inode_flag(FI_ATOMIC_FILE)
> - f2fs_vm_page_mkwrite
> - set_page_dirty
> - f2fs_update_dirty_page
> - f2fs_trace_pid
> - tag inmem page private to pid

Hmm, how about removing fs/f2fs/trace.c to make private more complicated
like this? I think we can get IO traces from tracepoints.

> - truncate
> - f2fs_invalidate_page
> - set page->mapping to NULL
> then it will cause panic once we
> access page->mapping
>
> The root cause is we missed to keep isolation of atomic write in the case
> of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
> lock to avoid this issue.
>
> Signed-off-by: Chao Yu <[email protected]>
> ---
> v2:
> - use i_mmap_sem to avoid mkwrite racing with below flows:
> * f2fs_ioc_start_atomic_write
> * f2fs_drop_inmem_pages
> * f2fs_commit_inmem_pages
>
> fs/f2fs/file.c | 3 +++
> fs/f2fs/segment.c | 7 +++++++
> 2 files changed, 10 insertions(+)
>
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 4e6d4b9120a8..a48ec650d691 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> goto out;
>
> down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> + down_write(&F2FS_I(inode)->i_mmap_sem);
>
> /*
> * Should wait end_io to count F2FS_WB_CP_DATA correctly by
> @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> inode->i_ino, get_dirty_pages(inode));
> ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
> if (ret) {
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> goto out;
> }
> @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> /* add inode in inmem_list first and set atomic_file */
> set_inode_flag(inode, FI_ATOMIC_FILE);
> clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>
> f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> index d8570b0359f5..dab870d9faf6 100644
> --- a/fs/f2fs/segment.c
> +++ b/fs/f2fs/segment.c
> @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> struct f2fs_inode_info *fi = F2FS_I(inode);
>
> + down_write(&F2FS_I(inode)->i_mmap_sem);
> +
> while (!list_empty(&fi->inmem_pages)) {
> mutex_lock(&fi->inmem_lock);
> __revoke_inmem_pages(inode, &fi->inmem_pages,
> @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> sbi->atomic_files--;
> }
> spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
> +
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> }
>
> void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
> @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> f2fs_balance_fs(sbi, true);
>
> down_write(&fi->i_gc_rwsem[WRITE]);
> + down_write(&F2FS_I(inode)->i_mmap_sem);
>
> f2fs_lock_op(sbi);
> set_inode_flag(inode, FI_ATOMIC_COMMIT);
> @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> clear_inode_flag(inode, FI_ATOMIC_COMMIT);
>
> f2fs_unlock_op(sbi);
> +
> + up_write(&F2FS_I(inode)->i_mmap_sem);
> up_write(&fi->i_gc_rwsem[WRITE]);
>
> return err;
> --
> 2.29.2

2021-01-15 08:45:31

by Chao Yu

[permalink] [raw]
Subject: Re: [PATCH v2] f2fs: fix to keep isolation of atomic write

On 2021/1/15 5:53, Jaegeuk Kim wrote:
> On 12/30, Chao Yu wrote:
>> ThreadA ThreadB
>> - f2fs_ioc_start_atomic_write
>> - write
>> - f2fs_ioc_commit_atomic_write
>> - f2fs_commit_inmem_pages
>> - f2fs_drop_inmem_pages
>> - f2fs_drop_inmem_pages
>> - __revoke_inmem_pages
>> - f2fs_vm_page_mkwrite
>> - set_page_dirty
>> - tag ATOMIC_WRITTEN_PAGE and add page
>> to inmem_pages list
>> - clear_inode_flag(FI_ATOMIC_FILE)
>> - f2fs_vm_page_mkwrite
>> - set_page_dirty
>> - f2fs_update_dirty_page
>> - f2fs_trace_pid
>> - tag inmem page private to pid
>
> Hmm, how about removing fs/f2fs/trace.c to make private more complicated
> like this? I think we can get IO traces from tracepoints.

Hmm, actually, there is are issues, one is the trace IO, the other is the
race issue (atomic_start,commit,drop vs mkwrite) which can make isolation
semantics of transaction be broken.

Or can we avoid atomic file racing with file mmap?

- atomic_start - file_mmap
- inode_lock
- if (FI_ATOMIC_FILE) return
- inode_lock
- if (FI_MMAP_FILE) return

Thanks,

>
>> - truncate
>> - f2fs_invalidate_page
>> - set page->mapping to NULL
>> then it will cause panic once we
>> access page->mapping
>>
>> The root cause is we missed to keep isolation of atomic write in the case
>> of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
>> lock to avoid this issue.
>>
>> Signed-off-by: Chao Yu <[email protected]>
>> ---
>> v2:
>> - use i_mmap_sem to avoid mkwrite racing with below flows:
>> * f2fs_ioc_start_atomic_write
>> * f2fs_drop_inmem_pages
>> * f2fs_commit_inmem_pages
>>
>> fs/f2fs/file.c | 3 +++
>> fs/f2fs/segment.c | 7 +++++++
>> 2 files changed, 10 insertions(+)
>>
>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>> index 4e6d4b9120a8..a48ec650d691 100644
>> --- a/fs/f2fs/file.c
>> +++ b/fs/f2fs/file.c
>> @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>> goto out;
>>
>> down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>
>> /*
>> * Should wait end_io to count F2FS_WB_CP_DATA correctly by
>> @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>> inode->i_ino, get_dirty_pages(inode));
>> ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
>> if (ret) {
>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>> goto out;
>> }
>> @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>> /* add inode in inmem_list first and set atomic_file */
>> set_inode_flag(inode, FI_ATOMIC_FILE);
>> clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>
>> f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
>> index d8570b0359f5..dab870d9faf6 100644
>> --- a/fs/f2fs/segment.c
>> +++ b/fs/f2fs/segment.c
>> @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>> struct f2fs_inode_info *fi = F2FS_I(inode);
>>
>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>> +
>> while (!list_empty(&fi->inmem_pages)) {
>> mutex_lock(&fi->inmem_lock);
>> __revoke_inmem_pages(inode, &fi->inmem_pages,
>> @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>> sbi->atomic_files--;
>> }
>> spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
>> +
>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>> }
>>
>> void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
>> @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>> f2fs_balance_fs(sbi, true);
>>
>> down_write(&fi->i_gc_rwsem[WRITE]);
>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>
>> f2fs_lock_op(sbi);
>> set_inode_flag(inode, FI_ATOMIC_COMMIT);
>> @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>> clear_inode_flag(inode, FI_ATOMIC_COMMIT);
>>
>> f2fs_unlock_op(sbi);
>> +
>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>> up_write(&fi->i_gc_rwsem[WRITE]);
>>
>> return err;
>> --
>> 2.29.2
> .
>

2021-01-19 19:28:15

by Jaegeuk Kim

[permalink] [raw]
Subject: Re: [PATCH v2] f2fs: fix to keep isolation of atomic write

On 01/15, Chao Yu wrote:
> On 2021/1/15 5:53, Jaegeuk Kim wrote:
> > On 12/30, Chao Yu wrote:
> > > ThreadA ThreadB
> > > - f2fs_ioc_start_atomic_write
> > > - write
> > > - f2fs_ioc_commit_atomic_write
> > > - f2fs_commit_inmem_pages
> > > - f2fs_drop_inmem_pages
> > > - f2fs_drop_inmem_pages
> > > - __revoke_inmem_pages
> > > - f2fs_vm_page_mkwrite
> > > - set_page_dirty
> > > - tag ATOMIC_WRITTEN_PAGE and add page
> > > to inmem_pages list
> > > - clear_inode_flag(FI_ATOMIC_FILE)
> > > - f2fs_vm_page_mkwrite
> > > - set_page_dirty
> > > - f2fs_update_dirty_page
> > > - f2fs_trace_pid
> > > - tag inmem page private to pid
> >
> > Hmm, how about removing fs/f2fs/trace.c to make private more complicated
> > like this? I think we can get IO traces from tracepoints.
>
> Hmm, actually, there is are issues, one is the trace IO, the other is the
> race issue (atomic_start,commit,drop vs mkwrite) which can make isolation
> semantics of transaction be broken.
>
> Or can we avoid atomic file racing with file mmap?

No, we can't. We may need to find other way to check the race. :)

>
> - atomic_start - file_mmap
> - inode_lock
> - if (FI_ATOMIC_FILE) return
> - inode_lock
> - if (FI_MMAP_FILE) return
>
> Thanks,
>
> >
> > > - truncate
> > > - f2fs_invalidate_page
> > > - set page->mapping to NULL
> > > then it will cause panic once we
> > > access page->mapping
> > >
> > > The root cause is we missed to keep isolation of atomic write in the case
> > > of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
> > > lock to avoid this issue.
> > >
> > > Signed-off-by: Chao Yu <[email protected]>
> > > ---
> > > v2:
> > > - use i_mmap_sem to avoid mkwrite racing with below flows:
> > > * f2fs_ioc_start_atomic_write
> > > * f2fs_drop_inmem_pages
> > > * f2fs_commit_inmem_pages
> > >
> > > fs/f2fs/file.c | 3 +++
> > > fs/f2fs/segment.c | 7 +++++++
> > > 2 files changed, 10 insertions(+)
> > >
> > > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > > index 4e6d4b9120a8..a48ec650d691 100644
> > > --- a/fs/f2fs/file.c
> > > +++ b/fs/f2fs/file.c
> > > @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > goto out;
> > > down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > /*
> > > * Should wait end_io to count F2FS_WB_CP_DATA correctly by
> > > @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > inode->i_ino, get_dirty_pages(inode));
> > > ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
> > > if (ret) {
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > goto out;
> > > }
> > > @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > /* add inode in inmem_list first and set atomic_file */
> > > set_inode_flag(inode, FI_ATOMIC_FILE);
> > > clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> > > index d8570b0359f5..dab870d9faf6 100644
> > > --- a/fs/f2fs/segment.c
> > > +++ b/fs/f2fs/segment.c
> > > @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > > struct f2fs_inode_info *fi = F2FS_I(inode);
> > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > +
> > > while (!list_empty(&fi->inmem_pages)) {
> > > mutex_lock(&fi->inmem_lock);
> > > __revoke_inmem_pages(inode, &fi->inmem_pages,
> > > @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > sbi->atomic_files--;
> > > }
> > > spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
> > > +
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > }
> > > void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
> > > @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > f2fs_balance_fs(sbi, true);
> > > down_write(&fi->i_gc_rwsem[WRITE]);
> > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > f2fs_lock_op(sbi);
> > > set_inode_flag(inode, FI_ATOMIC_COMMIT);
> > > @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > clear_inode_flag(inode, FI_ATOMIC_COMMIT);
> > > f2fs_unlock_op(sbi);
> > > +
> > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > up_write(&fi->i_gc_rwsem[WRITE]);
> > > return err;
> > > --
> > > 2.29.2
> > .
> >

2021-01-20 01:22:30

by Chao Yu

[permalink] [raw]
Subject: Re: [PATCH v2] f2fs: fix to keep isolation of atomic write

On 2021/1/20 3:06, Jaegeuk Kim wrote:
> On 01/15, Chao Yu wrote:
>> On 2021/1/15 5:53, Jaegeuk Kim wrote:
>>> On 12/30, Chao Yu wrote:
>>>> ThreadA ThreadB
>>>> - f2fs_ioc_start_atomic_write
>>>> - write
>>>> - f2fs_ioc_commit_atomic_write
>>>> - f2fs_commit_inmem_pages
>>>> - f2fs_drop_inmem_pages
>>>> - f2fs_drop_inmem_pages
>>>> - __revoke_inmem_pages
>>>> - f2fs_vm_page_mkwrite
>>>> - set_page_dirty
>>>> - tag ATOMIC_WRITTEN_PAGE and add page
>>>> to inmem_pages list
>>>> - clear_inode_flag(FI_ATOMIC_FILE)
>>>> - f2fs_vm_page_mkwrite
>>>> - set_page_dirty
>>>> - f2fs_update_dirty_page
>>>> - f2fs_trace_pid
>>>> - tag inmem page private to pid
>>>
>>> Hmm, how about removing fs/f2fs/trace.c to make private more complicated
>>> like this? I think we can get IO traces from tracepoints.
>>
>> Hmm, actually, there is are issues, one is the trace IO, the other is the
>> race issue (atomic_start,commit,drop vs mkwrite) which can make isolation
>> semantics of transaction be broken.
>>
>> Or can we avoid atomic file racing with file mmap?

Otherwise I think we should add i_mmap_sem to avoid the race.

>
> No, we can't. We may need to find other way to check the race. :)

Well, any thoughts about this issue?

Thanks,

>
>>
>> - atomic_start - file_mmap
>> - inode_lock
>> - if (FI_ATOMIC_FILE) return
>> - inode_lock
>> - if (FI_MMAP_FILE) return
>>
>> Thanks,
>>
>>>
>>>> - truncate
>>>> - f2fs_invalidate_page
>>>> - set page->mapping to NULL
>>>> then it will cause panic once we
>>>> access page->mapping
>>>>
>>>> The root cause is we missed to keep isolation of atomic write in the case
>>>> of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
>>>> lock to avoid this issue.
>>>>
>>>> Signed-off-by: Chao Yu <[email protected]>
>>>> ---
>>>> v2:
>>>> - use i_mmap_sem to avoid mkwrite racing with below flows:
>>>> * f2fs_ioc_start_atomic_write
>>>> * f2fs_drop_inmem_pages
>>>> * f2fs_commit_inmem_pages
>>>>
>>>> fs/f2fs/file.c | 3 +++
>>>> fs/f2fs/segment.c | 7 +++++++
>>>> 2 files changed, 10 insertions(+)
>>>>
>>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>>> index 4e6d4b9120a8..a48ec650d691 100644
>>>> --- a/fs/f2fs/file.c
>>>> +++ b/fs/f2fs/file.c
>>>> @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>> goto out;
>>>> down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>> /*
>>>> * Should wait end_io to count F2FS_WB_CP_DATA correctly by
>>>> @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>> inode->i_ino, get_dirty_pages(inode));
>>>> ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
>>>> if (ret) {
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>> goto out;
>>>> }
>>>> @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>> /* add inode in inmem_list first and set atomic_file */
>>>> set_inode_flag(inode, FI_ATOMIC_FILE);
>>>> clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>> f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
>>>> index d8570b0359f5..dab870d9faf6 100644
>>>> --- a/fs/f2fs/segment.c
>>>> +++ b/fs/f2fs/segment.c
>>>> @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>>> struct f2fs_inode_info *fi = F2FS_I(inode);
>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>> +
>>>> while (!list_empty(&fi->inmem_pages)) {
>>>> mutex_lock(&fi->inmem_lock);
>>>> __revoke_inmem_pages(inode, &fi->inmem_pages,
>>>> @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>> sbi->atomic_files--;
>>>> }
>>>> spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
>>>> +
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> }
>>>> void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
>>>> @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>> f2fs_balance_fs(sbi, true);
>>>> down_write(&fi->i_gc_rwsem[WRITE]);
>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>> f2fs_lock_op(sbi);
>>>> set_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>> @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>> clear_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>> f2fs_unlock_op(sbi);
>>>> +
>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>> up_write(&fi->i_gc_rwsem[WRITE]);
>>>> return err;
>>>> --
>>>> 2.29.2
>>> .
>>>
> .
>

2021-01-28 16:23:26

by Jaegeuk Kim

[permalink] [raw]
Subject: Re: [PATCH v2] f2fs: fix to keep isolation of atomic write

On 01/20, Chao Yu wrote:
> On 2021/1/20 3:06, Jaegeuk Kim wrote:
> > On 01/15, Chao Yu wrote:
> > > On 2021/1/15 5:53, Jaegeuk Kim wrote:
> > > > On 12/30, Chao Yu wrote:
> > > > > ThreadA ThreadB
> > > > > - f2fs_ioc_start_atomic_write
> > > > > - write
> > > > > - f2fs_ioc_commit_atomic_write
> > > > > - f2fs_commit_inmem_pages
> > > > > - f2fs_drop_inmem_pages
> > > > > - f2fs_drop_inmem_pages
> > > > > - __revoke_inmem_pages
> > > > > - f2fs_vm_page_mkwrite
> > > > > - set_page_dirty
> > > > > - tag ATOMIC_WRITTEN_PAGE and add page
> > > > > to inmem_pages list
> > > > > - clear_inode_flag(FI_ATOMIC_FILE)
> > > > > - f2fs_vm_page_mkwrite
> > > > > - set_page_dirty
> > > > > - f2fs_update_dirty_page
> > > > > - f2fs_trace_pid
> > > > > - tag inmem page private to pid
> > > >
> > > > Hmm, how about removing fs/f2fs/trace.c to make private more complicated
> > > > like this? I think we can get IO traces from tracepoints.
> > >
> > > Hmm, actually, there is are issues, one is the trace IO, the other is the
> > > race issue (atomic_start,commit,drop vs mkwrite) which can make isolation
> > > semantics of transaction be broken.
> > >
> > > Or can we avoid atomic file racing with file mmap?
>
> Otherwise I think we should add i_mmap_sem to avoid the race.
>
> >
> > No, we can't. We may need to find other way to check the race. :)
>
> Well, any thoughts about this issue?
>
> Thanks,
>
> >
> > >
> > > - atomic_start - file_mmap
> > > - inode_lock
> > > - if (FI_ATOMIC_FILE) return
> > > - inode_lock
> > > - if (FI_MMAP_FILE) return
> > >
> > > Thanks,
> > >
> > > >
> > > > > - truncate
> > > > > - f2fs_invalidate_page
> > > > > - set page->mapping to NULL
> > > > > then it will cause panic once we
> > > > > access page->mapping

Are we hitting this, since page was referenced by in-mem list?

> > > > >
> > > > > The root cause is we missed to keep isolation of atomic write in the case
> > > > > of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
> > > > > lock to avoid this issue.
> > > > >
> > > > > Signed-off-by: Chao Yu <[email protected]>
> > > > > ---
> > > > > v2:
> > > > > - use i_mmap_sem to avoid mkwrite racing with below flows:
> > > > > * f2fs_ioc_start_atomic_write
> > > > > * f2fs_drop_inmem_pages
> > > > > * f2fs_commit_inmem_pages
> > > > >
> > > > > fs/f2fs/file.c | 3 +++
> > > > > fs/f2fs/segment.c | 7 +++++++
> > > > > 2 files changed, 10 insertions(+)
> > > > >
> > > > > diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> > > > > index 4e6d4b9120a8..a48ec650d691 100644
> > > > > --- a/fs/f2fs/file.c
> > > > > +++ b/fs/f2fs/file.c
> > > > > @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > > > goto out;
> > > > > down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > /*
> > > > > * Should wait end_io to count F2FS_WB_CP_DATA correctly by
> > > > > @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > > > inode->i_ino, get_dirty_pages(inode));
> > > > > ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
> > > > > if (ret) {
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > > > goto out;
> > > > > }
> > > > > @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
> > > > > /* add inode in inmem_list first and set atomic_file */
> > > > > set_inode_flag(inode, FI_ATOMIC_FILE);
> > > > > clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
> > > > > f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > > > > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> > > > > index d8570b0359f5..dab870d9faf6 100644
> > > > > --- a/fs/f2fs/segment.c
> > > > > +++ b/fs/f2fs/segment.c
> > > > > @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > > > struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > > > > struct f2fs_inode_info *fi = F2FS_I(inode);
> > > > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > +
> > > > > while (!list_empty(&fi->inmem_pages)) {
> > > > > mutex_lock(&fi->inmem_lock);
> > > > > __revoke_inmem_pages(inode, &fi->inmem_pages,
> > > > > @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
> > > > > sbi->atomic_files--;
> > > > > }
> > > > > spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
> > > > > +
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > }
> > > > > void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
> > > > > @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > > > f2fs_balance_fs(sbi, true);
> > > > > down_write(&fi->i_gc_rwsem[WRITE]);
> > > > > + down_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > f2fs_lock_op(sbi);
> > > > > set_inode_flag(inode, FI_ATOMIC_COMMIT);
> > > > > @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
> > > > > clear_inode_flag(inode, FI_ATOMIC_COMMIT);
> > > > > f2fs_unlock_op(sbi);
> > > > > +
> > > > > + up_write(&F2FS_I(inode)->i_mmap_sem);
> > > > > up_write(&fi->i_gc_rwsem[WRITE]);
> > > > > return err;
> > > > > --
> > > > > 2.29.2
> > > > .
> > > >
> > .
> >

2021-01-29 01:43:42

by Chao Yu

[permalink] [raw]
Subject: Re: [PATCH v2] f2fs: fix to keep isolation of atomic write

On 2021/1/29 0:21, Jaegeuk Kim wrote:
> On 01/20, Chao Yu wrote:
>> On 2021/1/20 3:06, Jaegeuk Kim wrote:
>>> On 01/15, Chao Yu wrote:
>>>> On 2021/1/15 5:53, Jaegeuk Kim wrote:
>>>>> On 12/30, Chao Yu wrote:
>>>>>> ThreadA ThreadB
>>>>>> - f2fs_ioc_start_atomic_write
>>>>>> - write
>>>>>> - f2fs_ioc_commit_atomic_write
>>>>>> - f2fs_commit_inmem_pages
>>>>>> - f2fs_drop_inmem_pages
>>>>>> - f2fs_drop_inmem_pages
>>>>>> - __revoke_inmem_pages
>>>>>> - f2fs_vm_page_mkwrite
>>>>>> - set_page_dirty
>>>>>> - tag ATOMIC_WRITTEN_PAGE and add page
>>>>>> to inmem_pages list
>>>>>> - clear_inode_flag(FI_ATOMIC_FILE)
>>>>>> - f2fs_vm_page_mkwrite
>>>>>> - set_page_dirty
>>>>>> - f2fs_update_dirty_page
>>>>>> - f2fs_trace_pid
>>>>>> - tag inmem page private to pid
>>>>>
>>>>> Hmm, how about removing fs/f2fs/trace.c to make private more complicated
>>>>> like this? I think we can get IO traces from tracepoints.
>>>>
>>>> Hmm, actually, there is are issues, one is the trace IO, the other is the
>>>> race issue (atomic_start,commit,drop vs mkwrite) which can make isolation
>>>> semantics of transaction be broken.
>>>>
>>>> Or can we avoid atomic file racing with file mmap?
>>
>> Otherwise I think we should add i_mmap_sem to avoid the race.
>>
>>>
>>> No, we can't. We may need to find other way to check the race. :)
>>
>> Well, any thoughts about this issue?
>>
>> Thanks,
>>
>>>
>>>>
>>>> - atomic_start - file_mmap
>>>> - inode_lock
>>>> - if (FI_ATOMIC_FILE) return
>>>> - inode_lock
>>>> - if (FI_MMAP_FILE) return
>>>>
>>>> Thanks,
>>>>
>>>>>
>>>>>> - truncate
>>>>>> - f2fs_invalidate_page
>>>>>> - set page->mapping to NULL
>>>>>> then it will cause panic once we
>>>>>> access page->mapping
>
> Are we hitting this, since page was referenced by in-mem list?

Yes, we hit this NULL point dereferencing issue when running fuzz tool,
but the condition is not the same, because in our product,
CONFIG_F2FS_IO_TRACE was not set.

Thanks,

>
>>>>>>
>>>>>> The root cause is we missed to keep isolation of atomic write in the case
>>>>>> of commit_atomic_write vs mkwrite, let commit_atomic_write helds i_mmap_sem
>>>>>> lock to avoid this issue.
>>>>>>
>>>>>> Signed-off-by: Chao Yu <[email protected]>
>>>>>> ---
>>>>>> v2:
>>>>>> - use i_mmap_sem to avoid mkwrite racing with below flows:
>>>>>> * f2fs_ioc_start_atomic_write
>>>>>> * f2fs_drop_inmem_pages
>>>>>> * f2fs_commit_inmem_pages
>>>>>>
>>>>>> fs/f2fs/file.c | 3 +++
>>>>>> fs/f2fs/segment.c | 7 +++++++
>>>>>> 2 files changed, 10 insertions(+)
>>>>>>
>>>>>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
>>>>>> index 4e6d4b9120a8..a48ec650d691 100644
>>>>>> --- a/fs/f2fs/file.c
>>>>>> +++ b/fs/f2fs/file.c
>>>>>> @@ -2050,6 +2050,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>>>> goto out;
>>>>>> down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> /*
>>>>>> * Should wait end_io to count F2FS_WB_CP_DATA correctly by
>>>>>> @@ -2060,6 +2061,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>>>> inode->i_ino, get_dirty_pages(inode));
>>>>>> ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
>>>>>> if (ret) {
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>>>> goto out;
>>>>>> }
>>>>>> @@ -2073,6 +2075,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
>>>>>> /* add inode in inmem_list first and set atomic_file */
>>>>>> set_inode_flag(inode, FI_ATOMIC_FILE);
>>>>>> clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
>>>>>> f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>>>>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
>>>>>> index d8570b0359f5..dab870d9faf6 100644
>>>>>> --- a/fs/f2fs/segment.c
>>>>>> +++ b/fs/f2fs/segment.c
>>>>>> @@ -327,6 +327,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>>>> struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>>>>> struct f2fs_inode_info *fi = F2FS_I(inode);
>>>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> +
>>>>>> while (!list_empty(&fi->inmem_pages)) {
>>>>>> mutex_lock(&fi->inmem_lock);
>>>>>> __revoke_inmem_pages(inode, &fi->inmem_pages,
>>>>>> @@ -344,6 +346,8 @@ void f2fs_drop_inmem_pages(struct inode *inode)
>>>>>> sbi->atomic_files--;
>>>>>> }
>>>>>> spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
>>>>>> +
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> }
>>>>>> void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
>>>>>> @@ -467,6 +471,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>>>> f2fs_balance_fs(sbi, true);
>>>>>> down_write(&fi->i_gc_rwsem[WRITE]);
>>>>>> + down_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> f2fs_lock_op(sbi);
>>>>>> set_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>>>> @@ -478,6 +483,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
>>>>>> clear_inode_flag(inode, FI_ATOMIC_COMMIT);
>>>>>> f2fs_unlock_op(sbi);
>>>>>> +
>>>>>> + up_write(&F2FS_I(inode)->i_mmap_sem);
>>>>>> up_write(&fi->i_gc_rwsem[WRITE]);
>>>>>> return err;
>>>>>> --
>>>>>> 2.29.2
>>>>> .
>>>>>
>>> .
>>>
> .
>