On Thu, Mar 13, 2014 at 03:42:13PM -0400, Josef Bacik wrote:
> Lets try this again.  We can deadlock the box if we send on a box and try to
> write onto the same fs with the app that is trying to listen to the send pipe.
> This is because the writer could get stuck waiting for a transaction commit
> which is being blocked by the send.  So fix this by making sure looking at the
> commit roots is always going to be consistent.  We do this by keeping track of
> which roots need to have their commit roots swapped during commit, and then
> taking the commit_root_sem and swapping them all at once.  Then make sure we
> take a read lock on the commit_root_sem in cases where we search the commit 
> root
> to make sure we're always looking at a consistent view of the commit roots.
> Previously we had problems with this because we would swap a fs tree commit 
> root
> and then swap the extent tree commit root independently which would cause the
> backref walking code to screw up sometimes.  With this patch we no longer
> deadlock and pass all the weird send/receive corner cases.  Thanks,

   There's something still going on here. I managed to get about twice
as far through my test as I had before, but I again got an "unexpected
EOF in stream", with btrfs send returning 1. As before, I have this in
syslog:

Mar 13 22:09:12 s_src@amelia kernel: BTRFS error (device sda2): did not find 
backref in send_root. inode=1786631, offset=825257984, disk_byte=36504023040 
found extent=36504023040\x0a

   So, on the evidence of one data point (I'll have another one when I
wake up tomorrow morning), this has made the problem harder to trigger
but it's still possible.

   Hugo.

