static void csum_tree_block(struct extent_buffer *buf, u8 *result)
 {
        struct btrfs_fs_info *fs_info = buf->fs_info;
-       const int num_pages = num_extent_pages(buf);
-       const int first_page_part = min_t(u32, PAGE_SIZE, fs_info->nodesize);
+       int num_pages;
+       u32 first_page_part;
        SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
        char *kaddr;
        int i;
 
        shash->tfm = fs_info->csum_shash;
        crypto_shash_init(shash);
-       kaddr = page_address(buf->pages[0]) + offset_in_page(buf->start);
+
+       if (buf->addr) {
+               /* Pages are contiguous, handle them as a big one. */
+               kaddr = buf->addr;
+               first_page_part = fs_info->nodesize;
+               num_pages = 1;
+       } else {
+               kaddr = page_address(buf->pages[0]);
+               first_page_part = min_t(u32, PAGE_SIZE, fs_info->nodesize);
+               num_pages = num_extent_pages(buf);
+       }
+
        crypto_shash_update(shash, kaddr + BTRFS_CSUM_SIZE,
                            first_page_part - BTRFS_CSUM_SIZE);
 
 
        struct address_space *mapping = fs_info->btree_inode->i_mapping;
        struct btrfs_subpage *prealloc = NULL;
        u64 lockdep_owner = owner_root;
+       bool page_contig = true;
        int uptodate = 1;
        int ret;
 
 
                WARN_ON(btrfs_page_test_dirty(fs_info, p, eb->start, eb->len));
                eb->pages[i] = p;
+
+               /*
+                * Check if the current page is physically contiguous with previous eb
+                * page.
+                */
+               if (i && eb->pages[i - 1] + 1 != p)
+                       page_contig = false;
+
                if (!btrfs_page_test_uptodate(fs_info, p, eb->start, eb->len))
                        uptodate = 0;
 
        }
        if (uptodate)
                set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
+       /* All pages are physically contiguous, can skip cross page handling. */
+       if (page_contig)
+               eb->addr = page_address(eb->pages[0]) + offset_in_page(eb->start);
 again:
        ret = radix_tree_preload(GFP_NOFS);
        if (ret) {
                return;
        }
 
+       if (eb->addr) {
+               memcpy(dstv, eb->addr + start, len);
+               return;
+       }
+
        offset = get_eb_offset_in_page(eb, start);
 
        while (len > 0) {
        WARN_ON(start > eb->len);
        WARN_ON(start + len > eb->start + eb->len);
 
+       if (eb->addr) {
+               if (copy_to_user_nofault(dstv, eb->addr + start, len))
+                       ret = -EFAULT;
+               return ret;
+       }
+
        offset = get_eb_offset_in_page(eb, start);
 
        while (len > 0) {
        if (check_eb_range(eb, start, len))
                return -EINVAL;
 
+       if (eb->addr)
+               return memcmp(ptrv, eb->addr + start, len);
+
        offset = get_eb_offset_in_page(eb, start);
 
        while (len > 0) {
        if (check_eb_range(eb, start, len))
                return;
 
+       if (eb->addr) {
+               if (use_memmove)
+                       memmove(eb->addr + start, srcv, len);
+               else
+                       memcpy(eb->addr + start, srcv, len);
+               return;
+       }
+
        offset = get_eb_offset_in_page(eb, start);
 
        while (len > 0) {
 {
        unsigned long cur = start;
 
+       if (eb->addr) {
+               memset(eb->addr + start, c, len);
+               return;
+       }
+
        while (cur < start + len) {
                unsigned long index = get_eb_page_index(cur);
                unsigned int offset = get_eb_offset_in_page(eb, cur);
            check_eb_range(dst, src_offset, len))
                return;
 
+       if (dst->addr) {
+               const bool use_memmove = areas_overlap(src_offset, dst_offset, len);
+
+               if (use_memmove)
+                       memmove(dst->addr + dst_offset, dst->addr + src_offset, len);
+               else
+                       memcpy(dst->addr + dst_offset, dst->addr + src_offset, len);
+               return;
+       }
+
        while (cur_off < len) {
                unsigned long cur_src = cur_off + src_offset;
                unsigned long pg_index = get_eb_page_index(cur_src);
                return;
        }
 
+       if (dst->addr) {
+               memmove(dst->addr + dst_offset, dst->addr + src_offset, len);
+               return;
+       }
+
        while (len > 0) {
                unsigned long src_i;
                size_t cur;