Btrfs: just wait or commit our own log sub-transaction
authorMiao Xie <miaox@cn.fujitsu.com>
Thu, 20 Feb 2014 10:08:59 +0000 (18:08 +0800)
committerJosef Bacik <jbacik@fb.com>
Mon, 10 Mar 2014 19:16:43 +0000 (15:16 -0400)
We might commit the log sub-transaction which didn't contain the metadata we
logged. It was because we didn't record the log transid and just select
the current log sub-transaction to commit, but the right one might be
committed by the other task already. Actually, we needn't do anything
and it is safe that we go back directly in this case.

This patch improves the log sync by the above idea. We record the transid
of the log sub-transaction in which we log the metadata, and the transid
of the log sub-transaction we have committed. If the committed transid
is >= the transid we record when logging the metadata, we just go back.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Josef Bacik <jbacik@fb.com>
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/tree-log.c
fs/btrfs/tree-log.h

index 906410719acb205eb680f50b32289ac5838d363c..b2c0336db69116dfbefa03ac78fb68ca56373367 100644 (file)
@@ -1723,6 +1723,9 @@ struct btrfs_root {
        atomic_t log_commit[2];
        atomic_t log_batch;
        int log_transid;
+       /* No matter the commit succeeds or not*/
+       int log_transid_committed;
+       /* Just be updated when the commit succeeds. */
        int last_log_commit;
        pid_t log_start_pid;
        bool log_multiple_pids;
index 44f52d280b7d4f06572202567a2925b8e2f0a8c8..dd52146035b3a183b3fcd3e7943a51c5217c3ae7 100644 (file)
@@ -1209,6 +1209,7 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        atomic_set(&root->orphan_inodes, 0);
        atomic_set(&root->refs, 1);
        root->log_transid = 0;
+       root->log_transid_committed = -1;
        root->last_log_commit = 0;
        if (fs_info)
                extent_io_tree_init(&root->dirty_log_pages,
@@ -1422,6 +1423,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
        WARN_ON(root->log_root);
        root->log_root = log_root;
        root->log_transid = 0;
+       root->log_transid_committed = -1;
        root->last_log_commit = 0;
        return 0;
 }
index da6da274dce3315fc6f3730046303c0d7987a044..57d4ca7fd5558667779a6ef80cb6f30c4174784c 100644 (file)
@@ -156,6 +156,7 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
                if (ctx) {
                        index = root->log_transid % 2;
                        list_add_tail(&ctx->list, &root->log_ctxs[index]);
+                       ctx->log_transid = root->log_transid;
                }
                mutex_unlock(&root->log_mutex);
                return 0;
@@ -181,6 +182,7 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
        if (ctx) {
                index = root->log_transid % 2;
                list_add_tail(&ctx->list, &root->log_ctxs[index]);
+               ctx->log_transid = root->log_transid;
        }
 out:
        mutex_unlock(&root->log_mutex);
@@ -2387,13 +2389,13 @@ static void wait_log_commit(struct btrfs_trans_handle *trans,
                                &wait, TASK_UNINTERRUPTIBLE);
                mutex_unlock(&root->log_mutex);
 
-               if (root->log_transid < transid + 2 &&
+               if (root->log_transid_committed < transid &&
                    atomic_read(&root->log_commit[index]))
                        schedule();
 
                finish_wait(&root->log_commit_wait[index], &wait);
                mutex_lock(&root->log_mutex);
-       } while (root->log_transid < transid + 2 &&
+       } while (root->log_transid_committed < transid &&
                 atomic_read(&root->log_commit[index]));
 }
 
@@ -2470,18 +2472,24 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        struct blk_plug plug;
 
        mutex_lock(&root->log_mutex);
-       log_transid = root->log_transid;
-       index1 = root->log_transid % 2;
+       log_transid = ctx->log_transid;
+       if (root->log_transid_committed >= log_transid) {
+               mutex_unlock(&root->log_mutex);
+               return ctx->log_ret;
+       }
+
+       index1 = log_transid % 2;
        if (atomic_read(&root->log_commit[index1])) {
-               wait_log_commit(trans, root, root->log_transid);
+               wait_log_commit(trans, root, log_transid);
                mutex_unlock(&root->log_mutex);
                return ctx->log_ret;
        }
