* [PATCH 0/3] btrfs: fix a bug and cleanups in btrfs_page_mkwrite()
@ 2025-05-14 10:29 fdmanana
2025-05-14 10:29 ` [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
` (3 more replies)
0 siblings, 4 replies; 20+ messages in thread
From: fdmanana @ 2025-05-14 10:29 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
A bug fix for mmap writes and a couple cleanups. Details in the change logs.
Filipe Manana (3):
btrfs: fix wrong start offset for delalloc space release during mmap write
btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite()
btrfs: simplify early error checking in btrfs_page_mkwrite()
fs/btrfs/file.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
--
2.47.2
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write
2025-05-14 10:29 [PATCH 0/3] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() fdmanana
@ 2025-05-14 10:29 ` fdmanana
2025-05-14 10:38 ` Qu Wenruo
2025-05-14 10:29 ` [PATCH 2/3] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite() fdmanana
` (2 subsequent siblings)
3 siblings, 1 reply; 20+ messages in thread
From: fdmanana @ 2025-05-14 10:29 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
If we're doing a mmap write against a folio that has i_size somewhere in
the middle and we have multiple sectors in the folio, we may have to
release excess space previously reserved, for the range going from the
rounded up (to sector size) i_size to the folio's end offset. We are
calculating the right amount to release and passing it to
btrfs_delalloc_release_space(), but we are passing the wrong start offset
of that range - we're passing the folio's start offset instead of the
end offset, plus 1, of the range for which we keep the reservation. This
may result in releasing more space then we should and eventually trigger
an underflow of the data space_info's bytes_may_use counter.
So fix this by passing the start offset as 'end + 1' instead of
'page_start' to btrfs_delalloc_release_space().
Fixes: d0b7da88f640 ("Btrfs: btrfs_page_mkwrite: Reserve space in sectorsized units")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 660a73b6af90..32aad1b02b01 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1918,7 +1918,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
if (reserved_space < fsize) {
end = page_start + reserved_space - 1;
btrfs_delalloc_release_space(BTRFS_I(inode),
- data_reserved, page_start,
+ data_reserved, end + 1,
fsize - reserved_space, true);
}
}
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 2/3] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite()
2025-05-14 10:29 [PATCH 0/3] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() fdmanana
2025-05-14 10:29 ` [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
@ 2025-05-14 10:29 ` fdmanana
2025-05-14 10:40 ` Qu Wenruo
2025-05-14 10:29 ` [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
3 siblings, 1 reply; 20+ messages in thread
From: fdmanana @ 2025-05-14 10:29 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
In the last call to btrfs_delalloc_release_space() where the value of the
variable 'ret' is never zero, we pass the expression 'ret != 0' as the
value for the argument 'qgroup_free', which always evaluates to true.
Make this less confusing and more clear by explicitly passing true
instead.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 32aad1b02b01..a2b1fc536fdd 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1971,7 +1971,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
out:
btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
- reserved_space, (ret != 0));
+ reserved_space, true);
out_noreserve:
sb_end_pagefault(inode->i_sb);
extent_changeset_free(data_reserved);
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite()
2025-05-14 10:29 [PATCH 0/3] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() fdmanana
2025-05-14 10:29 ` [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
2025-05-14 10:29 ` [PATCH 2/3] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite() fdmanana
@ 2025-05-14 10:29 ` fdmanana
2025-05-14 10:50 ` Qu Wenruo
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
3 siblings, 1 reply; 20+ messages in thread
From: fdmanana @ 2025-05-14 10:29 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We have this entangled error checks early at btrfs_page_mkwrite():
1) Try to reserve delalloc space by calling btrfs_delalloc_reserve_space()
and storing the return value in the ret2 variable;
2) If the reservation succeed, call file_update_time() and store the
return value in ret2 and also set the local variable 'reserved' to
true (1);
3) Then do an error check on ret2 to see if any of the previous calls
failed and if so, jump either to the 'out' label or to the
'out_noreserve' label, depending on whether 'reserved' is true or
not.
This is unnecessarily complex. Instead change this to a simpler and
more straighforward approach:
1) Call btrfs_delalloc_reserve_space(), if that returns an error jump to
the 'out_noreserve' label;
2) The call file_update_time() and if that returns an error jump to the
'out' label.
Like this there's less nested if statements, no need to use a local
variable to track if space was reserved and if statements are used only
to check errors.
Also move the call to extent_changeset_free() out of the 'out_noreserve'
label and under the 'out' label since the changeset is allocated only if
the call to reserve delalloc space succeeded.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index a2b1fc536fdd..9ecb9f3bd057 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1843,7 +1843,6 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
size_t fsize = folio_size(folio);
vm_fault_t ret;
int ret2;
- int reserved = 0;
u64 reserved_space;
u64 page_start;
u64 page_end;
@@ -1866,17 +1865,17 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
*/
ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
page_start, reserved_space);
- if (!ret2) {
- ret2 = file_update_time(vmf->vma->vm_file);
- reserved = 1;
- }
if (ret2) {
ret = vmf_error(ret2);
- if (reserved)
- goto out;
goto out_noreserve;
}
+ ret2 = file_update_time(vmf->vma->vm_file);
+ if (ret2) {
+ ret = vmf_error(ret2);
+ goto out;
+ }
+
/* Make the VM retry the fault. */
ret = VM_FAULT_NOPAGE;
again:
@@ -1972,9 +1971,9 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
reserved_space, true);
+ extent_changeset_free(data_reserved);
out_noreserve:
sb_end_pagefault(inode->i_sb);
- extent_changeset_free(data_reserved);
return ret;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write
2025-05-14 10:29 ` [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
@ 2025-05-14 10:38 ` Qu Wenruo
2025-05-14 10:41 ` Filipe Manana
0 siblings, 1 reply; 20+ messages in thread
From: Qu Wenruo @ 2025-05-14 10:38 UTC (permalink / raw)
To: fdmanana, linux-btrfs
在 2025/5/14 19:59, fdmanana@kernel.org 写道:
> From: Filipe Manana <fdmanana@suse.com>
>
> If we're doing a mmap write against a folio that has i_size somewhere in
> the middle and we have multiple sectors in the folio, we may have to
> release excess space previously reserved, for the range going from the
> rounded up (to sector size) i_size to the folio's end offset. We are
> calculating the right amount to release and passing it to
> btrfs_delalloc_release_space(), but we are passing the wrong start offset
> of that range - we're passing the folio's start offset instead of the
> end offset, plus 1, of the range for which we keep the reservation. This
> may result in releasing more space then we should and eventually trigger
> an underflow of the data space_info's bytes_may_use counter.
>
> So fix this by passing the start offset as 'end + 1' instead of
> 'page_start' to btrfs_delalloc_release_space().
>
> Fixes: d0b7da88f640 ("Btrfs: btrfs_page_mkwrite: Reserve space in sectorsized units")
> Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Just curious how did you spot this, by pure eyeballing or you have some
tool exposing this?
Thanks,
Qu> ---
> fs/btrfs/file.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index 660a73b6af90..32aad1b02b01 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1918,7 +1918,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> if (reserved_space < fsize) {
> end = page_start + reserved_space - 1;
> btrfs_delalloc_release_space(BTRFS_I(inode),
> - data_reserved, page_start,
> + data_reserved, end + 1,
> fsize - reserved_space, true);
> }
> }
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite()
2025-05-14 10:29 ` [PATCH 2/3] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite() fdmanana
@ 2025-05-14 10:40 ` Qu Wenruo
0 siblings, 0 replies; 20+ messages in thread
From: Qu Wenruo @ 2025-05-14 10:40 UTC (permalink / raw)
To: fdmanana, linux-btrfs
在 2025/5/14 19:59, fdmanana@kernel.org 写道:
> From: Filipe Manana <fdmanana@suse.com>
>
> In the last call to btrfs_delalloc_release_space() where the value of the
> variable 'ret' is never zero, we pass the expression 'ret != 0' as the
> value for the argument 'qgroup_free', which always evaluates to true.
> Make this less confusing and more clear by explicitly passing true
> instead.
>
> Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Thanks,
Qu
> ---
> fs/btrfs/file.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index 32aad1b02b01..a2b1fc536fdd 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1971,7 +1971,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> out:
> btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
> btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
> - reserved_space, (ret != 0));
> + reserved_space, true);
> out_noreserve:
> sb_end_pagefault(inode->i_sb);
> extent_changeset_free(data_reserved);
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write
2025-05-14 10:38 ` Qu Wenruo
@ 2025-05-14 10:41 ` Filipe Manana
0 siblings, 0 replies; 20+ messages in thread
From: Filipe Manana @ 2025-05-14 10:41 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs
On Wed, May 14, 2025 at 11:38 AM Qu Wenruo <wqu@suse.com> wrote:
>
>
>
> 在 2025/5/14 19:59, fdmanana@kernel.org 写道:
> > From: Filipe Manana <fdmanana@suse.com>
> >
> > If we're doing a mmap write against a folio that has i_size somewhere in
> > the middle and we have multiple sectors in the folio, we may have to
> > release excess space previously reserved, for the range going from the
> > rounded up (to sector size) i_size to the folio's end offset. We are
> > calculating the right amount to release and passing it to
> > btrfs_delalloc_release_space(), but we are passing the wrong start offset
> > of that range - we're passing the folio's start offset instead of the
> > end offset, plus 1, of the range for which we keep the reservation. This
> > may result in releasing more space then we should and eventually trigger
> > an underflow of the data space_info's bytes_may_use counter.
> >
> > So fix this by passing the start offset as 'end + 1' instead of
> > 'page_start' to btrfs_delalloc_release_space().
> >
> > Fixes: d0b7da88f640 ("Btrfs: btrfs_page_mkwrite: Reserve space in sectorsized units")
> > Signed-off-by: Filipe Manana <fdmanana@suse.com>
>
> Reviewed-by: Qu Wenruo <wqu@suse.com>
>
> Just curious how did you spot this, by pure eyeballing or you have some
> tool exposing this?
Just eyeballing while working on other incoming changes to this function.
>
> Thanks,
> Qu> ---
> > fs/btrfs/file.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> > index 660a73b6af90..32aad1b02b01 100644
> > --- a/fs/btrfs/file.c
> > +++ b/fs/btrfs/file.c
> > @@ -1918,7 +1918,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> > if (reserved_space < fsize) {
> > end = page_start + reserved_space - 1;
> > btrfs_delalloc_release_space(BTRFS_I(inode),
> > - data_reserved, page_start,
> > + data_reserved, end + 1,
> > fsize - reserved_space, true);
> > }
> > }
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite()
2025-05-14 10:29 ` [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
@ 2025-05-14 10:50 ` Qu Wenruo
2025-05-14 10:57 ` Filipe Manana
0 siblings, 1 reply; 20+ messages in thread
From: Qu Wenruo @ 2025-05-14 10:50 UTC (permalink / raw)
To: fdmanana, linux-btrfs
在 2025/5/14 19:59, fdmanana@kernel.org 写道:
> From: Filipe Manana <fdmanana@suse.com>
>
> We have this entangled error checks early at btrfs_page_mkwrite():
>
> 1) Try to reserve delalloc space by calling btrfs_delalloc_reserve_space()
> and storing the return value in the ret2 variable;
>
> 2) If the reservation succeed, call file_update_time() and store the
> return value in ret2 and also set the local variable 'reserved' to
> true (1);
>
> 3) Then do an error check on ret2 to see if any of the previous calls
> failed and if so, jump either to the 'out' label or to the
> 'out_noreserve' label, depending on whether 'reserved' is true or
> not.
>
> This is unnecessarily complex. Instead change this to a simpler and
> more straighforward approach:
>
> 1) Call btrfs_delalloc_reserve_space(), if that returns an error jump to
> the 'out_noreserve' label;
>
> 2) The call file_update_time() and if that returns an error jump to the
> 'out' label.
>
> Like this there's less nested if statements, no need to use a local
> variable to track if space was reserved and if statements are used only
> to check errors.
>
> Also move the call to extent_changeset_free() out of the 'out_noreserve'
> label and under the 'out' label since the changeset is allocated only if
> the call to reserve delalloc space succeeded.
>
> Signed-off-by: Filipe Manana <fdmanana@suse.com>
Looks good to me, but I'm wondering can we do something better by
completely merging @ret2 and @ret.
In fact we didn't really utilize @ret except two sites:
- page got truncated out
- btrfs_set_extent_delalloc() failure
In that case, we should use vmf_error()
In the out_noreserve tag, do something like:
if (ret)
return vmf_error(ret);
return VM_FAULT_NOPAGE;
Now we only need a single "int ret" as usual and not need to bother the
VM_FAULT* return values at all.
Thanks,
Qu
> ---
> fs/btrfs/file.c | 15 +++++++--------
> 1 file changed, 7 insertions(+), 8 deletions(-)
>
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index a2b1fc536fdd..9ecb9f3bd057 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1843,7 +1843,6 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> size_t fsize = folio_size(folio);
> vm_fault_t ret;
> int ret2;
> - int reserved = 0;
> u64 reserved_space;
> u64 page_start;
> u64 page_end;
> @@ -1866,17 +1865,17 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> */
> ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
> page_start, reserved_space);
> - if (!ret2) {
> - ret2 = file_update_time(vmf->vma->vm_file);
> - reserved = 1;
> - }
> if (ret2) {
> ret = vmf_error(ret2);
> - if (reserved)
> - goto out;
> goto out_noreserve;
> }
>
> + ret2 = file_update_time(vmf->vma->vm_file);
> + if (ret2) {
> + ret = vmf_error(ret2);
> + goto out;
> + }
> +
> /* Make the VM retry the fault. */
> ret = VM_FAULT_NOPAGE;
> again:
> @@ -1972,9 +1971,9 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
> btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
> reserved_space, true);
> + extent_changeset_free(data_reserved);
> out_noreserve:
> sb_end_pagefault(inode->i_sb);
> - extent_changeset_free(data_reserved);
> return ret;
> }
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite()
2025-05-14 10:50 ` Qu Wenruo
@ 2025-05-14 10:57 ` Filipe Manana
2025-05-14 11:35 ` Filipe Manana
0 siblings, 1 reply; 20+ messages in thread
From: Filipe Manana @ 2025-05-14 10:57 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs
On Wed, May 14, 2025 at 11:51 AM Qu Wenruo <wqu@suse.com> wrote:
>
>
>
> 在 2025/5/14 19:59, fdmanana@kernel.org 写道:
> > From: Filipe Manana <fdmanana@suse.com>
> >
> > We have this entangled error checks early at btrfs_page_mkwrite():
> >
> > 1) Try to reserve delalloc space by calling btrfs_delalloc_reserve_space()
> > and storing the return value in the ret2 variable;
> >
> > 2) If the reservation succeed, call file_update_time() and store the
> > return value in ret2 and also set the local variable 'reserved' to
> > true (1);
> >
> > 3) Then do an error check on ret2 to see if any of the previous calls
> > failed and if so, jump either to the 'out' label or to the
> > 'out_noreserve' label, depending on whether 'reserved' is true or
> > not.
> >
> > This is unnecessarily complex. Instead change this to a simpler and
> > more straighforward approach:
> >
> > 1) Call btrfs_delalloc_reserve_space(), if that returns an error jump to
> > the 'out_noreserve' label;
> >
> > 2) The call file_update_time() and if that returns an error jump to the
> > 'out' label.
> >
> > Like this there's less nested if statements, no need to use a local
> > variable to track if space was reserved and if statements are used only
> > to check errors.
> >
> > Also move the call to extent_changeset_free() out of the 'out_noreserve'
> > label and under the 'out' label since the changeset is allocated only if
> > the call to reserve delalloc space succeeded.
> >
> > Signed-off-by: Filipe Manana <fdmanana@suse.com>
>
> Looks good to me, but I'm wondering can we do something better by
> completely merging @ret2 and @ret.
>
> In fact we didn't really utilize @ret except two sites:
>
> - page got truncated out
>
> - btrfs_set_extent_delalloc() failure
> In that case, we should use vmf_error()
>
> In the out_noreserve tag, do something like:
>
> if (ret)
> return vmf_error(ret);
> return VM_FAULT_NOPAGE;
>
> Now we only need a single "int ret" as usual and not need to bother the
> VM_FAULT* return values at all.
I considered all that, but it's not so simple, one thing is that 'ret'
is of a different type, vm_fault_t, which happens to be an unsigned
int - but I don't think it's good to make assumptions about that in
the code.
There's also the case where we set ret to VM_FAULT_SIGBUS.
I thought about getting rid of ret2 and have a single 'ret' or type
int, and then do something like:
if (ret < 0)
return vmf_error(ret);
return ret ? ret : VM_FAULT_NOPAGE;
But then I was making assumptions that vm_fault_t values are positive,
and didn't think it's a good thing to do because it's an opaque type.
>
> Thanks,
> Qu
>
> > ---
> > fs/btrfs/file.c | 15 +++++++--------
> > 1 file changed, 7 insertions(+), 8 deletions(-)
> >
> > diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> > index a2b1fc536fdd..9ecb9f3bd057 100644
> > --- a/fs/btrfs/file.c
> > +++ b/fs/btrfs/file.c
> > @@ -1843,7 +1843,6 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> > size_t fsize = folio_size(folio);
> > vm_fault_t ret;
> > int ret2;
> > - int reserved = 0;
> > u64 reserved_space;
> > u64 page_start;
> > u64 page_end;
> > @@ -1866,17 +1865,17 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> > */
> > ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
> > page_start, reserved_space);
> > - if (!ret2) {
> > - ret2 = file_update_time(vmf->vma->vm_file);
> > - reserved = 1;
> > - }
> > if (ret2) {
> > ret = vmf_error(ret2);
> > - if (reserved)
> > - goto out;
> > goto out_noreserve;
> > }
> >
> > + ret2 = file_update_time(vmf->vma->vm_file);
> > + if (ret2) {
> > + ret = vmf_error(ret2);
> > + goto out;
> > + }
> > +
> > /* Make the VM retry the fault. */
> > ret = VM_FAULT_NOPAGE;
> > again:
> > @@ -1972,9 +1971,9 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> > btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
> > btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
> > reserved_space, true);
> > + extent_changeset_free(data_reserved);
> > out_noreserve:
> > sb_end_pagefault(inode->i_sb);
> > - extent_changeset_free(data_reserved);
> > return ret;
> > }
> >
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite()
2025-05-14 10:57 ` Filipe Manana
@ 2025-05-14 11:35 ` Filipe Manana
0 siblings, 0 replies; 20+ messages in thread
From: Filipe Manana @ 2025-05-14 11:35 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs
On Wed, May 14, 2025 at 11:57 AM Filipe Manana <fdmanana@kernel.org> wrote:
>
> On Wed, May 14, 2025 at 11:51 AM Qu Wenruo <wqu@suse.com> wrote:
> >
> >
> >
> > 在 2025/5/14 19:59, fdmanana@kernel.org 写道:
> > > From: Filipe Manana <fdmanana@suse.com>
> > >
> > > We have this entangled error checks early at btrfs_page_mkwrite():
> > >
> > > 1) Try to reserve delalloc space by calling btrfs_delalloc_reserve_space()
> > > and storing the return value in the ret2 variable;
> > >
> > > 2) If the reservation succeed, call file_update_time() and store the
> > > return value in ret2 and also set the local variable 'reserved' to
> > > true (1);
> > >
> > > 3) Then do an error check on ret2 to see if any of the previous calls
> > > failed and if so, jump either to the 'out' label or to the
> > > 'out_noreserve' label, depending on whether 'reserved' is true or
> > > not.
> > >
> > > This is unnecessarily complex. Instead change this to a simpler and
> > > more straighforward approach:
> > >
> > > 1) Call btrfs_delalloc_reserve_space(), if that returns an error jump to
> > > the 'out_noreserve' label;
> > >
> > > 2) The call file_update_time() and if that returns an error jump to the
> > > 'out' label.
> > >
> > > Like this there's less nested if statements, no need to use a local
> > > variable to track if space was reserved and if statements are used only
> > > to check errors.
> > >
> > > Also move the call to extent_changeset_free() out of the 'out_noreserve'
> > > label and under the 'out' label since the changeset is allocated only if
> > > the call to reserve delalloc space succeeded.
> > >
> > > Signed-off-by: Filipe Manana <fdmanana@suse.com>
> >
> > Looks good to me, but I'm wondering can we do something better by
> > completely merging @ret2 and @ret.
> >
> > In fact we didn't really utilize @ret except two sites:
> >
> > - page got truncated out
> >
> > - btrfs_set_extent_delalloc() failure
> > In that case, we should use vmf_error()
> >
> > In the out_noreserve tag, do something like:
> >
> > if (ret)
> > return vmf_error(ret);
> > return VM_FAULT_NOPAGE;
> >
> > Now we only need a single "int ret" as usual and not need to bother the
> > VM_FAULT* return values at all.
>
> I considered all that, but it's not so simple, one thing is that 'ret'
> is of a different type, vm_fault_t, which happens to be an unsigned
> int - but I don't think it's good to make assumptions about that in
> the code.
>
> There's also the case where we set ret to VM_FAULT_SIGBUS.
Ok getting rid of this case, allows for a simple way to get rid of
having two return value variables.
Added two patches for that in v2 of the patchset.
> I thought about getting rid of ret2 and have a single 'ret' or type
> int, and then do something like:
>
> if (ret < 0)
> return vmf_error(ret);
>
> return ret ? ret : VM_FAULT_NOPAGE;
>
> But then I was making assumptions that vm_fault_t values are positive,
> and didn't think it's a good thing to do because it's an opaque type.
>
> >
> > Thanks,
> > Qu
> >
> > > ---
> > > fs/btrfs/file.c | 15 +++++++--------
> > > 1 file changed, 7 insertions(+), 8 deletions(-)
> > >
> > > diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> > > index a2b1fc536fdd..9ecb9f3bd057 100644
> > > --- a/fs/btrfs/file.c
> > > +++ b/fs/btrfs/file.c
> > > @@ -1843,7 +1843,6 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> > > size_t fsize = folio_size(folio);
> > > vm_fault_t ret;
> > > int ret2;
> > > - int reserved = 0;
> > > u64 reserved_space;
> > > u64 page_start;
> > > u64 page_end;
> > > @@ -1866,17 +1865,17 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> > > */
> > > ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
> > > page_start, reserved_space);
> > > - if (!ret2) {
> > > - ret2 = file_update_time(vmf->vma->vm_file);
> > > - reserved = 1;
> > > - }
> > > if (ret2) {
> > > ret = vmf_error(ret2);
> > > - if (reserved)
> > > - goto out;
> > > goto out_noreserve;
> > > }
> > >
> > > + ret2 = file_update_time(vmf->vma->vm_file);
> > > + if (ret2) {
> > > + ret = vmf_error(ret2);
> > > + goto out;
> > > + }
> > > +
> > > /* Make the VM retry the fault. */
> > > ret = VM_FAULT_NOPAGE;
> > > again:
> > > @@ -1972,9 +1971,9 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> > > btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
> > > btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
> > > reserved_space, true);
> > > + extent_changeset_free(data_reserved);
> > > out_noreserve:
> > > sb_end_pagefault(inode->i_sb);
> > > - extent_changeset_free(data_reserved);
> > > return ret;
> > > }
> > >
> >
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 0/5] btrfs: fix a bug and cleanups in btrfs_page_mkwrite()
2025-05-14 10:29 [PATCH 0/3] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() fdmanana
` (2 preceding siblings ...)
2025-05-14 10:29 ` [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
@ 2025-05-14 11:38 ` fdmanana
2025-05-14 11:38 ` [PATCH v2 1/5] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
` (5 more replies)
3 siblings, 6 replies; 20+ messages in thread
From: fdmanana @ 2025-05-14 11:38 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
A bug fix for mmap writes and a couple cleanups. Details in the change logs.
V2: Added two more patches (4/5 and 5/5).
Filipe Manana (5):
btrfs: fix wrong start offset for delalloc space release during mmap write
btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite()
btrfs: simplify early error checking in btrfs_page_mkwrite()
btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write
btrfs: use a single variable to track return value at btrfs_page_mkwrite()
fs/btrfs/file.c | 46 ++++++++++++++++++++--------------------------
1 file changed, 20 insertions(+), 26 deletions(-)
--
2.47.2
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 1/5] btrfs: fix wrong start offset for delalloc space release during mmap write
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
@ 2025-05-14 11:38 ` fdmanana
2025-05-14 11:38 ` [PATCH v2 2/5] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite() fdmanana
` (4 subsequent siblings)
5 siblings, 0 replies; 20+ messages in thread
From: fdmanana @ 2025-05-14 11:38 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
If we're doing a mmap write against a folio that has i_size somewhere in
the middle and we have multiple sectors in the folio, we may have to
release excess space previously reserved, for the range going from the
rounded up (to sector size) i_size to the folio's end offset. We are
calculating the right amount to release and passing it to
btrfs_delalloc_release_space(), but we are passing the wrong start offset
of that range - we're passing the folio's start offset instead of the
end offset, plus 1, of the range for which we keep the reservation. This
may result in releasing more space then we should and eventually trigger
an underflow of the data space_info's bytes_may_use counter.
So fix this by passing the start offset as 'end + 1' instead of
'page_start' to btrfs_delalloc_release_space().
Fixes: d0b7da88f640 ("Btrfs: btrfs_page_mkwrite: Reserve space in sectorsized units")
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 660a73b6af90..32aad1b02b01 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1918,7 +1918,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
if (reserved_space < fsize) {
end = page_start + reserved_space - 1;
btrfs_delalloc_release_space(BTRFS_I(inode),
- data_reserved, page_start,
+ data_reserved, end + 1,
fsize - reserved_space, true);
}
}
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 2/5] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite()
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
2025-05-14 11:38 ` [PATCH v2 1/5] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
@ 2025-05-14 11:38 ` fdmanana
2025-05-14 11:38 ` [PATCH v2 3/5] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
` (3 subsequent siblings)
5 siblings, 0 replies; 20+ messages in thread
From: fdmanana @ 2025-05-14 11:38 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
In the last call to btrfs_delalloc_release_space() where the value of the
variable 'ret' is never zero, we pass the expression 'ret != 0' as the
value for the argument 'qgroup_free', which always evaluates to true.
Make this less confusing and more clear by explicitly passing true
instead.
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 32aad1b02b01..a2b1fc536fdd 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1971,7 +1971,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
out:
btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
- reserved_space, (ret != 0));
+ reserved_space, true);
out_noreserve:
sb_end_pagefault(inode->i_sb);
extent_changeset_free(data_reserved);
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 3/5] btrfs: simplify early error checking in btrfs_page_mkwrite()
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
2025-05-14 11:38 ` [PATCH v2 1/5] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
2025-05-14 11:38 ` [PATCH v2 2/5] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite() fdmanana
@ 2025-05-14 11:38 ` fdmanana
2025-05-14 21:01 ` Qu Wenruo
2025-05-14 11:38 ` [PATCH v2 4/5] btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write fdmanana
` (2 subsequent siblings)
5 siblings, 1 reply; 20+ messages in thread
From: fdmanana @ 2025-05-14 11:38 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We have this entangled error checks early at btrfs_page_mkwrite():
1) Try to reserve delalloc space by calling btrfs_delalloc_reserve_space()
and storing the return value in the ret2 variable;
2) If the reservation succeed, call file_update_time() and store the
return value in ret2 and also set the local variable 'reserved' to
true (1);
3) Then do an error check on ret2 to see if any of the previous calls
failed and if so, jump either to the 'out' label or to the
'out_noreserve' label, depending on whether 'reserved' is true or
not.
This is unnecessarily complex. Instead change this to a simpler and
more straighforward approach:
1) Call btrfs_delalloc_reserve_space(), if that returns an error jump to
the 'out_noreserve' label;
2) The call file_update_time() and if that returns an error jump to the
'out' label.
Like this there's less nested if statements, no need to use a local
variable to track if space was reserved and if statements are used only
to check errors.
Also move the call to extent_changeset_free() out of the 'out_noreserve'
label and under the 'out' label since the changeset is allocated only if
the call to reserve delalloc space succeeded.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index a2b1fc536fdd..9ecb9f3bd057 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1843,7 +1843,6 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
size_t fsize = folio_size(folio);
vm_fault_t ret;
int ret2;
- int reserved = 0;
u64 reserved_space;
u64 page_start;
u64 page_end;
@@ -1866,17 +1865,17 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
*/
ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
page_start, reserved_space);
- if (!ret2) {
- ret2 = file_update_time(vmf->vma->vm_file);
- reserved = 1;
- }
if (ret2) {
ret = vmf_error(ret2);
- if (reserved)
- goto out;
goto out_noreserve;
}
+ ret2 = file_update_time(vmf->vma->vm_file);
+ if (ret2) {
+ ret = vmf_error(ret2);
+ goto out;
+ }
+
/* Make the VM retry the fault. */
ret = VM_FAULT_NOPAGE;
again:
@@ -1972,9 +1971,9 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
reserved_space, true);
+ extent_changeset_free(data_reserved);
out_noreserve:
sb_end_pagefault(inode->i_sb);
- extent_changeset_free(data_reserved);
return ret;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 4/5] btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
` (2 preceding siblings ...)
2025-05-14 11:38 ` [PATCH v2 3/5] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
@ 2025-05-14 11:38 ` fdmanana
2025-05-14 21:01 ` Qu Wenruo
2025-05-14 11:38 ` [PATCH v2 5/5] btrfs: use a single variable to track return value at btrfs_page_mkwrite() fdmanana
2025-05-15 13:19 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() David Sterba
5 siblings, 1 reply; 20+ messages in thread
From: fdmanana @ 2025-05-14 11:38 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
If the call to btrfs_set_extent_delalloc() fails we are always returning
VM_FAULT_SIGBUS, which is odd since the error means "bad access" and the
most likely cause for btrfs_set_extent_delalloc() is -ENOMEM, which should
be translated to VM_FAULT_OOM.
Instead of returning VM_FAULT_SIGBUS return vmf_error(ret2), which gives
us a more appropriate return value, and we use that everywhere else too.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 9ecb9f3bd057..f6b32f24185c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1937,7 +1937,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
&cached_state);
if (ret2) {
btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state);
- ret = VM_FAULT_SIGBUS;
+ ret = vmf_error(ret2);
goto out_unlock;
}
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 5/5] btrfs: use a single variable to track return value at btrfs_page_mkwrite()
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
` (3 preceding siblings ...)
2025-05-14 11:38 ` [PATCH v2 4/5] btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write fdmanana
@ 2025-05-14 11:38 ` fdmanana
2025-05-14 21:02 ` Qu Wenruo
2025-05-15 13:19 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() David Sterba
5 siblings, 1 reply; 20+ messages in thread
From: fdmanana @ 2025-05-14 11:38 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We have two variables to track return values, ret and ret2, with types
vm_fault_t (an unsigned int type) and int, which makes it a bit confusing
and harder to keep track. So use a single variable, of type int, and under
the 'out' label return vmf_error(ret) in case ret contains an error,
otherwise return VM_FAULT_NOPAGE. This is equivalent to what we had before
and it's simpler.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/file.c | 37 ++++++++++++++++---------------------
1 file changed, 16 insertions(+), 21 deletions(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index f6b32f24185c..8ce6f45f45e0 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1841,8 +1841,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
unsigned long zero_start;
loff_t size;
size_t fsize = folio_size(folio);
- vm_fault_t ret;
- int ret2;
+ int ret;
u64 reserved_space;
u64 page_start;
u64 page_end;
@@ -1863,21 +1862,14 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
* end up waiting indefinitely to get a lock on the page currently
* being processed by btrfs_page_mkwrite() function.
*/
- ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
- page_start, reserved_space);
- if (ret2) {
- ret = vmf_error(ret2);
+ ret = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
+ page_start, reserved_space);
+ if (ret < 0)
goto out_noreserve;
- }
- ret2 = file_update_time(vmf->vma->vm_file);
- if (ret2) {
- ret = vmf_error(ret2);
+ ret = file_update_time(vmf->vma->vm_file);
+ if (ret < 0)
goto out;
- }
-
- /* Make the VM retry the fault. */
- ret = VM_FAULT_NOPAGE;
again:
down_read(&BTRFS_I(inode)->i_mmap_lock);
folio_lock(folio);
@@ -1891,9 +1883,8 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
folio_wait_writeback(folio);
btrfs_lock_extent(io_tree, page_start, page_end, &cached_state);
- ret2 = set_folio_extent_mapped(folio);
- if (ret2 < 0) {
- ret = vmf_error(ret2);
+ ret = set_folio_extent_mapped(folio);
+ if (ret < 0) {
btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state);
goto out_unlock;
}
@@ -1933,11 +1924,10 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
EXTENT_DEFRAG, &cached_state);
- ret2 = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, end, 0,
+ ret = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, end, 0,
&cached_state);
- if (ret2) {
+ if (ret < 0) {
btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state);
- ret = vmf_error(ret2);
goto out_unlock;
}
@@ -1974,7 +1964,12 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
extent_changeset_free(data_reserved);
out_noreserve:
sb_end_pagefault(inode->i_sb);
- return ret;
+
+ if (ret < 0)
+ return vmf_error(ret);
+
+ /* Make the VM retry the fault. */
+ return VM_FAULT_NOPAGE;
}
static const struct vm_operations_struct btrfs_file_vm_ops = {
--
2.47.2
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v2 3/5] btrfs: simplify early error checking in btrfs_page_mkwrite()
2025-05-14 11:38 ` [PATCH v2 3/5] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
@ 2025-05-14 21:01 ` Qu Wenruo
0 siblings, 0 replies; 20+ messages in thread
From: Qu Wenruo @ 2025-05-14 21:01 UTC (permalink / raw)
To: fdmanana, linux-btrfs
在 2025/5/14 21:08, fdmanana@kernel.org 写道:
> From: Filipe Manana <fdmanana@suse.com>
>
> We have this entangled error checks early at btrfs_page_mkwrite():
>
> 1) Try to reserve delalloc space by calling btrfs_delalloc_reserve_space()
> and storing the return value in the ret2 variable;
>
> 2) If the reservation succeed, call file_update_time() and store the
> return value in ret2 and also set the local variable 'reserved' to
> true (1);
>
> 3) Then do an error check on ret2 to see if any of the previous calls
> failed and if so, jump either to the 'out' label or to the
> 'out_noreserve' label, depending on whether 'reserved' is true or
> not.
>
> This is unnecessarily complex. Instead change this to a simpler and
> more straighforward approach:
>
> 1) Call btrfs_delalloc_reserve_space(), if that returns an error jump to
> the 'out_noreserve' label;
>
> 2) The call file_update_time() and if that returns an error jump to the
> 'out' label.
>
> Like this there's less nested if statements, no need to use a local
> variable to track if space was reserved and if statements are used only
> to check errors.
>
> Also move the call to extent_changeset_free() out of the 'out_noreserve'
> label and under the 'out' label since the changeset is allocated only if
> the call to reserve delalloc space succeeded.
>
> Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Thanks,
Qu> ---
> fs/btrfs/file.c | 15 +++++++--------
> 1 file changed, 7 insertions(+), 8 deletions(-)
>
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index a2b1fc536fdd..9ecb9f3bd057 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1843,7 +1843,6 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> size_t fsize = folio_size(folio);
> vm_fault_t ret;
> int ret2;
> - int reserved = 0;
> u64 reserved_space;
> u64 page_start;
> u64 page_end;
> @@ -1866,17 +1865,17 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> */
> ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
> page_start, reserved_space);
> - if (!ret2) {
> - ret2 = file_update_time(vmf->vma->vm_file);
> - reserved = 1;
> - }
> if (ret2) {
> ret = vmf_error(ret2);
> - if (reserved)
> - goto out;
> goto out_noreserve;
> }
>
> + ret2 = file_update_time(vmf->vma->vm_file);
> + if (ret2) {
> + ret = vmf_error(ret2);
> + goto out;
> + }
> +
> /* Make the VM retry the fault. */
> ret = VM_FAULT_NOPAGE;
> again:
> @@ -1972,9 +1971,9 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> btrfs_delalloc_release_extents(BTRFS_I(inode), fsize);
> btrfs_delalloc_release_space(BTRFS_I(inode), data_reserved, page_start,
> reserved_space, true);
> + extent_changeset_free(data_reserved);
> out_noreserve:
> sb_end_pagefault(inode->i_sb);
> - extent_changeset_free(data_reserved);
> return ret;
> }
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 4/5] btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write
2025-05-14 11:38 ` [PATCH v2 4/5] btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write fdmanana
@ 2025-05-14 21:01 ` Qu Wenruo
0 siblings, 0 replies; 20+ messages in thread
From: Qu Wenruo @ 2025-05-14 21:01 UTC (permalink / raw)
To: fdmanana, linux-btrfs
在 2025/5/14 21:08, fdmanana@kernel.org 写道:
> From: Filipe Manana <fdmanana@suse.com>
>
> If the call to btrfs_set_extent_delalloc() fails we are always returning
> VM_FAULT_SIGBUS, which is odd since the error means "bad access" and the
> most likely cause for btrfs_set_extent_delalloc() is -ENOMEM, which should
> be translated to VM_FAULT_OOM.
>
> Instead of returning VM_FAULT_SIGBUS return vmf_error(ret2), which gives
> us a more appropriate return value, and we use that everywhere else too.
>
> Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Thanks,
Qu
> ---
> fs/btrfs/file.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index 9ecb9f3bd057..f6b32f24185c 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1937,7 +1937,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> &cached_state);
> if (ret2) {
> btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state);
> - ret = VM_FAULT_SIGBUS;
> + ret = vmf_error(ret2);
> goto out_unlock;
> }
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 5/5] btrfs: use a single variable to track return value at btrfs_page_mkwrite()
2025-05-14 11:38 ` [PATCH v2 5/5] btrfs: use a single variable to track return value at btrfs_page_mkwrite() fdmanana
@ 2025-05-14 21:02 ` Qu Wenruo
0 siblings, 0 replies; 20+ messages in thread
From: Qu Wenruo @ 2025-05-14 21:02 UTC (permalink / raw)
To: fdmanana, linux-btrfs
在 2025/5/14 21:08, fdmanana@kernel.org 写道:
> From: Filipe Manana <fdmanana@suse.com>
>
> We have two variables to track return values, ret and ret2, with types
> vm_fault_t (an unsigned int type) and int, which makes it a bit confusing
> and harder to keep track. So use a single variable, of type int, and under
> the 'out' label return vmf_error(ret) in case ret contains an error,
> otherwise return VM_FAULT_NOPAGE. This is equivalent to what we had before
> and it's simpler.
>
> Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Thanks,
Qu
> ---
> fs/btrfs/file.c | 37 ++++++++++++++++---------------------
> 1 file changed, 16 insertions(+), 21 deletions(-)
>
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index f6b32f24185c..8ce6f45f45e0 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1841,8 +1841,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> unsigned long zero_start;
> loff_t size;
> size_t fsize = folio_size(folio);
> - vm_fault_t ret;
> - int ret2;
> + int ret;
> u64 reserved_space;
> u64 page_start;
> u64 page_end;
> @@ -1863,21 +1862,14 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> * end up waiting indefinitely to get a lock on the page currently
> * being processed by btrfs_page_mkwrite() function.
> */
> - ret2 = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
> - page_start, reserved_space);
> - if (ret2) {
> - ret = vmf_error(ret2);
> + ret = btrfs_delalloc_reserve_space(BTRFS_I(inode), &data_reserved,
> + page_start, reserved_space);
> + if (ret < 0)
> goto out_noreserve;
> - }
>
> - ret2 = file_update_time(vmf->vma->vm_file);
> - if (ret2) {
> - ret = vmf_error(ret2);
> + ret = file_update_time(vmf->vma->vm_file);
> + if (ret < 0)
> goto out;
> - }
> -
> - /* Make the VM retry the fault. */
> - ret = VM_FAULT_NOPAGE;
> again:
> down_read(&BTRFS_I(inode)->i_mmap_lock);
> folio_lock(folio);
> @@ -1891,9 +1883,8 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> folio_wait_writeback(folio);
>
> btrfs_lock_extent(io_tree, page_start, page_end, &cached_state);
> - ret2 = set_folio_extent_mapped(folio);
> - if (ret2 < 0) {
> - ret = vmf_error(ret2);
> + ret = set_folio_extent_mapped(folio);
> + if (ret < 0) {
> btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state);
> goto out_unlock;
> }
> @@ -1933,11 +1924,10 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
> EXTENT_DEFRAG, &cached_state);
>
> - ret2 = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, end, 0,
> + ret = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, end, 0,
> &cached_state);
> - if (ret2) {
> + if (ret < 0) {
> btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state);
> - ret = vmf_error(ret2);
> goto out_unlock;
> }
>
> @@ -1974,7 +1964,12 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
> extent_changeset_free(data_reserved);
> out_noreserve:
> sb_end_pagefault(inode->i_sb);
> - return ret;
> +
> + if (ret < 0)
> + return vmf_error(ret);
> +
> + /* Make the VM retry the fault. */
> + return VM_FAULT_NOPAGE;
> }
>
> static const struct vm_operations_struct btrfs_file_vm_ops = {
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 0/5] btrfs: fix a bug and cleanups in btrfs_page_mkwrite()
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
` (4 preceding siblings ...)
2025-05-14 11:38 ` [PATCH v2 5/5] btrfs: use a single variable to track return value at btrfs_page_mkwrite() fdmanana
@ 2025-05-15 13:19 ` David Sterba
5 siblings, 0 replies; 20+ messages in thread
From: David Sterba @ 2025-05-15 13:19 UTC (permalink / raw)
To: fdmanana; +Cc: linux-btrfs
On Wed, May 14, 2025 at 12:38:32PM +0100, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
>
> A bug fix for mmap writes and a couple cleanups. Details in the change logs.
>
> V2: Added two more patches (4/5 and 5/5).
>
> Filipe Manana (5):
> btrfs: fix wrong start offset for delalloc space release during mmap write
> btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite()
> btrfs: simplify early error checking in btrfs_page_mkwrite()
> btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write
> btrfs: use a single variable to track return value at btrfs_page_mkwrite()
Reviewed-by: David Sterba <dsterba@suse.com>
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2025-05-15 13:19 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-14 10:29 [PATCH 0/3] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() fdmanana
2025-05-14 10:29 ` [PATCH 1/3] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
2025-05-14 10:38 ` Qu Wenruo
2025-05-14 10:41 ` Filipe Manana
2025-05-14 10:29 ` [PATCH 2/3] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite() fdmanana
2025-05-14 10:40 ` Qu Wenruo
2025-05-14 10:29 ` [PATCH 3/3] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
2025-05-14 10:50 ` Qu Wenruo
2025-05-14 10:57 ` Filipe Manana
2025-05-14 11:35 ` Filipe Manana
2025-05-14 11:38 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups " fdmanana
2025-05-14 11:38 ` [PATCH v2 1/5] btrfs: fix wrong start offset for delalloc space release during mmap write fdmanana
2025-05-14 11:38 ` [PATCH v2 2/5] btrfs: pass true to btrfs_delalloc_release_space() at btrfs_page_mkwrite() fdmanana
2025-05-14 11:38 ` [PATCH v2 3/5] btrfs: simplify early error checking in btrfs_page_mkwrite() fdmanana
2025-05-14 21:01 ` Qu Wenruo
2025-05-14 11:38 ` [PATCH v2 4/5] btrfs: don't return VM_FAULT_SIGBUS on failure to set delalloc for mmap write fdmanana
2025-05-14 21:01 ` Qu Wenruo
2025-05-14 11:38 ` [PATCH v2 5/5] btrfs: use a single variable to track return value at btrfs_page_mkwrite() fdmanana
2025-05-14 21:02 ` Qu Wenruo
2025-05-15 13:19 ` [PATCH v2 0/5] btrfs: fix a bug and cleanups in btrfs_page_mkwrite() David Sterba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox