int level, int slot);
 static void tree_mod_log_free_eb(struct btrfs_fs_info *fs_info,
                                 struct extent_buffer *eb);
-static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
 
 struct btrfs_path *btrfs_alloc_path(void)
 {
  * This may release the path, and so you may lose any locks held at the
  * time you call it.
  */
-static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
+int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
 {
        struct btrfs_key key;
        struct btrfs_disk_key found_key;
 
 #define BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF   (1ULL << 6)
 #define BTRFS_FEATURE_INCOMPAT_RAID56          (1ULL << 7)
 #define BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA (1ULL << 8)
+#define BTRFS_FEATURE_INCOMPAT_NO_HOLES                (1ULL << 9)
 
 #define BTRFS_FEATURE_COMPAT_SUPP              0ULL
 #define BTRFS_FEATURE_COMPAT_RO_SUPP           0ULL
         BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO |          \
         BTRFS_FEATURE_INCOMPAT_RAID56 |                \
         BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF |         \
-        BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)
+        BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA |       \
+        BTRFS_FEATURE_INCOMPAT_NO_HOLES)
 
 /*
  * A leaf is full of items. offset and size tell us where to find
 }
 
 int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path);
+int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
 int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path,
                        u64 time_seq);
 static inline int btrfs_next_old_item(struct btrfs_root *root,
 
        struct btrfs_key key;
        int ret;
 
+       if (btrfs_fs_incompat(root->fs_info, NO_HOLES))
+               goto out;
+
        key.objectid = btrfs_ino(inode);
        key.type = BTRFS_EXTENT_DATA_KEY;
        key.offset = offset;
 
-
        ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
        if (ret < 0)
                return ret;
        u64 drop_end;
        int ret = 0;
        int err = 0;
+       int rsv_count;
        bool same_page = ((offset >> PAGE_CACHE_SHIFT) ==
                          ((offset + len - 1) >> PAGE_CACHE_SHIFT));
+       bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
 
        ret = btrfs_wait_ordered_range(inode, offset, len);
        if (ret)
        /*
         * 1 - update the inode
         * 1 - removing the extents in the range
-        * 1 - adding the hole extent
+        * 1 - adding the hole extent if no_holes isn't set
         */
-       trans = btrfs_start_transaction(root, 3);
+       rsv_count = no_holes ? 2 : 3;
+       trans = btrfs_start_transaction(root, rsv_count);
        if (IS_ERR(trans)) {
                err = PTR_ERR(trans);
                goto out_free;
                btrfs_end_transaction(trans, root);
                btrfs_btree_balance_dirty(root);
 
-               trans = btrfs_start_transaction(root, 3);
+               trans = btrfs_start_transaction(root, rsv_count);
                if (IS_ERR(trans)) {
                        ret = PTR_ERR(trans);
                        trans = NULL;
 
        return ret;
 }
 
+static int maybe_insert_hole(struct btrfs_root *root, struct inode *inode,
+                            u64 offset, u64 len)
+{
+       struct btrfs_trans_handle *trans;
+       int ret;
+
+       /*
+        * Still need to make sure the inode looks like it's been updated so
+        * that any holes get logged if we fsync.
+        */
+       if (btrfs_fs_incompat(root->fs_info, NO_HOLES)) {
+               BTRFS_I(inode)->last_trans = root->fs_info->generation;
+               BTRFS_I(inode)->last_sub_trans = root->log_transid;
+               BTRFS_I(inode)->last_log_commit = root->last_log_commit;
+               return 0;
+       }
+
+       /*
+        * 1 - for the one we're dropping
+        * 1 - for the one we're adding
+        * 1 - for updating the inode.
+        */
+       trans = btrfs_start_transaction(root, 3);
+       if (IS_ERR(trans))
+               return PTR_ERR(trans);
+
+       ret = btrfs_drop_extents(trans, root, inode, offset, offset + len, 1);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               btrfs_end_transaction(trans, root);
+               return ret;
+       }
+
+       ret = btrfs_insert_file_extent(trans, root, btrfs_ino(inode), offset,
+                                      0, 0, len, 0, len, 0, 0, 0);
+       if (ret)
+               btrfs_abort_transaction(trans, root, ret);
+       else
+               btrfs_update_inode(trans, root, inode);
+       btrfs_end_transaction(trans, root);
+       return ret;
+}
+
 /*
  * This function puts in dummy file extents for the area we're creating a hole
  * for.  So if we are truncating this file to a larger size we need to insert
  */
 int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
 {
-       struct btrfs_trans_handle *trans;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct extent_map *em = NULL;
                        struct extent_map *hole_em;
                        hole_size = last_byte - cur_offset;
 
-                       trans = btrfs_start_transaction(root, 3);
-                       if (IS_ERR(trans)) {
-                               err = PTR_ERR(trans);
-                               break;
-                       }
-
-                       err = btrfs_drop_extents(trans, root, inode,
-                                                cur_offset,
-                                                cur_offset + hole_size, 1);
-                       if (err) {
-                               btrfs_abort_transaction(trans, root, err);
-                               btrfs_end_transaction(trans, root);
-                               break;
-                       }
-
-                       err = btrfs_insert_file_extent(trans, root,
-                                       btrfs_ino(inode), cur_offset, 0,
-                                       0, hole_size, 0, hole_size,
-                                       0, 0, 0);
-                       if (err) {
-                               btrfs_abort_transaction(trans, root, err);
-                               btrfs_end_transaction(trans, root);
+                       err = maybe_insert_hole(root, inode, cur_offset,
+                                               hole_size);
+                       if (err)
                                break;
-                       }
-
                        btrfs_drop_extent_cache(inode, cur_offset,
                                                cur_offset + hole_size - 1, 0);
                        hole_em = alloc_extent_map();
                        hole_em->ram_bytes = hole_size;
                        hole_em->bdev = root->fs_info->fs_devices->latest_bdev;
                        hole_em->compress_type = BTRFS_COMPRESS_NONE;
-                       hole_em->generation = trans->transid;
+                       hole_em->generation = root->fs_info->generation;
 
                        while (1) {
                                write_lock(&em_tree->lock);
                                                        hole_size - 1, 0);
                        }
                        free_extent_map(hole_em);