> Reportedy-by: Hugo Mills <h...@carfax.org.uk>
> Signed-off-by: Josef Bacik <jba...@fb.com>
> ---
>  fs/btrfs/backref.c     | 33 +++++++++++++++----
>  fs/btrfs/ctree.c       | 88 
> --------------------------------------------------
>  fs/btrfs/ctree.h       |  3 +-
>  fs/btrfs/disk-io.c     |  3 +-
>  fs/btrfs/extent-tree.c | 20 ++++++------
>  fs/btrfs/inode-map.c   | 14 ++++----
>  fs/btrfs/send.c        | 57 ++------------------------------
>  fs/btrfs/transaction.c | 45 ++++++++++++++++----------
>  fs/btrfs/transaction.h |  1 +
>  9 files changed, 77 insertions(+), 187 deletions(-)
> 
> diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
> index 860f4f2..0be0e94 100644
> --- a/fs/btrfs/backref.c
> +++ b/fs/btrfs/backref.c
> @@ -329,7 +329,10 @@ static int __resolve_indirect_ref(struct btrfs_fs_info 
> *fs_info,
>               goto out;
>       }
>  
> -     root_level = btrfs_old_root_level(root, time_seq);
> +     if (path->search_commit_root)
> +             root_level = btrfs_header_level(root->commit_root);
> +     else
> +             root_level = btrfs_old_root_level(root, time_seq);
>  
>       if (root_level + 1 == level) {
>               srcu_read_unlock(&fs_info->subvol_srcu, index);
> @@ -1092,9 +1095,9 @@ static int btrfs_find_all_leafs(struct 
> btrfs_trans_handle *trans,
>   *
>   * returns 0 on success, < 0 on error.
>   */
> -int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> -                             struct btrfs_fs_info *fs_info, u64 bytenr,
> -                             u64 time_seq, struct ulist **roots)
> +static int __btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> +                               struct btrfs_fs_info *fs_info, u64 bytenr,
> +                               u64 time_seq, struct ulist **roots)
>  {
>       struct ulist *tmp;
>       struct ulist_node *node = NULL;
> @@ -1130,6 +1133,20 @@ int btrfs_find_all_roots(struct btrfs_trans_handle 
> *trans,
>       return 0;
>  }
>  
> +int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> +                      struct btrfs_fs_info *fs_info, u64 bytenr,
> +                      u64 time_seq, struct ulist **roots)
> +{
> +     int ret;
> +
> +     if (!trans)
> +             down_read(&fs_info->commit_root_sem);
> +     ret = __btrfs_find_all_roots(trans, fs_info, bytenr, time_seq, roots);
> +     if (!trans)
> +             up_read(&fs_info->commit_root_sem);
> +     return ret;
> +}
> +
>  /*
>   * this makes the path point to (inum INODE_ITEM ioff)
>   */
> @@ -1509,6 +1526,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
>               if (IS_ERR(trans))
>                       return PTR_ERR(trans);
>               btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem);
> +     } else {
> +             down_read(&fs_info->commit_root_sem);
>       }
>  
>       ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid,
> @@ -1519,8 +1538,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
>  
>       ULIST_ITER_INIT(&ref_uiter);
>       while (!ret && (ref_node = ulist_next(refs, &ref_uiter))) {
> -             ret = btrfs_find_all_roots(trans, fs_info, ref_node->val,
> -                                        tree_mod_seq_elem.seq, &roots);
> +             ret = __btrfs_find_all_roots(trans, fs_info, ref_node->val,
> +                                          tree_mod_seq_elem.seq, &roots);
>               if (ret)
>                       break;
>               ULIST_ITER_INIT(&root_uiter);
> @@ -1542,6 +1561,8 @@ out:
>       if (!search_commit_root) {
>               btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
>               btrfs_end_transaction(trans, fs_info->extent_root);
> +     } else {
> +             up_read(&fs_info->commit_root_sem);
>       }
>  
>       return ret;
> diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
> index 88d1b1e..9d89c16 100644
> --- a/fs/btrfs/ctree.c
> +++ b/fs/btrfs/ctree.c
> @@ -5360,7 +5360,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>  {
>       int ret;
>       int cmp;
> -     struct btrfs_trans_handle *trans = NULL;
>       struct btrfs_path *left_path = NULL;
>       struct btrfs_path *right_path = NULL;
>       struct btrfs_key left_key;
> @@ -5378,9 +5377,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>       u64 right_blockptr;
>       u64 left_gen;
>       u64 right_gen;
> -     u64 left_start_ctransid;
> -     u64 right_start_ctransid;
> -     u64 ctransid;
>  
>       left_path = btrfs_alloc_path();
>       if (!left_path) {
> @@ -5404,21 +5400,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>       right_path->search_commit_root = 1;
>       right_path->skip_locking = 1;
>  
> -     spin_lock(&left_root->root_item_lock);
> -     left_start_ctransid = btrfs_root_ctransid(&left_root->root_item);
> -     spin_unlock(&left_root->root_item_lock);
> -
> -     spin_lock(&right_root->root_item_lock);
> -     right_start_ctransid = btrfs_root_ctransid(&right_root->root_item);
> -     spin_unlock(&right_root->root_item_lock);
> -
> -     trans = btrfs_join_transaction(left_root);
> -     if (IS_ERR(trans)) {
> -             ret = PTR_ERR(trans);
> -             trans = NULL;
> -             goto out;
> -     }
> -
>       /*
>        * Strategy: Go to the first items of both trees. Then do
>        *
> @@ -5482,67 +5463,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>       advance_left = advance_right = 0;
>  
>       while (1) {
> -             /*
> -              * We need to make sure the transaction does not get committed
> -              * while we do anything on commit roots. This means, we need to
> -              * join and leave transactions for every item that we process.
> -              */
> -             if (trans && btrfs_should_end_transaction(trans, left_root)) {
> -                     btrfs_release_path(left_path);
> -                     btrfs_release_path(right_path);
> -
> -                     ret = btrfs_end_transaction(trans, left_root);
> -                     trans = NULL;
> -                     if (ret < 0)
> -                             goto out;
> -             }
> -             /* now rejoin the transaction */
> -             if (!trans) {
> -                     trans = btrfs_join_transaction(left_root);
> -                     if (IS_ERR(trans)) {
> -                             ret = PTR_ERR(trans);
> -                             trans = NULL;
> -                             goto out;
> -                     }
> -
> -                     spin_lock(&left_root->root_item_lock);
> -                     ctransid = btrfs_root_ctransid(&left_root->root_item);
> -                     spin_unlock(&left_root->root_item_lock);
> -                     if (ctransid != left_start_ctransid)
> -                             left_start_ctransid = 0;
> -
> -                     spin_lock(&right_root->root_item_lock);
> -                     ctransid = btrfs_root_ctransid(&right_root->root_item);
> -                     spin_unlock(&right_root->root_item_lock);
> -                     if (ctransid != right_start_ctransid)
> -                             right_start_ctransid = 0;
> -
> -                     if (!left_start_ctransid || !right_start_ctransid) {
> -                             WARN(1, KERN_WARNING
> -                                     "BTRFS: btrfs_compare_tree detected "
> -                                     "a change in one of the trees while "
> -                                     "iterating. This is probably a "
> -                                     "bug.\n");
> -                             ret = -EIO;
> -                             goto out;
> -                     }
> -
> -                     /*
> -                      * the commit root may have changed, so start again
> -                      * where we stopped
> -                      */
> -                     left_path->lowest_level = left_level;
> -                     right_path->lowest_level = right_level;
> -                     ret = btrfs_search_slot(NULL, left_root,
> -                                     &left_key, left_path, 0, 0);
> -                     if (ret < 0)
> -                             goto out;
> -                     ret = btrfs_search_slot(NULL, right_root,
> -                                     &right_key, right_path, 0, 0);
> -                     if (ret < 0)
> -                             goto out;
> -             }
> -
>               if (advance_left && !left_end_reached) {
>                       ret = tree_advance(left_root, left_path, &left_level,
>                                       left_root_level,
> @@ -5672,14 +5592,6 @@ out:
>       btrfs_free_path(left_path);
>       btrfs_free_path(right_path);
>       kfree(tmp_buf);
> -
> -     if (trans) {
> -             if (!ret)
> -                     ret = btrfs_end_transaction(trans, left_root);
> -             else
> -                     btrfs_end_transaction(trans, left_root);
> -     }
> -
>       return ret;
>  }
>  
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 2a9d32e..4253ab2 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -1440,7 +1440,7 @@ struct btrfs_fs_info {
>        */
>       struct mutex ordered_extent_flush_mutex;
>  
> -     struct rw_semaphore extent_commit_sem;
> +     struct rw_semaphore commit_root_sem;
>  
>       struct rw_semaphore cleanup_work_sem;
>  
> @@ -1711,7 +1711,6 @@ struct btrfs_root {
>       struct btrfs_block_rsv *block_rsv;
>  
>       /* free ino cache stuff */
> -     struct mutex fs_commit_mutex;
>       struct btrfs_free_space_ctl *free_ino_ctl;
>       enum btrfs_caching_type cached;
>       spinlock_t cache_lock;
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index d9698fd..a152a96 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -1549,7 +1549,6 @@ int btrfs_init_fs_root(struct btrfs_root *root)
>       root->subv_writers = writers;
>  
>       btrfs_init_free_ino_ctl(root);
> -     mutex_init(&root->fs_commit_mutex);
>       spin_lock_init(&root->cache_lock);
>       init_waitqueue_head(&root->cache_wait);
>  
> @@ -2327,7 +2326,7 @@ int open_ctree(struct super_block *sb,
>       mutex_init(&fs_info->transaction_kthread_mutex);
>       mutex_init(&fs_info->cleaner_mutex);
>       mutex_init(&fs_info->volume_mutex);
> -     init_rwsem(&fs_info->extent_commit_sem);
> +     init_rwsem(&fs_info->commit_root_sem);
>       init_rwsem(&fs_info->cleanup_work_sem);
>       init_rwsem(&fs_info->subvol_sem);
>       sema_init(&fs_info->uuid_tree_rescan_sem, 1);
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index c6b6a6e..696f0b6 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -419,7 +419,7 @@ static noinline void caching_thread(struct btrfs_work 
> *work)
>  again:
>       mutex_lock(&caching_ctl->mutex);
>       /* need to make sure the commit_root doesn't disappear */
> -     down_read(&fs_info->extent_commit_sem);
> +     down_read(&fs_info->commit_root_sem);
>  
>  next:
>       ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
> @@ -443,10 +443,10 @@ next:
>                               break;
>  
>                       if (need_resched() ||
> -                         rwsem_is_contended(&fs_info->extent_commit_sem)) {
> +                         rwsem_is_contended(&fs_info->commit_root_sem)) {
>                               caching_ctl->progress = last;
>                               btrfs_release_path(path);
> -                             up_read(&fs_info->extent_commit_sem);
> +                             up_read(&fs_info->commit_root_sem);
>                               mutex_unlock(&caching_ctl->mutex);
>                               cond_resched();
>                               goto again;
> @@ -513,7 +513,7 @@ next:
>  
>  err:
>       btrfs_free_path(path);
> -     up_read(&fs_info->extent_commit_sem);
> +     up_read(&fs_info->commit_root_sem);
>  
>       free_excluded_extents(extent_root, block_group);
>  
> @@ -633,10 +633,10 @@ static int cache_block_group(struct 
> btrfs_block_group_cache *cache,
>               return 0;
>       }
>  
> -     down_write(&fs_info->extent_commit_sem);
> +     down_write(&fs_info->commit_root_sem);
>       atomic_inc(&caching_ctl->count);
>       list_add_tail(&caching_ctl->list, &fs_info->caching_block_groups);
> -     up_write(&fs_info->extent_commit_sem);
> +     up_write(&fs_info->commit_root_sem);
>  
>       btrfs_get_block_group(cache);
>  
> @@ -5470,7 +5470,7 @@ void btrfs_prepare_extent_commit(struct 
> btrfs_trans_handle *trans,
>       struct btrfs_block_group_cache *cache;
>       struct btrfs_space_info *space_info;
>  
> -     down_write(&fs_info->extent_commit_sem);
> +     down_write(&fs_info->commit_root_sem);
>  
>       list_for_each_entry_safe(caching_ctl, next,
>                                &fs_info->caching_block_groups, list) {
> @@ -5489,7 +5489,7 @@ void btrfs_prepare_extent_commit(struct 
> btrfs_trans_handle *trans,
>       else
>               fs_info->pinned_extents = &fs_info->freed_extents[0];
>  
> -     up_write(&fs_info->extent_commit_sem);
> +     up_write(&fs_info->commit_root_sem);
>  
>       list_for_each_entry_rcu(space_info, &fs_info->space_info, list)
>               percpu_counter_set(&space_info->total_bytes_pinned, 0);
> @@ -8255,14 +8255,14 @@ int btrfs_free_block_groups(struct btrfs_fs_info 
> *info)
>       struct btrfs_caching_control *caching_ctl;
>       struct rb_node *n;
>  
> -     down_write(&info->extent_commit_sem);
> +     down_write(&info->commit_root_sem);
>       while (!list_empty(&info->caching_block_groups)) {
>               caching_ctl = list_entry(info->caching_block_groups.next,
>                                        struct btrfs_caching_control, list);
>               list_del(&caching_ctl->list);
>               put_caching_control(caching_ctl);
>       }
> -     up_write(&info->extent_commit_sem);
> +     up_write(&info->commit_root_sem);
>  
>       spin_lock(&info->block_group_cache_lock);
>       while ((n = rb_last(&info->block_group_cache_tree)) != NULL) {
> diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c
> index ab485e5..cc8ca19 100644
> --- a/fs/btrfs/inode-map.c
> +++ b/fs/btrfs/inode-map.c
> @@ -55,7 +55,7 @@ static int caching_kthread(void *data)
>       key.type = BTRFS_INODE_ITEM_KEY;
>  again:
>       /* need to make sure the commit_root doesn't disappear */
> -     mutex_lock(&root->fs_commit_mutex);
> +     down_read(&fs_info->commit_root_sem);
>  
>       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
>       if (ret < 0)
> @@ -88,7 +88,7 @@ again:
>                               btrfs_item_key_to_cpu(leaf, &key, 0);
>                               btrfs_release_path(path);
>                               root->cache_progress = last;
> -                             mutex_unlock(&root->fs_commit_mutex);
> +                             up_read(&fs_info->commit_root_sem);
>                               schedule_timeout(1);
>                               goto again;
>                       } else
> @@ -127,7 +127,7 @@ next:
>       btrfs_unpin_free_ino(root);
>  out:
>       wake_up(&root->cache_wait);
> -     mutex_unlock(&root->fs_commit_mutex);
> +     up_read(&fs_info->commit_root_sem);
>  
>       btrfs_free_path(path);
>  
> @@ -223,11 +223,11 @@ again:
>                * or the caching work is done.
>                */
>  
> -             mutex_lock(&root->fs_commit_mutex);
> +             down_write(&root->fs_info->commit_root_sem);
>               spin_lock(&root->cache_lock);
>               if (root->cached == BTRFS_CACHE_FINISHED) {
>                       spin_unlock(&root->cache_lock);
> -                     mutex_unlock(&root->fs_commit_mutex);
> +                     up_write(&root->fs_info->commit_root_sem);
>                       goto again;
>               }
>               spin_unlock(&root->cache_lock);
> @@ -240,7 +240,7 @@ again:
>               else
>                       __btrfs_add_free_space(pinned, objectid, 1);
>  
> -             mutex_unlock(&root->fs_commit_mutex);
> +             up_write(&root->fs_info->commit_root_sem);
>       }
>  }
>  
> @@ -250,7 +250,7 @@ again:
>   * and others will just be dropped, because the commit root we were
>   * searching has changed.
>   *
> - * Must be called with root->fs_commit_mutex held
> + * Must be called with root->fs_info->commit_root_sem held
>   */
>  void btrfs_unpin_free_ino(struct btrfs_root *root)
>  {
> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
> index 6463691..0148999 100644
> --- a/fs/btrfs/send.c
> +++ b/fs/btrfs/send.c
> @@ -1268,8 +1268,10 @@ static int find_extent_clone(struct send_ctx *sctx,
>       }
>       logical = disk_byte + btrfs_file_extent_offset(eb, fi);
>  
> +     down_read(&sctx->send_root->fs_info->commit_root_sem);
>       ret = extent_from_logical(sctx->send_root->fs_info, disk_byte, tmp_path,
>                                 &found_key, &flags);
> +     up_read(&sctx->send_root->fs_info->commit_root_sem);
>       btrfs_release_path(tmp_path);
>  
>       if (ret < 0)
> @@ -5311,57 +5313,21 @@ out:
>  static int full_send_tree(struct send_ctx *sctx)
>  {
>       int ret;
> -     struct btrfs_trans_handle *trans = NULL;
>       struct btrfs_root *send_root = sctx->send_root;
>       struct btrfs_key key;
>       struct btrfs_key found_key;
>       struct btrfs_path *path;
>       struct extent_buffer *eb;
>       int slot;
> -     u64 start_ctransid;
> -     u64 ctransid;
>  
>       path = alloc_path_for_send();
>       if (!path)
>               return -ENOMEM;
>  
> -     spin_lock(&send_root->root_item_lock);
> -     start_ctransid = btrfs_root_ctransid(&send_root->root_item);
> -     spin_unlock(&send_root->root_item_lock);
> -
>       key.objectid = BTRFS_FIRST_FREE_OBJECTID;
>       key.type = BTRFS_INODE_ITEM_KEY;
>       key.offset = 0;
>  
> -join_trans:
> -     /*
> -      * We need to make sure the transaction does not get committed
> -      * while we do anything on commit roots. Join a transaction to prevent
> -      * this.
> -      */
> -     trans = btrfs_join_transaction(send_root);
> -     if (IS_ERR(trans)) {
> -             ret = PTR_ERR(trans);
> -             trans = NULL;
> -             goto out;
> -     }
> -
> -     /*
> -      * Make sure the tree has not changed after re-joining. We detect this
> -      * by comparing start_ctransid and ctransid. They should always match.
> -      */
> -     spin_lock(&send_root->root_item_lock);
> -     ctransid = btrfs_root_ctransid(&send_root->root_item);
> -     spin_unlock(&send_root->root_item_lock);
> -
> -     if (ctransid != start_ctransid) {
> -             WARN(1, KERN_WARNING "BTRFS: the root that you're trying to "
> -                                  "send was modified in between. This is "
> -                                  "probably a bug.\n");
> -             ret = -EIO;
> -             goto out;
> -     }
> -
>       ret = btrfs_search_slot_for_read(send_root, &key, path, 1, 0);
>       if (ret < 0)
>               goto out;
> @@ -5369,19 +5335,6 @@ join_trans:
>               goto out_finish;
>  
>       while (1) {
> -             /*
> -              * When someone want to commit while we iterate, end the
> -              * joined transaction and rejoin.
> -              */
> -             if (btrfs_should_end_transaction(trans, send_root)) {
> -                     ret = btrfs_end_transaction(trans, send_root);
> -                     trans = NULL;
> -                     if (ret < 0)
> -                             goto out;
> -                     btrfs_release_path(path);
> -                     goto join_trans;
> -             }
> -
>               eb = path->nodes[0];
>               slot = path->slots[0];
>               btrfs_item_key_to_cpu(eb, &found_key, slot);
> @@ -5409,12 +5362,6 @@ out_finish:
>  
>  out:
>       btrfs_free_path(path);
> -     if (trans) {
> -             if (!ret)
> -                     ret = btrfs_end_transaction(trans, send_root);
> -             else
> -                     btrfs_end_transaction(trans, send_root);
> -     }
>       return ret;
>  }
>  
> diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
> index a999b85..ab8e6fd 100644
> --- a/fs/btrfs/transaction.c
> +++ b/fs/btrfs/transaction.c
> @@ -75,10 +75,21 @@ void btrfs_put_transaction(struct btrfs_transaction 
> *transaction)
>       }
>  }
>  
> -static noinline void switch_commit_root(struct btrfs_root *root)
> +static noinline void switch_commit_roots(struct btrfs_transaction *trans,
> +                                      struct btrfs_fs_info *fs_info)
>  {
> -     free_extent_buffer(root->commit_root);
> -     root->commit_root = btrfs_root_node(root);
> +     struct btrfs_root *root, *tmp;
> +
> +     down_write(&fs_info->commit_root_sem);
> +     list_for_each_entry_safe(root, tmp, &trans->switch_commits,
> +                              dirty_list) {
> +             list_del_init(&root->dirty_list);
> +             free_extent_buffer(root->commit_root);
> +             root->commit_root = btrfs_root_node(root);
> +             if (is_fstree(root->objectid))
> +                     btrfs_unpin_free_ino(root);
> +     }
> +     up_write(&fs_info->commit_root_sem);
>  }
>  
>  static inline void extwriter_counter_inc(struct btrfs_transaction *trans,
> @@ -208,6 +219,7 @@ loop:
>       INIT_LIST_HEAD(&cur_trans->pending_snapshots);
>       INIT_LIST_HEAD(&cur_trans->ordered_operations);
>       INIT_LIST_HEAD(&cur_trans->pending_chunks);
> +     INIT_LIST_HEAD(&cur_trans->switch_commits);
>       list_add_tail(&cur_trans->list, &fs_info->trans_list);
>       extent_io_tree_init(&cur_trans->dirty_pages,
>                            fs_info->btree_inode->i_mapping);
> @@ -925,9 +937,6 @@ static int update_cowonly_root(struct btrfs_trans_handle 
> *trans,
>                       return ret;
>       }
>  
> -     if (root != root->fs_info->extent_root)
> -             switch_commit_root(root);
> -
>       return 0;
>  }
>  
> @@ -983,15 +992,16 @@ static noinline int commit_cowonly_roots(struct 
> btrfs_trans_handle *trans,
>               list_del_init(next);
>               root = list_entry(next, struct btrfs_root, dirty_list);
>  
> +             if (root != fs_info->extent_root)
> +                     list_add_tail(&root->dirty_list,
> +                                   &trans->transaction->switch_commits);
>               ret = update_cowonly_root(trans, root);
>               if (ret)
>                       return ret;
>       }
>  
> -     down_write(&fs_info->extent_commit_sem);
> -     switch_commit_root(fs_info->extent_root);
> -     up_write(&fs_info->extent_commit_sem);
> -
> +     list_add_tail(&fs_info->extent_root->dirty_list,
> +                   &trans->transaction->switch_commits);
>       btrfs_after_dev_replace_commit(fs_info);
>  
>       return 0;
> @@ -1048,11 +1058,8 @@ static noinline int commit_fs_roots(struct 
> btrfs_trans_handle *trans,
>                       smp_wmb();
>  
>                       if (root->commit_root != root->node) {
> -                             mutex_lock(&root->fs_commit_mutex);
> -                             switch_commit_root(root);
> -                             btrfs_unpin_free_ino(root);
> -                             mutex_unlock(&root->fs_commit_mutex);
> -
> +                             list_add_tail(&root->dirty_list,
> +                                     &trans->transaction->switch_commits);
>                               btrfs_set_root_node(&root->root_item,
>                                                   root->node);
>                       }
> @@ -1863,11 +1870,15 @@ int btrfs_commit_transaction(struct 
> btrfs_trans_handle *trans,
>  
>       btrfs_set_root_node(&root->fs_info->tree_root->root_item,
>                           root->fs_info->tree_root->node);
> -     switch_commit_root(root->fs_info->tree_root);
> +     list_add_tail(&root->fs_info->tree_root->dirty_list,
> +                   &cur_trans->switch_commits);
>  
>       btrfs_set_root_node(&root->fs_info->chunk_root->root_item,
>                           root->fs_info->chunk_root->node);
> -     switch_commit_root(root->fs_info->chunk_root);
> +     list_add_tail(&root->fs_info->chunk_root->dirty_list,
> +                   &cur_trans->switch_commits);
> +
> +     switch_commit_roots(cur_trans, root->fs_info);
>  
>       assert_qgroups_uptodate(trans);
>       update_super_roots(root);
> diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
> index 6ac037e..aa014fe 100644
> --- a/fs/btrfs/transaction.h
> +++ b/fs/btrfs/transaction.h
> @@ -57,6 +57,7 @@ struct btrfs_transaction {
>       struct list_head pending_snapshots;
>       struct list_head ordered_operations;
>       struct list_head pending_chunks;
> +     struct list_head switch_commits;
>       struct btrfs_delayed_ref_root delayed_refs;
>       int aborted;
>  };

-- 
=== Hugo Mills: hugo@... carfax.org.uk | darksatanic.net | lug.org.uk ===
  PGP key: 65E74AC0 from wwwkeys.eu.pgp.net or http://www.carfax.org.uk
   --- The enemy have elected for Death by Powerpoint. That's what ---   
                             they shall get.                             

Attachment: signature.asc
Description: Digital signature

Reply via email to