aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-09-30 11:36:38 -0400
committerChris Mason <chris.mason@fusionio.com>2013-11-11 21:54:03 -0500
commit724e2315db3d59a8201d4a87c7c7a873e60e1ce0 (patch)
tree0a1af5a08bfd4312ad169ed4c1ac9a8e804b3419 /fs/btrfs
parentc16ce1901431629fbe5b9387cc966d62a089e4df (diff)
Btrfs: fix two use-after-free bugs with transaction cleanup
I was noticing the slab redzone stuff going off every once and a while during transaction aborts. This was caused by two things 1) We would walk the pending snapshots and set their error to -ECANCELED. We don't need to do this, the snapshot stuff waits for a transaction commit and if there is a problem we just free our pending snapshot object and exit. Doing this was causing us to touch the pending snapshot object after the thing had already been freed. 2) We were freeing the transaction manually with wanton disregard for it's use_count reference counter. To fix this I cleaned up the transaction freeing loop to either wait for the transaction commit to finish if it was in the middle of that (since it will be cleaned and freed up there) or to do the cleanup oursevles. I also moved the global "kill all things dirty everywhere" stuff outside of the transaction cleanup loop since that only needs to be done once. With this patch I'm no longer seeing slab corruption because of use after frees. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/disk-io.c111
-rw-r--r--fs/btrfs/transaction.c22
-rw-r--r--fs/btrfs/transaction.h1
3 files changed, 52 insertions, 82 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 01a26e2eb310..b131d716d5a5 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -64,7 +64,6 @@ static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t,
64static void btrfs_destroy_ordered_extents(struct btrfs_root *root); 64static void btrfs_destroy_ordered_extents(struct btrfs_root *root);
65static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, 65static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
66 struct btrfs_root *root); 66 struct btrfs_root *root);
67static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t);
68static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root); 67static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root);
69static int btrfs_destroy_marked_extents(struct btrfs_root *root, 68static int btrfs_destroy_marked_extents(struct btrfs_root *root,
70 struct extent_io_tree *dirty_pages, 69 struct extent_io_tree *dirty_pages,
@@ -3915,24 +3914,6 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
3915 return ret; 3914 return ret;
3916} 3915}
3917 3916
3918static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t)
3919{
3920 struct btrfs_pending_snapshot *snapshot;
3921 struct list_head splice;
3922
3923 INIT_LIST_HEAD(&splice);
3924
3925 list_splice_init(&t->pending_snapshots, &splice);
3926
3927 while (!list_empty(&splice)) {
3928 snapshot = list_entry(splice.next,
3929 struct btrfs_pending_snapshot,
3930 list);
3931 snapshot->error = -ECANCELED;
3932 list_del_init(&snapshot->list);
3933 }
3934}
3935
3936static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root) 3917static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root)
3937{ 3918{
3938 struct btrfs_inode *btrfs_inode; 3919 struct btrfs_inode *btrfs_inode;
@@ -4062,6 +4043,8 @@ again:
4062void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans, 4043void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
4063 struct btrfs_root *root) 4044 struct btrfs_root *root)
4064{ 4045{
4046 btrfs_destroy_ordered_operations(cur_trans, root);
4047
4065 btrfs_destroy_delayed_refs(cur_trans, root); 4048 btrfs_destroy_delayed_refs(cur_trans, root);
4066 btrfs_block_rsv_release(root, &root->fs_info->trans_block_rsv, 4049 btrfs_block_rsv_release(root, &root->fs_info->trans_block_rsv,
4067 cur_trans->dirty_pages.dirty_bytes); 4050 cur_trans->dirty_pages.dirty_bytes);
@@ -4069,8 +4052,6 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
4069 cur_trans->state = TRANS_STATE_COMMIT_START; 4052 cur_trans->state = TRANS_STATE_COMMIT_START;
4070 wake_up(&root->fs_info->transaction_blocked_wait); 4053 wake_up(&root->fs_info->transaction_blocked_wait);
4071 4054
4072 btrfs_evict_pending_snapshots(cur_trans);
4073
4074 cur_trans->state = TRANS_STATE_UNBLOCKED; 4055 cur_trans->state = TRANS_STATE_UNBLOCKED;
4075 wake_up(&root->fs_info->transaction_wait); 4056 wake_up(&root->fs_info->transaction_wait);
4076 4057
@@ -4094,63 +4075,51 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
4094static int btrfs_cleanup_transaction(struct btrfs_root *root) 4075static int btrfs_cleanup_transaction(struct btrfs_root *root)
4095{ 4076{
4096 struct btrfs_transaction *t; 4077 struct btrfs_transaction *t;
4097 LIST_HEAD(list);
4098 4078
4099 mutex_lock(&root->fs_info->transaction_kthread_mutex); 4079 mutex_lock(&root->fs_info->transaction_kthread_mutex);
4100 4080
4101 spin_lock(&root->fs_info->trans_lock); 4081 spin_lock(&root->fs_info->trans_lock);
4102 list_splice_init(&root->fs_info->trans_list, &list); 4082 while (!list_empty(&root->fs_info->trans_list)) {
4103 root->fs_info->running_transaction = NULL; 4083 t = list_first_entry(&root->fs_info->trans_list,
4104 spin_unlock(&root->fs_info->trans_lock); 4084 struct btrfs_transaction, list);
4105 4085 if (t->state >= TRANS_STATE_COMMIT_START) {
4106 while (!list_empty(&list)) { 4086 atomic_inc(&t->use_count);
4107 t = list_entry(list.next, struct btrfs_transaction, list); 4087 spin_unlock(&root->fs_info->trans_lock);
4108 4088 btrfs_wait_for_commit(root, t->transid);
4109 btrfs_destroy_ordered_operations(t, root); 4089 btrfs_put_transaction(t);
4110 4090 spin_lock(&root->fs_info->trans_lock);
4111 btrfs_destroy_all_ordered_extents(root->fs_info); 4091 continue;
4112 4092 }
4113 btrfs_destroy_delayed_refs(t, root); 4093 if (t == root->fs_info->running_transaction) {
4114 4094 t->state = TRANS_STATE_COMMIT_DOING;
4115 /* 4095 spin_unlock(&root->fs_info->trans_lock);
4116 * FIXME: cleanup wait for commit 4096 /*
4117 * We needn't acquire the lock here, because we are during 4097 * We wait for 0 num_writers since we don't hold a trans
4118 * the umount, there is no other task which will change it. 4098 * handle open currently for this transaction.
4119 */ 4099 */
4120 t->state = TRANS_STATE_COMMIT_START; 4100 wait_event(t->writer_wait,
4121 smp_mb(); 4101 atomic_read(&t->num_writers) == 0);
4122 if (waitqueue_active(&root->fs_info->transaction_blocked_wait)) 4102 } else {
4123 wake_up(&root->fs_info->transaction_blocked_wait); 4103 spin_unlock(&root->fs_info->trans_lock);
4124 4104 }
4125 btrfs_evict_pending_snapshots(t); 4105 btrfs_cleanup_one_transaction(t, root);
4126
4127 t->state = TRANS_STATE_UNBLOCKED;
4128 smp_mb();
4129 if (waitqueue_active(&root->fs_info->transaction_wait))
4130 wake_up(&root->fs_info->transaction_wait);
4131
4132 btrfs_destroy_delayed_inodes(root);
4133 btrfs_assert_delayed_root_empty(root);
4134
4135 btrfs_destroy_all_delalloc_inodes(root->fs_info);
4136
4137 btrfs_destroy_marked_extents(root, &t->dirty_pages,
4138 EXTENT_DIRTY);
4139
4140 btrfs_destroy_pinned_extent(root,
4141 root->fs_info->pinned_extents);
4142
4143 t->state = TRANS_STATE_COMPLETED;
4144 smp_mb();
4145 if (waitqueue_active(&t->commit_wait))
4146 wake_up(&t->commit_wait);
4147 4106
4148 atomic_set(&t->use_count, 0); 4107 spin_lock(&root->fs_info->trans_lock);
4108 if (t == root->fs_info->running_transaction)
4109 root->fs_info->running_transaction = NULL;
4149 list_del_init(&t->list); 4110 list_del_init(&t->list);
4150 memset(t, 0, sizeof(*t)); 4111 spin_unlock(&root->fs_info->trans_lock);
4151 kmem_cache_free(btrfs_transaction_cachep, t);
4152 }
4153 4112
4113 btrfs_put_transaction(t);
4114 trace_btrfs_transaction_commit(root);
4115 spin_lock(&root->fs_info->trans_lock);
4116 }
4117 spin_unlock(&root->fs_info->trans_lock);
4118 btrfs_destroy_all_ordered_extents(root->fs_info);
4119 btrfs_destroy_delayed_inodes(root);
4120 btrfs_assert_delayed_root_empty(root);
4121 btrfs_destroy_pinned_extent(root, root->fs_info->pinned_extents);
4122 btrfs_destroy_all_delalloc_inodes(root->fs_info);
4154 mutex_unlock(&root->fs_info->transaction_kthread_mutex); 4123 mutex_unlock(&root->fs_info->transaction_kthread_mutex);
4155 4124
4156 return 0; 4125 return 0;
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index f08e22885c21..134039fd59bb 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -57,7 +57,7 @@ static unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = {
57 __TRANS_JOIN_NOLOCK), 57 __TRANS_JOIN_NOLOCK),
58}; 58};
59 59
60static void put_transaction(struct btrfs_transaction *transaction) 60void btrfs_put_transaction(struct btrfs_transaction *transaction)
61{ 61{
62 WARN_ON(atomic_read(&transaction->use_count) == 0); 62 WARN_ON(atomic_read(&transaction->use_count) == 0);
63 if (atomic_dec_and_test(&transaction->use_count)) { 63 if (atomic_dec_and_test(&transaction->use_count)) {
@@ -332,7 +332,7 @@ static void wait_current_trans(struct btrfs_root *root)
332 wait_event(root->fs_info->transaction_wait, 332 wait_event(root->fs_info->transaction_wait,
333 cur_trans->state >= TRANS_STATE_UNBLOCKED || 333 cur_trans->state >= TRANS_STATE_UNBLOCKED ||
334 cur_trans->aborted); 334 cur_trans->aborted);
335 put_transaction(cur_trans); 335 btrfs_put_transaction(cur_trans);
336 } else { 336 } else {
337 spin_unlock(&root->fs_info->trans_lock); 337 spin_unlock(&root->fs_info->trans_lock);
338 } 338 }
@@ -610,7 +610,7 @@ int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
610 } 610 }
611 611
612 wait_for_commit(root, cur_trans); 612 wait_for_commit(root, cur_trans);
613 put_transaction(cur_trans); 613 btrfs_put_transaction(cur_trans);
614out: 614out:
615 return ret; 615 return ret;
616} 616}
@@ -735,7 +735,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
735 smp_mb(); 735 smp_mb();
736 if (waitqueue_active(&cur_trans->writer_wait)) 736 if (waitqueue_active(&cur_trans->writer_wait))
737 wake_up(&cur_trans->writer_wait); 737 wake_up(&cur_trans->writer_wait);
738 put_transaction(cur_trans); 738 btrfs_put_transaction(cur_trans);
739 739
740 if (current->journal_info == trans) 740 if (current->journal_info == trans)
741 current->journal_info = NULL; 741 current->journal_info = NULL;
@@ -1515,7 +1515,7 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
1515 if (current->journal_info == trans) 1515 if (current->journal_info == trans)
1516 current->journal_info = NULL; 1516 current->journal_info = NULL;
1517 1517
1518 put_transaction(cur_trans); 1518 btrfs_put_transaction(cur_trans);
1519 return 0; 1519 return 0;
1520} 1520}
1521 1521
@@ -1559,8 +1559,8 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans,
1559 1559
1560 if (trans->type & __TRANS_FREEZABLE) 1560 if (trans->type & __TRANS_FREEZABLE)
1561 sb_end_intwrite(root->fs_info->sb); 1561 sb_end_intwrite(root->fs_info->sb);
1562 put_transaction(cur_trans); 1562 btrfs_put_transaction(cur_trans);
1563 put_transaction(cur_trans); 1563 btrfs_put_transaction(cur_trans);
1564 1564
1565 trace_btrfs_transaction_commit(root); 1565 trace_btrfs_transaction_commit(root);
1566 1566
@@ -1676,7 +1676,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
1676 1676
1677 wait_for_commit(root, cur_trans); 1677 wait_for_commit(root, cur_trans);
1678 1678
1679 put_transaction(cur_trans); 1679 btrfs_put_transaction(cur_trans);
1680 1680
1681 return ret; 1681 return ret;
1682 } 1682 }
@@ -1693,7 +1693,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
1693 1693
1694 wait_for_commit(root, prev_trans); 1694 wait_for_commit(root, prev_trans);
1695 1695
1696 put_transaction(prev_trans); 1696 btrfs_put_transaction(prev_trans);
1697 } else { 1697 } else {
1698 spin_unlock(&root->fs_info->trans_lock); 1698 spin_unlock(&root->fs_info->trans_lock);
1699 } 1699 }
@@ -1892,8 +1892,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
1892 list_del_init(&cur_trans->list); 1892 list_del_init(&cur_trans->list);
1893 spin_unlock(&root->fs_info->trans_lock); 1893 spin_unlock(&root->fs_info->trans_lock);
1894 1894
1895 put_transaction(cur_trans); 1895 btrfs_put_transaction(cur_trans);
1896 put_transaction(cur_trans); 1896 btrfs_put_transaction(cur_trans);
1897 1897
1898 if (trans->type & __TRANS_FREEZABLE) 1898 if (trans->type & __TRANS_FREEZABLE)
1899 sb_end_intwrite(root->fs_info->sb); 1899 sb_end_intwrite(root->fs_info->sb);
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 5c2af8491621..306f88ae1de3 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -166,4 +166,5 @@ int btrfs_wait_marked_extents(struct btrfs_root *root,
166 struct extent_io_tree *dirty_pages, int mark); 166 struct extent_io_tree *dirty_pages, int mark);
167int btrfs_transaction_blocked(struct btrfs_fs_info *info); 167int btrfs_transaction_blocked(struct btrfs_fs_info *info);
168int btrfs_transaction_in_commit(struct btrfs_fs_info *info); 168int btrfs_transaction_in_commit(struct btrfs_fs_info *info);
169void btrfs_put_transaction(struct btrfs_transaction *transaction);
169#endif 170#endif