@@ -1047,6 +1047,12 @@ static int btree_writepages(struct address_space *mapping,
static int btree_readpage(struct file *file, struct page *page)
{
+ /*
+ * For subpage, we don't support VFS to call btree_readpages(),
+ * directly.
+ */
+ if (btrfs_is_subpage(page_to_fs_info(page)))
+ return -ENOTTY;
return extent_read_full_page(page, btree_get_extent, 0);
}
@@ -2772,6 +2772,48 @@ blk_status_t btrfs_submit_read_repair(struct inode *inode,
return status;
}
+/*
+ * A helper for locate subpage extent buffer.
+ *
+ * NOTE: returned extent buffer won't has its ref increased.
+ *
+ * @extra_bits: Extra bits to match.
+ * The returned eb range will match all extra_bits.
+ *
+ * Return 0 if we found one extent buffer and record it in @eb_ret.
+ * Return 1 if there is no extent buffer in the range.
+ */
+static int find_first_subpage_eb(struct btrfs_fs_info *fs_info,
+ struct extent_buffer **eb_ret, u64 start,
+ u64 end, u32 extra_bits)
+{
+ struct extent_io_tree *io_tree = info_to_btree_io_tree(fs_info);
+ u64 found_start;
+ u64 found_end;
+ int ret;
+
+ ASSERT(btrfs_is_subpage(fs_info) && eb_ret);
+
+ ret = find_first_extent_bit(io_tree, start, &found_start, &found_end,
+ EXTENT_HAS_TREE_BLOCK | extra_bits, true, NULL);
+ if (ret > 0 || found_start > end)
+ return 1;
+
+ /* found_start can be smaller than start */
+ start = max(start, found_start);
+
+ /*
+ * Here we can't call find_extent_buffer() which will increase
+ * eb->refs.
+ */
+ rcu_read_lock();
+ *eb_ret = radix_tree_lookup(&fs_info->buffer_radix,
+ start / fs_info->sectorsize);
+ rcu_read_unlock();
+ ASSERT(*eb_ret);
+ return 0;
+}
+
/* lots and lots of room for performance fixes in the end_bio funcs */
void end_extent_writepage(struct page *page, int err, u64 start, u64 end)
@@ -6389,10 +6431,51 @@ void memmove_extent_buffer(const struct extent_buffer *dst,
}
}
+static int try_release_subpage_eb(struct page *page)
+{
+ struct btrfs_fs_info *fs_info = page_to_fs_info(page);
+ struct extent_io_tree *io_tree = info_to_btree_io_tree(fs_info);
+ u64 cur = page_offset(page);
+ u64 end = page_offset(page) + PAGE_SIZE - 1;
+ int ret;
+
+ while (cur <= end) {
+ struct extent_buffer *eb;
+
+ ret = find_first_subpage_eb(fs_info, &eb, cur, end, 0);
+ if (ret > 0)
+ break;
+
+ cur = eb->start + eb->len;
+
+ 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 EXTENT_HAS_TREE_BLOCK bit at the end.
+ */
+ release_extent_buffer(eb);
+ }
+
+ /* Finally check if there is any EXTENT_HAS_TREE_BLOCK bit remaining */
+ if (test_range_bit(io_tree, page_offset(page), end,
+ EXTENT_HAS_TREE_BLOCK, 0, NULL))
+ ret = 0;
+ else
+ ret = 1;
+ return ret;
+}
+
int try_release_extent_buffer(struct page *page)
{
struct extent_buffer *eb;
+ if (btrfs_is_subpage(page_to_fs_info(page)))
+ return try_release_subpage_eb(page);
/*
* We need to make sure nobody is attaching this page to an eb right
* now.
For try_release_extent_buffer(), we just iterate through all the range with EXTENT_NEW set, and try freeing each extent buffer. Also introduce a helper, find_first_subpage_eb(), to locate find the first eb in the range. This helper will also be utilized for later subpage patches. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/disk-io.c | 6 ++++ fs/btrfs/extent_io.c | 83 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+)