mm: task dirty accounting fix
[safe/jmp/linux-2.6] / fs / btrfs / tree-log.c
index 3f4b139..9c462fb 100644 (file)
@@ -23,6 +23,7 @@
 #include "locking.h"
 #include "print-tree.h"
 #include "compat.h"
+#include "tree-log.h"
 
 /* magic values for the inode_only field in btrfs_log_inode:
  *
@@ -49,6 +50,9 @@
 static int __btrfs_log_inode(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root, struct inode *inode,
                             int inode_only);
+static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
+                            struct btrfs_root *root,
+                            struct btrfs_path *path, u64 objectid);
 
 /*
  * tree logging is a special write ahead log used to make sure that
@@ -74,90 +78,6 @@ static int __btrfs_log_inode(struct btrfs_trans_handle *trans,
  */
 
 /*
- * btrfs_add_log_tree adds a new per-subvolume log tree into the
- * tree of log tree roots.  This must be called with a tree log transaction
- * running (see start_log_trans).
- */
-int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
-                     struct btrfs_root *root)
-{
-       struct btrfs_key key;
-       struct btrfs_root_item root_item;
-       struct btrfs_inode_item *inode_item;
-       struct extent_buffer *leaf;
-       struct btrfs_root *new_root = root;
-       int ret;
-       u64 objectid = root->root_key.objectid;
-
-       leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
-                                     BTRFS_TREE_LOG_OBJECTID,
-                                     0, 0, 0, 0, 0);
-       if (IS_ERR(leaf)) {
-               ret = PTR_ERR(leaf);
-               return ret;
-       }
-
-       btrfs_set_header_nritems(leaf, 0);
-       btrfs_set_header_level(leaf, 0);
-       btrfs_set_header_bytenr(leaf, leaf->start);
-       btrfs_set_header_generation(leaf, trans->transid);
-       btrfs_set_header_owner(leaf, BTRFS_TREE_LOG_OBJECTID);
-
-       write_extent_buffer(leaf, root->fs_info->fsid,
-                           (unsigned long)btrfs_header_fsid(leaf),
-                           BTRFS_FSID_SIZE);
-       btrfs_mark_buffer_dirty(leaf);
-
-       inode_item = &root_item.inode;
-       memset(inode_item, 0, sizeof(*inode_item));
-       inode_item->generation = cpu_to_le64(1);
-       inode_item->size = cpu_to_le64(3);
-       inode_item->nlink = cpu_to_le32(1);
-       inode_item->nblocks = cpu_to_le64(1);
-       inode_item->mode = cpu_to_le32(S_IFDIR | 0755);
-
-       btrfs_set_root_bytenr(&root_item, leaf->start);
-       btrfs_set_root_level(&root_item, 0);
-       btrfs_set_root_refs(&root_item, 0);
-       btrfs_set_root_used(&root_item, 0);
-
-       memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress));
-       root_item.drop_level = 0;
-
-       btrfs_tree_unlock(leaf);
-       free_extent_buffer(leaf);
-       leaf = NULL;
-
-       btrfs_set_root_dirid(&root_item, 0);
-
-       key.objectid = BTRFS_TREE_LOG_OBJECTID;
-       key.offset = objectid;
-       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
-       ret = btrfs_insert_root(trans, root->fs_info->log_root_tree, &key,
-                               &root_item);
-       if (ret)
-               goto fail;
-
-       new_root = btrfs_read_fs_root_no_radix(root->fs_info->log_root_tree,
-                                              &key);
-       BUG_ON(!new_root);
-
-       WARN_ON(root->log_root);
-       root->log_root = new_root;
-
-       /*
-        * log trees do not get reference counted because they go away
-        * before a real commit is actually done.  They do store pointers
-        * to file data extents, and those reference counts still get
-        * updated (along with back refs to the log tree).
-        */
-       new_root->ref_cows = 0;
-       new_root->last_trans = trans->transid;
-fail:
-       return ret;
-}
-
-/*
  * start a sub transaction and setup the log tree
  * this increments the log tree writer count to make the people
  * syncing the tree wait for us to finish
@@ -166,6 +86,14 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root)
 {
        int ret;
+
+       mutex_lock(&root->log_mutex);
+       if (root->log_root) {
+               root->log_batch++;
+               atomic_inc(&root->log_writers);
+               mutex_unlock(&root->log_mutex);
+               return 0;
+       }
        mutex_lock(&root->fs_info->tree_log_mutex);
        if (!root->fs_info->log_root_tree) {
                ret = btrfs_init_log_root_tree(trans, root->fs_info);
@@ -175,9 +103,10 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
                ret = btrfs_add_log_tree(trans, root);
                BUG_ON(ret);
        }
-       atomic_inc(&root->fs_info->tree_log_writers);
-       root->fs_info->tree_log_batch++;
        mutex_unlock(&root->fs_info->tree_log_mutex);
+       root->log_batch++;
+       atomic_inc(&root->log_writers);
+       mutex_unlock(&root->log_mutex);
        return 0;
 }
 
@@ -194,13 +123,12 @@ static int join_running_log_trans(struct btrfs_root *root)
        if (!root->log_root)
                return -ENOENT;
 
-       mutex_lock(&root->fs_info->tree_log_mutex);
+       mutex_lock(&root->log_mutex);
        if (root->log_root) {
                ret = 0;
-               atomic_inc(&root->fs_info->tree_log_writers);
-               root->fs_info->tree_log_batch++;
+               atomic_inc(&root->log_writers);
        }
-       mutex_unlock(&root->fs_info->tree_log_mutex);
+       mutex_unlock(&root->log_mutex);
        return ret;
 }
 
@@ -210,10 +138,11 @@ static int join_running_log_trans(struct btrfs_root *root)
  */
 static int end_log_trans(struct btrfs_root *root)
 {
-       atomic_dec(&root->fs_info->tree_log_writers);
-       smp_mb();
-       if (waitqueue_active(&root->fs_info->tree_log_wait))
-               wake_up(&root->fs_info->tree_log_wait);
+       if (atomic_dec_and_test(&root->log_writers)) {
+               smp_mb();
+               if (waitqueue_active(&root->log_writer_wait))
+                       wake_up(&root->log_writer_wait);
+       }
        return 0;
 }
 
