* syncing the tree wait for us to finish
  */
 static int start_log_trans(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root)
+                          struct btrfs_root *root,
+                          struct btrfs_log_ctx *ctx)
 {
+       int index;
        int ret;
 
        mutex_lock(&root->log_mutex);
 
                atomic_inc(&root->log_batch);
                atomic_inc(&root->log_writers);
+               if (ctx) {
+                       index = root->log_transid % 2;
+                       list_add_tail(&ctx->list, &root->log_ctxs[index]);
+               }
                mutex_unlock(&root->log_mutex);
                return 0;
        }
        root->log_start_pid = current->pid;
        atomic_inc(&root->log_batch);
        atomic_inc(&root->log_writers);
+       if (ctx) {
+               index = root->log_transid % 2;
+               list_add_tail(&ctx->list, &root->log_ctxs[index]);
+       }
 out:
        mutex_unlock(&root->log_mutex);
        return ret;
        return ret;
 }
 
-static int wait_log_commit(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root, int transid)
+static void wait_log_commit(struct btrfs_trans_handle *trans,
+                           struct btrfs_root *root, int transid)
 {
        DEFINE_WAIT(wait);
        int index = transid % 2;
-       int ret = 0;
 
        /*
         * we only allow two pending log transactions at a time,
         * current transaction, we're done
         */
        do {
-               if (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) ==
-                   trans->transid) {
-                       ret = -EAGAIN;
-                       break;
-               }
-
                prepare_to_wait(&root->log_commit_wait[index],
                                &wait, TASK_UNINTERRUPTIBLE);
                mutex_unlock(&root->log_mutex);
                mutex_lock(&root->log_mutex);
        } while (root->log_transid < transid + 2 &&
                 atomic_read(&root->log_commit[index]));
-
-       return ret;
 }
 
 static void wait_for_writer(struct btrfs_trans_handle *trans,
                            struct btrfs_root *root)
 {
        DEFINE_WAIT(wait);
-       while (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) !=
-              trans->transid && atomic_read(&root->log_writers)) {
+
+       while (atomic_read(&root->log_writers)) {
                prepare_to_wait(&root->log_writer_wait,
                                &wait, TASK_UNINTERRUPTIBLE);
                mutex_unlock(&root->log_mutex);
-               if (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) !=
-                   trans->transid && atomic_read(&root->log_writers))
+               if (atomic_read(&root->log_writers))
                        schedule();
                mutex_lock(&root->log_mutex);
                finish_wait(&root->log_writer_wait, &wait);
        }
 }
 
+static inline void btrfs_remove_log_ctx(struct btrfs_root *root,
+                                       struct btrfs_log_ctx *ctx)
+{
+       if (!ctx)
+               return;
+
+       mutex_lock(&root->log_mutex);
+       list_del_init(&ctx->list);
+       mutex_unlock(&root->log_mutex);
+}
+
+/* 
+ * Invoked in log mutex context, or be sure there is no other task which
+ * can access the list.
+ */
+static inline void btrfs_remove_all_log_ctxs(struct btrfs_root *root,
+                                            int index, int error)
+{
+       struct btrfs_log_ctx *ctx;
+
+       if (!error) {
+               INIT_LIST_HEAD(&root->log_ctxs[index]);
+               return;
+       }
+
+       list_for_each_entry(ctx, &root->log_ctxs[index], list)
+               ctx->log_ret = error;
+
+       INIT_LIST_HEAD(&root->log_ctxs[index]);
+}
+
 /*
  * btrfs_sync_log does sends a given tree log down to the disk and
  * updates the super blocks to record it.  When this call is done,
  * that has happened.
  */
 int btrfs_sync_log(struct btrfs_trans_handle *trans,
-                  struct btrfs_root *root)
+                  struct btrfs_root *root, struct btrfs_log_ctx *ctx)
 {
        int index1;
        int index2;
        struct btrfs_root *log = root->log_root;
        struct btrfs_root *log_root_tree = root->fs_info->log_root_tree;
        int log_transid = 0;
+       struct btrfs_log_ctx root_log_ctx;
        struct blk_plug plug;
 
        mutex_lock(&root->log_mutex);
        log_transid = root->log_transid;
        index1 = root->log_transid % 2;
        if (atomic_read(&root->log_commit[index1])) {
-               ret = wait_log_commit(trans, root, root->log_transid);
+               wait_log_commit(trans, root, root->log_transid);
                mutex_unlock(&root->log_mutex);
-               return ret;
+               return ctx->log_ret;
        }
        atomic_set(&root->log_commit[index1], 1);
 
        }
 
        index2 = log_root_tree->log_transid % 2;
