[PATCH] btrfs: fix inline reflink deadlock at non-zero offset

Guangshuo Li posted 1 patch 4 days, 17 hours ago
fs/btrfs/reflink.c | 2 ++
1 file changed, 2 insertions(+)
[PATCH] btrfs: fix inline reflink deadlock at non-zero offset
Posted by Guangshuo Li 4 days, 17 hours ago
clone_copy_inline_extent() can copy an inline extent into a page of the
destination inode before starting a transaction. If that page is beyond
EOF, writeback started by a transaction commit with flushoncommit can
invalidate the folio and block on the same extent range lock held by the
reflink task, while the reflink task waits to start a transaction.

The copy_to_page path already updates i_size after copy_inline_to_page(),
so writeback no longer treats the copied page as beyond EOF. However,
clone_copy_inline_extent() has an earlier new_key->offset > 0 path that
also calls copy_inline_to_page() and then goes to out, where it starts a
transaction. That path still leaves i_size unchanged before starting the
transaction.

Update i_size after a successful copy_inline_to_page() in the early
non-zero offset path as well, matching the existing copy_to_page path.

Fixes: 05a5a7621ce66 ("Btrfs: implement full reflink support for inline extents")
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
---
 fs/btrfs/reflink.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
index 49865a463780..5722cccb53eb 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -185,6 +185,8 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode,
 	if (new_key->offset > 0) {
 		ret = copy_inline_to_page(inode, new_key->offset,
 					  inline_data, size, datal, comp_type);
+		if (ret == 0 && new_key->offset + datal > i_size_read(&inode->vfs_inode))
+			i_size_write(&inode->vfs_inode, new_key->offset + datal);
 		goto out;
 	}
 
-- 
2.43.0
Re: [PATCH] btrfs: fix inline reflink deadlock at non-zero offset
Posted by Filipe Manana 4 days, 16 hours ago
On Wed, Jun 3, 2026 at 1:25 PM Guangshuo Li <lgs201920130244@gmail.com> wrote:
>
> clone_copy_inline_extent() can copy an inline extent into a page of the
> destination inode before starting a transaction. If that page is beyond
> EOF, writeback started by a transaction commit with flushoncommit can
> invalidate the folio and block on the same extent range lock held by the
> reflink task, while the reflink task waits to start a transaction.
>
> The copy_to_page path already updates i_size after copy_inline_to_page(),
> so writeback no longer treats the copied page as beyond EOF. However,
> clone_copy_inline_extent() has an earlier new_key->offset > 0 path that
> also calls copy_inline_to_page() and then goes to out, where it starts a
> transaction. That path still leaves i_size unchanged before starting the
> transaction.
>
> Update i_size after a successful copy_inline_to_page() in the early
> non-zero offset path as well, matching the existing copy_to_page path.
>
> Fixes: 05a5a7621ce66 ("Btrfs: implement full reflink support for inline extents")
> Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>

Again?

You already sent this patch yesterday, and there's a reply:

https://lore.kernel.org/linux-btrfs/20260603120443.1394044-1-lgs201920130244@gmail.com/

> ---
>  fs/btrfs/reflink.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
> index 49865a463780..5722cccb53eb 100644
> --- a/fs/btrfs/reflink.c
> +++ b/fs/btrfs/reflink.c
> @@ -185,6 +185,8 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode,
>         if (new_key->offset > 0) {
>                 ret = copy_inline_to_page(inode, new_key->offset,
>                                           inline_data, size, datal, comp_type);
> +               if (ret == 0 && new_key->offset + datal > i_size_read(&inode->vfs_inode))
> +                       i_size_write(&inode->vfs_inode, new_key->offset + datal);
>                 goto out;
>         }
>
> --
> 2.43.0
>
>
Re: [PATCH] btrfs: fix inline reflink deadlock at non-zero offset
Posted by Guangshuo Li 4 days, 15 hours ago
Hi Filipe,

On Wed, 3 Jun 2026 at 20:36, Filipe Manana <fdmanana@kernel.org> wrote:
>
> On Wed, Jun 3, 2026 at 1:25 PM Guangshuo Li <lgs201920130244@gmail.com> wrote:
> >
> > clone_copy_inline_extent() can copy an inline extent into a page of the
> > destination inode before starting a transaction. If that page is beyond
> > EOF, writeback started by a transaction commit with flushoncommit can
> > invalidate the folio and block on the same extent range lock held by the
> > reflink task, while the reflink task waits to start a transaction.
> >
> > The copy_to_page path already updates i_size after copy_inline_to_page(),
> > so writeback no longer treats the copied page as beyond EOF. However,
> > clone_copy_inline_extent() has an earlier new_key->offset > 0 path that
> > also calls copy_inline_to_page() and then goes to out, where it starts a
> > transaction. That path still leaves i_size unchanged before starting the
> > transaction.
> >
> > Update i_size after a successful copy_inline_to_page() in the early
> > non-zero offset path as well, matching the existing copy_to_page path.
> >
> > Fixes: 05a5a7621ce66 ("Btrfs: implement full reflink support for inline extents")
> > Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
>
> Again?
>
> You already sent this patch yesterday, and there's a reply:
>
> https://lore.kernel.org/linux-btrfs/20260603120443.1394044-1-lgs201920130244@gmail.com/
>
> > ---
> >  fs/btrfs/reflink.c | 2 ++
> >  1 file changed, 2 insertions(+)
> >
> > diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
> > index 49865a463780..5722cccb53eb 100644
> > --- a/fs/btrfs/reflink.c
> > +++ b/fs/btrfs/reflink.c
> > @@ -185,6 +185,8 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode,
> >         if (new_key->offset > 0) {
> >                 ret = copy_inline_to_page(inode, new_key->offset,
> >                                           inline_data, size, datal, comp_type);
> > +               if (ret == 0 && new_key->offset + datal > i_size_read(&inode->vfs_inode))
> > +                       i_size_write(&inode->vfs_inode, new_key->offset + datal);
> >                 goto out;
> >         }
> >
> > --
> > 2.43.0
> >
> >
Sorry, this was my mistake. I accidentally sent the same patch again.

Sorry for the noise.

Thanks,
Guangshuo