aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiao Xie <miaox@cn.fujitsu.com>2013-03-04 04:44:29 -0500
committerJosef Bacik <jbacik@fusionio.com>2013-03-04 16:33:22 -0500
commitaec8030a8745221c8658f2033b22c98528897b13 (patch)
tree58634fad399097dbde3d4969e498cb913a48aa96
parent9bf7a4890518186238d2579be16ecc5190a707c0 (diff)
Btrfs: fix wrong handle at error path of create_snapshot() when the commit fails
There are several bugs at error path of create_snapshot() when the transaction commitment failed. - access the freed transaction handler. At the end of the transaction commitment, the transaction handler was freed, so we should not access it after the transaction commitment. - we were not aware of the error which happened during the snapshot creation if we submitted a async transaction commitment. - pending snapshot access vs pending snapshot free. when something wrong happened after we submitted a async transaction commitment, the transaction committer would cleanup the pending snapshots and free them. But the snapshot creators were not aware of it, they would access the freed pending snapshots. This patch fixes the above problems by: - remove the dangerous code that accessed the freed handler - assign ->error if the error happens during the snapshot creation - the transaction committer doesn't free the pending snapshots, just assigns the error number and evicts them before we unblock the transaction. Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
-rw-r--r--fs/btrfs/disk-io.c16
-rw-r--r--fs/btrfs/ioctl.c6
-rw-r--r--fs/btrfs/transaction.c58
3 files changed, 41 insertions, 39 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 02369a3c162e..7d84651e850b 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -62,7 +62,7 @@ static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t,
62static void btrfs_destroy_ordered_extents(struct btrfs_root *root); 62static void btrfs_destroy_ordered_extents(struct btrfs_root *root);
63static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, 63static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
64 struct btrfs_root *root); 64 struct btrfs_root *root);
65static void btrfs_destroy_pending_snapshots(struct btrfs_transaction *t); 65static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t);
66static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root); 66static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root);
67static int btrfs_destroy_marked_extents(struct btrfs_root *root, 67static int btrfs_destroy_marked_extents(struct btrfs_root *root,
68 struct extent_io_tree *dirty_pages, 68 struct extent_io_tree *dirty_pages,
@@ -3687,7 +3687,7 @@ int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
3687 return ret; 3687 return ret;
3688} 3688}
3689 3689
3690static void btrfs_destroy_pending_snapshots(struct btrfs_transaction *t) 3690static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t)
3691{ 3691{
3692 struct btrfs_pending_snapshot *snapshot; 3692 struct btrfs_pending_snapshot *snapshot;
3693 struct list_head splice; 3693 struct list_head splice;
@@ -3700,10 +3700,8 @@ static void btrfs_destroy_pending_snapshots(struct btrfs_transaction *t)
3700 snapshot = list_entry(splice.next, 3700 snapshot = list_entry(splice.next,
3701 struct btrfs_pending_snapshot, 3701 struct btrfs_pending_snapshot,
3702 list); 3702 list);
3703 3703 snapshot->error = -ECANCELED;
3704 list_del_init(&snapshot->list); 3704 list_del_init(&snapshot->list);
3705
3706 kfree(snapshot);
3707 } 3705 }
3708} 3706}
3709 3707
@@ -3840,6 +3838,8 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
3840 cur_trans->blocked = 1; 3838 cur_trans->blocked = 1;
3841 wake_up(&root->fs_info->transaction_blocked_wait); 3839 wake_up(&root->fs_info->transaction_blocked_wait);
3842 3840
3841 btrfs_evict_pending_snapshots(cur_trans);
3842
3843 cur_trans->blocked = 0; 3843 cur_trans->blocked = 0;
3844 wake_up(&root->fs_info->transaction_wait); 3844 wake_up(&root->fs_info->transaction_wait);
3845 3845
@@ -3849,8 +3849,6 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
3849 btrfs_destroy_delayed_inodes(root); 3849 btrfs_destroy_delayed_inodes(root);
3850 btrfs_assert_delayed_root_empty(root); 3850 btrfs_assert_delayed_root_empty(root);
3851 3851
3852 btrfs_destroy_pending_snapshots(cur_trans);
3853
3854 btrfs_destroy_marked_extents(root, &cur_trans->dirty_pages, 3852 btrfs_destroy_marked_extents(root, &cur_trans->dirty_pages,
3855 EXTENT_DIRTY); 3853 EXTENT_DIRTY);
3856 btrfs_destroy_pinned_extent(root, 3854 btrfs_destroy_pinned_extent(root,
@@ -3894,6 +3892,8 @@ int btrfs_cleanup_transaction(struct btrfs_root *root)
3894 if (waitqueue_active(&root->fs_info->transaction_blocked_wait)) 3892 if (waitqueue_active(&root->fs_info->transaction_blocked_wait))
3895 wake_up(&root->fs_info->transaction_blocked_wait); 3893 wake_up(&root->fs_info->transaction_blocked_wait);
3896 3894
3895 btrfs_evict_pending_snapshots(t);
3896
3897 t->blocked = 0; 3897 t->blocked = 0;
3898 smp_mb(); 3898 smp_mb();
3899 if (waitqueue_active(&root->fs_info->transaction_wait)) 3899 if (waitqueue_active(&root->fs_info->transaction_wait))
@@ -3907,8 +3907,6 @@ int btrfs_cleanup_transaction(struct btrfs_root *root)
3907 btrfs_destroy_delayed_inodes(root); 3907 btrfs_destroy_delayed_inodes(root);
3908 btrfs_assert_delayed_root_empty(root); 3908 btrfs_assert_delayed_root_empty(root);
3909 3909
3910 btrfs_destroy_pending_snapshots(t);
3911
3912 btrfs_destroy_delalloc_inodes(root); 3910 btrfs_destroy_delalloc_inodes(root);
3913 3911
3914 spin_lock(&root->fs_info->trans_lock); 3912 spin_lock(&root->fs_info->trans_lock);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index b908960c9746..94c0e42dfa1e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -596,12 +596,8 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
596 ret = btrfs_commit_transaction(trans, 596 ret = btrfs_commit_transaction(trans,
597 root->fs_info->extent_root); 597 root->fs_info->extent_root);
598 } 598 }
599 if (ret) { 599 if (ret)
600 /* cleanup_transaction has freed this for us */
601 if (trans->aborted)
602 pending_snapshot = NULL;
603 goto fail; 600 goto fail;
604 }
605 601
606 ret = pending_snapshot->error; 602 ret = pending_snapshot->error;
607 if (ret) 603 if (ret)
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index f11c2e0a3746..d8fce6fe9cf8 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -1053,7 +1053,12 @@ int btrfs_defrag_root(struct btrfs_root *root)
1053 1053
1054/* 1054/*
1055 * new snapshots need to be created at a very specific time in the 1055 * new snapshots need to be created at a very specific time in the
1056 * transaction commit. This does the actual creation 1056 * transaction commit. This does the actual creation.
1057 *
1058 * Note:
1059 * If the error which may affect the commitment of the current transaction
1060 * happens, we should return the error number. If the error which just affect
1061 * the creation of the pending snapshots, just return 0.
1057 */ 1062 */
1058static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, 1063static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
1059 struct btrfs_fs_info *fs_info, 1064 struct btrfs_fs_info *fs_info,
@@ -1072,7 +1077,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
1072 struct extent_buffer *tmp; 1077 struct extent_buffer *tmp;
1073 struct extent_buffer *old; 1078 struct extent_buffer *old;
1074 struct timespec cur_time = CURRENT_TIME; 1079 struct timespec cur_time = CURRENT_TIME;
1075 int ret; 1080 int ret = 0;
1076 u64 to_reserve = 0; 1081 u64 to_reserve = 0;
1077 u64 index = 0; 1082 u64 index = 0;
1078 u64 objectid; 1083 u64 objectid;
@@ -1081,40 +1086,36 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
1081 1086
1082 path = btrfs_alloc_path(); 1087 path = btrfs_alloc_path();
1083 if (!path) { 1088 if (!path) {
1084 ret = pending->error = -ENOMEM; 1089 pending->error = -ENOMEM;
1085 return ret; 1090 return 0;
1086 } 1091 }
1087 1092
1088 new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS); 1093 new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
1089 if (!new_root_item) { 1094 if (!new_root_item) {
1090 ret = pending->error = -ENOMEM; 1095 pending->error = -ENOMEM;
1091 goto root_item_alloc_fail; 1096 goto root_item_alloc_fail;
1092 } 1097 }
1093 1098
1094 ret = btrfs_find_free_objectid(tree_root, &objectid); 1099 pending->error = btrfs_find_free_objectid(tree_root, &objectid);
1095 if (ret) { 1100 if (pending->error)
1096 pending->error = ret;
1097 goto no_free_objectid; 1101 goto no_free_objectid;
1098 }
1099 1102
1100 btrfs_reloc_pre_snapshot(trans, pending, &to_reserve); 1103 btrfs_reloc_pre_snapshot(trans, pending, &to_reserve);
1101 1104
1102 if (to_reserve > 0) { 1105 if (to_reserve > 0) {
1103 ret = btrfs_block_rsv_add(root, &pending->block_rsv, 1106 pending->error = btrfs_block_rsv_add(root,
1104 to_reserve, 1107 &pending->block_rsv,
1105 BTRFS_RESERVE_NO_FLUSH); 1108 to_reserve,
1106 if (ret) { 1109 BTRFS_RESERVE_NO_FLUSH);
1107 pending->error = ret; 1110 if (pending->error)
1108 goto no_free_objectid; 1111 goto no_free_objectid;
1109 }
1110 } 1112 }
1111 1113
1112 ret = btrfs_qgroup_inherit(trans, fs_info, root->root_key.objectid, 1114 pending->error = btrfs_qgroup_inherit(trans, fs_info,
1113 objectid, pending->inherit); 1115 root->root_key.objectid,
1114 if (ret) { 1116 objectid, pending->inherit);
1115 pending->error = ret; 1117 if (pending->error)
1116 goto no_free_objectid; 1118 goto no_free_objectid;
1117 }
1118 1119
1119 key.objectid = objectid; 1120 key.objectid = objectid;
1120 key.offset = (u64)-1; 1121 key.offset = (u64)-1;
@@ -1142,7 +1143,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
1142 dentry->d_name.len, 0); 1143 dentry->d_name.len, 0);
1143 if (dir_item != NULL && !IS_ERR(dir_item)) { 1144 if (dir_item != NULL && !IS_ERR(dir_item)) {
1144 pending->error = -EEXIST; 1145 pending->error = -EEXIST;
1145 goto fail; 1146 goto dir_item_existed;
1146 } else if (IS_ERR(dir_item)) { 1147 } else if (IS_ERR(dir_item)) {
1147 ret = PTR_ERR(dir_item); 1148 ret = PTR_ERR(dir_item);
1148 btrfs_abort_transaction(trans, root, ret); 1149 btrfs_abort_transaction(trans, root, ret);
@@ -1273,6 +1274,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
1273 if (ret) 1274 if (ret)
1274 btrfs_abort_transaction(trans, root, ret); 1275 btrfs_abort_transaction(trans, root, ret);
1275fail: 1276fail:
1277 pending->error = ret;
1278dir_item_existed:
1276 trans->block_rsv = rsv; 1279 trans->block_rsv = rsv;
1277 trans->bytes_reserved = 0; 1280 trans->bytes_reserved = 0;
1278no_free_objectid: 1281no_free_objectid:
@@ -1288,12 +1291,17 @@ root_item_alloc_fail:
1288static noinline int create_pending_snapshots(struct btrfs_trans_handle *trans, 1291static noinline int create_pending_snapshots(struct btrfs_trans_handle *trans,
1289 struct btrfs_fs_info *fs_info) 1292 struct btrfs_fs_info *fs_info)
1290{ 1293{
1291 struct btrfs_pending_snapshot *pending; 1294 struct btrfs_pending_snapshot *pending, *next;
1292 struct list_head *head = &trans->transaction->pending_snapshots; 1295 struct list_head *head = &trans->transaction->pending_snapshots;
1296 int ret = 0;
1293 1297
1294 list_for_each_entry(pending, head, list) 1298 list_for_each_entry_safe(pending, next, head, list) {
1295 create_pending_snapshot(trans, fs_info, pending); 1299 list_del(&pending->list);
1296 return 0; 1300 ret = create_pending_snapshot(trans, fs_info, pending);
1301 if (ret)
1302 break;
1303 }
1304 return ret;
1297} 1305}
1298 1306
1299static void update_super_roots(struct btrfs_root *root) 1307static void update_super_roots(struct btrfs_root *root)