-next:
-                       btrfs_update_inode(trans, root, inode);
-                       btrfs_end_transaction(trans, root);
                }
+next:
                free_extent_map(em);
                em = NULL;
                cur_offset = last_byte;
                if (cur_offset >= block_end)
                        break;
        }
-
        free_extent_map(em);
        unlock_extent_cached(io_tree, hole_start, block_end - 1, &cached_state,
                             GFP_NOFS);
 
        int cur_inode_deleted;
        u64 cur_inode_size;
        u64 cur_inode_mode;
+       u64 cur_inode_last_extent;
 
        u64 send_progress;
 
        char name[];
 };
 
+static int need_send_hole(struct send_ctx *sctx)
+{
+       return (sctx->parent_root && !sctx->cur_inode_new &&
+               !sctx->cur_inode_new_gen && !sctx->cur_inode_deleted &&
+               S_ISREG(sctx->cur_inode_mode));
+}
+
 static void fs_path_reset(struct fs_path *p)
 {
        if (p->reversed) {
        return ret;
 }
 
+static int send_hole(struct send_ctx *sctx, u64 end)
+{
+       struct fs_path *p = NULL;
+       u64 offset = sctx->cur_inode_last_extent;
+       u64 len;
+       int ret = 0;
+
+       p = fs_path_alloc();
+       if (!p)
+               return -ENOMEM;
+       memset(sctx->read_buf, 0, BTRFS_SEND_READ_SIZE);
+       while (offset < end) {
+               len = min_t(u64, end - offset, BTRFS_SEND_READ_SIZE);
+
+               ret = begin_cmd(sctx, BTRFS_SEND_C_WRITE);
+               if (ret < 0)
+                       break;
+               ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p);
+               if (ret < 0)
+                       break;
+               TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
+               TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset);
+               TLV_PUT(sctx, BTRFS_SEND_A_DATA, sctx->read_buf, len);
+               ret = send_cmd(sctx);
+               if (ret < 0)
+                       break;
+               offset += len;
+       }
+tlv_put_failure:
+       fs_path_free(p);
+       return ret;
+}
+
 static int send_write_or_clone(struct send_ctx *sctx,
                               struct btrfs_path *path,
                               struct btrfs_key *key,
        return ret;
 }
 
