aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorMiao Xie <miaox@cn.fujitsu.com>2012-11-26 04:25:38 -0500
committerChris Mason <chris.mason@fusionio.com>2012-12-16 20:46:12 -0500
commit8ddc473433b5e8ce8693db9f6e251f5a28267528 (patch)
treebfbf05f0ada45b8b77f4982a895c17059b1456f2 /fs/btrfs
parent9247f3170b2c3d648707c93bbebcd763fac17c06 (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.c70
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 */
94static void __btrfs_add_inode_defrag(struct inode *inode, 94static 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
130exists: 131static 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 */
195void 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;
214out:
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 */
181struct inode_defrag *btrfs_find_defrag_inode(struct btrfs_fs_info *info, 221struct 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