+       ASSERT(log_transid == root->log_transid);
        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(trans, root, root->log_transid - 1);
+               wait_log_commit(trans, root, log_transid - 1);
 
        while (1) {
                int batch = atomic_read(&root->log_batch);
@@ -2535,9 +2543,16 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
         */
        mutex_unlock(&root->log_mutex);
 
+       btrfs_init_log_ctx(&root_log_ctx);
+
        mutex_lock(&log_root_tree->log_mutex);
        atomic_inc(&log_root_tree->log_batch);
        atomic_inc(&log_root_tree->log_writers);
+
+       index2 = log_root_tree->log_transid % 2;
+       list_add_tail(&root_log_ctx.list, &log_root_tree->log_ctxs[index2]);
+       root_log_ctx.log_transid = log_root_tree->log_transid;
+
        mutex_unlock(&log_root_tree->log_mutex);
 
        ret = update_log_root(trans, log);
@@ -2550,6 +2565,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        }
 
        if (ret) {
+               if (!list_empty(&root_log_ctx.list))
+                       list_del_init(&root_log_ctx.list);
+
                blk_finish_plug(&plug);
                if (ret != -ENOSPC) {
                        btrfs_abort_transaction(trans, root, ret);
@@ -2565,26 +2583,29 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
                goto out;
        }
 
-       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 (log_root_tree->log_transid_committed >= root_log_ctx.log_transid) {
+               mutex_unlock(&log_root_tree->log_mutex);
+               ret = root_log_ctx.log_ret;
+               goto out;
+       }
 
+       index2 = root_log_ctx.log_transid % 2;
        if (atomic_read(&log_root_tree->log_commit[index2])) {
                blk_finish_plug(&plug);
                btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
                wait_log_commit(trans, log_root_tree,
-                               log_root_tree->log_transid);
+                               root_log_ctx.log_transid);
                btrfs_free_logged_extents(log, log_transid);
                mutex_unlock(&log_root_tree->log_mutex);
                ret = root_log_ctx.log_ret;
                goto out;
        }
+       ASSERT(root_log_ctx.log_transid == log_root_tree->log_transid);
        atomic_set(&log_root_tree->log_commit[index2], 1);
 
        if (atomic_read(&log_root_tree->log_commit[(index2 + 1) % 2])) {
                wait_log_commit(trans, log_root_tree,
-                               log_root_tree->log_transid - 1);
+                               root_log_ctx.log_transid - 1);
        }
 
        wait_for_writer(trans, log_root_tree);
@@ -2652,26 +2673,22 @@ out_wake_log_root:
         */
        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();
-
+       mutex_lock(&log_root_tree->log_mutex);
+       log_root_tree->log_transid_committed++;
        atomic_set(&log_root_tree->log_commit[index2], 0);
-       smp_mb();
+       mutex_unlock(&log_root_tree->log_mutex);
+
        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();
+       mutex_lock(&root->log_mutex);
+       root->log_transid_committed++;
        atomic_set(&root->log_commit[index1], 0);
+       mutex_unlock(&root->log_mutex);
 
-       smp_mb();
        if (waitqueue_active(&root->log_commit_wait[index1]))
                wake_up(&root->log_commit_wait[index1]);
        return ret;
index 59c1edb31d19f0049987e2e920a0aceb805e1bad..91b145fce3336719a745085dc8497bed380405de 100644 (file)
 
 struct btrfs_log_ctx {
        int log_ret;
+       int log_transid;
        struct list_head list;
 };
 
 static inline void btrfs_init_log_ctx(struct btrfs_log_ctx *ctx)
 {
        ctx->log_ret = 0;
+       ctx->log_transid = 0;
        INIT_LIST_HEAD(&ctx->list);
 }