@@ -6340,10 +6340,79 @@ void memmove_extent_buffer(const struct extent_buffer *dst,
}
}
+static int try_release_subpage_extent_buffer(struct page *page)
+{
+ struct btrfs_fs_info *fs_info = btrfs_sb(page->mapping->host->i_sb);
+ u64 page_start = page_offset(page);
+ int bitmap_size = BTRFS_SUBPAGE_BITMAP_SIZE;
+ int bit_start = 0;
+ int ret;
+
+ while (bit_start < bitmap_size) {
+ struct btrfs_subpage *subpage;
+ struct extent_buffer *eb;
+ u64 start;
+
+ /*
+ * Make sure the page still has private, as previous run can
+ * detach the private
+ */
+ spin_lock(&page->mapping->private_lock);
+ if (!PagePrivate(page)) {
+ spin_unlock(&page->mapping->private_lock);
+ break;
+ }
+ subpage = (struct btrfs_subpage *)page->private;
+ spin_unlock(&page->mapping->private_lock);
+
+ spin_lock_bh(&subpage->lock);
+ bit_start = find_next_bit(subpage->tree_block_bitmap,
+ BTRFS_SUBPAGE_BITMAP_SIZE, bit_start);
+ spin_unlock_bh(&subpage->lock);
+ if (bit_start >= bitmap_size)
+ break;
+ start = bit_start * fs_info->sectorsize + page_start;
+ bit_start += fs_info->nodesize >> fs_info->sectorsize_bits;
+ /*
+ * Here we can't call find_extent_buffer() which will increase
+ * eb->refs.
+ */
+ rcu_read_lock();
+ eb = radix_tree_lookup(&fs_info->buffer_radix,
+ start >> fs_info->sectorsize_bits);
+ rcu_read_unlock();
+ ASSERT(eb);
+ spin_lock(&eb->refs_lock);
+ if (atomic_read(&eb->refs) != 1 || extent_buffer_under_io(eb) ||
+ !test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) {
+ spin_unlock(&eb->refs_lock);
+ continue;
+ }
+ /*
+ * Here we don't care the return value, we will always check
+ * the page private at the end.
+ * And release_extent_buffer() will release the refs_lock.
+ */
+ release_extent_buffer(eb);
+ }
+ /* Finally to check if we have cleared page private */
+ spin_lock(&page->mapping->private_lock);
+ if (!PagePrivate(page))
+ ret = 1;
+ else
+ ret = 0;
+ spin_unlock(&page->mapping->private_lock);
+ return ret;
+
+}
+
int try_release_extent_buffer(struct page *page)
{
struct extent_buffer *eb;
+ if (btrfs_is_subpage(btrfs_sb(page->mapping->host->i_sb)))
+ return try_release_subpage_extent_buffer(page);
+
/*
* We need to make sure nobody is attaching this page to an eb right
* now.
Unlike the original try_release_extent_buffer, try_release_subpage_extent_buffer() will iterate through btrfs_subpage::tree_block_bitmap, and try to release each extent buffer. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/extent_io.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+)