Thread (14 messages) 14 messages, 3 authors, 2021-07-05
STALE1817d

[PATCH RFC 2/8] btrfs: make btrfs_subpage_end_and_test_writer() to handle pages not locked by btrfs_page_start_writer_lock()

From: Qu Wenruo <hidden>
Date: 2021-06-23 05:55:39
Subsystem: btrfs file system, filesystems (vfs and infrastructure), the rest · Maintainers: Chris Mason, David Sterba, Alexander Viro, Christian Brauner, Linus Torvalds

Normally if a page is locked by page_lock() other than
btrfs_page_start_writer_lock(), it should be passed as @locked_page for
various extent_clear_unlock_delalloc() call sites.

But there are quite some call sites in compression path, where we
intentionally call extent_clear_unlock_delalloc() with @locked_page ==
NULL.

This will abuse extent_clear_unlock_delalloc() to unlock @locked_page.

This works fine for regular page size, but not really for subpage, as if
a page is locked by btrfs_page_start_writer_lock() it will have proper
@writers value increased.

If a page not locked by btrfs_page_start_writer_lock() we will underflow
the value.

But thankfully, for such pages its @writers value should be zero, and
we can use that to distinguish page locked by
btrfs_page_start_writer_lock() and @locked_page by delalloc.

Signed-off-by: Qu Wenruo <redacted>
---
 fs/btrfs/subpage.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c
index a61aa33aeeee..72d5d4712933 100644
--- a/fs/btrfs/subpage.c
+++ b/fs/btrfs/subpage.c
@@ -245,11 +245,33 @@ bool btrfs_subpage_end_and_test_writer(const struct btrfs_fs_info *fs_info,
 {
 	struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private;
 	const int nbits = (len >> fs_info->sectorsize_bits);
+	unsigned long flags;
+	bool ret;
 
 	btrfs_subpage_assert(fs_info, page, start, len);
 
+	/* Will do two atomic checks, no longer atomic and need spinlock */
+	spin_lock_irqsave(&subpage->lock, flags);
+
+	/*
+	 * In compression path, we have extent_clear_unlock_delalloc() call
+	 * sites which intentionally pass @locked_page == NULL to unlock
+	 * the locked page.
+	 *
+	 * In that case, @locked_page should has no writer counts, as it's
+	 * not locked by btrfs_page_start_writer_lock().
+	 * For such case, we just return true so that
+	 * btrfs_page_end_writer_lock() will unlock the page.
+	 */
+	if (atomic_read(&subpage->writers) == 0) {
+		ret = true;
+		spin_unlock_irqrestore(&subpage->lock, flags);
+		return ret;
+	}
 	ASSERT(atomic_read(&subpage->writers) >= nbits);
-	return atomic_sub_and_test(nbits, &subpage->writers);
+	ret = atomic_sub_and_test(nbits, &subpage->writers);
+	spin_unlock_irqrestore(&subpage->lock, flags);
+	return ret;
 }
 
 /*
-- 
2.32.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help