btrfs: introduce write_one_subpage_eb() function
authorQu Wenruo <wqu@suse.com>
Tue, 6 Apr 2021 00:36:01 +0000 (08:36 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 19 Apr 2021 15:25:22 +0000 (17:25 +0200)
The new function, write_one_subpage_eb(), as a subroutine for subpage
metadata write, will handle the extent buffer bio submission.

The major differences between the new write_one_subpage_eb() and
write_one_eb() is:

- No page locking
  When entering write_one_subpage_eb() the page is no longer locked.
  We only lock the page for its status update, and unlock immediately.
  Now we completely rely on extent io tree locking.

- Extra bitmap update along with page status update
  Now page dirty and writeback is controlled by
  btrfs_subpage::dirty_bitmap and btrfs_subpage::writeback_bitmap.
  They both follow the schema that any sector is dirty/writeback, then
  the full page gets dirty/writeback.

- When to update the nr_written number
  Now we take a shortcut, if we have cleared the last dirty bit of the
  page, we update nr_written.
  This is not completely perfect, but should emulate the old behavior
  well enough.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_io.c

index 8c006a1..e0713b0 100644 (file)
@@ -4197,6 +4197,56 @@ static void end_bio_extent_buffer_writepage(struct bio *bio)
        bio_put(bio);
 }
 
+/*
+ * Unlike the work in write_one_eb(), we rely completely on extent locking.
+ * Page locking is only utilized at minimum to keep the VMM code happy.
+ *
+ * Caller should still call write_one_eb() other than this function directly.
+ * As write_one_eb() has extra preparation before submitting the extent buffer.
+ */
+static int write_one_subpage_eb(struct extent_buffer *eb,
+                               struct writeback_control *wbc,
+                               struct extent_page_data *epd)
+{
+       struct btrfs_fs_info *fs_info = eb->fs_info;
+       struct page *page = eb->pages[0];
+       unsigned int write_flags = wbc_to_write_flags(wbc) | REQ_META;
+       bool no_dirty_ebs = false;
+       int ret;
+
+       /* clear_page_dirty_for_io() in subpage helper needs page locked */
+       lock_page(page);
+       btrfs_subpage_set_writeback(fs_info, page, eb->start, eb->len);
+
+       /* Check if this is the last dirty bit to update nr_written */
+       no_dirty_ebs = btrfs_subpage_clear_and_test_dirty(fs_info, page,
+                                                         eb->start, eb->len);
+       if (no_dirty_ebs)
+               clear_page_dirty_for_io(page);
+
+       ret = submit_extent_page(REQ_OP_WRITE | write_flags, wbc, page,
+                       eb->start, eb->len, eb->start - page_offset(page),
+                       &epd->bio, end_bio_extent_buffer_writepage, 0, 0, 0,
+                       false);
+       if (ret) {
+               btrfs_subpage_clear_writeback(fs_info, page, eb->start, eb->len);
+               set_btree_ioerr(page, eb);
+               unlock_page(page);
+
+               if (atomic_dec_and_test(&eb->io_pages))
+                       end_extent_buffer_writeback(eb);
+               return -EIO;
+       }
+       unlock_page(page);
+       /*
+        * Submission finished without problem, if no range of the page is
+        * dirty anymore, we have submitted a page.  Update nr_written in wbc.
+        */
+       if (no_dirty_ebs)
+               update_nr_written(wbc, 1);
+       return ret;
+}
+
 static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
                        struct writeback_control *wbc,
                        struct extent_page_data *epd)
@@ -4228,6 +4278,9 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
                memzero_extent_buffer(eb, start, end - start);
        }
 
+       if (eb->fs_info->sectorsize < PAGE_SIZE)
+               return write_one_subpage_eb(eb, wbc, epd);
+
        for (i = 0; i < num_pages; i++) {
                struct page *p = eb->pages[i];