diff options
author | Miao Xie <miaox@cn.fujitsu.com> | 2012-11-26 04:25:38 -0500 |
---|---|---|
committer | Chris Mason <chris.mason@fusionio.com> | 2012-12-16 20:46:12 -0500 |
commit | 8ddc473433b5e8ce8693db9f6e251f5a28267528 (patch) | |
tree | bfbf05f0ada45b8b77f4982a895c17059b1456f2 /fs/btrfs | |
parent | 9247f3170b2c3d648707c93bbebcd763fac17c06 (diff) |
Btrfs: fix unprotected defragable inode insertion
We forget to get the defrag lock when we re-add the defragable inode,
Fix it.
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/file.c | 70 |
1 files changed, 55 insertions, 15 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 15117eae85c4..00918321e390 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -91,7 +91,7 @@ static int __compare_inode_defrag(struct inode_defrag *defrag1, | |||
91 | * If an existing record is found the defrag item you | 91 | * If an existing record is found the defrag item you |
92 | * pass in is freed | 92 | * pass in is freed |
93 | */ | 93 | */ |
94 | static void __btrfs_add_inode_defrag(struct inode *inode, | 94 | static int __btrfs_add_inode_defrag(struct inode *inode, |
95 | struct inode_defrag *defrag) | 95 | struct inode_defrag *defrag) |
96 | { | 96 | { |
97 | struct btrfs_root *root = BTRFS_I(inode)->root; | 97 | struct btrfs_root *root = BTRFS_I(inode)->root; |
@@ -119,18 +119,24 @@ static void __btrfs_add_inode_defrag(struct inode *inode, | |||
119 | entry->transid = defrag->transid; | 119 | entry->transid = defrag->transid; |
120 | if (defrag->last_offset > entry->last_offset) | 120 | if (defrag->last_offset > entry->last_offset) |
121 | entry->last_offset = defrag->last_offset; | 121 | entry->last_offset = defrag->last_offset; |
122 | goto exists; | 122 | return -EEXIST; |
123 | } | 123 | } |
124 | } | 124 | } |
125 | set_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags); | 125 | set_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags); |
126 | rb_link_node(&defrag->rb_node, parent, p); | 126 | rb_link_node(&defrag->rb_node, parent, p); |
127 | rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes); | 127 | rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes); |
128 | return; | 128 | return 0; |
129 | } | ||
129 | 130 | ||
130 | exists: | 131 | static inline int __need_auto_defrag(struct btrfs_root *root) |
131 | kmem_cache_free(btrfs_inode_defrag_cachep, defrag); | 132 | { |
132 | return; | 133 | if (!btrfs_test_opt(root, AUTO_DEFRAG)) |
134 | return 0; | ||
135 | |||
136 | if (btrfs_fs_closing(root->fs_info)) | ||
137 | return 0; | ||
133 | 138 | ||
139 | return 1; | ||
134 | } | 140 | } |
135 | 141 | ||
136 | /* | 142 | /* |
@@ -143,11 +149,9 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans, | |||
143 | struct btrfs_root *root = BTRFS_I(inode)->root; | 149 | struct btrfs_root *root = BTRFS_I(inode)->root; |
144 | struct inode_defrag *defrag; | 150 | struct inode_defrag *defrag; |
145 | u64 transid; | 151 | u64 transid; |
152 | int ret; | ||
146 | 153 | ||
147 | if (!btrfs_test_opt(root, AUTO_DEFRAG)) | 154 | if (!__need_auto_defrag(root)) |
148 | return 0; | ||
149 | |||
150 | if (btrfs_fs_closing(root->fs_info)) | ||
151 | return 0; | 155 | return 0; |
152 | 156 | ||
153 | if (test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) | 157 | if (test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) |
@@ -167,15 +171,51 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans, | |||
167 | defrag->root = root->root_key.objectid; | 171 | defrag->root = root->root_key.objectid; |
168 | 172 | ||
169 | spin_lock(&root->fs_info->defrag_inodes_lock); | 173 | spin_lock(&root->fs_info->defrag_inodes_lock); |
170 | if (!test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) | 174 | if (!test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) { |
171 | __btrfs_add_inode_defrag(inode, defrag); | 175 | /* |
172 | else | 176 | * If we set IN_DEFRAG flag and evict the inode from memory, |
177 | * and then re-read this inode, this new inode doesn't have | ||
178 | * IN_DEFRAG flag. At the case, we may find the existed defrag. | ||
179 | */ | ||
180 | ret = __btrfs_add_inode_defrag(inode, defrag); | ||
181 | if (ret) | ||
182 | kmem_cache_free(btrfs_inode_defrag_cachep, defrag); | ||
183 | } else { | ||
173 | kmem_cache_free(btrfs_inode_defrag_cachep, defrag); | 184 | kmem_cache_free(btrfs_inode_defrag_cachep, defrag); |
185 | } | ||
174 | spin_unlock(&root->fs_info->defrag_inodes_lock); | 186 | spin_unlock(&root->fs_info->defrag_inodes_lock); |
175 | return 0; | 187 | return 0; |
176 | } | 188 | } |
177 | 189 | ||
178 | /* | 190 | /* |
191 | * Requeue the defrag object. If there is a defrag object that points to | ||
192 | * the same inode in the tree, we will merge them together (by | ||
193 | * __btrfs_add_inode_defrag()) and free the one that we want to requeue. | ||
194 | */ | ||
195 | void btrfs_requeue_inode_defrag(struct inode *inode, | ||
196 | struct inode_defrag *defrag) | ||
197 | { | ||
198 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
199 | int ret; | ||
200 | |||
201 | if (!__need_auto_defrag(root)) | ||
202 | goto out; | ||
203 | |||
204 | /* | ||
205 | * Here we don't check the IN_DEFRAG flag, because we need merge | ||
206 | * them together. | ||
207 | */ | ||
208 | spin_lock(&root->fs_info->defrag_inodes_lock); | ||
209 | ret = __btrfs_add_inode_defrag(inode, defrag); | ||
210 | spin_unlock(&root->fs_info->defrag_inodes_lock); | ||
211 | if (ret) | ||
212 | goto out; | ||
213 | return; | ||
214 | out: | ||
215 | kmem_cache_free(btrfs_inode_defrag_cachep, defrag); | ||
216 | } | ||
217 | |||
218 | /* | ||
179 | * must be called with the defrag_inodes lock held | 219 | * must be called with the defrag_inodes lock held |
180 | */ | 220 | */ |
181 | struct inode_defrag *btrfs_find_defrag_inode(struct btrfs_fs_info *info, | 221 | struct inode_defrag *btrfs_find_defrag_inode(struct btrfs_fs_info *info, |
@@ -294,7 +334,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info) | |||
294 | */ | 334 | */ |
295 | if (num_defrag == defrag_batch) { | 335 | if (num_defrag == defrag_batch) { |
296 | defrag->last_offset = range.start; | 336 | defrag->last_offset = range.start; |
297 | __btrfs_add_inode_defrag(inode, defrag); | 337 | btrfs_requeue_inode_defrag(inode, defrag); |
298 | /* | 338 | /* |
299 | * we don't want to kfree defrag, we added it back to | 339 | * we don't want to kfree defrag, we added it back to |
300 | * the rbtree | 340 | * the rbtree |
@@ -308,7 +348,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info) | |||
308 | */ | 348 | */ |
309 | defrag->last_offset = 0; | 349 | defrag->last_offset = 0; |
310 | defrag->cycled = 1; | 350 | defrag->cycled = 1; |
311 | __btrfs_add_inode_defrag(inode, defrag); | 351 | btrfs_requeue_inode_defrag(inode, defrag); |
312 | defrag = NULL; | 352 | defrag = NULL; |
313 | } | 353 | } |
314 | 354 | ||