+static int get_last_extent(struct send_ctx *sctx, u64 offset)
+{
+       struct btrfs_path *path;
+       struct btrfs_root *root = sctx->send_root;
+       struct btrfs_file_extent_item *fi;
+       struct btrfs_key key;
+       u64 extent_end;
+       u8 type;
+       int ret;
+
+       path = alloc_path_for_send();
+       if (!path)
+               return -ENOMEM;
+
+       sctx->cur_inode_last_extent = 0;
+
+       key.objectid = sctx->cur_ino;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = offset;
+       ret = btrfs_search_slot_for_read(root, &key, path, 0, 1);
+       if (ret < 0)
+               goto out;
+       ret = 0;
+       btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+       if (key.objectid != sctx->cur_ino || key.type != BTRFS_EXTENT_DATA_KEY)
+               goto out;
+
+       fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+                           struct btrfs_file_extent_item);
+       type = btrfs_file_extent_type(path->nodes[0], fi);
+       if (type == BTRFS_FILE_EXTENT_INLINE) {
+               u64 size = btrfs_file_extent_inline_len(path->nodes[0], fi);
+               extent_end = ALIGN(key.offset + size,
+                                  sctx->send_root->sectorsize);
+       } else {
+               extent_end = key.offset +
+                       btrfs_file_extent_num_bytes(path->nodes[0], fi);
+       }
+       sctx->cur_inode_last_extent = extent_end;
+out:
+       btrfs_free_path(path);
+       return ret;
+}
+
+static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
+                          struct btrfs_key *key)
+{
+       struct btrfs_file_extent_item *fi;
+       u64 extent_end;
+       u8 type;
+       int ret = 0;
+
+       if (sctx->cur_ino != key->objectid || !need_send_hole(sctx))
+               return 0;
+
+       if (sctx->cur_inode_last_extent == (u64)-1) {
+               ret = get_last_extent(sctx, key->offset - 1);
+               if (ret)
+                       return ret;
+       }
+
+       fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+                           struct btrfs_file_extent_item);
+       type = btrfs_file_extent_type(path->nodes[0], fi);
+       if (type == BTRFS_FILE_EXTENT_INLINE) {
+               u64 size = btrfs_file_extent_inline_len(path->nodes[0], fi);
+               extent_end = ALIGN(key->offset + size,
+                                  sctx->send_root->sectorsize);
+       } else {
+               extent_end = key->offset +
+                       btrfs_file_extent_num_bytes(path->nodes[0], fi);
+       }
+       if (sctx->cur_inode_last_extent < key->offset)
+               ret = send_hole(sctx, key->offset);
+       sctx->cur_inode_last_extent = extent_end;
+       return ret;
+}
+
 static int process_extent(struct send_ctx *sctx,
                          struct btrfs_path *path,
                          struct btrfs_key *key)
                        goto out;
                if (ret) {
                        ret = 0;
-                       goto out;
+                       goto out_hole;
                }
        } else {
                struct btrfs_file_extent_item *ei;
                goto out;
 
        ret = send_write_or_clone(sctx, path, key, found_clone);
-
+       if (ret)
+               goto out;
+out_hole:
+       ret = maybe_send_hole(sctx, path, key);
 out:
        return ret;
 }
        }
 
        if (S_ISREG(sctx->cur_inode_mode)) {
+               if (need_send_hole(sctx)) {
+                       if (sctx->cur_inode_last_extent == (u64)-1) {
+                               ret = get_last_extent(sctx, (u64)-1);
+                               if (ret)
+                                       goto out;
+                       }
+                       if (sctx->cur_inode_last_extent <
+                           sctx->cur_inode_size) {
+                               ret = send_hole(sctx, sctx->cur_inode_size);
+                               if (ret)
+                                       goto out;
+                       }
+               }
                ret = send_truncate(sctx, sctx->cur_ino, sctx->cur_inode_gen,
                                sctx->cur_inode_size);
                if (ret < 0)
 
        sctx->cur_ino = key->objectid;
        sctx->cur_inode_new_gen = 0;
+       sctx->cur_inode_last_extent = (u64)-1;
 
        /*
         * Set send_progress to current inode. This will tell all get_cur_xxx
        struct send_ctx *sctx = ctx;
 
        if (result == BTRFS_COMPARE_TREE_SAME) {
-               if (key->type != BTRFS_INODE_REF_KEY &&
-                   key->type != BTRFS_INODE_EXTREF_KEY)
-                       return 0;
-               ret = compare_refs(sctx, left_path, key);
-               if (!ret)
+               if (key->type == BTRFS_INODE_REF_KEY ||
+                   key->type == BTRFS_INODE_EXTREF_KEY) {
+                       ret = compare_refs(sctx, left_path, key);
+                       if (!ret)
+                               return 0;
+                       if (ret < 0)
+                               return ret;
+               } else if (key->type == BTRFS_EXTENT_DATA_KEY) {
+                       return maybe_send_hole(sctx, left_path, key);
+               } else {
                        return 0;
-               if (ret < 0)
-                       return ret;
+               }
                result = BTRFS_COMPARE_TREE_CHANGED;
                ret = 0;
        }
 
 static noinline int copy_items(struct btrfs_trans_handle *trans,
                               struct inode *inode,
                               struct btrfs_path *dst_path,
-                              struct extent_buffer *src,
+                              struct btrfs_path *src_path, u64 *last_extent,
                               int start_slot, int nr, int inode_only)
 {
        unsigned long src_offset;
        struct btrfs_root *log = BTRFS_I(inode)->root->log_root;
        struct btrfs_file_extent_item *extent;
        struct btrfs_inode_item *inode_item;
+       struct extent_buffer *src = src_path->nodes[0];
+       struct btrfs_key first_key, last_key, key;
        int ret;
        struct btrfs_key *ins_keys;
        u32 *ins_sizes;
        int i;
        struct list_head ordered_sums;
        int skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
+       bool has_extents = false;
+       bool need_find_last_extent = (*last_extent == 0);
+       bool done = false;
 
        INIT_LIST_HEAD(&ordered_sums);
 
        if (!ins_data)
                return -ENOMEM;
 
+       first_key.objectid = (u64)-1;
+
        ins_sizes = (u32 *)ins_data;
        ins_keys = (struct btrfs_key *)(ins_data + nr * sizeof(u32));
 
 
                src_offset = btrfs_item_ptr_offset(src, start_slot + i);
 
+               if ((i == (nr - 1)))
+                       last_key = ins_keys[i];
+
                if (ins_keys[i].type == BTRFS_INODE_ITEM_KEY) {
                        inode_item = btrfs_item_ptr(dst_path->nodes[0],
                                                    dst_path->slots[0],
                                           src_offset, ins_sizes[i]);
                }
 
+               /*
+                * We set need_find_last_extent here in case we know we were
+                * processing other items and then walk into the first extent in
+                * the inode.  If we don't hit an extent then nothing changes,
+                * we'll do the last search the next time around.
+                */
+               if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY) {
+                       has_extents = true;
+                       if (need_find_last_extent &&
+                           first_key.objectid == (u64)-1)
+                               first_key = ins_keys[i];
+               } else {
+                       need_find_last_extent = false;
+               }
+
                /* take a reference on file data extents so that truncates
                 * or deletes of this inode don't have to relog the inode
                 * again
                list_del(&sums->list);
                kfree(sums);
        }
+
+       if (!has_extents)
+               return ret;
+
+       /*
+        * Because we use btrfs_search_forward we could skip leaves that were
+        * not modified and then assume *last_extent is valid when it really
+        * isn't.  So back up to the previous leaf and read the end of the last
+        * extent before we go and fill in holes.
+        */
+       if (need_find_last_extent) {
+               u64 len;
+
+               ret = btrfs_prev_leaf(BTRFS_I(inode)->root, src_path);
+               if (ret < 0)
+                       return ret;
+               if (ret)
+                       goto fill_holes;
+               if (src_path->slots[0])
+                       src_path->slots[0]--;
+               src = src_path->nodes[0];
+               btrfs_item_key_to_cpu(src, &key, src_path->slots[0]);
+               if (key.objectid != btrfs_ino(inode) ||
+                   key.type != BTRFS_EXTENT_DATA_KEY)
+                       goto fill_holes;
+               extent = btrfs_item_ptr(src, src_path->slots[0],
+                                       struct btrfs_file_extent_item);
+               if (btrfs_file_extent_type(src, extent) ==
+                   BTRFS_FILE_EXTENT_INLINE) {
+                       len = btrfs_file_extent_inline_len(src, extent);
+                       *last_extent = ALIGN(key.offset + len,
+                                            log->sectorsize);
+               } else {
+                       len = btrfs_file_extent_num_bytes(src, extent);
+                       *last_extent = key.offset + len;
+               }
+       }
+fill_holes:
+       /* So we did prev_leaf, now we need to move to the next leaf, but a few
+        * things could have happened
+        *
+        * 1) A merge could have happened, so we could currently be on a leaf
+        * that holds what we were copying in the first place.
+        * 2) A split could have happened, and now not all of the items we want
+        * are on the same leaf.
+        *
+        * So we need to adjust how we search for holes, we need to drop the
+        * path and re-search for the first extent key we found, and then walk
+        * forward until we hit the last one we copied.
+        */
+       if (need_find_last_extent) {
+               /* btrfs_prev_leaf could return 1 without releasing the path */
+               btrfs_release_path(src_path);
+               ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &first_key,
+                                       src_path, 0, 0);
+               if (ret < 0)
+                       return ret;
+               ASSERT(ret == 0);
+               src = src_path->nodes[0];
+               i = src_path->slots[0];
+       } else {
+               i = start_slot;
+       }
+
+       /*
+        * Ok so here we need to go through and fill in any holes we may have
+        * to make sure that holes are punched for those areas in case they had
+        * extents previously.
+        */
+       while (!done) {
+               u64 offset, len;
+               u64 extent_end;
+
+               if (i >= btrfs_header_nritems(src_path->nodes[0])) {
+                       ret = btrfs_next_leaf(BTRFS_I(inode)->root, src_path);
+                       if (ret < 0)
+                               return ret;
+                       ASSERT(ret == 0);
+                       src = src_path->nodes[0];
+                       i = 0;
+               }
+
+               btrfs_item_key_to_cpu(src, &key, i);
+               if (!btrfs_comp_cpu_keys(&key, &last_key))
+                       done = true;
+               if (key.objectid != btrfs_ino(inode) ||
+                   key.type != BTRFS_EXTENT_DATA_KEY) {
+                       i++;
+                       continue;
+               }
+               extent = btrfs_item_ptr(src, i, struct btrfs_file_extent_item);
+               if (btrfs_file_extent_type(src, extent) ==
+                   BTRFS_FILE_EXTENT_INLINE) {
+                       len = btrfs_file_extent_inline_len(src, extent);
+                       extent_end = ALIGN(key.offset + len, log->sectorsize);
+               } else {
+                       len = btrfs_file_extent_num_bytes(src, extent);
+                       extent_end = key.offset + len;
+               }
+               i++;
+
+               if (*last_extent == key.offset) {
+                       *last_extent = extent_end;
+                       continue;
+               }
+               offset = *last_extent;
+               len = key.offset - *last_extent;
+               ret = btrfs_insert_file_extent(trans, log, btrfs_ino(inode),
+                                              offset, 0, 0, len, 0, len, 0,
+                                              0, 0);
+               if (ret)
+                       break;
+               *last_extent = offset + len;
+       }
+       /*
+        * Need to let the callers know we dropped the path so they should
+        * re-search.
+        */
+       if (!ret && need_find_last_extent)
+               ret = 1;
        return ret;
 }
 
        struct btrfs_key max_key;
        struct btrfs_root *log = root->log_root;
        struct extent_buffer *src = NULL;
