Btrfs: make sure logged extents complete in the current transaction V3
authorJosef Bacik <jbacik@fb.com>
Fri, 21 Nov 2014 19:52:38 +0000 (14:52 -0500)
committerChris Mason <clm@fb.com>
Fri, 21 Nov 2014 19:58:32 +0000 (11:58 -0800)
Liu Bo pointed out that my previous fix would lose the generation update in the
scenario I described.  It is actually much worse than that, we could lose the
entire extent if we lose power right after the transaction commits.  Consider
the following

write extent 0-4k
log extent in log tree
commit transaction
< power fail happens here
ordered extent completes

We would lose the 0-4k extent because it hasn't updated the actual fs tree, and
the transaction commit will reset the log so it isn't replayed.  If we lose
power before the transaction commit we are save, otherwise we are not.

Fix this by keeping track of all extents we logged in this transaction.  Then
when we go to commit the transaction make sure we wait for all of those ordered
extents to complete before proceeding.  This will make sure that if we lose
power after the transaction commit we still have our data.  This also fixes the
problem of the improperly updated extent generation.  Thanks,

cc: stable@vger.kernel.org
Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/disk-io.c
fs/btrfs/ordered-data.c
fs/btrfs/ordered-data.h
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/tree-log.c

index 7af9a1978a2f93a5a40e1d1fd4c621f24a438abd..6efaee8d773959e80bf07ccb5bd4b9e20b954b79 100644 (file)
@@ -4129,6 +4129,25 @@ again:
        return 0;
 }
 
+static void btrfs_free_pending_ordered(struct btrfs_transaction *cur_trans,
+                                      struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_ordered_extent *ordered;
+
+       spin_lock(&fs_info->trans_lock);
+       while (!list_empty(&cur_trans->pending_ordered)) {
+               ordered = list_first_entry(&cur_trans->pending_ordered,
+                                          struct btrfs_ordered_extent,
+                                          trans_list);
+               list_del_init(&ordered->trans_list);
+               spin_unlock(&fs_info->trans_lock);
+
+               btrfs_put_ordered_extent(ordered);
+               spin_lock(&fs_info->trans_lock);
+       }
+       spin_unlock(&fs_info->trans_lock);
+}
+
 void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
                                   struct btrfs_root *root)
 {
@@ -4140,6 +4159,7 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
        cur_trans->state = TRANS_STATE_UNBLOCKED;
        wake_up(&root->fs_info->transaction_wait);
 
+       btrfs_free_pending_ordered(cur_trans, root->fs_info);
        btrfs_destroy_delayed_inodes(root);
        btrfs_assert_delayed_root_empty(root);
 
index 1401b1af4f06657814592e81d744a5f5ae17131b..9c28eb4da4dd0451dd985a8a98eb0b1507091e34 100644 (file)
@@ -220,6 +220,7 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
        INIT_LIST_HEAD(&entry->work_list);
        init_completion(&entry->completion);
        INIT_LIST_HEAD(&entry->log_list);
+       INIT_LIST_HEAD(&entry->trans_list);
 
        trace_btrfs_ordered_extent_add(inode, entry);
 
@@ -443,6 +444,8 @@ void btrfs_get_logged_extents(struct inode *inode,
                ordered = rb_entry(n, struct btrfs_ordered_extent, rb_node);
                if (!list_empty(&ordered->log_list))
                        continue;
+               if (test_bit(BTRFS_ORDERED_LOGGED, &ordered->flags))
+                       continue;
                list_add_tail(&ordered->log_list, logged_list);
                atomic_inc(&ordered->refs);
        }
@@ -472,7 +475,8 @@ void btrfs_submit_logged_extents(struct list_head *logged_list,
        spin_unlock_irq(&log->log_extents_lock[index]);
 }
 
-void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid)
+void btrfs_wait_logged_extents(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *log, u64 transid)
 {
        struct btrfs_ordered_extent *ordered;
        int index = transid % 2;
@@ -497,7 +501,8 @@ void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid)
                wait_event(ordered->wait, test_bit(BTRFS_ORDERED_IO_DONE,
                                                   &ordered->flags));
 
-               btrfs_put_ordered_extent(ordered);
+               if (!test_and_set_bit(BTRFS_ORDERED_LOGGED, &ordered->flags))
+                       list_add_tail(&ordered->trans_list, &trans->ordered);
                spin_lock_irq(&log->log_extents_lock[index]);
        }
        spin_unlock_irq(&log->log_extents_lock[index]);
