* [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock()
@ 2024-10-08 23:07 Qu Wenruo
2024-10-08 23:07 ` [PATCH v3 1/2] btrfs: fix the delalloc range locking if sector size < page size Qu Wenruo
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Qu Wenruo @ 2024-10-08 23:07 UTC (permalink / raw)
To: linux-btrfs
[Changelog]
v2:
- Remove the unused btrfs_folio_start_writer_lock()
v3:
- Split out the btrfs_folio_start_writer_lock() removal
As the initial fix needs to go backported to 5.15+, keeps the
modification as small as possible
The function lacks the proper folio->mapping check, thus we can even get
a folio not belonging to btrfs, and cause unexpeceted folio->private
updates.
Fix the only caller of btrfs_folio_start_writer_lock() inside
lock_delalloc_folios() and other sector size < page size handling of
lock_delalloc_folios().
Then finally remove btrfs_folio_start_writer_lock()
Qu Wenruo (2):
btrfs: fix the delalloc range locking if sector size < page size
btrfs: remove unused btrfs_folio_start_writer_lock()
fs/btrfs/extent_io.c | 17 ++++++++--------
fs/btrfs/subpage.c | 47 --------------------------------------------
fs/btrfs/subpage.h | 2 --
3 files changed, 9 insertions(+), 57 deletions(-)
--
2.46.2
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 1/2] btrfs: fix the delalloc range locking if sector size < page size
2024-10-08 23:07 [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock() Qu Wenruo
@ 2024-10-08 23:07 ` Qu Wenruo
2024-10-08 23:07 ` [PATCH v3 2/2] btrfs: remove unused btrfs_folio_start_writer_lock() Qu Wenruo
2024-10-16 15:16 ` [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock() David Sterba
2 siblings, 0 replies; 4+ messages in thread
From: Qu Wenruo @ 2024-10-08 23:07 UTC (permalink / raw)
To: linux-btrfs
Inside lock_delalloc_folios(), there are several problems related to
sector size < page size handling:
- Set the writer locks without checking if the folio is still valid
We call btrfs_folio_start_writer_lock() just like it's folio_lock().
But since the folio may not even be the folio of the current mapping,
we can easily screw up the folio->private.
- The range is not clamped inside the page
This means we can over write other bitmaps if the start/len is not
properly handled, and trigger the btrfs_subpage_assert().
- @processed_end is always rounded up to page end
If the delalloc range is not page aligned, and we need to retry
(returning -EAGAIN), then we will unlock to the page end.
Thankfully this is not a huge problem, as now
btrfs_folio_end_writer_lock() can handle range larger than the locked
range, and only unlock what is already locked.
Fix all these problems by:
- Lock and check the folio first, then call
btrfs_folio_set_writer_lock()
So that if we got a folio not belonging to the inode, we won't
touch folio->private.
- Properly truncate the range inside the page
- Update @processed_end to the locked range end
Fixes: 1e1de38792e0 ("btrfs: make process_one_page() to handle subpage locking")
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/extent_io.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 9fbc83c76b94..6aa39e0be2e8 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -262,22 +262,23 @@ static noinline int lock_delalloc_folios(struct inode *inode,
for (i = 0; i < found_folios; i++) {
struct folio *folio = fbatch.folios[i];
- u32 len = end + 1 - start;
+ u64 range_start;
+ u32 range_len;
if (folio == locked_folio)
continue;
- if (btrfs_folio_start_writer_lock(fs_info, folio, start,
- len))
- goto out;
-
+ folio_lock(folio);
if (!folio_test_dirty(folio) || folio->mapping != mapping) {
- btrfs_folio_end_writer_lock(fs_info, folio, start,
- len);
+ folio_unlock(folio);
goto out;
}
+ range_start = max_t(u64, folio_pos(folio), start);
+ range_len = min_t(u64, folio_pos(folio) + folio_size(folio),
+ end + 1) - range_start;
+ btrfs_folio_set_writer_lock(fs_info, folio, range_start, range_len);
- processed_end = folio_pos(folio) + folio_size(folio) - 1;
+ processed_end = range_start + range_len - 1;
}
folio_batch_release(&fbatch);
cond_resched();
--
2.46.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v3 2/2] btrfs: remove unused btrfs_folio_start_writer_lock()
2024-10-08 23:07 [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock() Qu Wenruo
2024-10-08 23:07 ` [PATCH v3 1/2] btrfs: fix the delalloc range locking if sector size < page size Qu Wenruo
@ 2024-10-08 23:07 ` Qu Wenruo
2024-10-16 15:16 ` [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock() David Sterba
2 siblings, 0 replies; 4+ messages in thread
From: Qu Wenruo @ 2024-10-08 23:07 UTC (permalink / raw)
To: linux-btrfs
This function is not really suitable to lock a folio, as it lacks the
proper mapping checks, thus the locked folio may not even belong to
btrfs.
And due to the above reason, the last user inside lock_dealloc_folios()
is already removed, and we can remove this function.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/subpage.c | 47 ----------------------------------------------
fs/btrfs/subpage.h | 2 --
2 files changed, 49 deletions(-)
diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c
index 17bc53a8df01..26d4d05dd165 100644
--- a/fs/btrfs/subpage.c
+++ b/fs/btrfs/subpage.c
@@ -295,26 +295,6 @@ static void btrfs_subpage_clamp_range(struct folio *folio, u64 *start, u32 *len)
orig_start + orig_len) - *start;
}
-static void btrfs_subpage_start_writer(const struct btrfs_fs_info *fs_info,
- struct folio *folio, u64 start, u32 len)
-{
- struct btrfs_subpage *subpage = folio_get_private(folio);
- const int start_bit = subpage_calc_start_bit(fs_info, folio, locked, start, len);
- const int nbits = (len >> fs_info->sectorsize_bits);
- unsigned long flags;
- int ret;
-
- btrfs_subpage_assert(fs_info, folio, start, len);
-
- spin_lock_irqsave(&subpage->lock, flags);
- ASSERT(atomic_read(&subpage->readers) == 0);
- ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits));
- bitmap_set(subpage->bitmaps, start_bit, nbits);
- ret = atomic_add_return(nbits, &subpage->writers);
- ASSERT(ret == nbits);
- spin_unlock_irqrestore(&subpage->lock, flags);
-}
-
static bool btrfs_subpage_end_and_test_writer(const struct btrfs_fs_info *fs_info,
struct folio *folio, u64 start, u32 len)
{
@@ -351,33 +331,6 @@ static bool btrfs_subpage_end_and_test_writer(const struct btrfs_fs_info *fs_inf
return last;
}
-/*
- * Lock a folio for delalloc page writeback.
- *
- * Return -EAGAIN if the page is not properly initialized.
- * Return 0 with the page locked, and writer counter updated.
- *
- * Even with 0 returned, the page still need extra check to make sure
- * it's really the correct page, as the caller is using
- * filemap_get_folios_contig(), which can race with page invalidating.
- */
-int btrfs_folio_start_writer_lock(const struct btrfs_fs_info *fs_info,
- struct folio *folio, u64 start, u32 len)
-{
- if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, folio->mapping)) {
- folio_lock(folio);
- return 0;
- }
- folio_lock(folio);
- if (!folio_test_private(folio) || !folio_get_private(folio)) {
- folio_unlock(folio);
- return -EAGAIN;
- }
- btrfs_subpage_clamp_range(folio, &start, &len);
- btrfs_subpage_start_writer(fs_info, folio, start, len);
- return 0;
-}
-
/*
* Handle different locked folios:
*
diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h
index d16fbddeda68..e4076c5b06bc 100644
--- a/fs/btrfs/subpage.h
+++ b/fs/btrfs/subpage.h
@@ -100,8 +100,6 @@ void btrfs_subpage_start_reader(const struct btrfs_fs_info *fs_info,
void btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info,
struct folio *folio, u64 start, u32 len);
-int btrfs_folio_start_writer_lock(const struct btrfs_fs_info *fs_info,
- struct folio *folio, u64 start, u32 len);
void btrfs_folio_end_writer_lock(const struct btrfs_fs_info *fs_info,
struct folio *folio, u64 start, u32 len);
void btrfs_folio_set_writer_lock(const struct btrfs_fs_info *fs_info,
--
2.46.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock()
2024-10-08 23:07 [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock() Qu Wenruo
2024-10-08 23:07 ` [PATCH v3 1/2] btrfs: fix the delalloc range locking if sector size < page size Qu Wenruo
2024-10-08 23:07 ` [PATCH v3 2/2] btrfs: remove unused btrfs_folio_start_writer_lock() Qu Wenruo
@ 2024-10-16 15:16 ` David Sterba
2 siblings, 0 replies; 4+ messages in thread
From: David Sterba @ 2024-10-16 15:16 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs
On Wed, Oct 09, 2024 at 09:37:02AM +1030, Qu Wenruo wrote:
> [Changelog]
> v2:
> - Remove the unused btrfs_folio_start_writer_lock()
>
> v3:
> - Split out the btrfs_folio_start_writer_lock() removal
> As the initial fix needs to go backported to 5.15+, keeps the
> modification as small as possible
>
> The function lacks the proper folio->mapping check, thus we can even get
> a folio not belonging to btrfs, and cause unexpeceted folio->private
> updates.
>
> Fix the only caller of btrfs_folio_start_writer_lock() inside
> lock_delalloc_folios() and other sector size < page size handling of
> lock_delalloc_folios().
>
> Then finally remove btrfs_folio_start_writer_lock()
>
> Qu Wenruo (2):
> btrfs: fix the delalloc range locking if sector size < page size
> btrfs: remove unused btrfs_folio_start_writer_lock()
Reviewed-by: David Sterba <dsterba@suse.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-10-16 15:17 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-08 23:07 [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock() Qu Wenruo
2024-10-08 23:07 ` [PATCH v3 1/2] btrfs: fix the delalloc range locking if sector size < page size Qu Wenruo
2024-10-08 23:07 ` [PATCH v3 2/2] btrfs: remove unused btrfs_folio_start_writer_lock() Qu Wenruo
2024-10-16 15:16 ` [PATCH v3 0/2] btrfs: fixes related to btrfs_folio_start_writer_lock() David Sterba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).