+       u64 last_extent = 0;
        int err = 0;
        int ret;
        int nritems;
                        goto next_slot;
                }
 
-               ret = copy_items(trans, inode, dst_path, src, ins_start_slot,
-                                ins_nr, inode_only);
-               if (ret) {
+               ret = copy_items(trans, inode, dst_path, path, &last_extent,
+                                ins_start_slot, ins_nr, inode_only);
+               if (ret < 0) {
                        err = ret;
                        goto out_unlock;
+               } if (ret) {
+                       ins_nr = 0;
+                       btrfs_release_path(path);
+                       continue;
                }
                ins_nr = 1;
                ins_start_slot = path->slots[0];
                        goto again;
                }
                if (ins_nr) {
-                       ret = copy_items(trans, inode, dst_path, src,
-                                        ins_start_slot,
+                       ret = copy_items(trans, inode, dst_path, path,
+                                        &last_extent, ins_start_slot,
                                         ins_nr, inode_only);
-                       if (ret) {
+                       if (ret < 0) {
                                err = ret;
                                goto out_unlock;
                        }
+                       ret = 0;
                        ins_nr = 0;
                }
                btrfs_release_path(path);
                }
        }
        if (ins_nr) {
-               ret = copy_items(trans, inode, dst_path, src, ins_start_slot,
-                                ins_nr, inode_only);
-               if (ret) {
+               ret = copy_items(trans, inode, dst_path, path, &last_extent,
+                                ins_start_slot, ins_nr, inode_only);
+               if (ret < 0) {
                        err = ret;
                        goto out_unlock;
                }
+               ret = 0;
                ins_nr = 0;
        }