On 03/09/2012 02:37 AM, Josef Bacik wrote:
> We spend a lot of time looking up extent buffers from pages when we could just
> store the pointer to the eb the page is associated with in page->private.  
> This
> patch does just that, and it makes things a little simpler and reduces a bit 
> of
> CPU overhead involved with doing metadata IO.  Thanks,
> 

Hi Josef,

This is not against the normal branch, so we must inform people that. :)

Have a quick look, and I guess it is for "Btrfs: allow metadata blocks larger 
than the page size", isn't it?

thanks,
liubo

> Signed-off-by: Josef Bacik <jo...@redhat.com>
> ---
>  fs/btrfs/disk-io.c   |   92 ++++++++++++-------------------------------------
>  fs/btrfs/extent_io.c |   92 ++++++++++++++++++++++++++++++++++++-------------
>  fs/btrfs/extent_io.h |    1 +
>  3 files changed, 91 insertions(+), 94 deletions(-)
> 
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index 33114bc..6f97129 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -403,39 +403,28 @@ static int csum_dirty_buffer(struct btrfs_root *root, 
> struct page *page)
>       struct extent_io_tree *tree;
>       u64 start = (u64)page->index << PAGE_CACHE_SHIFT;
>       u64 found_start;
> -     unsigned long len;
>       struct extent_buffer *eb;
>  
>       tree = &BTRFS_I(page->mapping->host)->io_tree;
>  
> -     if (page->private == EXTENT_PAGE_PRIVATE)
> -             goto out;
> -     if (!page->private) {
> -             WARN_ON(1);
> -             goto out;
> -     }
> -     len = page->private >> 2;
> -     WARN_ON(len == 0);
> -
> -     eb = find_extent_buffer(tree, start, len);
> +     eb = (struct extent_buffer *)page->private;
> +     if (page != eb->pages[0])
> +             return 0;
>  
>       found_start = btrfs_header_bytenr(eb);
>       if (found_start != start) {
>               WARN_ON(1);
> -             goto err;
> +             return 0;
>       }
>       if (eb->pages[0] != page) {
>               WARN_ON(1);
> -             goto err;
> +             return 0;
>       }
>       if (!PageUptodate(page)) {
>               WARN_ON(1);
> -             goto err;
> +             return 0;
>       }
>       csum_tree_block(root, eb, 0);
> -err:
> -     free_extent_buffer(eb);
> -out:
>       return 0;
>  }
>  
> @@ -566,7 +555,6 @@ static int btree_readpage_end_io_hook(struct page *page, 
> u64 start, u64 end,
>       struct extent_io_tree *tree;
>       u64 found_start;
>       int found_level;
> -     unsigned long len;
>       struct extent_buffer *eb;
>       struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
>       int ret = 0;
> @@ -576,13 +564,8 @@ static int btree_readpage_end_io_hook(struct page *page, 
> u64 start, u64 end,
>               goto out;
>  
>       tree = &BTRFS_I(page->mapping->host)->io_tree;
> -     len = page->private >> 2;
> +     eb = (struct extent_buffer *)page->private;
>  
> -     eb = find_eb_for_page(tree, page, max(root->leafsize, root->nodesize));
> -     if (!eb) {
> -             ret = -EIO;
> -             goto out;
> -     }
>       reads_done = atomic_dec_and_test(&eb->pages_reading);
>       if (!reads_done)
>               goto err;
> @@ -631,7 +614,6 @@ err:
>  
>       if (ret && eb)
>               clear_extent_buffer_uptodate(tree, eb, NULL);
> -     free_extent_buffer(eb);
>  out:
>       return ret;
>  }
> @@ -640,31 +622,17 @@ static int btree_io_failed_hook(struct bio *failed_bio,
>                        struct page *page, u64 start, u64 end,
>                        int mirror_num, struct extent_state *state)
>  {
> -     struct extent_io_tree *tree;
> -     unsigned long len;
>       struct extent_buffer *eb;
>       struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
>  
> -     tree = &BTRFS_I(page->mapping->host)->io_tree;
> -     if (page->private == EXTENT_PAGE_PRIVATE)
> -             goto out;
> -     if (!page->private)
> -             goto out;
> -
> -     len = page->private >> 2;
> -     WARN_ON(len == 0);
> -
> -     eb = alloc_extent_buffer(tree, start, len);
> -     if (eb == NULL)
> -             goto out;
> +     eb = (struct extent_buffer *)page->private;
> +     if (page != eb->pages[0])
> +             return -EIO;
>  
>       if (test_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags)) {
>               clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags);
>               btree_readahead_hook(root, eb, eb->start, -EIO);
>       }
> -     free_extent_buffer(eb);
> -
> -out:
>       return -EIO;    /* we fixed nothing */
>  }
>  
> @@ -955,10 +923,8 @@ static int btree_readpage(struct file *file, struct page 
> *page)
>  
>  static int btree_releasepage(struct page *page, gfp_t gfp_flags)
>  {
> -     struct extent_io_tree *tree;
>       struct extent_map_tree *map;
> -     struct extent_buffer *eb;
> -     struct btrfs_root *root;
> +     struct extent_io_tree *tree;
>       int ret;
>  
>       if (PageWriteback(page) || PageDirty(page))
> @@ -967,26 +933,11 @@ static int btree_releasepage(struct page *page, gfp_t 
> gfp_flags)
>       tree = &BTRFS_I(page->mapping->host)->io_tree;
>       map = &BTRFS_I(page->mapping->host)->extent_tree;
>  
> -     root = BTRFS_I(page->mapping->host)->root;
> -     if (page->private == EXTENT_PAGE_PRIVATE) {
> -             eb = find_eb_for_page(tree, page, max(root->leafsize, 
> root->nodesize));
> -             free_extent_buffer(eb);
> -             if (eb)
> -                     return 0;
> -     }
> -
>       ret = try_release_extent_state(map, tree, page, gfp_flags);
>       if (!ret)
>               return 0;
>  
> -     ret = try_release_extent_buffer(tree, page);
> -     if (ret == 1) {
> -             ClearPagePrivate(page);
> -             set_page_private(page, 0);
> -             page_cache_release(page);
> -     }
> -
> -     return ret;
> +     return try_release_extent_buffer(tree, page);
>  }
>  
>  static void btree_invalidatepage(struct page *page, unsigned long offset)
> @@ -3173,17 +3124,21 @@ static int btree_lock_page_hook(struct page *page, 
> void *data,
>  {
>       struct inode *inode = page->mapping->host;
>       struct btrfs_root *root = BTRFS_I(inode)->root;
> -     struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
>       struct extent_buffer *eb;
> -     unsigned long len;
> -     u64 bytenr = page_offset(page);
>  
> -     if (page->private == EXTENT_PAGE_PRIVATE)
> +     /*
> +      * We culled this eb but the page is still hanging out on the mapping,
> +      * carry on.
> +      */
> +     if (!PagePrivate(page))
>               goto out;
>  
> -     len = page->private >> 2;
> -     eb = find_extent_buffer(io_tree, bytenr, len);
> -     if (!eb)
> +     eb = (struct extent_buffer *)page->private;
> +     if (!eb) {
> +             WARN_ON(1);
> +             goto out;
> +     }
> +     if (page != eb->pages[0])
>               goto out;
>  
>       if (!btrfs_try_tree_write_lock(eb)) {
> @@ -3202,7 +3157,6 @@ static int btree_lock_page_hook(struct page *page, void 
> *data,
>       }
>  
>       btrfs_tree_unlock(eb);
> -     free_extent_buffer(eb);
>  out:
>       if (!trylock_page(page)) {
>               flush_fn(data);
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index d4795fc..197595a 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -2462,19 +2462,24 @@ static int submit_extent_page(int rw, struct 
> extent_io_tree *tree,
>       return ret;
>  }
>  
> -void set_page_extent_mapped(struct page *page)
> +void attach_extent_buffer_page(struct extent_buffer *eb, struct page *page)
>  {
>       if (!PagePrivate(page)) {
>               SetPagePrivate(page);
>               page_cache_get(page);
> -             set_page_private(page, EXTENT_PAGE_PRIVATE);
> +             set_page_private(page, (unsigned long)eb);
> +     } else {
> +             WARN_ON(page->private != (unsigned long)eb);
>       }
>  }
>  
> -static void set_page_extent_head(struct page *page, unsigned long len)
> +void set_page_extent_mapped(struct page *page)
>  {
> -     WARN_ON(!PagePrivate(page));
> -     set_page_private(page, EXTENT_PAGE_PRIVATE_FIRST_PAGE | len << 2);
> +     if (!PagePrivate(page)) {
> +             SetPagePrivate(page);
> +             page_cache_get(page);
> +             set_page_private(page, EXTENT_PAGE_PRIVATE);
> +     }
>  }
>  
>  /*
> @@ -3567,6 +3572,7 @@ static struct extent_buffer 
> *__alloc_extent_buffer(struct extent_io_tree *tree,
>               return NULL;
>       eb->start = start;
>       eb->len = len;
> +     eb->tree = tree;
>       rwlock_init(&eb->lock);
>       atomic_set(&eb->write_locks, 0);
>       atomic_set(&eb->read_locks, 0);
> @@ -3619,8 +3625,31 @@ static void btrfs_release_extent_buffer_page(struct 
> extent_buffer *eb,
>       do {
>               index--;
>               page = extent_buffer_page(eb, index);
> -             if (page)
> +             if (page) {
> +                     spin_lock(&page->mapping->private_lock);
> +                     /*
> +                      * We do this since we'll remove the pages after we've
> +                      * removed the eb from the radix tree, so we could race
> +                      * and have this page now attached to the new eb.  So
> +                      * only clear page_private if it's still connected to
> +                      * this eb.
> +                      */
> +                     if (PagePrivate(page) &&
> +                         page->private == (unsigned long)eb) {
> +                             /*
> +                              * We need to make sure we haven't be attached
> +                              * to a new eb.
> +                              */
> +                             ClearPagePrivate(page);
> +                             set_page_private(page, 0);
> +                             /* One for the page private */
> +                             page_cache_release(page);
> +                     }
> +                     spin_unlock(&page->mapping->private_lock);
> +
> +                     /* One for when we alloced the page */
>                       page_cache_release(page);
> +             }
>       } while (index != start_idx);
>  }
>  
> @@ -3665,6 +3694,32 @@ struct extent_buffer *alloc_extent_buffer(struct 
> extent_io_tree *tree,
>                       WARN_ON(1);
>                       goto free_eb;
>               }
> +
> +             spin_lock(&mapping->private_lock);
> +             if (PagePrivate(p)) {
> +                     /*
> +                      * We could have already allocated an eb for this page
> +                      * and attached one so lets see if we can get a ref on
> +                      * the existing eb, and if we can we know it's good and
> +                      * we can just return that one, else we know we can just
> +                      * overwrite page->private.
> +                      */
> +                     exists = (struct extent_buffer *)p->private;
> +                     if (atomic_inc_not_zero(&exists->refs)) {
> +                             spin_unlock(&mapping->private_lock);
> +                             unlock_page(p);
> +                             goto free_eb;
> +                     }
> +
> +                     /* 
> +                      * Do this so attach doesn't complain and we need to
> +                      * drop the ref the old guy had.
> +                      */
> +                     ClearPagePrivate(p);
> +                     page_cache_release(p);
> +             }
> +             attach_extent_buffer_page(eb, p);
> +             spin_unlock(&mapping->private_lock);
>               mark_page_accessed(p);
>               eb->pages[i] = p;
>               if (!PageUptodate(p))
> @@ -3687,7 +3742,6 @@ struct extent_buffer *alloc_extent_buffer(struct 
> extent_io_tree *tree,
>       if (ret == -EEXIST) {
>               exists = radix_tree_lookup(&tree->buffer,
>                                               start >> PAGE_CACHE_SHIFT);
> -             /* add one reference for the caller */
>               atomic_inc(&exists->refs);
>               spin_unlock(&tree->buffer_lock);
>               radix_tree_preload_end();
> @@ -3707,12 +3761,9 @@ struct extent_buffer *alloc_extent_buffer(struct 
> extent_io_tree *tree,
>        * after the extent buffer is in the radix tree so
>        * it doesn't get lost
>        */
> -     set_page_extent_mapped(eb->pages[0]);
> -     set_page_extent_head(eb->pages[0], eb->len);
>       SetPageChecked(eb->pages[0]);
>       for (i = 1; i < num_pages; i++) {
>               p = extent_buffer_page(eb, i);
> -             set_page_extent_mapped(p);
>               ClearPageChecked(p);
>               unlock_page(p);
>       }
> @@ -3776,10 +3827,6 @@ int clear_extent_buffer_dirty(struct extent_io_tree 
> *tree,
>               lock_page(page);
>               WARN_ON(!PagePrivate(page));
>  
> -             set_page_extent_mapped(page);
> -             if (i == 0)
> -                     set_page_extent_head(page, eb->len);
> -
>               clear_page_dirty_for_io(page);
>               spin_lock_irq(&page->mapping->tree_lock);
>               if (!PageDirty(page)) {
> @@ -3991,9 +4038,6 @@ int read_extent_buffer_pages(struct extent_io_tree 
> *tree,
>       atomic_set(&eb->pages_reading, num_reads);
>       for (i = start_i; i < num_pages; i++) {
>               page = extent_buffer_page(eb, i);
> -             set_page_extent_mapped(page);
> -             if (i == 0)
> -                     set_page_extent_head(page, eb->len);
>               if (!PageUptodate(page)) {
>                       ClearPageError(page);
>                       err = __extent_read_full_page(tree, page,
> @@ -4376,22 +4420,19 @@ static inline void 
> btrfs_release_extent_buffer_rcu(struct rcu_head *head)
>       struct extent_buffer *eb =
>                       container_of(head, struct extent_buffer, rcu_head);
>  
> -     btrfs_release_extent_buffer(eb);
> +     __free_extent_buffer(eb);
>  }
>  
>  int try_release_extent_buffer(struct extent_io_tree *tree, struct page *page)
>  {
>       u64 start = page_offset(page);
> -     struct extent_buffer *eb;
> +     struct extent_buffer *eb = (struct extent_buffer *)page->private;
>       int ret = 1;
>  
> -     spin_lock(&tree->buffer_lock);
> -     eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
> -     if (!eb) {
> -             spin_unlock(&tree->buffer_lock);
> -             return ret;
> -     }
> +     if (!PagePrivate(page) || !eb)
> +             return 1;
>  
> +     spin_lock(&tree->buffer_lock);
>       if (atomic_read(&eb->refs) > 1 ||
>           test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
>               ret = 0;
> @@ -4407,6 +4448,7 @@ int try_release_extent_buffer(struct extent_io_tree 
> *tree, struct page *page)
>               goto out;
>       }
>       radix_tree_delete(&tree->buffer, start >> PAGE_CACHE_SHIFT);
> +     btrfs_release_extent_buffer_page(eb, 0);
>  out:
>       spin_unlock(&tree->buffer_lock);
>  
> diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
> index 9b6c8f3..f030a2d 100644
> --- a/fs/btrfs/extent_io.h
> +++ b/fs/btrfs/extent_io.h
> @@ -127,6 +127,7 @@ struct extent_buffer {
>       unsigned long map_start;
>       unsigned long map_len;
>       unsigned long bflags;
> +     struct extent_io_tree *tree;
>       atomic_t refs;
>       atomic_t pages_reading;
>       struct list_head leak_list;

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to