diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-08-07 15:52:19 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-08-07 15:52:19 -0400 |
commit | 9f3a742736cecda5a8778be70faa2f779458839f (patch) | |
tree | 500ef3701521e63254dbe82e5c71e7ccff746275 /fs/btrfs/transaction.c | |
parent | 8578f0f1fda41f8429a1037022b720275102ca65 (diff) |
Btrfs: Do snapshot deletion in smaller chunks.
Before, snapshot deletion was a single atomic unit. This caused considerable
lock contention and required an unbounded amount of space. Now,
the drop_progress field in the root item is used to indicate how far along
snapshot deletion is, and to resume where it left off.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/transaction.c')
-rw-r--r-- | fs/btrfs/transaction.c | 63 |
1 files changed, 38 insertions, 25 deletions
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 0f494fe365cb..498626470a04 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -213,10 +213,7 @@ static int wait_for_commit(struct btrfs_root *root, | |||
213 | 213 | ||
214 | struct dirty_root { | 214 | struct dirty_root { |
215 | struct list_head list; | 215 | struct list_head list; |
216 | struct btrfs_key snap_key; | ||
217 | struct buffer_head *commit_root; | ||
218 | struct btrfs_root *root; | 216 | struct btrfs_root *root; |
219 | int free_on_drop; | ||
220 | }; | 217 | }; |
221 | 218 | ||
222 | int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list) | 219 | int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list) |
@@ -226,10 +223,7 @@ int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list) | |||
226 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); | 223 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); |
227 | if (!dirty) | 224 | if (!dirty) |
228 | return -ENOMEM; | 225 | return -ENOMEM; |
229 | memcpy(&dirty->snap_key, &root->root_key, sizeof(root->root_key)); | ||
230 | dirty->commit_root = root->node; | ||
231 | dirty->root = root; | 226 | dirty->root = root; |
232 | dirty->free_on_drop = 1; | ||
233 | list_add(&dirty->list, dead_list); | 227 | list_add(&dirty->list, dead_list); |
234 | return 0; | 228 | return 0; |
235 | } | 229 | } |
@@ -241,7 +235,6 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
241 | struct dirty_root *dirty; | 235 | struct dirty_root *dirty; |
242 | struct btrfs_root *gang[8]; | 236 | struct btrfs_root *gang[8]; |
243 | struct btrfs_root *root; | 237 | struct btrfs_root *root; |
244 | struct btrfs_root_item tmp_item; | ||
245 | int i; | 238 | int i; |
246 | int ret; | 239 | int ret; |
247 | int err = 0; | 240 | int err = 0; |
@@ -267,13 +260,16 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
267 | } | 260 | } |
268 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); | 261 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); |
269 | BUG_ON(!dirty); | 262 | BUG_ON(!dirty); |
270 | memcpy(&dirty->snap_key, &root->root_key, | 263 | dirty->root = kmalloc(sizeof(*dirty->root), GFP_NOFS); |
271 | sizeof(root->root_key)); | 264 | BUG_ON(!dirty->root); |
272 | dirty->commit_root = root->commit_root; | 265 | |
266 | memset(&root->root_item.drop_progress, 0, | ||
267 | sizeof(struct btrfs_disk_key)); | ||
268 | root->root_item.drop_level = 0; | ||
269 | |||
270 | memcpy(dirty->root, root, sizeof(*root)); | ||
271 | dirty->root->node = root->commit_root; | ||
273 | root->commit_root = NULL; | 272 | root->commit_root = NULL; |
274 | dirty->root = root; | ||
275 | dirty->free_on_drop = 0; | ||
276 | memcpy(&tmp_item, &root->root_item, sizeof(tmp_item)); | ||
277 | 273 | ||
278 | root->root_key.offset = root->fs_info->generation; | 274 | root->root_key.offset = root->fs_info->generation; |
279 | btrfs_set_root_blocknr(&root->root_item, | 275 | btrfs_set_root_blocknr(&root->root_item, |
@@ -283,17 +279,21 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
283 | &root->root_item); | 279 | &root->root_item); |
284 | if (err) | 280 | if (err) |
285 | break; | 281 | break; |
286 | refs = btrfs_root_refs(&tmp_item); | 282 | |
287 | btrfs_set_root_refs(&tmp_item, refs - 1); | 283 | refs = btrfs_root_refs(&dirty->root->root_item); |
284 | btrfs_set_root_refs(&dirty->root->root_item, refs - 1); | ||
288 | err = btrfs_update_root(trans, root->fs_info->tree_root, | 285 | err = btrfs_update_root(trans, root->fs_info->tree_root, |
289 | &dirty->snap_key, | 286 | &dirty->root->root_key, |
290 | &tmp_item); | 287 | &dirty->root->root_item); |
291 | 288 | ||
292 | BUG_ON(err); | 289 | BUG_ON(err); |
293 | if (refs == 1) | 290 | if (refs == 1) { |
294 | list_add(&dirty->list, list); | 291 | list_add(&dirty->list, list); |
295 | else | 292 | } else { |
293 | WARN_ON(1); | ||
294 | kfree(dirty->root); | ||
296 | kfree(dirty); | 295 | kfree(dirty); |
296 | } | ||
297 | } | 297 | } |
298 | } | 298 | } |
299 | return err; | 299 | return err; |
@@ -305,23 +305,36 @@ static int drop_dirty_roots(struct btrfs_root *tree_root, | |||
305 | struct dirty_root *dirty; | 305 | struct dirty_root *dirty; |
306 | struct btrfs_trans_handle *trans; | 306 | struct btrfs_trans_handle *trans; |
307 | int ret = 0; | 307 | int ret = 0; |
308 | int err; | ||
309 | |||
308 | while(!list_empty(list)) { | 310 | while(!list_empty(list)) { |
309 | mutex_lock(&tree_root->fs_info->fs_mutex); | 311 | mutex_lock(&tree_root->fs_info->fs_mutex); |
310 | dirty = list_entry(list->next, struct dirty_root, list); | 312 | dirty = list_entry(list->next, struct dirty_root, list); |
311 | list_del_init(&dirty->list); | 313 | list_del_init(&dirty->list); |
312 | 314 | ||
313 | trans = btrfs_start_transaction(tree_root, 1); | 315 | while(1) { |
314 | ret = btrfs_drop_snapshot(trans, dirty->root, | 316 | trans = btrfs_start_transaction(tree_root, 1); |
315 | dirty->commit_root); | 317 | ret = btrfs_drop_snapshot(trans, dirty->root); |
318 | if (ret != -EAGAIN) { | ||
319 | break; | ||
320 | } | ||
321 | err = btrfs_update_root(trans, | ||
322 | tree_root, | ||
323 | &dirty->root->root_key, | ||
324 | &dirty->root->root_item); | ||
325 | if (err) | ||
326 | ret = err; | ||
327 | ret = btrfs_end_transaction(trans, tree_root); | ||
328 | BUG_ON(ret); | ||
329 | } | ||
316 | BUG_ON(ret); | 330 | BUG_ON(ret); |
317 | ret = btrfs_del_root(trans, tree_root, &dirty->snap_key); | 331 | ret = btrfs_del_root(trans, tree_root, &dirty->root->root_key); |
318 | if (ret) | 332 | if (ret) |
319 | break; | 333 | break; |
320 | ret = btrfs_end_transaction(trans, tree_root); | 334 | ret = btrfs_end_transaction(trans, tree_root); |
321 | BUG_ON(ret); | 335 | BUG_ON(ret); |
322 | 336 | ||
323 | if (dirty->free_on_drop) | 337 | kfree(dirty->root); |
324 | kfree(dirty->root); | ||
325 | kfree(dirty); | 338 | kfree(dirty); |
326 | mutex_unlock(&tree_root->fs_info->fs_mutex); | 339 | mutex_unlock(&tree_root->fs_info->fs_mutex); |
327 | btrfs_btree_balance_dirty(tree_root); | 340 | btrfs_btree_balance_dirty(tree_root); |