diff options
author | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2013-04-13 09:19:55 -0400 |
---|---|---|
committer | Josef Bacik <jbacik@fusionio.com> | 2013-05-06 15:54:48 -0400 |
commit | 47fb091fb787420cd195e66f162737401cce023f (patch) | |
tree | 0214d77b462613aa605d3107633bec7445d53d57 /fs/btrfs/ctree.c | |
parent | 30b0463a9394d9e41596e96def5461fe33222f13 (diff) |
Btrfs: fix unlock after free on rewinded tree blocks
When tree_mod_log_rewind decides to make a copy of the current tree buffer
for its modifications, it subsequently freed the buffer before unlocking it.
Obviously, those operations are required in reverse order.
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r-- | fs/btrfs/ctree.c | 18 |
1 files changed, 11 insertions, 7 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index d7e0576038fb..c3c3d37f9c58 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -1191,6 +1191,13 @@ __tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq, | |||
1191 | btrfs_set_header_nritems(eb, n); | 1191 | btrfs_set_header_nritems(eb, n); |
1192 | } | 1192 | } |
1193 | 1193 | ||
1194 | /* | ||
1195 | * Called with eb read locked. If the buffer cannot be rewinded, the same buffer | ||
1196 | * is returned. If rewind operations happen, a fresh buffer is returned. The | ||
1197 | * returned buffer is always read-locked. If the returned buffer is not the | ||
1198 | * input buffer, the lock on the input buffer is released and the input buffer | ||
1199 | * is freed (its refcount is decremented). | ||
1200 | */ | ||
1194 | static struct extent_buffer * | 1201 | static struct extent_buffer * |
1195 | tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, | 1202 | tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, |
1196 | u64 time_seq) | 1203 | u64 time_seq) |
@@ -1224,8 +1231,11 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, | |||
1224 | } | 1231 | } |
1225 | 1232 | ||
1226 | extent_buffer_get(eb_rewin); | 1233 | extent_buffer_get(eb_rewin); |
1234 | btrfs_tree_read_unlock(eb); | ||
1227 | free_extent_buffer(eb); | 1235 | free_extent_buffer(eb); |
1228 | 1236 | ||
1237 | extent_buffer_get(eb_rewin); | ||
1238 | btrfs_tree_read_lock(eb_rewin); | ||
1229 | __tree_mod_log_rewind(eb_rewin, time_seq, tm); | 1239 | __tree_mod_log_rewind(eb_rewin, time_seq, tm); |
1230 | WARN_ON(btrfs_header_nritems(eb_rewin) > | 1240 | WARN_ON(btrfs_header_nritems(eb_rewin) > |
1231 | BTRFS_NODEPTRS_PER_BLOCK(fs_info->tree_root)); | 1241 | BTRFS_NODEPTRS_PER_BLOCK(fs_info->tree_root)); |
@@ -2794,15 +2804,9 @@ again: | |||
2794 | btrfs_clear_path_blocking(p, b, | 2804 | btrfs_clear_path_blocking(p, b, |
2795 | BTRFS_READ_LOCK); | 2805 | BTRFS_READ_LOCK); |
2796 | } | 2806 | } |
2807 | b = tree_mod_log_rewind(root->fs_info, b, time_seq); | ||
2797 | p->locks[level] = BTRFS_READ_LOCK; | 2808 | p->locks[level] = BTRFS_READ_LOCK; |
2798 | p->nodes[level] = b; | 2809 | p->nodes[level] = b; |
2799 | b = tree_mod_log_rewind(root->fs_info, b, time_seq); | ||
2800 | if (b != p->nodes[level]) { | ||
2801 | btrfs_tree_unlock_rw(p->nodes[level], | ||
2802 | p->locks[level]); | ||
2803 | p->locks[level] = 0; | ||
2804 | p->nodes[level] = b; | ||
2805 | } | ||
2806 | } else { | 2810 | } else { |
2807 | p->slots[level] = slot; | 2811 | p->slots[level] = slot; |
2808 | unlock_up(p, level, lowest_unlock, 0, NULL); | 2812 | unlock_up(p, level, lowest_unlock, 0, NULL); |