index d81a274d621ee47cf2510b2323997d67567a3a88..0124bffc775fd5e5aad8a750f2e9094ff904b27c 100644 (file)
@@ -71,6 +71,8 @@ struct btrfs_ordered_sum {
                                       ordered extent */
 #define BTRFS_ORDERED_TRUNCATED 9 /* Set when we have to truncate an extent */
 
+#define BTRFS_ORDERED_LOGGED 10 /* Set when we've waited on this ordered extent
+                                * in the logging code. */
 struct btrfs_ordered_extent {
        /* logical offset in the file */
        u64 file_offset;
@@ -121,6 +123,9 @@ struct btrfs_ordered_extent {
        /* If we need to wait on this to be done */
        struct list_head log_list;
 
+       /* If the transaction needs to wait on this ordered extent */
+       struct list_head trans_list;
+
        /* used to wait for the BTRFS_ORDERED_COMPLETE bit */
        wait_queue_head_t wait;
 
@@ -197,7 +202,8 @@ void btrfs_get_logged_extents(struct inode *inode,
 void btrfs_put_logged_extents(struct list_head *logged_list);
 void btrfs_submit_logged_extents(struct list_head *logged_list,
                                 struct btrfs_root *log);
-void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid);
+void btrfs_wait_logged_extents(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *log, u64 transid);
 void btrfs_free_logged_extents(struct btrfs_root *log, u64 transid);
 int __init ordered_data_init(void);
 void ordered_data_exit(void);
index 16c704b68704c7dcc979b2e1f52c502ea4ab6c1a..295a135c9c2432ba7506f0b4ea6078a2aac064c4 100644 (file)
@@ -247,6 +247,7 @@ loop:
        INIT_LIST_HEAD(&cur_trans->pending_snapshots);
        INIT_LIST_HEAD(&cur_trans->pending_chunks);
        INIT_LIST_HEAD(&cur_trans->switch_commits);
+       INIT_LIST_HEAD(&cur_trans->pending_ordered);
        list_add_tail(&cur_trans->list, &fs_info->trans_list);
        extent_io_tree_init(&cur_trans->dirty_pages,
                             fs_info->btree_inode->i_mapping);
@@ -515,6 +516,7 @@ again:
        h->sync = false;
        INIT_LIST_HEAD(&h->qgroup_ref_list);
        INIT_LIST_HEAD(&h->new_bgs);
+       INIT_LIST_HEAD(&h->ordered);
 
        smp_mb();
        if (cur_trans->state >= TRANS_STATE_BLOCKED &&
@@ -746,6 +748,12 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
        if (!list_empty(&trans->new_bgs))
                btrfs_create_pending_block_groups(trans, root);
 
+       if (!list_empty(&trans->ordered)) {
+               spin_lock(&info->trans_lock);
+               list_splice(&trans->ordered, &cur_trans->pending_ordered);
+               spin_unlock(&info->trans_lock);
+       }
+
        trans->delayed_ref_updates = 0;
        if (!trans->sync) {
                must_run_delayed_refs =
@@ -1715,6 +1723,28 @@ static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info)
                btrfs_wait_ordered_roots(fs_info, -1);
 }
 
+static inline void
+btrfs_wait_pending_ordered(struct btrfs_transaction *cur_trans,
+                          struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_ordered_extent *ordered;
+
+       spin_lock(&fs_info->trans_lock);
+       while (!list_empty(&cur_trans->pending_ordered)) {
+               ordered = list_first_entry(&cur_trans->pending_ordered,
+                                          struct btrfs_ordered_extent,
+                                          trans_list);
+               list_del_init(&ordered->trans_list);
+               spin_unlock(&fs_info->trans_lock);
+
+               wait_event(ordered->wait, test_bit(BTRFS_ORDERED_COMPLETE,
+                                                  &ordered->flags));
+               btrfs_put_ordered_extent(ordered);
+               spin_lock(&fs_info->trans_lock);
+       }
+       spin_unlock(&fs_info->trans_lock);
+}
+
 int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root)
 {
@@ -1765,6 +1795,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        }
 
        spin_lock(&root->fs_info->trans_lock);
+       list_splice(&trans->ordered, &cur_trans->pending_ordered);
        if (cur_trans->state >= TRANS_STATE_COMMIT_START) {
                spin_unlock(&root->fs_info->trans_lock);
                atomic_inc(&cur_trans->use_count);
@@ -1817,6 +1848,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
 
        btrfs_wait_delalloc_flush(root->fs_info);
 
+       btrfs_wait_pending_ordered(cur_trans, root->fs_info);
+
        btrfs_scrub_pause(root);
        /*
         * Ok now we need to make sure to block out any other joins while we
index b3f5b40aab227012d1abdd33d652c6c8c8570569..fd400a3668a835e26ad85a6fb509cf46218581eb 100644 (file)
@@ -56,6 +56,7 @@ struct btrfs_transaction {
        wait_queue_head_t commit_wait;
        struct list_head pending_snapshots;
        struct list_head pending_chunks;
+       struct list_head pending_ordered;
        struct list_head switch_commits;
        struct btrfs_delayed_ref_root delayed_refs;
        int aborted;
@@ -105,6 +106,7 @@ struct btrfs_trans_handle {
         */
        struct btrfs_root *root;
        struct seq_list delayed_ref_elem;
+       struct list_head ordered;
        struct list_head qgroup_ref_list;
        struct list_head new_bgs;
 };
index fc715ff31d260bf4fe29374d9ac5d2d9ba55cfe9..7d96cc9616631eceb719d4560205f58e225be1d2 100644 (file)
@@ -2600,7 +2600,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        if (atomic_read(&log_root_tree->log_commit[index2])) {
                blk_finish_plug(&plug);
                btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
-               btrfs_wait_logged_extents(log, log_transid);
+               btrfs_wait_logged_extents(trans, log, log_transid);
                wait_log_commit(trans, log_root_tree,
                                root_log_ctx.log_transid);
                mutex_unlock(&log_root_tree->log_mutex);
@@ -2645,7 +2645,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        btrfs_wait_marked_extents(log_root_tree,
                                  &log_root_tree->dirty_log_pages,
                                  EXTENT_NEW | EXTENT_DIRTY);
-       btrfs_wait_logged_extents(log, log_transid);
+       btrfs_wait_logged_extents(trans, log, log_transid);
 
        btrfs_set_super_log_root(root->fs_info->super_for_commit,
                                log_root_tree->node->start);
@@ -3766,7 +3766,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
        fi = btrfs_item_ptr(leaf, path->slots[0],
                            struct btrfs_file_extent_item);
 
-       btrfs_set_token_file_extent_generation(leaf, fi, em->generation,
+       btrfs_set_token_file_extent_generation(leaf, fi, trans->transid,
                                               &token);
        if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
                btrfs_set_token_file_extent_type(leaf, fi,