@@ -271,10 +200,10 @@ static int process_one_buffer(struct btrfs_root *log,
                              struct walk_control *wc, u64 gen)
 {
        if (wc->pin) {
-               mutex_lock(&log->fs_info->alloc_mutex);
+               mutex_lock(&log->fs_info->pinned_mutex);
                btrfs_update_pinned_extents(log->fs_info->extent_root,
                                            eb->start, eb->len, 1);
-               mutex_unlock(&log->fs_info->alloc_mutex);
+               mutex_unlock(&log->fs_info->pinned_mutex);
        }
 
        if (btrfs_buffer_uptodate(eb, gen)) {
@@ -372,13 +301,8 @@ insert:
                if (found_size > item_size) {
                        btrfs_truncate_item(trans, root, path, item_size, 1);
                } else if (found_size < item_size) {
-                       ret = btrfs_del_item(trans, root,
-                                            path);
-                       BUG_ON(ret);
-
-                       btrfs_release_path(root, path);
-                       ret = btrfs_insert_empty_item(trans,
-                                 root, path, key, item_size);
+                       ret = btrfs_extend_item(trans, root, path,
+                                               item_size - found_size);
                        BUG_ON(ret);
                }
        } else if (ret) {
@@ -487,6 +411,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
        u64 extent_end;
        u64 alloc_hint;
        u64 start = key->offset;
+       u64 saved_nbytes;
        struct btrfs_file_extent_item *item;
        struct inode *inode = NULL;
        unsigned long size;
@@ -495,11 +420,11 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
        item = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
        found_type = btrfs_file_extent_type(eb, item);
 
-       if (found_type == BTRFS_FILE_EXTENT_REG)
+       if (found_type == BTRFS_FILE_EXTENT_REG ||
+           found_type == BTRFS_FILE_EXTENT_PREALLOC)
                extent_end = start + btrfs_file_extent_num_bytes(eb, item);
        else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
-               size = btrfs_file_extent_inline_len(eb,
-                                                   btrfs_item_nr(eb, slot));
+               size = btrfs_file_extent_inline_len(eb, item);
                extent_end = (start + size + mask) & ~mask;
        } else {
                ret = 0;
@@ -520,7 +445,9 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
        ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino,
                                       start, 0);
 
-       if (ret == 0 && found_type == BTRFS_FILE_EXTENT_REG) {
+       if (ret == 0 &&
+           (found_type == BTRFS_FILE_EXTENT_REG ||
+            found_type == BTRFS_FILE_EXTENT_PREALLOC)) {
                struct btrfs_file_extent_item cmp1;
                struct btrfs_file_extent_item cmp2;
                struct btrfs_file_extent_item *existing;
@@ -546,52 +473,95 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
        }
        btrfs_release_path(root, path);
 
+       saved_nbytes = inode_get_bytes(inode);
        /* drop any overlapping extents */
        ret = btrfs_drop_extents(trans, root, inode,
                         start, extent_end, start, &alloc_hint);
        BUG_ON(ret);
 
-       BUG_ON(ret);
-       if (found_type == BTRFS_FILE_EXTENT_REG) {
+       if (found_type == BTRFS_FILE_EXTENT_REG ||
+           found_type == BTRFS_FILE_EXTENT_PREALLOC) {
+               unsigned long dest_offset;
                struct btrfs_key ins;
 
+               ret = btrfs_insert_empty_item(trans, root, path, key,
+                                             sizeof(*item));
+               BUG_ON(ret);
+               dest_offset = btrfs_item_ptr_offset(path->nodes[0],
+                                                   path->slots[0]);
+               copy_extent_buffer(path->nodes[0], eb, dest_offset,
+                               (unsigned long)item,  sizeof(*item));
+
                ins.objectid = btrfs_file_extent_disk_bytenr(eb, item);
                ins.offset = btrfs_file_extent_disk_num_bytes(eb, item);
                ins.type = BTRFS_EXTENT_ITEM_KEY;
 
-               /* insert the extent pointer in the file */
-               ret = overwrite_item(trans, root, path, eb, slot, key);
-               BUG_ON(ret);
-
-               /*
-                * is this extent already allocated in the extent
-                * allocation tree?  If so, just add a reference
-                */
-               ret = btrfs_lookup_extent(root, path, ins.objectid, ins.offset);
-               btrfs_release_path(root, path);
-               if (ret == 0) {
-                       ret = btrfs_inc_extent_ref(trans, root,
-                                  ins.objectid, ins.offset,
-                                  root->root_key.objectid,
-                                  trans->transid, key->objectid, start);
-               } else {
+               if (ins.objectid > 0) {
+                       u64 csum_start;
+                       u64 csum_end;
+                       LIST_HEAD(ordered_sums);
                        /*
-                        * insert the extent pointer in the extent
-                        * allocation tree
+                        * is this extent already allocated in the extent
+                        * allocation tree?  If so, just add a reference
                         */
-                       ret = btrfs_alloc_logged_extent(trans, root,
+                       ret = btrfs_lookup_extent(root, ins.objectid,
+                                               ins.offset);
+                       if (ret == 0) {
+                               ret = btrfs_inc_extent_ref(trans, root,
+                                               ins.objectid, ins.offset,
+                                               path->nodes[0]->start,
+                                               root->root_key.objectid,
+                                               trans->transid, key->objectid);
+                       } else {
+                               /*
+                                * insert the extent pointer in the extent
+                                * allocation tree
+                                */
+                               ret = btrfs_alloc_logged_extent(trans, root,
+                                               path->nodes[0]->start,
                                                root->root_key.objectid,
                                                trans->transid, key->objectid,
-                                               start, &ins);
+                                               &ins);
+                               BUG_ON(ret);
+                       }
+                       btrfs_release_path(root, path);
+
+                       if (btrfs_file_extent_compression(eb, item)) {
+                               csum_start = ins.objectid;
+                               csum_end = csum_start + ins.offset;
+                       } else {
+                               csum_start = ins.objectid +
+                                       btrfs_file_extent_offset(eb, item);
+                               csum_end = csum_start +
+                                       btrfs_file_extent_num_bytes(eb, item);
+                       }
+
+                       ret = btrfs_lookup_csums_range(root->log_root,
+                                               csum_start, csum_end - 1,
+                                               &ordered_sums);
                        BUG_ON(ret);
+                       while (!list_empty(&ordered_sums)) {
+                               struct btrfs_ordered_sum *sums;
+                               sums = list_entry(ordered_sums.next,
+                                               struct btrfs_ordered_sum,
+                                               list);
+                               ret = btrfs_csum_file_blocks(trans,
+                                               root->fs_info->csum_root,
+                                               sums);
+                               BUG_ON(ret);
+                               list_del(&sums->list);
+                               kfree(sums);
+                       }
+               } else {
+                       btrfs_release_path(root, path);
                }
        } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
                /* inline extents are easy, we just overwrite them */
                ret = overwrite_item(trans, root, path, eb, slot, key);
                BUG_ON(ret);
        }
-       /* btrfs_drop_extents changes i_blocks, update it here */
-       inode->i_blocks += (extent_end - start) >> 9;
+
+       inode_set_bytes(inode, saved_nbytes);
        btrfs_update_inode(trans, root, inode);
 out:
        if (inode)
@@ -631,8 +601,10 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
        inode = read_one_inode(root, location.objectid);
        BUG_ON(!inode);
 
-       btrfs_inc_nlink(inode);
+       ret = link_to_fixup_dir(trans, root, path, location.objectid);
+       BUG_ON(ret);
        ret = btrfs_unlink_inode(trans, root, dir, inode, name, name_len);
+       BUG_ON(ret);
        kfree(name);
 
        iput(inode);
@@ -817,7 +789,7 @@ conflict_again:
                 */
                ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
                ptr_end = ptr + btrfs_item_size_nr(leaf, path->slots[0]);
-               while(ptr < ptr_end) {
+               while (ptr < ptr_end) {
                        victim_ref = (struct btrfs_inode_ref *)ptr;
                        victim_name_len = btrfs_inode_ref_name_len(leaf,
                                                                   victim_ref);
@@ -891,78 +863,6 @@ out_nowrite:
 }
 
 /*
- * replay one csum item from the log tree into the subvolume 'root'
- * eb, slot and key all refer to the log tree
- * path is for temp use by this function and should be released on return
- *
- * This copies the checksums out of the log tree and inserts them into
- * the subvolume.  Any existing checksums for this range in the file
- * are overwritten, and new items are added where required.
- *
- * We keep this simple by reusing the btrfs_ordered_sum code from
- * the data=ordered mode.  This basically means making a copy
- * of all the checksums in ram, which we have to do anyway for kmap
- * rules.
- *
- * The copy is then sent down to btrfs_csum_file_blocks, which
- * does all the hard work of finding existing items in the file
- * or adding new ones.
- */
-static noinline int replay_one_csum(struct btrfs_trans_handle *trans,
-                                     struct btrfs_root *root,
-                                     struct btrfs_path *path,
-                                     struct extent_buffer *eb, int slot,
-                                     struct btrfs_key *key)
-{
-       int ret;
-       u32 item_size = btrfs_item_size_nr(eb, slot);
-       u64 cur_offset;
-       unsigned long file_bytes;
-       struct btrfs_ordered_sum *sums;
-       struct btrfs_sector_sum *sector_sum;
-       struct inode *inode;
-       unsigned long ptr;
-
-       file_bytes = (item_size / BTRFS_CRC32_SIZE) * root->sectorsize;
-       inode = read_one_inode(root, key->objectid);
-       if (!inode) {
-               return -EIO;
-       }
-
-       sums = kzalloc(btrfs_ordered_sum_size(root, file_bytes), GFP_NOFS);
-       if (!sums) {
-               iput(inode);
-               return -ENOMEM;
-       }
-
-       INIT_LIST_HEAD(&sums->list);
-       sums->len = file_bytes;
-       sums->file_offset = key->offset;
-
-       /*
-        * copy all the sums into the ordered sum struct
-        */
-       sector_sum = sums->sums;
-       cur_offset = key->offset;
-       ptr = btrfs_item_ptr_offset(eb, slot);
-       while(item_size > 0) {
-               sector_sum->offset = cur_offset;
-               read_extent_buffer(eb, &sector_sum->sum, ptr, BTRFS_CRC32_SIZE);
-               sector_sum++;
-               item_size -= BTRFS_CRC32_SIZE;
-               ptr += BTRFS_CRC32_SIZE;
-               cur_offset += root->sectorsize;
-       }
-
-       /* let btrfs_csum_file_blocks add them into the file */
-       ret = btrfs_csum_file_blocks(trans, root, inode, sums);
-       BUG_ON(ret);
-       kfree(sums);
-       iput(inode);
-
-       return 0;
-}
-/*
  * There are a few corners where the link count of the file can't
  * be properly maintained during replay.  So, instead of adding
  * lots of complexity to the log code, we just scan the backrefs
@@ -990,7 +890,7 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
 
        path = btrfs_alloc_path();
 
-       while(1) {
+       while (1) {
                ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
                if (ret < 0)
                        break;
@@ -1007,7 +907,7 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans,
                ptr = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
                ptr_end = ptr + btrfs_item_size_nr(path->nodes[0],
                                                   path->slots[0]);
-               while(ptr < ptr_end) {
+               while (ptr < ptr_end) {
                        struct btrfs_inode_ref *ref;
 
                        ref = (struct btrfs_inode_ref *)ptr;
@@ -1043,7 +943,7 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans,
        key.objectid = BTRFS_TREE_LOG_FIXUP_OBJECTID;
        key.type = BTRFS_ORPHAN_ITEM_KEY;
        key.offset = (u64)-1;
-       while(1) {
+       while (1) {
                ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
                if (ret < 0)
                        break;
@@ -1201,8 +1101,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
        if (key->type == BTRFS_DIR_ITEM_KEY) {
                dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid,
                                       name, name_len, 1);
-       }
-       else if (key->type == BTRFS_DIR_INDEX_KEY) {
+       } else if (key->type == BTRFS_DIR_INDEX_KEY) {
                dst_di = btrfs_lookup_dir_index_item(trans, root, path,
                                                     key->objectid,
                                                     key->offset, name,
@@ -1277,7 +1176,7 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
 
        ptr = btrfs_item_ptr_offset(eb, slot);
        ptr_end = ptr + item_size;
-       while(ptr < ptr_end) {
+       while (ptr < ptr_end) {
                di = (struct btrfs_dir_item *)ptr;
                name_len = btrfs_dir_name_len(eb, di);
                ret = replay_one_name(trans, root, path, eb, di, key);
@@ -1403,7 +1302,7 @@ again:
        item_size = btrfs_item_size_nr(eb, slot);
        ptr = btrfs_item_ptr_offset(eb, slot);
        ptr_end = ptr + item_size;
-       while(ptr < ptr_end) {
+       while (ptr < ptr_end) {
                di = (struct btrfs_dir_item *)ptr;
                name_len = btrfs_dir_name_len(eb, di);
                name = kmalloc(name_len, GFP_NOFS);
@@ -1508,14 +1407,14 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
 again:
        range_start = 0;
        range_end = 0;
-       while(1) {
+       while (1) {
                ret = find_dir_range(log, path, dirid, key_type,
                                     &range_start, &range_end);
                if (ret != 0)
                        break;
 
                dir_key.offset = range_start;
-               while(1) {
+               while (1) {
                        int nritems;
                        ret = btrfs_search_slot(NULL, root, &dir_key, path,
                                                0, 0);
@@ -1656,10 +1555,6 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                        ret = replay_one_extent(wc->trans, root, path,
                                                eb, i, &key);
                        BUG_ON(ret);
-               } else if (key.type == BTRFS_CSUM_ITEM_KEY) {
-                       ret = replay_one_csum(wc->trans, root, path,
-                                             eb, i, &key);
-                       BUG_ON(ret);
                } else if (key.type == BTRFS_DIR_ITEM_KEY ||
                           key.type == BTRFS_DIR_INDEX_KEY) {
                        ret = replay_one_dir_item(wc->trans, root, path,
@@ -1671,7 +1566,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
        return 0;
 }
 
-static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
+static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                                   struct btrfs_root *root,
                                   struct btrfs_path *path, int *level,
                                   struct walk_control *wc)
@@ -1689,7 +1584,7 @@ static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
        WARN_ON(*level < 0);
        WARN_ON(*level >= BTRFS_MAX_LEVEL);
 
-       while(*level > 0) {
+       while (*level > 0) {
                WARN_ON(*level < 0);
                WARN_ON(*level >= BTRFS_MAX_LEVEL);
                cur = path->nodes[*level];
@@ -1720,6 +1615,7 @@ static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
 
                                btrfs_tree_lock(next);
                                clean_tree_block(trans, root, next);
+                               btrfs_set_lock_blocking(next);
                                btrfs_wait_tree_block_writeback(next);
                                btrfs_tree_unlock(next);
 
@@ -1728,9 +1624,8 @@ static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
 
                                WARN_ON(root_owner !=
                                        BTRFS_TREE_LOG_OBJECTID);
-                               ret = btrfs_free_extent(trans, root, bytenr,
-                                                       blocksize, root_owner,
-                                                       root_gen, 0, 0, 1);
+                               ret = btrfs_free_reserved_extent(root,
+                                                        bytenr, blocksize);
                                BUG_ON(ret);
                        }
                        free_extent_buffer(next);
@@ -1749,11 +1644,11 @@ static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
        WARN_ON(*level < 0);
        WARN_ON(*level >= BTRFS_MAX_LEVEL);
 
-       if (path->nodes[*level] == root->node) {
+       if (path->nodes[*level] == root->node)
                parent = path->nodes[*level];
-       } else {
+       else
                parent = path->nodes[*level + 1];
-       }
+
        bytenr = path->nodes[*level]->start;
 
        blocksize = btrfs_level_size(root, *level);
@@ -1767,6 +1662,7 @@ static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
                next = path->nodes[*level];
                btrfs_tree_lock(next);
                clean_tree_block(trans, root, next);
+               btrfs_set_lock_blocking(next);
                btrfs_wait_tree_block_writeback(next);
                btrfs_tree_unlock(next);
 
@@ -1775,8 +1671,7 @@ static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
                        BUG_ON(ret);
                }
                WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
-               ret = btrfs_free_extent(trans, root, bytenr, blocksize,
-                                         root_owner, root_gen, 0, 0, 1);
+               ret = btrfs_free_reserved_extent(root, bytenr, blocksize);
                BUG_ON(ret);
        }
        free_extent_buffer(path->nodes[*level]);
@@ -1787,7 +1682,7 @@ static int noinline walk_down_log_tree(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-static int noinline walk_up_log_tree(struct btrfs_trans_handle *trans,
+static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
                                 struct btrfs_root *root,
                                 struct btrfs_path *path, int *level,
                                 struct walk_control *wc)
@@ -1798,7 +1693,7 @@ static int noinline walk_up_log_tree(struct btrfs_trans_handle *trans,
        int slot;
        int ret;
 
-       for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
+       for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
                slot = path->slots[i];
                if (slot < btrfs_header_nritems(path->nodes[i]) - 1) {
                        struct extent_buffer *node;
@@ -1808,16 +1703,14 @@ static int noinline walk_up_log_tree(struct btrfs_trans_handle *trans,
                        WARN_ON(*level == 0);
                        return 0;
                } else {
-                       if (path->nodes[*level] == root->node) {
-                               root_owner = root->root_key.objectid;
-                               root_gen =
-                                  btrfs_header_generation(path->nodes[*level]);
-                       } else {
-                               struct extent_buffer *node;
-                               node = path->nodes[*level + 1];
-                               root_owner = btrfs_header_owner(node);
-                               root_gen = btrfs_header_generation(node);
-                       }
+                       struct extent_buffer *parent;
+                       if (path->nodes[*level] == root->node)
+                               parent = path->nodes[*level];
+                       else
+                               parent = path->nodes[*level + 1];
+
+                       root_owner = btrfs_header_owner(parent);
+                       root_gen = btrfs_header_generation(parent);
                        wc->process_func(root, path->nodes[*level], wc,
                                 btrfs_header_generation(path->nodes[*level]));
                        if (wc->free) {
@@ -1827,6 +1720,7 @@ static int noinline walk_up_log_tree(struct btrfs_trans_handle *trans,
 
                                btrfs_tree_lock(next);
                                clean_tree_block(trans, root, next);
+                               btrfs_set_lock_blocking(next);
                                btrfs_wait_tree_block_writeback(next);
                                btrfs_tree_unlock(next);
 
@@ -1837,10 +1731,9 @@ static int noinline walk_up_log_tree(struct btrfs_trans_handle *trans,
                                }
 
                                WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
-                               ret = btrfs_free_extent(trans, root,
+                               ret = btrfs_free_reserved_extent(root,
                                                path->nodes[*level]->start,
-                                               path->nodes[*level]->len,
-                                               root_owner, root_gen, 0, 0, 1);
+                                               path->nodes[*level]->len);
                                BUG_ON(ret);
                        }
                        free_extent_buffer(path->nodes[*level]);
@@ -1875,7 +1768,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
        extent_buffer_get(log->node);
        path->slots[level] = 0;
 
-       while(1) {
+       while (1) {
                wret = walk_down_log_tree(trans, log, path, &level, wc);
                if (wret > 0)
                        break;
@@ -1900,6 +1793,7 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
 
                        btrfs_tree_lock(next);
                        clean_tree_block(trans, log, next);
+                       btrfs_set_lock_blocking(next);
                        btrfs_wait_tree_block_writeback(next);
                        btrfs_tree_unlock(next);
 
@@ -1910,11 +1804,8 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
                        }
                        WARN_ON(log->root_key.objectid !=
                                BTRFS_TREE_LOG_OBJECTID);
-                       ret = btrfs_free_extent(trans, log,
-                                               next->start, next->len,
-                                               log->root_key.objectid,
-                                               btrfs_header_generation(next),
-                                               0, 0, 1);
+                       ret = btrfs_free_reserved_extent(log, next->start,
+                                                        next->len);
                        BUG_ON(ret);
                }
        }
@@ -1926,26 +1817,65 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
                }
        }
        btrfs_free_path(path);
-       if (wc->free)
-               free_extent_buffer(log->node);
        return ret;
 }
 
-int wait_log_commit(struct btrfs_root *log)
+/*
+ * helper function to update the item for a given subvolumes log root
+ * in the tree of log roots
+ */
+static int update_log_root(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *log)
+{
+       int ret;
+
+       if (log->log_transid == 1) {
+               /* insert root item on the first sync */
+               ret = btrfs_insert_root(trans, log->fs_info->log_root_tree,
+                               &log->root_key, &log->root_item);
+       } else {
+               ret = btrfs_update_root(trans, log->fs_info->log_root_tree,
+                               &log->root_key, &log->root_item);
+       }
+       return ret;
+}
+
+static int wait_log_commit(struct btrfs_root *root, unsigned long transid)
 {
        DEFINE_WAIT(wait);
-       u64 transid = log->fs_info->tree_log_transid;
+       int index = transid % 2;
 
+       /*
+        * we only allow two pending log transactions at a time,
+        * so we know that if ours is more than 2 older than the
+        * current transaction, we're done
+        */
        do {
-               prepare_to_wait(&log->fs_info->tree_log_wait, &wait,
-                               TASK_UNINTERRUPTIBLE);
-               mutex_unlock(&log->fs_info->tree_log_mutex);
-               if (atomic_read(&log->fs_info->tree_log_commit))
+               prepare_to_wait(&root->log_commit_wait[index],
+                               &wait, TASK_UNINTERRUPTIBLE);
+               mutex_unlock(&root->log_mutex);
+               if (root->log_transid < transid + 2 &&
+                   atomic_read(&root->log_commit[index]))
                        schedule();
-               finish_wait(&log->fs_info->tree_log_wait, &wait);
-               mutex_lock(&log->fs_info->tree_log_mutex);
-       } while(transid == log->fs_info->tree_log_transid &&
-               atomic_read(&log->fs_info->tree_log_commit));
+               finish_wait(&root->log_commit_wait[index], &wait);
+               mutex_lock(&root->log_mutex);
+       } while (root->log_transid < transid + 2 &&
+                atomic_read(&root->log_commit[index]));
+       return 0;
+}
+
+static int wait_for_writer(struct btrfs_root *root)
+{
+       DEFINE_WAIT(wait);
+       while (atomic_read(&root->log_writers)) {
+               prepare_to_wait(&root->log_writer_wait,
+                               &wait, TASK_UNINTERRUPTIBLE);
+               mutex_unlock(&root->log_mutex);
+               if (atomic_read(&root->log_writers))
+                       schedule();
+               mutex_lock(&root->log_mutex);
+               finish_wait(&root->log_writer_wait, &wait);
+       }
        return 0;
 }
 
@@ -1957,75 +1887,118 @@ int wait_log_commit(struct btrfs_root *log)
 int btrfs_sync_log(struct btrfs_trans_handle *trans,
                   struct btrfs_root *root)
 {
+       int index1;
+       int index2;
        int ret;
-       unsigned long batch;
        struct btrfs_root *log = root->log_root;
-       struct walk_control wc = {
-               .write = 1,
-               .process_func = process_one_buffer
-       };
+       struct btrfs_root *log_root_tree = root->fs_info->log_root_tree;
 
-       mutex_lock(&log->fs_info->tree_log_mutex);
-       if (atomic_read(&log->fs_info->tree_log_commit)) {
-               wait_log_commit(log);
-               goto out;
+       mutex_lock(&root->log_mutex);
+       index1 = root->log_transid % 2;
+       if (atomic_read(&root->log_commit[index1])) {
+               wait_log_commit(root, root->log_transid);
+               mutex_unlock(&root->log_mutex);
+               return 0;
        }
-       atomic_set(&log->fs_info->tree_log_commit, 1);
+       atomic_set(&root->log_commit[index1], 1);
+
+       /* wait for previous tree log sync to complete */
+       if (atomic_read(&root->log_commit[(index1 + 1) % 2]))
+               wait_log_commit(root, root->log_transid - 1);
 
-       while(1) {
-               mutex_unlock(&log->fs_info->tree_log_mutex);
+       while (1) {
+               unsigned long batch = root->log_batch;
+               mutex_unlock(&root->log_mutex);
                schedule_timeout_uninterruptible(1);
-               mutex_lock(&log->fs_info->tree_log_mutex);
-               batch = log->fs_info->tree_log_batch;
-
-               while(atomic_read(&log->fs_info->tree_log_writers)) {
-                       DEFINE_WAIT(wait);
-                       prepare_to_wait(&log->fs_info->tree_log_wait, &wait,
-                                       TASK_UNINTERRUPTIBLE);
-                       batch = log->fs_info->tree_log_batch;
-                       mutex_unlock(&log->fs_info->tree_log_mutex);
-                       if (atomic_read(&log->fs_info->tree_log_writers))
-                               schedule();
-                       mutex_lock(&log->fs_info->tree_log_mutex);
-                       finish_wait(&log->fs_info->tree_log_wait, &wait);
-               }
-               if (batch == log->fs_info->tree_log_batch)
+               mutex_lock(&root->log_mutex);
+               wait_for_writer(root);
+               if (batch == root->log_batch)
                        break;
        }
-       ret = walk_log_tree(trans, log, &wc);
-       BUG_ON(ret);
 
-       ret = walk_log_tree(trans, log->fs_info->log_root_tree, &wc);
+       ret = btrfs_write_and_wait_marked_extents(log, &log->dirty_log_pages);
        BUG_ON(ret);
 
-       wc.wait = 1;
+       btrfs_set_root_bytenr(&log->root_item, log->node->start);
+       btrfs_set_root_generation(&log->root_item, trans->transid);
+       btrfs_set_root_level(&log->root_item, btrfs_header_level(log->node));
 
-       ret = walk_log_tree(trans, log, &wc);
+       root->log_batch = 0;
+       root->log_transid++;
+       log->log_transid = root->log_transid;
+       smp_mb();
+       /*
+        * log tree has been flushed to disk, new modifications of
+        * the log will be written to new positions. so it's safe to
+        * allow log writers to go in.
+        */
+       mutex_unlock(&root->log_mutex);
+
+       mutex_lock(&log_root_tree->log_mutex);
+       log_root_tree->log_batch++;
+       atomic_inc(&log_root_tree->log_writers);
+       mutex_unlock(&log_root_tree->log_mutex);
+
+       ret = update_log_root(trans, log);
        BUG_ON(ret);
 
-       ret = walk_log_tree(trans, log->fs_info->log_root_tree, &wc);
+       mutex_lock(&log_root_tree->log_mutex);
+       if (atomic_dec_and_test(&log_root_tree->log_writers)) {
+               smp_mb();
+               if (waitqueue_active(&log_root_tree->log_writer_wait))
+                       wake_up(&log_root_tree->log_writer_wait);
+       }
+
+       index2 = log_root_tree->log_transid % 2;
+       if (atomic_read(&log_root_tree->log_commit[index2])) {
+               wait_log_commit(log_root_tree, log_root_tree->log_transid);
+               mutex_unlock(&log_root_tree->log_mutex);
+               goto out;
+       }
+       atomic_set(&log_root_tree->log_commit[index2], 1);
+
+       if (atomic_read(&log_root_tree->log_commit[(index2 + 1) % 2]))
+               wait_log_commit(log_root_tree, log_root_tree->log_transid - 1);
+
+       wait_for_writer(log_root_tree);
+
+       ret = btrfs_write_and_wait_marked_extents(log_root_tree,
+                               &log_root_tree->dirty_log_pages);
        BUG_ON(ret);
 
        btrfs_set_super_log_root(&root->fs_info->super_for_commit,
-                                log->fs_info->log_root_tree->node->start);
+                               log_root_tree->node->start);
        btrfs_set_super_log_root_level(&root->fs_info->super_for_commit,
-                      btrfs_header_level(log->fs_info->log_root_tree->node));
+                               btrfs_header_level(log_root_tree->node));
+
+       log_root_tree->log_batch = 0;
+       log_root_tree->log_transid++;
+       smp_mb();
 
-       write_ctree_super(trans, log->fs_info->tree_root);
-       log->fs_info->tree_log_transid++;
-       log->fs_info->tree_log_batch = 0;
-       atomic_set(&log->fs_info->tree_log_commit, 0);
+       mutex_unlock(&log_root_tree->log_mutex);
+
+       /*
+        * nobody else is going to jump in and write the the ctree
+        * super here because the log_commit atomic below is protecting
+        * us.  We must be called with a transaction handle pinning
+        * the running transaction open, so a full commit can't hop
+        * in and cause problems either.
+        */
+       write_ctree_super(trans, root->fs_info->tree_root, 2);
+
+       atomic_set(&log_root_tree->log_commit[index2], 0);
        smp_mb();
-       if (waitqueue_active(&log->fs_info->tree_log_wait))
-               wake_up(&log->fs_info->tree_log_wait);
+       if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
+               wake_up(&log_root_tree->log_commit_wait[index2]);
 out:
-       mutex_unlock(&log->fs_info->tree_log_mutex);
+       atomic_set(&root->log_commit[index1], 0);
+       smp_mb();
+       if (waitqueue_active(&root->log_commit_wait[index1]))
+               wake_up(&root->log_commit_wait[index1]);
        return 0;
-
 }
 
-/*
- * free all the extents used by the tree log.  This should be called
+/* * free all the extents used by the tree log.  This should be called
  * at commit time of the full transaction
  */
 int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
@@ -2033,46 +2006,39 @@ int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
        int ret;
        struct btrfs_root *log;
        struct key;
+       u64 start;
+       u64 end;
        struct walk_control wc = {
                .free = 1,
                .process_func = process_one_buffer
        };
 
-       if (!root->log_root)
+       if (!root->log_root || root->fs_info->log_root_recovering)
                return 0;
 
        log = root->log_root;
        ret = walk_log_tree(trans, log, &wc);
        BUG_ON(ret);
 
-       log = root->log_root;
-       ret = btrfs_del_root(trans, root->fs_info->log_root_tree,
-                            &log->root_key);
-       BUG_ON(ret);
-       root->log_root = NULL;
-       kfree(root->log_root);
-       return 0;
-}
-
-/*
- * helper function to update the item for a given subvolumes log root
- * in the tree of log roots
- */
-static int update_log_root(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *log)
-{
-       u64 bytenr = btrfs_root_bytenr(&log->root_item);
-       int ret;
+       while (1) {
+               ret = find_first_extent_bit(&log->dirty_log_pages,
+                                   0, &start, &end, EXTENT_DIRTY);
+               if (ret)
+                       break;
 
-       if (log->node->start == bytenr)
-               return 0;
+               clear_extent_dirty(&log->dirty_log_pages,
+                                  start, end, GFP_NOFS);
+       }
 
-       btrfs_set_root_bytenr(&log->root_item, log->node->start);
-       btrfs_set_root_level(&log->root_item, btrfs_header_level(log->node));
-       ret = btrfs_update_root(trans, log->fs_info->log_root_tree,
-                               &log->root_key, &log->root_item);
-       BUG_ON(ret);
-       return ret;
+       if (log->log_transid > 0) {
+               ret = btrfs_del_root(trans, root->fs_info->log_root_tree,
+                                    &log->root_key);
+               BUG_ON(ret);
+       }
+       root->log_root = NULL;
+       free_extent_buffer(log->node);
+       kfree(log);
+       return 0;
 }
 
 /*
@@ -2107,6 +2073,9 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
        int ret;
        int bytes_del = 0;
 
+       if (BTRFS_I(dir)->logged_trans < trans->transid)
+               return 0;
+
        ret = join_running_log_trans(root);
        if (ret)
                return 0;
@@ -2178,6 +2147,9 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
        u64 index;
        int ret;
 
+       if (BTRFS_I(inode)->logged_trans < trans->transid)
+               return 0;
+
        ret = join_running_log_trans(root);
        if (ret)
                return 0;
@@ -2189,8 +2161,6 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
        mutex_unlock(&BTRFS_I(inode)->log_mutex);
        end_log_trans(root);
 
-       if (ret == 0 || ret == -ENOENT)
-               return 0;
        return ret;
 }
 
@@ -2287,9 +2257,8 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
                        struct btrfs_key tmp;
                        btrfs_item_key_to_cpu(path->nodes[0], &tmp,
                                              path->slots[0]);
-                       if (key_type == tmp.type) {
+                       if (key_type == tmp.type)
                                first_offset = max(min_offset, tmp.offset) + 1;
-                       }
                }
                goto done;
        }
@@ -2319,7 +2288,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
         * we have a block from this transaction, log every item in it
         * from our directory
         */
-       while(1) {
+       while (1) {
                struct btrfs_key tmp;
                src = path->nodes[0];
                nritems = btrfs_header_nritems(src);
@@ -2396,7 +2365,7 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
 again:
        min_key = 0;
        max_key = 0;
-       while(1) {
+       while (1) {
                ret = log_dir_items(trans, root, inode, path,
                                    dst_path, key_type, min_key,
                                    &max_key);
@@ -2432,7 +2401,7 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,
        key.type = max_key_type;
        key.offset = (u64)-1;
 
-       while(1) {
+       while (1) {
                ret = btrfs_search_slot(trans, log, &key, path, -1, 1);
 
                if (ret != 1)
@@ -2456,6 +2425,126 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+static noinline int copy_items(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *log,
+                              struct btrfs_path *dst_path,
+                              struct extent_buffer *src,
+                              int start_slot, int nr, int inode_only)
+{
+       unsigned long src_offset;
+       unsigned long dst_offset;
+       struct btrfs_file_extent_item *extent;
+       struct btrfs_inode_item *inode_item;
+       int ret;
+       struct btrfs_key *ins_keys;
+       u32 *ins_sizes;
+       char *ins_data;
+       int i;
+       struct list_head ordered_sums;
+
+       INIT_LIST_HEAD(&ordered_sums);
+
+       ins_data = kmalloc(nr * sizeof(struct btrfs_key) +
+                          nr * sizeof(u32), GFP_NOFS);
+       ins_sizes = (u32 *)ins_data;
+       ins_keys = (struct btrfs_key *)(ins_data + nr * sizeof(u32));
+
+       for (i = 0; i < nr; i++) {
+               ins_sizes[i] = btrfs_item_size_nr(src, i + start_slot);
+               btrfs_item_key_to_cpu(src, ins_keys + i, i + start_slot);
+       }
+       ret = btrfs_insert_empty_items(trans, log, dst_path,
+                                      ins_keys, ins_sizes, nr);
+       BUG_ON(ret);
+
+       for (i = 0; i < nr; i++) {
+               dst_offset = btrfs_item_ptr_offset(dst_path->nodes[0],
+                                                  dst_path->slots[0]);
+
+               src_offset = btrfs_item_ptr_offset(src, start_slot + i);
+
+               copy_extent_buffer(dst_path->nodes[0], src, dst_offset,
+                                  src_offset, ins_sizes[i]);
+
+               if (inode_only == LOG_INODE_EXISTS &&
+                   ins_keys[i].type == BTRFS_INODE_ITEM_KEY) {
+                       inode_item = btrfs_item_ptr(dst_path->nodes[0],
+                                                   dst_path->slots[0],
+                                                   struct btrfs_inode_item);
+                       btrfs_set_inode_size(dst_path->nodes[0], inode_item, 0);
+
+                       /* set the generation to zero so the recover code
+                        * can tell the difference between an logging
+                        * just to say 'this inode exists' and a logging
+                        * to say 'update this inode with these values'
+                        */
+                       btrfs_set_inode_generation(dst_path->nodes[0],
+                                                  inode_item, 0);
+               }
+               /* take a reference on file data extents so that truncates
+                * or deletes of this inode don't have to relog the inode
+                * again
+                */
+               if (btrfs_key_type(ins_keys + i) == BTRFS_EXTENT_DATA_KEY) {
+                       int found_type;
+                       extent = btrfs_item_ptr(src, start_slot + i,
+                                               struct btrfs_file_extent_item);
+
+                       found_type = btrfs_file_extent_type(src, extent);
+                       if (found_type == BTRFS_FILE_EXTENT_REG ||
+                           found_type == BTRFS_FILE_EXTENT_PREALLOC) {
+                               u64 ds = btrfs_file_extent_disk_bytenr(src,
+                                                                  extent);
+                               u64 dl = btrfs_file_extent_disk_num_bytes(src,
+                                                                     extent);
+                               u64 cs = btrfs_file_extent_offset(src, extent);
+                               u64 cl = btrfs_file_extent_num_bytes(src,
+                                                                    extent);;
+                               if (btrfs_file_extent_compression(src,
+                                                                 extent)) {
+                                       cs = 0;
+                                       cl = dl;
+                               }
+                               /* ds == 0 is a hole */
+                               if (ds != 0) {
+                                       ret = btrfs_inc_extent_ref(trans, log,
+                                                  ds, dl,
+                                                  dst_path->nodes[0]->start,
+                                                  BTRFS_TREE_LOG_OBJECTID,
+                                                  trans->transid,
+                                                  ins_keys[i].objectid);
+                                       BUG_ON(ret);
+                                       ret = btrfs_lookup_csums_range(
+                                                  log->fs_info->csum_root,
+                                                  ds + cs, ds + cs + cl - 1,
+                                                  &ordered_sums);
+                                       BUG_ON(ret);
+                               }
+                       }
+               }
+               dst_path->slots[0]++;
+       }
+
+       btrfs_mark_buffer_dirty(dst_path->nodes[0]);
+       btrfs_release_path(log, dst_path);
+       kfree(ins_data);
+
+       /*
+        * we have to do this after the loop above to avoid changing the
+        * log tree while trying to change the log tree.
+        */
+       while (!list_empty(&ordered_sums)) {
+               struct btrfs_ordered_sum *sums = list_entry(ordered_sums.next,
+                                                  struct btrfs_ordered_sum,
+                                                  list);
+               ret = btrfs_csum_file_blocks(trans, log, sums);
+               BUG_ON(ret);
+               list_del(&sums->list);
+               kfree(sums);
+       }
+       return 0;
+}
+
 /* log a single inode in the tree log.
  * At least one parent directory for this inode must exist in the tree
  * or be logged already.
@@ -2479,13 +2568,12 @@ static int __btrfs_log_inode(struct btrfs_trans_handle *trans,
        struct btrfs_key min_key;
        struct btrfs_key max_key;
        struct btrfs_root *log = root->log_root;
-       unsigned long src_offset;
-       unsigned long dst_offset;
-       struct extent_buffer *src;
-       struct btrfs_file_extent_item *extent;
-       struct btrfs_inode_item *inode_item;
+       struct extent_buffer *src = NULL;
        u32 size;
        int ret;
+       int nritems;
+       int ins_start_slot = 0;
+       int ins_nr;
 
        log = root->log_root;
 
@@ -2538,12 +2626,14 @@ static int __btrfs_log_inode(struct btrfs_trans_handle *trans,
        BUG_ON(ret);
        path->keep_locks = 1;
 
-       while(1) {
+       while (1) {
+               ins_nr = 0;
                ret = btrfs_search_forward(root, &min_key, &max_key,
                                           path, 0, trans->transid);
                if (ret != 0)
                        break;
-
+again:
+               /* note, ins_nr might be > 0 here, cleanup outside the loop */
                if (min_key.objectid != inode->i_ino)
                        break;
                if (min_key.type > max_key.type)
@@ -2551,65 +2641,37 @@ static int __btrfs_log_inode(struct btrfs_trans_handle *trans,
 
                src = path->nodes[0];
                size = btrfs_item_size_nr(src, path->slots[0]);
-               ret = btrfs_insert_empty_item(trans, log, dst_path, &min_key,
-                                             size);
-               if (ret)
-                       BUG();
-
-               dst_offset = btrfs_item_ptr_offset(dst_path->nodes[0],
-                                                  dst_path->slots[0]);
-
-               src_offset = btrfs_item_ptr_offset(src, path->slots[0]);
-
-               copy_extent_buffer(dst_path->nodes[0], src, dst_offset,
-                                  src_offset, size);
+               if (ins_nr && ins_start_slot + ins_nr == path->slots[0]) {
+                       ins_nr++;
+                       goto next_slot;
+               } else if (!ins_nr) {
+                       ins_start_slot = path->slots[0];
+                       ins_nr = 1;
+                       goto next_slot;
+               }
 
-               if (inode_only == LOG_INODE_EXISTS &&
-                   min_key.type == BTRFS_INODE_ITEM_KEY) {
-                       inode_item = btrfs_item_ptr(dst_path->nodes[0],
-                                                   dst_path->slots[0],
-                                                   struct btrfs_inode_item);
-                       btrfs_set_inode_size(dst_path->nodes[0], inode_item, 0);
+               ret = copy_items(trans, log, dst_path, src, ins_start_slot,
+                                ins_nr, inode_only);
+               BUG_ON(ret);
+               ins_nr = 1;
+               ins_start_slot = path->slots[0];
+next_slot:
 
-                       /* set the generation to zero so the recover code
-                        * can tell the difference between an logging
-                        * just to say 'this inode exists' and a logging
-                        * to say 'update this inode with these values'
-                        */
-                       btrfs_set_inode_generation(dst_path->nodes[0],
-                                                  inode_item, 0);
+               nritems = btrfs_header_nritems(path->nodes[0]);
+               path->slots[0]++;
+               if (path->slots[0] < nritems) {
+                       btrfs_item_key_to_cpu(path->nodes[0], &min_key,
+                                             path->slots[0]);
+                       goto again;
                }
-               /* take a reference on file data extents so that truncates
-                * or deletes of this inode don't have to relog the inode
-                * again
-                */
-               if (btrfs_key_type(&min_key) == BTRFS_EXTENT_DATA_KEY) {
-                       int found_type;
-                       extent = btrfs_item_ptr(src, path->slots[0],
-                                               struct btrfs_file_extent_item);
-
-                       found_type = btrfs_file_extent_type(src, extent);
-                       if (found_type == BTRFS_FILE_EXTENT_REG) {
-                               u64 ds = btrfs_file_extent_disk_bytenr(src,
-                                                                  extent);
-                               u64 dl = btrfs_file_extent_disk_num_bytes(src,
-                                                                     extent);
-                               /* ds == 0 is a hole */
-                               if (ds != 0) {
-                                       ret = btrfs_inc_extent_ref(trans, log,
-                                                  ds, dl,
-                                                  log->root_key.objectid,
-                                                  0,
-                                                  inode->i_ino,
-                                                  min_key.offset);
-                                       BUG_ON(ret);
-                               }
-                       }
+               if (ins_nr) {
+                       ret = copy_items(trans, log, dst_path, src,
+                                        ins_start_slot,
+                                        ins_nr, inode_only);
+                       BUG_ON(ret);
+                       ins_nr = 0;
                }
-
-               btrfs_mark_buffer_dirty(dst_path->nodes[0]);
                btrfs_release_path(root, path);
-               btrfs_release_path(log, dst_path);
 
                if (min_key.offset < (u64)-1)
                        min_key.offset++;
@@ -2620,21 +2682,26 @@ static int __btrfs_log_inode(struct btrfs_trans_handle *trans,
                else
                        break;
        }
+       if (ins_nr) {
+               ret = copy_items(trans, log, dst_path, src,
+                                ins_start_slot,
+                                ins_nr, inode_only);
+               BUG_ON(ret);
+               ins_nr = 0;
+       }
+       WARN_ON(ins_nr);
        if (inode_only == LOG_INODE_ALL && S_ISDIR(inode->i_mode)) {
                btrfs_release_path(root, path);
                btrfs_release_path(log, dst_path);
+               BTRFS_I(inode)->log_dirty_trans = 0;
                ret = log_directory_changes(trans, root, inode, path, dst_path);
                BUG_ON(ret);
        }
+       BTRFS_I(inode)->logged_trans = trans->transid;
        mutex_unlock(&BTRFS_I(inode)->log_mutex);
 
        btrfs_free_path(path);
        btrfs_free_path(dst_path);
-
-       mutex_lock(&root->fs_info->tree_log_mutex);
-       ret = update_log_root(trans, log);
-       BUG_ON(ret);
-       mutex_unlock(&root->fs_info->tree_log_mutex);
 out:
        return 0;
 }
@@ -2666,7 +2733,7 @@ int btrfs_log_dentry(struct btrfs_trans_handle *trans,
 
        start_log_trans(trans, root);
        sb = dentry->d_inode->i_sb;
-       while(1) {
+       while (1) {
                ret = __btrfs_log_inode(trans, root, dentry->d_inode,
                                        inode_only);
                BUG_ON(ret);
@@ -2737,7 +2804,7 @@ again:
        key.offset = (u64)-1;
        btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
 
-       while(1) {
+       while (1) {
                ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0);
                if (ret < 0)
                        break;
@@ -2762,10 +2829,12 @@ again:
                tmp_key.offset = (u64)-1;
 
                wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);
-
                BUG_ON(!wc.replay_dest);
 
+               wc.replay_dest->log_root = log;
+               mutex_lock(&fs_info->trans_mutex);
                btrfs_record_root_in_trans(wc.replay_dest);
+               mutex_unlock(&fs_info->trans_mutex);
                ret = walk_log_tree(trans, log, &wc);
                BUG_ON(ret);
 
@@ -2781,6 +2850,7 @@ again:
                }
 
                key.offset = found_key.offset - 1;
+               wc.replay_dest->log_root = NULL;
                free_extent_buffer(log->node);
                kfree(log);