Btrfs: stop using write_one_page
authorJosef Bacik <josef@redhat.com>
Mon, 26 Sep 2011 17:58:47 +0000 (13:58 -0400)
committerJosef Bacik <josef@redhat.com>
Wed, 19 Oct 2011 19:12:48 +0000 (15:12 -0400)
While looking for a performance regression a user was complaining about, I
noticed that we had a regression with the varmail test of filebench.  This was
introduced by

0d10ee2e6deb5c8409ae65b970846344897d5e4e

which keeps us from calling writepages in writepage.  This is a correct change,
however it happens to help the varmail test because we write out in larger
chunks.  This is largly to do with how we write out dirty pages for each
transaction.  If you run filebench with

load varmail
set $dir=/mnt/btrfs-test
run 60

prior to this patch you would get ~1420 ops/second, but with the patch you get
~1200 ops/second.  This is a 16% decrease.  So since we know the range of dirty
pages we want to write out, don't write out in one page chunks, write out in
ranges.  So to do this we call filemap_fdatawrite_range() on the range of bytes.
Then we convert the DIRTY extents to NEED_WAIT extents.  When we then call
btrfs_wait_marked_extents() we only have to filemap_fdatawait_range() on that
range and clear the NEED_WAIT extents.  This doesn't get us back to our original
speeds, but I've been seeing ~1380 ops/second, which is a <5% regression as
opposed to a >15% regression.  That is acceptable given that the original commit
greatly reduces our latency to begin with.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
fs/btrfs/extent_io.h
fs/btrfs/transaction.c

index cea445d..325a346 100644 (file)
@@ -17,6 +17,7 @@
 #define EXTENT_NODATASUM (1 << 10)
 #define EXTENT_DO_ACCOUNTING (1 << 11)
 #define EXTENT_FIRST_DELALLOC (1 << 12)
+#define EXTENT_NEED_WAIT (1 << 13)
 #define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)
 #define EXTENT_CTLBITS (EXTENT_DO_ACCOUNTING | EXTENT_FIRST_DELALLOC)
 
index 7debbf3..4565579 100644 (file)
@@ -572,50 +572,21 @@ int btrfs_end_transaction_dmeta(struct btrfs_trans_handle *trans,
 int btrfs_write_marked_extents(struct btrfs_root *root,
                               struct extent_io_tree *dirty_pages, int mark)
 {
-       int ret;
        int err = 0;
        int werr = 0;
-       struct page *page;
-       struct inode *btree_inode = root->fs_info->btree_inode;
+       struct address_space *mapping = root->fs_info->btree_inode->i_mapping;
        u64 start = 0;
        u64 end;
-       unsigned long index;
 
-       while (1) {
-               ret = find_first_extent_bit(dirty_pages, start, &start, &end,
-                                           mark);
-               if (ret)
-                       break;
-               while (start <= end) {
-                       cond_resched();
-
-                       index = start >> PAGE_CACHE_SHIFT;
-                       start = (u64)(index + 1) << PAGE_CACHE_SHIFT;
-                       page = find_get_page(btree_inode->i_mapping, index);
-                       if (!page)
-                               continue;
-
-                       btree_lock_page_hook(page);
-                       if (!page->mapping) {
-                               unlock_page(page);
-                               page_cache_release(page);
-                               continue;
-                       }
-
-                       if (PageWriteback(page)) {
-                               if (PageDirty(page))
-                                       wait_on_page_writeback(page);
-                               else {
-                                       unlock_page(page);
-                                       page_cache_release(page);
-                                       continue;
-                               }
-                       }
-                       err = write_one_page(page, 0);
-                       if (err)
-                               werr = err;
-                       page_cache_release(page);
-               }
+       while (!find_first_extent_bit(dirty_pages, start, &start, &end,
+                                     mark)) {
+               convert_extent_bit(dirty_pages, start, end, EXTENT_NEED_WAIT, mark,
+                                  GFP_NOFS);
+               err = filemap_fdatawrite_range(mapping, start, end);
+               if (err)
+                       werr = err;
+               cond_resched();
+               start = end + 1;
        }
        if (err)
                werr = err;
@@ -631,39 +602,20 @@ int btrfs_write_marked_extents(struct btrfs_root *root,
 int btrfs_wait_marked_extents(struct btrfs_root *root,
                              struct extent_io_tree *dirty_pages, int mark)
 {
-       int ret;
        int err = 0;
        int werr = 0;
-       struct page *page;
-       struct inode *btree_inode = root->fs_info->btree_inode;
+       struct address_space *mapping = root->fs_info->btree_inode->i_mapping;
        u64 start = 0;
        u64 end;
-       unsigned long index;
 
-       while (1) {
-               ret = find_first_extent_bit(dirty_pages, start, &start, &end,
-                                           mark);
-               if (ret)
-                       break;
-
-               clear_extent_bits(dirty_pages, start, end, mark, GFP_NOFS);
-               while (start <= end) {
-                       index = start >> PAGE_CACHE_SHIFT;
-                       start = (u64)(index + 1) << PAGE_CACHE_SHIFT;
-                       page = find_get_page(btree_inode->i_mapping, index);
-                       if (!page)
-                               continue;
-                       if (PageDirty(page)) {
-                               btree_lock_page_hook(page);
-                               wait_on_page_writeback(page);
-                               err = write_one_page(page, 0);
-                               if (err)
-                                       werr = err;
-                       }
-                       wait_on_page_writeback(page);
-                       page_cache_release(page);
-                       cond_resched();
-               }
+       while (!find_first_extent_bit(dirty_pages, start, &start, &end,
+                                     EXTENT_NEED_WAIT)) {
+               clear_extent_bits(dirty_pages, start, end, EXTENT_NEED_WAIT, GFP_NOFS);
+               err = filemap_fdatawait_range(mapping, start, end);
+               if (err)
+                       werr = err;
+               cond_resched();
+               start = end + 1;
        }
        if (err)
                werr = err;