linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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).