@@ -5762,30 +5762,53 @@ void free_extent_buffer_stale(struct extent_buffer *eb)
release_extent_buffer(eb);
}
+static void btree_clear_page_dirty(struct page *page)
+{
+ ASSERT(PageDirty(page));
+
+ lock_page(page);
+ clear_page_dirty_for_io(page);
+ xa_lock_irq(&page->mapping->i_pages);
+ if (!PageDirty(page))
+ __xa_clear_mark(&page->mapping->i_pages,
+ page_index(page), PAGECACHE_TAG_DIRTY);
+ xa_unlock_irq(&page->mapping->i_pages);
+ ClearPageError(page);
+ unlock_page(page);
+}
+
+static void clear_subpage_extent_buffer_dirty(const struct extent_buffer *eb)
+{
+ struct btrfs_fs_info *fs_info = eb->fs_info;
+ struct extent_io_tree *io_tree = info_to_btree_io_tree(fs_info);
+ struct page *page = eb->pages[0];
+ u64 page_start = page_offset(page);
+ u64 page_end = page_start + PAGE_SIZE - 1;
+ int ret;
+
+ clear_extent_dirty(io_tree, eb->start, eb->start + eb->len - 1, NULL);
+ ret = test_range_bit(io_tree, page_start, page_end, EXTENT_DIRTY, 0, NULL);
+ /* All extent buffers in the page range is cleared now */
+ if (ret == 0 && PageDirty(page))
+ btree_clear_page_dirty(page);
+ WARN_ON(atomic_read(&eb->refs) == 0);
+}
+
void clear_extent_buffer_dirty(const struct extent_buffer *eb)
{
int i;
int num_pages;
struct page *page;
+ if (btrfs_is_subpage(eb->fs_info))
+ return clear_subpage_extent_buffer_dirty(eb);
num_pages = num_extent_pages(eb);
for (i = 0; i < num_pages; i++) {
page = eb->pages[i];
if (!PageDirty(page))
continue;
-
- lock_page(page);
- WARN_ON(!PagePrivate(page));
-
- clear_page_dirty_for_io(page);
- xa_lock_irq(&page->mapping->i_pages);
- if (!PageDirty(page))
- __xa_clear_mark(&page->mapping->i_pages,
- page_index(page), PAGECACHE_TAG_DIRTY);
- xa_unlock_irq(&page->mapping->i_pages);
- ClearPageError(page);
- unlock_page(page);
+ btree_clear_page_dirty(page);
}
WARN_ON(atomic_read(&eb->refs) == 0);
}
To support subpage metadata, clear_extent_buffer_dirty() needs to clear the page dirty if and only if all extent buffers in the page range are no longer dirty. This is pretty different from the exist clear_extent_buffer_dirty() routine, so add a new helper function, clear_subpage_extent_buffer_dirty() to do this for subpage metadata. Also since the main part of clearing page dirty code is still the same, extract that into btree_clear_page_dirty() so that it can be utilized for both cases. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/extent_io.c | 47 +++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-)