All of lore.kernel.org
 help / color / mirror / Atom feed
From: saeed bishara <saeed.bishara@gmail.com>
To: Chandra Seetharaman <sekharan@us.ibm.com>
Cc: linux-btrfs@vger.kernel.org
Subject: Re: [PATCH 1/7] btrfs: subpagesize-blocksize: Define extent_buffer_head
Date: Mon, 16 Dec 2013 14:32:33 +0200	[thread overview]
Message-ID: <CAMAG_ecWBoFJ_vap2iFBvvW99iHVVTuMdVvaNycaFLzqt5fNUg@mail.gmail.com> (raw)
In-Reply-To: <1386805122-23972-2-git-send-email-sekharan@us.ibm.com>

On Thu, Dec 12, 2013 at 1:38 AM, Chandra Seetharaman
<sekharan@us.ibm.com> wrote:
> In order to handle multiple extent buffers per page, first we
> need to create a way to handle all the extent buffers that
> are attached to a page.
>
> This patch creates a new data structure eb_head, and moves
> fields that are common to all extent buffers in a page from
> extent buffer to eb_head.
>
> This also adds changes that are needed to handle multiple
> extent buffers per page case.
>
> Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
> ---
>  fs/btrfs/backref.c           |   6 +-
>  fs/btrfs/ctree.c             |   2 +-
>  fs/btrfs/ctree.h             |   6 +-
>  fs/btrfs/disk-io.c           | 109 +++++++----
>  fs/btrfs/extent-tree.c       |   6 +-
>  fs/btrfs/extent_io.c         | 429 +++++++++++++++++++++++++++----------------
>  fs/btrfs/extent_io.h         |  55 ++++--
>  fs/btrfs/volumes.c           |   2 +-
>  include/trace/events/btrfs.h |   2 +-
>  9 files changed, 390 insertions(+), 227 deletions(-)
>
> diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
> index 3775947..af1943f 100644
> --- a/fs/btrfs/backref.c
> +++ b/fs/btrfs/backref.c
> @@ -1283,7 +1283,7 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
>                 eb = path->nodes[0];
>                 /* make sure we can use eb after releasing the path */
>                 if (eb != eb_in) {
> -                       atomic_inc(&eb->refs);
> +                       atomic_inc(&eb_head(eb)->refs);
>                         btrfs_tree_read_lock(eb);
>                         btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
>                 }
> @@ -1616,7 +1616,7 @@ static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
>                 slot = path->slots[0];
>                 eb = path->nodes[0];
>                 /* make sure we can use eb after releasing the path */
> -               atomic_inc(&eb->refs);
> +               atomic_inc(&eb_head(eb)->refs);
>                 btrfs_tree_read_lock(eb);
>                 btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
>                 btrfs_release_path(path);
> @@ -1676,7 +1676,7 @@ static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root,
>                 slot = path->slots[0];
>                 eb = path->nodes[0];
>                 /* make sure we can use eb after releasing the path */
> -               atomic_inc(&eb->refs);
> +               atomic_inc(&eb_head(eb)->refs);
>
>                 btrfs_tree_read_lock(eb);
>                 btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
> diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
> index 316136b..611b27e 100644
> --- a/fs/btrfs/ctree.c
> +++ b/fs/btrfs/ctree.c
> @@ -170,7 +170,7 @@ struct extent_buffer *btrfs_root_node(struct btrfs_root *root)
>                  * the inc_not_zero dance and if it doesn't work then
>                  * synchronize_rcu and try again.
>                  */
> -               if (atomic_inc_not_zero(&eb->refs)) {
> +               if (atomic_inc_not_zero(&eb_head(eb)->refs)) {
>                         rcu_read_unlock();
>                         break;
>                 }
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 54ab861..02de448 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -2106,14 +2106,16 @@ static inline void btrfs_set_token_##name(struct extent_buffer *eb,     \
>  #define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits)            \
>  static inline u##bits btrfs_##name(struct extent_buffer *eb)           \
>  {                                                                      \
> -       type *p = page_address(eb->pages[0]);                           \
> +       type *p = page_address(eb_head(eb)->pages[0]) +                 \
> +                               (eb->start & (PAGE_CACHE_SIZE -1));     \
you can use PAGE_CACHE_MASK instead of PAGE_CACHE_SIZE - 1
>         u##bits res = le##bits##_to_cpu(p->member);                     \
>         return res;                                                     \
>  }                                                                      \
>  static inline void btrfs_set_##name(struct extent_buffer *eb,          \
>                                     u##bits val)                        \
>  {                                                                      \
> -       type *p = page_address(eb->pages[0]);                           \
> +       type *p = page_address(eb_head(eb)->pages[0]) +                 \
> +                               (eb->start & (PAGE_CACHE_SIZE -1));     \
>         p->member = cpu_to_le##bits(val);                               \
>  }
>
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index 8072cfa..ca1526d 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -411,7 +411,7 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
>         int mirror_num = 0;
>         int failed_mirror = 0;
>
> -       clear_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
> +       clear_bit(EXTENT_BUFFER_CORRUPT, &eb_head(eb)->bflags);
>         io_tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
>         while (1) {
>                 ret = read_extent_buffer_pages(io_tree, eb, start,
> @@ -430,7 +430,7 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
>                  * there is no reason to read the other copies, they won't be
>                  * any less wrong.
>                  */
> -               if (test_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags))
> +               if (test_bit(EXTENT_BUFFER_CORRUPT, &eb_head(eb)->bflags))
>                         break;
>
>                 num_copies = btrfs_num_copies(root->fs_info,
> @@ -440,7 +440,7 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
>
>                 if (!failed_mirror) {
>                         failed = 1;
> -                       failed_mirror = eb->read_mirror;
> +                       failed_mirror = eb_head(eb)->read_mirror;
>                 }
>
>                 mirror_num++;
> @@ -465,19 +465,22 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
>  static int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
>  {
>         struct extent_io_tree *tree;
> -       u64 start = page_offset(page);
>         u64 found_start;
>         struct extent_buffer *eb;
> +       struct extent_buffer_head *eb_head;
>
>         tree = &BTRFS_I(page->mapping->host)->io_tree;
>
> -       eb = (struct extent_buffer *)page->private;
> -       if (page != eb->pages[0])
> +       eb_head = (struct extent_buffer_head *)page->private;
> +       if (page != eb_head->pages[0])
>                 return 0;
> -       found_start = btrfs_header_bytenr(eb);
> -       if (WARN_ON(found_start != start || !PageUptodate(page)))
> +       if (WARN_ON(!PageUptodate(page)))
>                 return 0;
> -       csum_tree_block(root, eb, 0);
> +       for (eb = &eb_head->extent_buf[0]; eb->start; eb++) {
> +               found_start = btrfs_header_bytenr(eb);
> +               if (found_start == eb->start)
> +                       csum_tree_block(root, eb, 0);
> +       }
>         return 0;
>  }
>
> @@ -575,25 +578,34 @@ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
>         struct extent_buffer *eb;
>         struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
>         int ret = 0;
> -       int reads_done;
> +       int reads_done = 0;
> +       struct extent_buffer_head *eb_head;
>
>         if (!page->private)
>                 goto out;
>
>         tree = &BTRFS_I(page->mapping->host)->io_tree;
> -       eb = (struct extent_buffer *)page->private;
> +       eb_head = (struct extent_buffer_head *)page->private;
> +
> +       /* Get the eb corresponding to this IO */
> +       eb = eb_head->io_eb;
> +       if (!eb) {
> +               ret = -EIO;
> +               goto err;
> +       }
> +       eb_head->io_eb = NULL;
>
>         /* the pending IO might have been the only thing that kept this buffer
>          * in memory.  Make sure we have a ref for all this other checks
>          */
>         extent_buffer_get(eb);
>
> -       reads_done = atomic_dec_and_test(&eb->io_pages);
> +       reads_done = atomic_dec_and_test(&eb_head->io_pages);
>         if (!reads_done)
>                 goto err;
>
> -       eb->read_mirror = mirror;
> -       if (test_bit(EXTENT_BUFFER_IOERR, &eb->bflags)) {
> +       eb_head->read_mirror = mirror;
> +       if (test_bit(EXTENT_BUFFER_IOERR, &eb_head->bflags)) {
>                 ret = -EIO;
>                 goto err;
>         }
> @@ -635,7 +647,7 @@ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
>          * return -EIO.
>          */
>         if (found_level == 0 && check_leaf(root, eb)) {
> -               set_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
> +               set_bit(EXTENT_BUFFER_CORRUPT, &eb_head->bflags);
>                 ret = -EIO;
>         }
>
> @@ -643,7 +655,7 @@ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
>                 set_extent_buffer_uptodate(eb);
>  err:
>         if (reads_done &&
> -           test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags))
> +           test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb_head->bflags))
>                 btree_readahead_hook(root, eb, eb->start, ret);
>
>         if (ret) {
> @@ -652,7 +664,7 @@ err:
>                  * again, we have to make sure it has something
>                  * to decrement
>                  */
> -               atomic_inc(&eb->io_pages);
> +               atomic_inc(&eb_head->io_pages);
>                 clear_extent_buffer_uptodate(eb);
>         }
>         free_extent_buffer(eb);
> @@ -662,15 +674,22 @@ out:
>
>  static int btree_io_failed_hook(struct page *page, int failed_mirror)
>  {
> +       struct extent_buffer_head *eb_head
> +                       =  (struct extent_buffer_head *)page->private;
>         struct extent_buffer *eb;
>         struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
>
> -       eb = (struct extent_buffer *)page->private;
> -       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
> -       eb->read_mirror = failed_mirror;
> -       atomic_dec(&eb->io_pages);
> -       if (test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags))
> +       set_bit(EXTENT_BUFFER_IOERR, &eb_head->bflags);
> +       eb_head->read_mirror = failed_mirror;
> +       atomic_dec(&eb_head->io_pages);
> +       /* Get the eb corresponding to this IO */
> +       eb = eb_head->io_eb;
> +       if (!eb)
> +               goto out;
> +       eb_head->io_eb = NULL;
> +       if (test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb_head->bflags))
>                 btree_readahead_hook(root, eb, eb->start, -EIO);
> +out:
>         return -EIO;    /* we fixed nothing */
>  }
>
> @@ -1021,14 +1040,20 @@ static void btree_invalidatepage(struct page *page, unsigned int offset,
>  static int btree_set_page_dirty(struct page *page)
>  {
>  #ifdef DEBUG
> +       struct extent_buffer_head *ebh;
>         struct extent_buffer *eb;
> +       int i, dirty = 0;
>
>         BUG_ON(!PagePrivate(page));
> -       eb = (struct extent_buffer *)page->private;
> -       BUG_ON(!eb);
> -       BUG_ON(!test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
> -       BUG_ON(!atomic_read(&eb->refs));
> -       btrfs_assert_tree_locked(eb);
> +       ebh = (struct extent_buffer_head *)page->private;
> +       BUG_ON(!ebh);
> +       for (i = 0; i < MAX_EXTENT_BUFFERS_PER_PAGE && !dirty; i++) {
> +               eb = &ebh->extent_buf[i];
> +               dirty = test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->ebflags);
> +       }
> +       BUG_ON(dirty);
> +       BUG_ON(!atomic_read(&ebh->refs));
> +       btrfs_assert_tree_locked(&ebh->extent_buf[0]);
>  #endif
>         return __set_page_dirty_nobuffers(page);
>  }
> @@ -1072,7 +1097,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
>         if (!buf)
>                 return 0;
>
> -       set_bit(EXTENT_BUFFER_READAHEAD, &buf->bflags);
> +       set_bit(EXTENT_BUFFER_READAHEAD, &eb_head(buf)->bflags);
>
>         ret = read_extent_buffer_pages(io_tree, buf, 0, WAIT_PAGE_LOCK,
>                                        btree_get_extent, mirror_num);
> @@ -1081,7 +1106,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
>                 return ret;
>         }
>
> -       if (test_bit(EXTENT_BUFFER_CORRUPT, &buf->bflags)) {
> +       if (test_bit(EXTENT_BUFFER_CORRUPT, &eb_head(buf)->bflags)) {
>                 free_extent_buffer(buf);
>                 return -EIO;
>         } else if (extent_buffer_uptodate(buf)) {
> @@ -1115,14 +1140,16 @@ struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
>
>  int btrfs_write_tree_block(struct extent_buffer *buf)
>  {
> -       return filemap_fdatawrite_range(buf->pages[0]->mapping, buf->start,
> +       return filemap_fdatawrite_range(eb_head(buf)->pages[0]->mapping,
> +                                       buf->start,
>                                         buf->start + buf->len - 1);
>  }
>
>  int btrfs_wait_tree_block_writeback(struct extent_buffer *buf)
>  {
> -       return filemap_fdatawait_range(buf->pages[0]->mapping,
> -                                      buf->start, buf->start + buf->len - 1);
> +       return filemap_fdatawait_range(eb_head(buf)->pages[0]->mapping,
> +                                       buf->start,
> +                                       buf->start + buf->len - 1);
>  }
>
>  struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
> @@ -1153,7 +1180,8 @@ void clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
>             fs_info->running_transaction->transid) {
>                 btrfs_assert_tree_locked(buf);
>
> -               if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &buf->bflags)) {
> +               if (test_and_clear_bit(EXTENT_BUFFER_DIRTY,
> +                                               &buf->ebflags)) {
>                         __percpu_counter_add(&fs_info->dirty_metadata_bytes,
>                                              -buf->len,
>                                              fs_info->dirty_metadata_batch);
> @@ -2613,7 +2641,8 @@ int open_ctree(struct super_block *sb,
>                                            btrfs_super_chunk_root(disk_super),
>                                            blocksize, generation);
>         if (!chunk_root->node ||
> -           !test_bit(EXTENT_BUFFER_UPTODATE, &chunk_root->node->bflags)) {
> +           !test_bit(EXTENT_BUFFER_UPTODATE,
> +                                       &eb_head(chunk_root->node)->bflags)) {
>                 printk(KERN_WARNING "btrfs: failed to read chunk root on %s\n",
>                        sb->s_id);
>                 goto fail_tree_roots;
> @@ -2652,7 +2681,8 @@ retry_root_backup:
>                                           btrfs_super_root(disk_super),
>                                           blocksize, generation);
>         if (!tree_root->node ||
> -           !test_bit(EXTENT_BUFFER_UPTODATE, &tree_root->node->bflags)) {
> +           !test_bit(EXTENT_BUFFER_UPTODATE,
> +                                       &eb_head(tree_root->node)->bflags)) {
>                 printk(KERN_WARNING "btrfs: failed to read tree root on %s\n",
>                        sb->s_id);
>
> @@ -3619,7 +3649,7 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid,
>                           int atomic)
>  {
>         int ret;
> -       struct inode *btree_inode = buf->pages[0]->mapping->host;
> +       struct inode *btree_inode = eb_head(buf)->pages[0]->mapping->host;
>
>         ret = extent_buffer_uptodate(buf);
>         if (!ret)
> @@ -3652,7 +3682,7 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
>         if (unlikely(test_bit(EXTENT_BUFFER_DUMMY, &buf->bflags)))
>                 return;
>  #endif
> -       root = BTRFS_I(buf->pages[0]->mapping->host)->root;
> +       root = BTRFS_I(eb_head(buf)->pages[0]->mapping->host)->root;
>         btrfs_assert_tree_locked(buf);
>         if (transid != root->fs_info->generation)
>                 WARN(1, KERN_CRIT "btrfs transid mismatch buffer %llu, "
> @@ -3701,7 +3731,8 @@ void btrfs_btree_balance_dirty_nodelay(struct btrfs_root *root)
>
>  int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)
>  {
> -       struct btrfs_root *root = BTRFS_I(buf->pages[0]->mapping->host)->root;
> +       struct btrfs_root *root =
> +                       BTRFS_I(eb_head(buf)->pages[0]->mapping->host)->root;
>         return btree_read_extent_buffer_pages(root, buf, 0, parent_transid);
>  }
>
> @@ -3938,7 +3969,7 @@ static int btrfs_destroy_marked_extents(struct btrfs_root *root,
>                         wait_on_extent_buffer_writeback(eb);
>
>                         if (test_and_clear_bit(EXTENT_BUFFER_DIRTY,
> -                                              &eb->bflags))
> +                                              &eb->ebflags))
>                                 clear_extent_buffer_dirty(eb);
>                         free_extent_buffer_stale(eb);
>                 }
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index 45d98d0..79cf87f 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -6019,7 +6019,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
>                         goto out;
>                 }
>
> -               WARN_ON(test_bit(EXTENT_BUFFER_DIRTY, &buf->bflags));
> +               WARN_ON(test_bit(EXTENT_BUFFER_DIRTY, &buf->ebflags));
>
>                 btrfs_add_free_space(cache, buf->start, buf->len);
>                 btrfs_update_reserved_bytes(cache, buf->len, RESERVE_FREE);
> @@ -6036,7 +6036,7 @@ out:
>          * Deleting the buffer, clear the corrupt flag since it doesn't matter
>          * anymore.
>          */
> -       clear_bit(EXTENT_BUFFER_CORRUPT, &buf->bflags);
> +       clear_bit(EXTENT_BUFFER_CORRUPT, &eb_head(buf)->bflags);
>         btrfs_put_block_group(cache);
>  }
>
> @@ -6910,7 +6910,7 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
>         btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level);
>         btrfs_tree_lock(buf);
>         clean_tree_block(trans, root, buf);
> -       clear_bit(EXTENT_BUFFER_STALE, &buf->bflags);
> +       clear_bit(EXTENT_BUFFER_STALE, &eb_head(buf)->bflags);
>
>         btrfs_set_lock_blocking(buf);
>         btrfs_set_buffer_uptodate(buf);
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index ff43802..a1a849b 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -54,8 +54,10 @@ void btrfs_leak_debug_del(struct list_head *entry)
>  static inline
>  void btrfs_leak_debug_check(void)
>  {
> +       int i;
>         struct extent_state *state;
>         struct extent_buffer *eb;
> +       struct extent_buffer_head *ebh;
>
>         while (!list_empty(&states)) {
>                 state = list_entry(states.next, struct extent_state, leak_list);
> @@ -68,12 +70,17 @@ void btrfs_leak_debug_check(void)
>         }
>
>         while (!list_empty(&buffers)) {
> -               eb = list_entry(buffers.next, struct extent_buffer, leak_list);
> -               printk(KERN_ERR "btrfs buffer leak start %llu len %lu "
> -                      "refs %d\n",
> -                      eb->start, eb->len, atomic_read(&eb->refs));
> -               list_del(&eb->leak_list);
> -               kmem_cache_free(extent_buffer_cache, eb);
> +               ebh = list_entry(buffers.next, struct extent_buffer_head, leak_list);
> +               printk(KERN_ERR "btrfs buffer leak ");
> +               for (i = 0; i < MAX_EXTENT_BUFFERS_PER_PAGE; i++) {
> +                       eb = &ebh->extent_buf[i];
> +                       if (!eb->start)
> +                               break;
> +                       printk(KERN_ERR "eb %p %llu:%lu ", eb, eb->start, eb->len);
> +               }
> +               printk(KERN_ERR "refs %d\n", atomic_read(&ebh->refs));
> +               list_del(&ebh->leak_list);
> +               kmem_cache_free(extent_buffer_cache, ebh);
>         }
>  }
>
> @@ -136,7 +143,7 @@ int __init extent_io_init(void)
>                 return -ENOMEM;
>
>         extent_buffer_cache = kmem_cache_create("btrfs_extent_buffer",
> -                       sizeof(struct extent_buffer), 0,
> +                       sizeof(struct extent_buffer_head), 0,
>                         SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
>         if (!extent_buffer_cache)
>                 goto free_state_cache;
> @@ -2023,7 +2030,7 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start,
>  int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
>                          int mirror_num)
>  {
> -       u64 start = eb->start;
> +       u64 start = eb_head(eb)->extent_buf[0].start;
>         unsigned long i, num_pages = num_extent_pages(eb->start, eb->len);
>         int ret = 0;
>
> @@ -2680,15 +2687,15 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree,
>         return ret;
>  }
>
> -static void attach_extent_buffer_page(struct extent_buffer *eb,
> +static void attach_extent_buffer_page(struct extent_buffer_head *ebh,
>                                       struct page *page)
>  {
>         if (!PagePrivate(page)) {
>                 SetPagePrivate(page);
>                 page_cache_get(page);
> -               set_page_private(page, (unsigned long)eb);
> +               set_page_private(page, (unsigned long)ebh);
>         } else {
> -               WARN_ON(page->private != (unsigned long)eb);
> +               WARN_ON(page->private != (unsigned long)ebh);
>         }
>  }
>
> @@ -3327,17 +3334,19 @@ static int eb_wait(void *word)
>
>  void wait_on_extent_buffer_writeback(struct extent_buffer *eb)
>  {
> -       wait_on_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK, eb_wait,
> +       wait_on_bit(&eb_head(eb)->bflags, EXTENT_BUFFER_WRITEBACK, eb_wait,
>                     TASK_UNINTERRUPTIBLE);
>  }
>
> -static int lock_extent_buffer_for_io(struct extent_buffer *eb,
> +static int lock_extent_buffer_for_io(struct extent_buffer_head *ebh,
>                                      struct btrfs_fs_info *fs_info,
>                                      struct extent_page_data *epd)
>  {
>         unsigned long i, num_pages;
>         int flush = 0;
> +       bool dirty = false, dirty_arr[MAX_EXTENT_BUFFERS_PER_PAGE];
>         int ret = 0;
> +       struct extent_buffer *eb = &ebh->extent_buf[0], *ebtemp;
>
>         if (!btrfs_try_tree_write_lock(eb)) {
>                 flush = 1;
> @@ -3345,7 +3354,7 @@ static int lock_extent_buffer_for_io(struct extent_buffer *eb,
>                 btrfs_tree_lock(eb);
>         }
>
> -       if (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags)) {
> +       if (test_bit(EXTENT_BUFFER_WRITEBACK, &ebh->bflags)) {
>                 btrfs_tree_unlock(eb);
>                 if (!epd->sync_io)
>                         return 0;
> @@ -3356,7 +3365,7 @@ static int lock_extent_buffer_for_io(struct extent_buffer *eb,
>                 while (1) {
>                         wait_on_extent_buffer_writeback(eb);
>                         btrfs_tree_lock(eb);
> -                       if (!test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags))
> +                       if (!test_bit(EXTENT_BUFFER_WRITEBACK, &ebh->bflags))
>                                 break;
>                         btrfs_tree_unlock(eb);
>                 }
> @@ -3367,17 +3376,27 @@ static int lock_extent_buffer_for_io(struct extent_buffer *eb,
>          * under IO since we can end up having no IO bits set for a short period
>          * of time.
>          */
> -       spin_lock(&eb->refs_lock);
> -       if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
> -               set_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags);
> -               spin_unlock(&eb->refs_lock);
> -               btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
> -               __percpu_counter_add(&fs_info->dirty_metadata_bytes,
> -                                    -eb->len,
> +       spin_lock(&ebh->refs_lock);
> +       for (i = 0; i < MAX_EXTENT_BUFFERS_PER_PAGE; i++) {
> +               ebtemp = &ebh->extent_buf[i];
> +               dirty_arr[i] |= test_and_clear_bit(EXTENT_BUFFER_DIRTY, &ebtemp->ebflags);
dirty_arr wasn't initialized, changing the "|=" to = fixed a crash
issue when doing writes
> +               dirty = dirty || dirty_arr[i];
> +       }
> +       if (dirty) {
> +               set_bit(EXTENT_BUFFER_WRITEBACK, &ebh->bflags);
> +               spin_unlock(&ebh->refs_lock);
> +               for (i = 0; i < MAX_EXTENT_BUFFERS_PER_PAGE; i++) {
> +                       if (dirty_arr[i] == false)
> +                               continue;
> +                       ebtemp = &ebh->extent_buf[i];
> +                       btrfs_set_header_flag(ebtemp, BTRFS_HEADER_FLAG_WRITTEN);
> +                       __percpu_counter_add(&fs_info->dirty_metadata_bytes,
> +                                    -ebtemp->len,
>                                      fs_info->dirty_metadata_batch);
> +               }
>                 ret = 1;
>         } else {
> -               spin_unlock(&eb->refs_lock);
> +               spin_unlock(&ebh->refs_lock);
>         }
>
>         btrfs_tree_unlock(eb);
> @@ -3401,30 +3420,30 @@ static int lock_extent_buffer_for_io(struct extent_buffer *eb,
>         return ret;
>  }
>
> -static void end_extent_buffer_writeback(struct extent_buffer *eb)
> +static void end_extent_buffer_writeback(struct extent_buffer_head *ebh)
>  {
> -       clear_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags);
> +       clear_bit(EXTENT_BUFFER_WRITEBACK, &ebh->bflags);
>         smp_mb__after_clear_bit();
> -       wake_up_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK);
> +       wake_up_bit(&ebh->bflags, EXTENT_BUFFER_WRITEBACK);
>  }
>
>  static void end_bio_extent_buffer_writepage(struct bio *bio, int err)
>  {
>         int uptodate = err == 0;
>         struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
> -       struct extent_buffer *eb;
> +       struct extent_buffer_head *ebh;
>         int done;
>
>         do {
>                 struct page *page = bvec->bv_page;
>
>                 bvec--;
> -               eb = (struct extent_buffer *)page->private;
> -               BUG_ON(!eb);
> -               done = atomic_dec_and_test(&eb->io_pages);
> +               ebh = (struct extent_buffer_head *)page->private;
> +               BUG_ON(!ebh);
> +               done = atomic_dec_and_test(&ebh->io_pages);
>
> -               if (!uptodate || test_bit(EXTENT_BUFFER_IOERR, &eb->bflags)) {
> -                       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
> +               if (!uptodate || test_bit(EXTENT_BUFFER_IOERR, &ebh->bflags)) {
> +                       set_bit(EXTENT_BUFFER_IOERR, &ebh->bflags);
>                         ClearPageUptodate(page);
>                         SetPageError(page);
>                 }
> @@ -3434,7 +3453,7 @@ static void end_bio_extent_buffer_writepage(struct bio *bio, int err)
>                 if (!done)
>                         continue;
>
> -               end_extent_buffer_writeback(eb);
> +               end_extent_buffer_writeback(ebh);
>         } while (bvec >= bio->bi_io_vec);
>
>         bio_put(bio);
> @@ -3447,15 +3466,15 @@ static int write_one_eb(struct extent_buffer *eb,
>                         struct extent_page_data *epd)
>  {
>         struct block_device *bdev = fs_info->fs_devices->latest_bdev;
> -       u64 offset = eb->start;
> +       u64 offset = eb->start & ~(PAGE_CACHE_SIZE - 1);
>         unsigned long i, num_pages;
>         unsigned long bio_flags = 0;
>         int rw = (epd->sync_io ? WRITE_SYNC : WRITE) | REQ_META;
>         int ret = 0;
>
> -       clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
> +       clear_bit(EXTENT_BUFFER_IOERR, &eb_head(eb)->bflags);
>         num_pages = num_extent_pages(eb->start, eb->len);
> -       atomic_set(&eb->io_pages, num_pages);
> +       atomic_set(&eb_head(eb)->io_pages, num_pages);
>         if (btrfs_header_owner(eb) == BTRFS_TREE_LOG_OBJECTID)
>                 bio_flags = EXTENT_BIO_TREE_LOG;
>
> @@ -3464,16 +3483,17 @@ static int write_one_eb(struct extent_buffer *eb,
>
>                 clear_page_dirty_for_io(p);
>                 set_page_writeback(p);
> -               ret = submit_extent_page(rw, eb->tree, p, offset >> 9,
> +               ret = submit_extent_page(rw, eb_head(eb)->tree, p, offset >> 9,
>                                          PAGE_CACHE_SIZE, 0, bdev, &epd->bio,
>                                          -1, end_bio_extent_buffer_writepage,
>                                          0, epd->bio_flags, bio_flags);
>                 epd->bio_flags = bio_flags;
>                 if (ret) {
> -                       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
> +                       set_bit(EXTENT_BUFFER_IOERR, &eb_head(eb)->bflags);
>                         SetPageError(p);
> -                       if (atomic_sub_and_test(num_pages - i, &eb->io_pages))
> -                               end_extent_buffer_writeback(eb);
> +                       if (atomic_sub_and_test(num_pages - i,
> +                                                       &eb_head(eb)->io_pages))
> +                               end_extent_buffer_writeback(eb_head(eb));
>                         ret = -EIO;
>                         break;
>                 }
> @@ -3497,7 +3517,8 @@ int btree_write_cache_pages(struct address_space *mapping,
>  {
>         struct extent_io_tree *tree = &BTRFS_I(mapping->host)->io_tree;
>         struct btrfs_fs_info *fs_info = BTRFS_I(mapping->host)->root->fs_info;
> -       struct extent_buffer *eb, *prev_eb = NULL;
> +       struct extent_buffer *eb;
> +       struct extent_buffer_head *ebh, *prev_ebh = NULL;
>         struct extent_page_data epd = {
>                 .bio = NULL,
>                 .tree = tree,
> @@ -3554,30 +3575,31 @@ retry:
>                                 continue;
>                         }
>
> -                       eb = (struct extent_buffer *)page->private;
> +                       ebh = (struct extent_buffer_head *)page->private;
>
>                         /*
>                          * Shouldn't happen and normally this would be a BUG_ON
>                          * but no sense in crashing the users box for something
>                          * we can survive anyway.
>                          */
> -                       if (WARN_ON(!eb)) {
> +                       if (WARN_ON(!ebh)) {
>                                 spin_unlock(&mapping->private_lock);
>                                 continue;
>                         }
>
> -                       if (eb == prev_eb) {
> +                       if (ebh == prev_ebh) {
>                                 spin_unlock(&mapping->private_lock);
>                                 continue;
>                         }
>
> -                       ret = atomic_inc_not_zero(&eb->refs);
> +                       ret = atomic_inc_not_zero(&ebh->refs);
>                         spin_unlock(&mapping->private_lock);
>                         if (!ret)
>                                 continue;
>
> -                       prev_eb = eb;
> -                       ret = lock_extent_buffer_for_io(eb, fs_info, &epd);
> +                       eb = &ebh->extent_buf[0];
> +                       prev_ebh = ebh;
> +                       ret = lock_extent_buffer_for_io(ebh, fs_info, &epd);
>                         if (!ret) {
>                                 free_extent_buffer(eb);
>                                 continue;
> @@ -4257,17 +4279,23 @@ out:
>         return ret;
>  }
>
> -static void __free_extent_buffer(struct extent_buffer *eb)
> +static void __free_extent_buffer(struct extent_buffer_head *ebh)
>  {
> -       btrfs_leak_debug_del(&eb->leak_list);
> -       kmem_cache_free(extent_buffer_cache, eb);
> +       btrfs_leak_debug_del(&ebh->leak_list);
> +       kmem_cache_free(extent_buffer_cache, ebh);
>  }
>
> -static int extent_buffer_under_io(struct extent_buffer *eb)
> +static int extent_buffer_under_io(struct extent_buffer_head *ebh)
>  {
> -       return (atomic_read(&eb->io_pages) ||
> -               test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) ||
> -               test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
> +       int i, dirty = 0;
> +       struct extent_buffer *eb;
> +
> +       for (i = 0; i < MAX_EXTENT_BUFFERS_PER_PAGE && !dirty; i++) {
> +               eb = &ebh->extent_buf[i];
> +               dirty = test_bit(EXTENT_BUFFER_DIRTY, &eb->ebflags);
> +       }
> +       return (dirty || atomic_read(&ebh->io_pages) ||
> +               test_bit(EXTENT_BUFFER_WRITEBACK, &ebh->bflags));
>  }
>
>  /*
> @@ -4279,9 +4307,10 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
>         unsigned long index;
>         unsigned long num_pages;
>         struct page *page;
> -       int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
> +       struct extent_buffer_head *ebh = eb_head(eb);
> +       int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &ebh->bflags);
>
> -       BUG_ON(extent_buffer_under_io(eb));
> +       BUG_ON(extent_buffer_under_io(ebh));
>
>         num_pages = num_extent_pages(eb->start, eb->len);
>         index = start_idx + num_pages;
> @@ -4301,8 +4330,8 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
>                          * this eb.
>                          */
>                         if (PagePrivate(page) &&
> -                           page->private == (unsigned long)eb) {
> -                               BUG_ON(test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
> +                           page->private == (unsigned long)ebh) {
> +                               BUG_ON(test_bit(EXTENT_BUFFER_DIRTY, &eb->ebflags));
>                                 BUG_ON(PageDirty(page));
>                                 BUG_ON(PageWriteback(page));
>                                 /*
> @@ -4330,23 +4359,14 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
>  static inline void btrfs_release_extent_buffer(struct extent_buffer *eb)
>  {
>         btrfs_release_extent_buffer_page(eb, 0);
> -       __free_extent_buffer(eb);
> +       __free_extent_buffer(eb_head(eb));
>  }
>
> -static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
> -                                                  u64 start,
> -                                                  unsigned long len,
> -                                                  gfp_t mask)
> +static void __init_extent_buffer(struct extent_buffer *eb, u64 start,
> +                               unsigned long len)
>  {
> -       struct extent_buffer *eb = NULL;
> -
> -       eb = kmem_cache_zalloc(extent_buffer_cache, mask);
> -       if (eb == NULL)
> -               return NULL;
>         eb->start = start;
>         eb->len = len;
> -       eb->tree = tree;
> -       eb->bflags = 0;
>         rwlock_init(&eb->lock);
>         atomic_set(&eb->write_locks, 0);
>         atomic_set(&eb->read_locks, 0);
> @@ -4357,12 +4377,27 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
>         eb->lock_nested = 0;
>         init_waitqueue_head(&eb->write_lock_wq);
>         init_waitqueue_head(&eb->read_lock_wq);
> +}
> +
> +static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
> +                                                  u64 start,
> +                                                  unsigned long len,
> +                                                  gfp_t mask)
> +{
> +       struct extent_buffer_head *ebh = NULL;
> +       struct extent_buffer *eb = NULL;
> +       int i, index = -1;
>
> -       btrfs_leak_debug_add(&eb->leak_list, &buffers);
> +       ebh = kmem_cache_zalloc(extent_buffer_cache, mask);
> +       if (ebh == NULL)
> +               return NULL;
> +       ebh->tree = tree;
> +       ebh->bflags = 0;
> +       btrfs_leak_debug_add(&ebh->leak_list, &buffers);
>
> -       spin_lock_init(&eb->refs_lock);
> -       atomic_set(&eb->refs, 1);
> -       atomic_set(&eb->io_pages, 0);
> +       spin_lock_init(&ebh->refs_lock);
> +       atomic_set(&ebh->refs, 1);
> +       atomic_set(&ebh->io_pages, 0);
>
>         /*
>          * Sanity checks, currently the maximum is 64k covered by 16x 4k pages
> @@ -4371,6 +4406,34 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
>                 > MAX_INLINE_EXTENT_BUFFER_SIZE);
>         BUG_ON(len > MAX_INLINE_EXTENT_BUFFER_SIZE);
>
> +       if (len < PAGE_CACHE_SIZE) {
> +               u64 st = start & ~(PAGE_CACHE_SIZE - 1);
> +               unsigned long totlen = 0;
> +               /*
> +                * Make sure we have enough room to fit extent buffers
> +                * that belong a single page in a single extent_buffer_head.
> +                * If this BUG_ON is tripped, then it means either the
> +                * blocksize, i.e len, is too small or we need to increase
> +                * MAX_EXTENT_BUFFERS_PER_PAGE.
> +                */
> +               BUG_ON(len * MAX_EXTENT_BUFFERS_PER_PAGE < PAGE_CACHE_SIZE);
> +
> +               for (i = 0; i < MAX_EXTENT_BUFFERS_PER_PAGE
> +                               && totlen < PAGE_CACHE_SIZE ;
> +                               i++, st += len, totlen += len) {
> +                       __init_extent_buffer(&ebh->extent_buf[i], st, len);
> +                       if (st == start) {
> +                               index = i;
> +                               eb = &ebh->extent_buf[i];
> +                       }
> +
> +               }
> +               BUG_ON(!eb);
> +       } else {
> +               eb = &ebh->extent_buf[0];
> +               __init_extent_buffer(eb, start, len);
> +       }
> +
>         return eb;
>  }
>
> @@ -4391,15 +4454,15 @@ struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
>                         btrfs_release_extent_buffer(new);
>                         return NULL;
>                 }
> -               attach_extent_buffer_page(new, p);
> +               attach_extent_buffer_page(eb_head(new), p);
>                 WARN_ON(PageDirty(p));
>                 SetPageUptodate(p);
> -               new->pages[i] = p;
> +               eb_head(new)->pages[i] = p;
>         }
>
>         copy_extent_buffer(new, src, 0, 0, src->len);
> -       set_bit(EXTENT_BUFFER_UPTODATE, &new->bflags);
> -       set_bit(EXTENT_BUFFER_DUMMY, &new->bflags);
> +       set_bit(EXTENT_BUFFER_UPTODATE, &eb_head(new)->bflags);
> +       set_bit(EXTENT_BUFFER_DUMMY, &eb_head(new)->bflags);
>
>         return new;
>  }
> @@ -4415,19 +4478,19 @@ struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len)
>                 return NULL;
>
>         for (i = 0; i < num_pages; i++) {
> -               eb->pages[i] = alloc_page(GFP_NOFS);
> -               if (!eb->pages[i])
> +               eb_head(eb)->pages[i] = alloc_page(GFP_NOFS);
> +               if (!eb_head(eb)->pages[i])
>                         goto err;
>         }
>         set_extent_buffer_uptodate(eb);
>         btrfs_set_header_nritems(eb, 0);
> -       set_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
> +       set_bit(EXTENT_BUFFER_DUMMY, &eb_head(eb)->bflags);
>
>         return eb;
>  err:
>         for (; i > 0; i--)
> -               __free_page(eb->pages[i - 1]);
> -       __free_extent_buffer(eb);
> +               __free_page(eb_head(eb)->pages[i - 1]);
> +       __free_extent_buffer(eb_head(eb));
>         return NULL;
>  }
>
> @@ -4454,14 +4517,15 @@ static void check_buffer_tree_ref(struct extent_buffer *eb)
>          * So bump the ref count first, then set the bit.  If someone
>          * beat us to it, drop the ref we added.
>          */
> -       refs = atomic_read(&eb->refs);
> -       if (refs >= 2 && test_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags))
> +       refs = atomic_read(&eb_head(eb)->refs);
> +       if (refs >= 2 && test_bit(EXTENT_BUFFER_TREE_REF,
> +                                               &eb_head(eb)->bflags))
>                 return;
>
> -       spin_lock(&eb->refs_lock);
> -       if (!test_and_set_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags))
> -               atomic_inc(&eb->refs);
> -       spin_unlock(&eb->refs_lock);
> +       spin_lock(&eb_head(eb)->refs_lock);
> +       if (!test_and_set_bit(EXTENT_BUFFER_TREE_REF, &eb_head(eb)->bflags))
> +               atomic_inc(&eb_head(eb)->refs);
> +       spin_unlock(&eb_head(eb)->refs_lock);
>  }
>
>  static void mark_extent_buffer_accessed(struct extent_buffer *eb)
> @@ -4481,13 +4545,22 @@ struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
>                                                         u64 start)
>  {
>         struct extent_buffer *eb;
> +       struct extent_buffer_head *ebh;
> +       int i = 0;
>
>         rcu_read_lock();
> -       eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
> -       if (eb && atomic_inc_not_zero(&eb->refs)) {
> +       ebh = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
> +       if (ebh && atomic_inc_not_zero(&ebh->refs)) {
>                 rcu_read_unlock();
> -               mark_extent_buffer_accessed(eb);
> -               return eb;
> +
> +               do {
> +                       eb = &ebh->extent_buf[i++];
> +                       if (eb->start == start) {
> +                               mark_extent_buffer_accessed(eb);
> +                               return eb;
> +                       }
> +               } while (i < MAX_EXTENT_BUFFERS_PER_PAGE);
> +               BUG();
>         }
>         rcu_read_unlock();
>
> @@ -4500,8 +4573,8 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
>         unsigned long num_pages = num_extent_pages(start, len);
>         unsigned long i;
>         unsigned long index = start >> PAGE_CACHE_SHIFT;
> -       struct extent_buffer *eb;
> -       struct extent_buffer *exists = NULL;
> +       struct extent_buffer *eb, *old_eb = NULL;
> +       struct extent_buffer_head *exists = NULL;
>         struct page *p;
>         struct address_space *mapping = tree->mapping;
>         int uptodate = 1;
> @@ -4530,13 +4603,20 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
>                          * we can just return that one, else we know we can just
>                          * overwrite page->private.
>                          */
> -                       exists = (struct extent_buffer *)p->private;
> +                       exists = (struct extent_buffer_head *)p->private;
>                         if (atomic_inc_not_zero(&exists->refs)) {
> +                               int j = 0;
>                                 spin_unlock(&mapping->private_lock);
>                                 unlock_page(p);
>                                 page_cache_release(p);
> -                               mark_extent_buffer_accessed(exists);
> -                               goto free_eb;
> +                               do {
> +                                       old_eb = &exists->extent_buf[j++];
> +                                       if (old_eb->start == start) {
> +                                               mark_extent_buffer_accessed(old_eb);
> +                                               goto free_eb;
> +                                       }
> +                               } while (j < MAX_EXTENT_BUFFERS_PER_PAGE);
> +                               BUG();
>                         }
>
>                         /*
> @@ -4547,11 +4627,11 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
>                         WARN_ON(PageDirty(p));
>                         page_cache_release(p);
>                 }
> -               attach_extent_buffer_page(eb, p);
> +               attach_extent_buffer_page(eb_head(eb), p);
>                 spin_unlock(&mapping->private_lock);
>                 WARN_ON(PageDirty(p));
>                 mark_page_accessed(p);
> -               eb->pages[i] = p;
> +               eb_head(eb)->pages[i] = p;
>                 if (!PageUptodate(p))
>                         uptodate = 0;
>
> @@ -4561,19 +4641,20 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
>                  */
>         }
>         if (uptodate)
> -               set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
> +               set_bit(EXTENT_BUFFER_UPTODATE, &eb_head(eb)->bflags);
>  again:
>         ret = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM);
>         if (ret)
>                 goto free_eb;
>
>         spin_lock(&tree->buffer_lock);
> -       ret = radix_tree_insert(&tree->buffer, start >> PAGE_CACHE_SHIFT, eb);
> +       ret = radix_tree_insert(&tree->buffer,
> +                               start >> PAGE_CACHE_SHIFT, eb_head(eb));
>         spin_unlock(&tree->buffer_lock);
>         radix_tree_preload_end();
>         if (ret == -EEXIST) {
> -               exists = find_extent_buffer(tree, start);
> -               if (exists)
> +               old_eb = find_extent_buffer(tree, start);
> +               if (old_eb)
>                         goto free_eb;
>                 else
>                         goto again;
> @@ -4590,58 +4671,58 @@ again:
>          * after the extent buffer is in the radix tree so
>          * it doesn't get lost
>          */
> -       SetPageChecked(eb->pages[0]);
> +       SetPageChecked(eb_head(eb)->pages[0]);
>         for (i = 1; i < num_pages; i++) {
>                 p = extent_buffer_page(eb, i);
>                 ClearPageChecked(p);
>                 unlock_page(p);
>         }
> -       unlock_page(eb->pages[0]);
> +       unlock_page(eb_head(eb)->pages[0]);
>         return eb;
>
>  free_eb:
>         for (i = 0; i < num_pages; i++) {
> -               if (eb->pages[i])
> -                       unlock_page(eb->pages[i]);
> +               if (eb_head(eb)->pages[i])
> +                       unlock_page(eb_head(eb)->pages[i]);
>         }
>
> -       WARN_ON(!atomic_dec_and_test(&eb->refs));
> +       WARN_ON(!atomic_dec_and_test(&eb_head(eb)->refs));
>         btrfs_release_extent_buffer(eb);
> -       return exists;
> +       return old_eb;
>  }
>
>  static inline void btrfs_release_extent_buffer_rcu(struct rcu_head *head)
>  {
> -       struct extent_buffer *eb =
> -                       container_of(head, struct extent_buffer, rcu_head);
> +       struct extent_buffer_head *ebh =
> +                       container_of(head, struct extent_buffer_head, rcu_head);
>
> -       __free_extent_buffer(eb);
> +       __free_extent_buffer(ebh);
>  }
>
>  /* Expects to have eb->eb_lock already held */
> -static int release_extent_buffer(struct extent_buffer *eb)
> +static int release_extent_buffer(struct extent_buffer_head *ebh)
>  {
> -       WARN_ON(atomic_read(&eb->refs) == 0);
> -       if (atomic_dec_and_test(&eb->refs)) {
> -               if (test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags)) {
> -                       spin_unlock(&eb->refs_lock);
> +       WARN_ON(atomic_read(&ebh->refs) == 0);
> +       if (atomic_dec_and_test(&ebh->refs)) {
> +               if (test_bit(EXTENT_BUFFER_DUMMY, &ebh->bflags)) {
> +                       spin_unlock(&ebh->refs_lock);
>                 } else {
> -                       struct extent_io_tree *tree = eb->tree;
> +                       struct extent_io_tree *tree = ebh->tree;
>
> -                       spin_unlock(&eb->refs_lock);
> +                       spin_unlock(&ebh->refs_lock);
>
>                         spin_lock(&tree->buffer_lock);
>                         radix_tree_delete(&tree->buffer,
> -                                         eb->start >> PAGE_CACHE_SHIFT);
> +                               ebh->extent_buf[0].start >> PAGE_CACHE_SHIFT);
>                         spin_unlock(&tree->buffer_lock);
>                 }
>
>                 /* Should be safe to release our pages at this point */
> -               btrfs_release_extent_buffer_page(eb, 0);
> -               call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
> +               btrfs_release_extent_buffer_page(&ebh->extent_buf[0], 0);
> +               call_rcu(&ebh->rcu_head, btrfs_release_extent_buffer_rcu);
>                 return 1;
>         }
> -       spin_unlock(&eb->refs_lock);
> +       spin_unlock(&ebh->refs_lock);
>
>         return 0;
>  }
> @@ -4650,48 +4731,52 @@ void free_extent_buffer(struct extent_buffer *eb)
>  {
>         int refs;
>         int old;
> +       struct extent_buffer_head *ebh;
>         if (!eb)
>                 return;
>
> +       ebh = eb_head(eb);
>         while (1) {
> -               refs = atomic_read(&eb->refs);
> +               refs = atomic_read(&ebh->refs);
>                 if (refs <= 3)
>                         break;
> -               old = atomic_cmpxchg(&eb->refs, refs, refs - 1);
> +               old = atomic_cmpxchg(&ebh->refs, refs, refs - 1);
>                 if (old == refs)
>                         return;
>         }
>
> -       spin_lock(&eb->refs_lock);
> -       if (atomic_read(&eb->refs) == 2 &&
> -           test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags))
> -               atomic_dec(&eb->refs);
> +       spin_lock(&ebh->refs_lock);
> +       if (atomic_read(&ebh->refs) == 2 &&
> +           test_bit(EXTENT_BUFFER_DUMMY, &ebh->bflags))
> +               atomic_dec(&ebh->refs);
>
> -       if (atomic_read(&eb->refs) == 2 &&
> -           test_bit(EXTENT_BUFFER_STALE, &eb->bflags) &&
> -           !extent_buffer_under_io(eb) &&
> -           test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags))
> -               atomic_dec(&eb->refs);
> +       if (atomic_read(&ebh->refs) == 2 &&
> +           test_bit(EXTENT_BUFFER_STALE, &ebh->bflags) &&
> +           !extent_buffer_under_io(ebh) &&
> +           test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &ebh->bflags))
> +               atomic_dec(&ebh->refs);
>
>         /*
>          * I know this is terrible, but it's temporary until we stop tracking
>          * the uptodate bits and such for the extent buffers.
>          */
> -       release_extent_buffer(eb);
> +       release_extent_buffer(ebh);
>  }
>
>  void free_extent_buffer_stale(struct extent_buffer *eb)
>  {
> +       struct extent_buffer_head *ebh;
>         if (!eb)
>                 return;
>
> -       spin_lock(&eb->refs_lock);
> -       set_bit(EXTENT_BUFFER_STALE, &eb->bflags);
> +       ebh = eb_head(eb);
> +       spin_lock(&ebh->refs_lock);
> +       set_bit(EXTENT_BUFFER_STALE, &ebh->bflags);
>
> -       if (atomic_read(&eb->refs) == 2 && !extent_buffer_under_io(eb) &&
> -           test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags))
> -               atomic_dec(&eb->refs);
> -       release_extent_buffer(eb);
> +       if (atomic_read(&ebh->refs) == 2 && !extent_buffer_under_io(ebh) &&
> +           test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &ebh->bflags))
> +               atomic_dec(&ebh->refs);
> +       release_extent_buffer(ebh);
>  }
>
>  void clear_extent_buffer_dirty(struct extent_buffer *eb)
> @@ -4721,7 +4806,7 @@ void clear_extent_buffer_dirty(struct extent_buffer *eb)
>                 ClearPageError(page);
>                 unlock_page(page);
>         }
> -       WARN_ON(atomic_read(&eb->refs) == 0);
> +       WARN_ON(atomic_read(&eb_head(eb)->refs) == 0);
>  }
>
>  int set_extent_buffer_dirty(struct extent_buffer *eb)
> @@ -4732,11 +4817,11 @@ int set_extent_buffer_dirty(struct extent_buffer *eb)
>
>         check_buffer_tree_ref(eb);
>
> -       was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
> +       was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->ebflags);
>
>         num_pages = num_extent_pages(eb->start, eb->len);
> -       WARN_ON(atomic_read(&eb->refs) == 0);
> -       WARN_ON(!test_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags));
> +       WARN_ON(atomic_read(&eb_head(eb)->refs) == 0);
> +       WARN_ON(!test_bit(EXTENT_BUFFER_TREE_REF, &eb_head(eb)->bflags));
>
>         for (i = 0; i < num_pages; i++)
>                 set_page_dirty(extent_buffer_page(eb, i));
> @@ -4749,7 +4834,9 @@ int clear_extent_buffer_uptodate(struct extent_buffer *eb)
>         struct page *page;
>         unsigned long num_pages;
>
> -       clear_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
> +       if (!eb || !eb_head(eb))
> +               return 0;
> +       clear_bit(EXTENT_BUFFER_UPTODATE, &eb_head(eb)->bflags);
>         num_pages = num_extent_pages(eb->start, eb->len);
>         for (i = 0; i < num_pages; i++) {
>                 page = extent_buffer_page(eb, i);
> @@ -4765,7 +4852,7 @@ int set_extent_buffer_uptodate(struct extent_buffer *eb)
>         struct page *page;
>         unsigned long num_pages;
>
> -       set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
> +       set_bit(EXTENT_BUFFER_UPTODATE, &eb_head(eb)->bflags);
>         num_pages = num_extent_pages(eb->start, eb->len);
>         for (i = 0; i < num_pages; i++) {
>                 page = extent_buffer_page(eb, i);
> @@ -4776,7 +4863,7 @@ int set_extent_buffer_uptodate(struct extent_buffer *eb)
>
>  int extent_buffer_uptodate(struct extent_buffer *eb)
>  {
> -       return test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
> +       return test_bit(EXTENT_BUFFER_UPTODATE, &eb_head(eb)->bflags);
>  }
>
>  int read_extent_buffer_pages(struct extent_io_tree *tree,
> @@ -4794,8 +4881,9 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
>         unsigned long num_reads = 0;
>         struct bio *bio = NULL;
>         unsigned long bio_flags = 0;
> +       struct extent_buffer_head *ebh = eb_head(eb);
>
> -       if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
> +       if (test_bit(EXTENT_BUFFER_UPTODATE, &ebh->bflags))
>                 return 0;
>
>         if (start) {
> @@ -4806,6 +4894,7 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
>                 start_i = 0;
>         }
>
> +recheck:
>         num_pages = num_extent_pages(eb->start, eb->len);
>         for (i = start_i; i < num_pages; i++) {
>                 page = extent_buffer_page(eb, i);
> @@ -4823,13 +4912,26 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
>         }
>         if (all_uptodate) {
>                 if (start_i == 0)
> -                       set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
> +                       set_bit(EXTENT_BUFFER_UPTODATE, &ebh->bflags);
>                 goto unlock_exit;
>         }
>
> -       clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
> -       eb->read_mirror = 0;
> -       atomic_set(&eb->io_pages, num_reads);
> +       if (eb_head(eb)->io_eb) {
> +               all_uptodate = 1;
> +               i = start_i;
> +               while (locked_pages > 0) {
> +                       page = extent_buffer_page(eb, i);
> +                       i++;
> +                       unlock_page(page);
> +                       locked_pages--;
> +               }
> +               goto recheck;
> +       }
> +       BUG_ON(eb_head(eb)->io_eb);
> +       eb_head(eb)->io_eb = eb;
> +       clear_bit(EXTENT_BUFFER_IOERR, &ebh->bflags);
> +       ebh->read_mirror = 0;
> +       atomic_set(&ebh->io_pages, num_reads);
>         for (i = start_i; i < num_pages; i++) {
>                 page = extent_buffer_page(eb, i);
>                 if (!PageUptodate(page)) {
> @@ -5196,7 +5298,7 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
>
>  int try_release_extent_buffer(struct page *page)
>  {
> -       struct extent_buffer *eb;
> +       struct extent_buffer_head *ebh;
>
>         /*
>          * We need to make sure noboody is attaching this page to an eb right
> @@ -5208,17 +5310,17 @@ int try_release_extent_buffer(struct page *page)
>                 return 1;
>         }
>
> -       eb = (struct extent_buffer *)page->private;
> -       BUG_ON(!eb);
> +       ebh = (struct extent_buffer_head *)page->private;
> +       BUG_ON(!ebh);
>
>         /*
>          * This is a little awful but should be ok, we need to make sure that
>          * the eb doesn't disappear out from under us while we're looking at
>          * this page.
>          */
> -       spin_lock(&eb->refs_lock);
> -       if (atomic_read(&eb->refs) != 1 || extent_buffer_under_io(eb)) {
> -               spin_unlock(&eb->refs_lock);
> +       spin_lock(&ebh->refs_lock);
> +       if (atomic_read(&ebh->refs) != 1 || extent_buffer_under_io(ebh)) {
> +               spin_unlock(&ebh->refs_lock);
>                 spin_unlock(&page->mapping->private_lock);
>                 return 0;
>         }
> @@ -5228,10 +5330,11 @@ int try_release_extent_buffer(struct page *page)
>          * If tree ref isn't set then we know the ref on this eb is a real ref,
>          * so just return, this page will likely be freed soon anyway.
>          */
> -       if (!test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) {
> -               spin_unlock(&eb->refs_lock);
> +       if (!test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &ebh->bflags)) {
> +               spin_unlock(&ebh->refs_lock);
>                 return 0;
>         }
>
> -       return release_extent_buffer(eb);
> +       return release_extent_buffer(ebh);
>  }
> +
> diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
> index 19620c5..b56de28 100644
> --- a/fs/btrfs/extent_io.h
> +++ b/fs/btrfs/extent_io.h
> @@ -124,19 +124,12 @@ struct extent_state {
>
>  #define INLINE_EXTENT_BUFFER_PAGES 16
>  #define MAX_INLINE_EXTENT_BUFFER_SIZE (INLINE_EXTENT_BUFFER_PAGES * PAGE_CACHE_SIZE)
> +#define MAX_EXTENT_BUFFERS_PER_PAGE 16
> +
>  struct extent_buffer {
>         u64 start;
>         unsigned long len;
> -       unsigned long map_start;
> -       unsigned long map_len;
> -       unsigned long bflags;
> -       struct extent_io_tree *tree;
> -       spinlock_t refs_lock;
> -       atomic_t refs;
> -       atomic_t io_pages;
> -       int read_mirror;
> -       struct rcu_head rcu_head;
> -       pid_t lock_owner;
> +       unsigned long ebflags;
>
>         /* count of read lock holders on the extent buffer */
>         atomic_t write_locks;
> @@ -147,6 +140,8 @@ struct extent_buffer {
>         atomic_t spinning_writers;
>         int lock_nested;
>
> +       pid_t lock_owner;
> +
>         /* protects write locks */
>         rwlock_t lock;
>
> @@ -160,7 +155,21 @@ struct extent_buffer {
>          */
>         wait_queue_head_t read_lock_wq;
>         wait_queue_head_t lock_wq;
> +};
> +
> +struct extent_buffer_head {
> +       unsigned long bflags;
> +       struct extent_io_tree *tree;
> +       spinlock_t refs_lock;
> +       atomic_t refs;
> +       atomic_t io_pages;
> +       int read_mirror;
> +       struct rcu_head rcu_head;
> +
>         struct page *pages[INLINE_EXTENT_BUFFER_PAGES];
> +
> +       struct extent_buffer extent_buf[MAX_EXTENT_BUFFERS_PER_PAGE];
> +       struct extent_buffer *io_eb; /* eb that submitted the current I/O */
>  #ifdef CONFIG_BTRFS_DEBUG
>         struct list_head leak_list;
>  #endif
> @@ -177,6 +186,24 @@ static inline int extent_compress_type(unsigned long bio_flags)
>         return bio_flags >> EXTENT_BIO_FLAG_SHIFT;
>  }
>
> +/*
> + * return the extent_buffer_head that contains the extent buffer provided.
> + */
> +static inline struct extent_buffer_head *eb_head(struct extent_buffer *eb)
> +{
> +       int start, index;
> +       struct extent_buffer_head *ebh;
> +       struct extent_buffer *eb_base;
> +
> +       BUG_ON(!eb);
> +       start = eb->start & (PAGE_CACHE_SIZE - 1);
> +       index = start >> (ffs(eb->len) - 1);
> +       eb_base = eb - index;
> +       ebh = (struct extent_buffer_head *)
> +               ((char *) eb_base - offsetof(struct extent_buffer_head, extent_buf));
> +       return ebh;
> +
> +}
>  struct extent_map_tree;
>
>  typedef struct extent_map *(get_extent_t)(struct inode *inode,
> @@ -288,15 +315,15 @@ static inline unsigned long num_extent_pages(u64 start, u64 len)
>                 (start >> PAGE_CACHE_SHIFT);
>  }
>
> -static inline struct page *extent_buffer_page(struct extent_buffer *eb,
> -                                             unsigned long i)
> +static inline struct page *extent_buffer_page(
> +                       struct extent_buffer *eb, unsigned long i)
>  {
> -       return eb->pages[i];
> +       return eb_head(eb)->pages[i];
>  }
>
>  static inline void extent_buffer_get(struct extent_buffer *eb)
>  {
> -       atomic_inc(&eb->refs);
> +       atomic_inc(&eb_head(eb)->refs);
>  }
>
>  int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
> index 92303f4..37b2698 100644
> --- a/fs/btrfs/volumes.c
> +++ b/fs/btrfs/volumes.c
> @@ -5921,7 +5921,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
>          * to silence the warning eg. on PowerPC 64.
>          */
>         if (PAGE_CACHE_SIZE > BTRFS_SUPER_INFO_SIZE)
> -               SetPageUptodate(sb->pages[0]);
> +               SetPageUptodate(eb_head(sb)->pages[0]);
>
>         write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE);
>         array_size = btrfs_super_sys_array_size(super_copy);
> diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h
> index 4832d75..ceb194f 100644
> --- a/include/trace/events/btrfs.h
> +++ b/include/trace/events/btrfs.h
> @@ -694,7 +694,7 @@ TRACE_EVENT(btrfs_cow_block,
>         TP_fast_assign(
>                 __entry->root_objectid  = root->root_key.objectid;
>                 __entry->buf_start      = buf->start;
> -               __entry->refs           = atomic_read(&buf->refs);
> +               __entry->refs           = atomic_read(&eb_head(buf)->refs);
>                 __entry->cow_start      = cow->start;
>                 __entry->buf_level      = btrfs_header_level(buf);
>                 __entry->cow_level      = btrfs_header_level(cow);
> --
> 1.7.12.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

  reply	other threads:[~2013-12-16 12:32 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-12-11 23:38 [PATCH 0/7] Patches to support subpagesize blocksize Chandra Seetharaman
2013-12-11 23:38 ` [PATCH 1/7] btrfs: subpagesize-blocksize: Define extent_buffer_head Chandra Seetharaman
2013-12-16 12:32   ` saeed bishara [this message]
2013-12-16 16:17     ` Chandra Seetharaman
2013-12-17 15:35       ` David Sterba
2013-12-11 23:38 ` [PATCH 2/7] btrfs: subpagesize-blocksize: Use a global alignment for size Chandra Seetharaman
2013-12-16 12:33   ` saeed bishara
2013-12-16 14:48     ` David Sterba
2013-12-16 16:18       ` Chandra Seetharaman
2013-12-11 23:38 ` [PATCH 3/7] btrfs: subpagesize-blocksize: Handle small extent maps properly Chandra Seetharaman
2013-12-11 23:38 ` [PATCH 4/7] btrfs: subpagesize-blocksize: Handle iosize properly in submit_extent_page() Chandra Seetharaman
2013-12-11 23:38 ` [PATCH 5/7] btrfs: subpagesize-blocksize: handle checksum calculations properly Chandra Seetharaman
2013-12-11 23:38 ` [PATCH 6/7] btrfs: subpagesize-blocksize: Handle relocation clusters appropriately Chandra Seetharaman
2013-12-11 23:38 ` [PATCH 7/7] btrfs: subpagesize-blocksize: Allow mounting filesystems where sectorsize != PAGE_SIZE Chandra Seetharaman
2013-12-13  1:07   ` David Sterba
2013-12-16 12:50     ` saeed bishara
2013-12-12 20:40 ` [PATCH 0/7] Patches to support subpagesize blocksize Josef Bacik
2013-12-13  1:17 ` David Sterba
2013-12-13 15:17   ` Chandra Seetharaman
2013-12-13 15:58     ` David Sterba
2013-12-13 18:39 ` Josef Bacik
2013-12-13 22:09   ` Chandra Seetharaman
2014-01-08 20:06   ` Chandra Seetharaman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAMAG_ecWBoFJ_vap2iFBvvW99iHVVTuMdVvaNycaFLzqt5fNUg@mail.gmail.com \
    --to=saeed.bishara@gmail.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=sekharan@us.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.