+
+       btrfs_init_log_ctx(&root_log_ctx);
+       list_add_tail(&root_log_ctx.list, &log_root_tree->log_ctxs[index2]);
+
        if (atomic_read(&log_root_tree->log_commit[index2])) {
                blk_finish_plug(&plug);
                btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
-               ret = wait_log_commit(trans, log_root_tree,
-                                     log_root_tree->log_transid);
+               wait_log_commit(trans, log_root_tree,
+                               log_root_tree->log_transid);
                btrfs_free_logged_extents(log, log_transid);
                mutex_unlock(&log_root_tree->log_mutex);
+               ret = root_log_ctx.log_ret;
                goto out;
        }
        atomic_set(&log_root_tree->log_commit[index2], 1);
        mutex_unlock(&root->log_mutex);
 
 out_wake_log_root:
+       /*
+        * We needn't get log_mutex here because we are sure all
+        * the other tasks are blocked.
+        */
+       btrfs_remove_all_log_ctxs(log_root_tree, index2, ret);
+
+       /*
+        * It is dangerous if log_commit is changed before we set
+        * ->log_ret of log ctx. Because the readers may not get
+        *  the return value.
+        */
+       smp_wmb();
+
        atomic_set(&log_root_tree->log_commit[index2], 0);
        smp_mb();
        if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
                wake_up(&log_root_tree->log_commit_wait[index2]);
 out:
+       /* See above. */
+       btrfs_remove_all_log_ctxs(root, index1, ret);
+
+       /* See above. */
+       smp_wmb();
        atomic_set(&root->log_commit[index1], 0);
+
        smp_mb();
        if (waitqueue_active(&root->log_commit_wait[index1]))
                wake_up(&root->log_commit_wait[index1]);
  */
 static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
                                  struct btrfs_root *root, struct inode *inode,
-                                 struct dentry *parent, int exists_only)
+                                 struct dentry *parent, int exists_only,
+                                 struct btrfs_log_ctx *ctx)
 {
        int inode_only = exists_only ? LOG_INODE_EXISTS : LOG_INODE_ALL;
        struct super_block *sb;
                goto end_no_trans;
        }
 
-       ret = start_log_trans(trans, root);
+       ret = start_log_trans(trans, root, ctx);
        if (ret)
                goto end_no_trans;
 
                root->fs_info->last_trans_log_full_commit = trans->transid;
                ret = 1;
        }
+
+       if (ret)
+               btrfs_remove_log_ctx(root, ctx);
        btrfs_end_log_trans(root);
 end_no_trans:
        return ret;
  * data on disk.
  */
 int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
-                         struct btrfs_root *root, struct dentry *dentry)
+                         struct btrfs_root *root, struct dentry *dentry,
+                         struct btrfs_log_ctx *ctx)
 {
        struct dentry *parent = dget_parent(dentry);
        int ret;
 
-       ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent, 0);
+       ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent,
+                                    0, ctx);
        dput(parent);
 
        return ret;
                    root->fs_info->last_trans_committed))
                return 0;
 
-       return btrfs_log_inode_parent(trans, root, inode, parent, 1);
+       return btrfs_log_inode_parent(trans, root, inode, parent, 1, NULL);
 }