diff options
Diffstat (limited to 'fs/btrfs/transaction.c')
-rw-r--r-- | fs/btrfs/transaction.c | 67 |
1 files changed, 49 insertions, 18 deletions
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 768b0d223e68..543e5ee4033a 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "disk-io.h" | 24 | #include "disk-io.h" |
25 | #include "transaction.h" | 25 | #include "transaction.h" |
26 | #include "locking.h" | 26 | #include "locking.h" |
27 | #include "ref-cache.h" | ||
27 | 28 | ||
28 | static int total_trans = 0; | 29 | static int total_trans = 0; |
29 | extern struct kmem_cache *btrfs_trans_handle_cachep; | 30 | extern struct kmem_cache *btrfs_trans_handle_cachep; |
@@ -31,6 +32,13 @@ extern struct kmem_cache *btrfs_transaction_cachep; | |||
31 | 32 | ||
32 | #define BTRFS_ROOT_TRANS_TAG 0 | 33 | #define BTRFS_ROOT_TRANS_TAG 0 |
33 | 34 | ||
35 | struct dirty_root { | ||
36 | struct list_head list; | ||
37 | struct btrfs_root *root; | ||
38 | struct btrfs_root *latest_root; | ||
39 | struct btrfs_leaf_ref_tree ref_tree; | ||
40 | }; | ||
41 | |||
34 | static noinline void put_transaction(struct btrfs_transaction *transaction) | 42 | static noinline void put_transaction(struct btrfs_transaction *transaction) |
35 | { | 43 | { |
36 | WARN_ON(transaction->use_count == 0); | 44 | WARN_ON(transaction->use_count == 0); |
@@ -84,6 +92,7 @@ static noinline int join_transaction(struct btrfs_root *root) | |||
84 | 92 | ||
85 | static noinline int record_root_in_trans(struct btrfs_root *root) | 93 | static noinline int record_root_in_trans(struct btrfs_root *root) |
86 | { | 94 | { |
95 | struct dirty_root *dirty; | ||
87 | u64 running_trans_id = root->fs_info->running_transaction->transid; | 96 | u64 running_trans_id = root->fs_info->running_transaction->transid; |
88 | if (root->ref_cows && root->last_trans < running_trans_id) { | 97 | if (root->ref_cows && root->last_trans < running_trans_id) { |
89 | WARN_ON(root == root->fs_info->extent_root); | 98 | WARN_ON(root == root->fs_info->extent_root); |
@@ -91,7 +100,25 @@ static noinline int record_root_in_trans(struct btrfs_root *root) | |||
91 | radix_tree_tag_set(&root->fs_info->fs_roots_radix, | 100 | radix_tree_tag_set(&root->fs_info->fs_roots_radix, |
92 | (unsigned long)root->root_key.objectid, | 101 | (unsigned long)root->root_key.objectid, |
93 | BTRFS_ROOT_TRANS_TAG); | 102 | BTRFS_ROOT_TRANS_TAG); |
103 | |||
104 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); | ||
105 | BUG_ON(!dirty); | ||
106 | dirty->root = kmalloc(sizeof(*dirty->root), GFP_NOFS); | ||
107 | BUG_ON(!dirty->root); | ||
108 | |||
109 | dirty->latest_root = root; | ||
110 | INIT_LIST_HEAD(&dirty->list); | ||
111 | btrfs_leaf_ref_tree_init(&dirty->ref_tree); | ||
112 | dirty->ref_tree.generation = running_trans_id; | ||
113 | |||
94 | root->commit_root = btrfs_root_node(root); | 114 | root->commit_root = btrfs_root_node(root); |
115 | root->ref_tree = &dirty->ref_tree; | ||
116 | |||
117 | memcpy(dirty->root, root, sizeof(*root)); | ||
118 | spin_lock_init(&dirty->root->node_lock); | ||
119 | mutex_init(&dirty->root->objectid_mutex); | ||
120 | dirty->root->node = root->commit_root; | ||
121 | dirty->root->commit_root = NULL; | ||
95 | } else { | 122 | } else { |
96 | WARN_ON(1); | 123 | WARN_ON(1); |
97 | } | 124 | } |
@@ -310,12 +337,6 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, | |||
310 | return 0; | 337 | return 0; |
311 | } | 338 | } |
312 | 339 | ||
313 | struct dirty_root { | ||
314 | struct list_head list; | ||
315 | struct btrfs_root *root; | ||
316 | struct btrfs_root *latest_root; | ||
317 | }; | ||
318 | |||
319 | int btrfs_add_dead_root(struct btrfs_root *root, | 340 | int btrfs_add_dead_root(struct btrfs_root *root, |
320 | struct btrfs_root *latest, | 341 | struct btrfs_root *latest, |
321 | struct list_head *dead_list) | 342 | struct list_head *dead_list) |
@@ -325,8 +346,10 @@ int btrfs_add_dead_root(struct btrfs_root *root, | |||
325 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); | 346 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); |
326 | if (!dirty) | 347 | if (!dirty) |
327 | return -ENOMEM; | 348 | return -ENOMEM; |
349 | btrfs_leaf_ref_tree_init(&dirty->ref_tree); | ||
328 | dirty->root = root; | 350 | dirty->root = root; |
329 | dirty->latest_root = latest; | 351 | dirty->latest_root = latest; |
352 | root->ref_tree = NULL; | ||
330 | list_add(&dirty->list, dead_list); | 353 | list_add(&dirty->list, dead_list); |
331 | return 0; | 354 | return 0; |
332 | } | 355 | } |
@@ -354,11 +377,23 @@ static noinline int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
354 | radix_tree_tag_clear(radix, | 377 | radix_tree_tag_clear(radix, |
355 | (unsigned long)root->root_key.objectid, | 378 | (unsigned long)root->root_key.objectid, |
356 | BTRFS_ROOT_TRANS_TAG); | 379 | BTRFS_ROOT_TRANS_TAG); |
380 | |||
381 | BUG_ON(!root->ref_tree); | ||
382 | dirty = container_of(root->ref_tree, struct dirty_root, | ||
383 | ref_tree); | ||
384 | |||
357 | if (root->commit_root == root->node) { | 385 | if (root->commit_root == root->node) { |
358 | WARN_ON(root->node->start != | 386 | WARN_ON(root->node->start != |
359 | btrfs_root_bytenr(&root->root_item)); | 387 | btrfs_root_bytenr(&root->root_item)); |
388 | |||
389 | BUG_ON(!btrfs_leaf_ref_tree_empty( | ||
390 | root->ref_tree)); | ||
360 | free_extent_buffer(root->commit_root); | 391 | free_extent_buffer(root->commit_root); |
361 | root->commit_root = NULL; | 392 | root->commit_root = NULL; |
393 | root->ref_tree = NULL; | ||
394 | |||
395 | kfree(dirty->root); | ||
396 | kfree(dirty); | ||
362 | 397 | ||
363 | /* make sure to update the root on disk | 398 | /* make sure to update the root on disk |
364 | * so we get any updates to the block used | 399 | * so we get any updates to the block used |
@@ -370,23 +405,12 @@ static noinline int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
370 | &root->root_item); | 405 | &root->root_item); |
371 | continue; | 406 | continue; |
372 | } | 407 | } |
373 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); | ||
374 | BUG_ON(!dirty); | ||
375 | dirty->root = kmalloc(sizeof(*dirty->root), GFP_NOFS); | ||
376 | BUG_ON(!dirty->root); | ||
377 | 408 | ||
378 | memset(&root->root_item.drop_progress, 0, | 409 | memset(&root->root_item.drop_progress, 0, |
379 | sizeof(struct btrfs_disk_key)); | 410 | sizeof(struct btrfs_disk_key)); |
380 | root->root_item.drop_level = 0; | 411 | root->root_item.drop_level = 0; |
381 | |||
382 | memcpy(dirty->root, root, sizeof(*root)); | ||
383 | dirty->root->node = root->commit_root; | ||
384 | dirty->latest_root = root; | ||
385 | spin_lock_init(&dirty->root->node_lock); | ||
386 | mutex_init(&dirty->root->objectid_mutex); | ||
387 | |||
388 | root->commit_root = NULL; | 412 | root->commit_root = NULL; |
389 | 413 | root->ref_tree = NULL; | |
390 | root->root_key.offset = root->fs_info->generation; | 414 | root->root_key.offset = root->fs_info->generation; |
391 | btrfs_set_root_bytenr(&root->root_item, | 415 | btrfs_set_root_bytenr(&root->root_item, |
392 | root->node->start); | 416 | root->node->start); |
@@ -409,6 +433,7 @@ static noinline int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
409 | list_add(&dirty->list, list); | 433 | list_add(&dirty->list, list); |
410 | } else { | 434 | } else { |
411 | WARN_ON(1); | 435 | WARN_ON(1); |
436 | free_extent_buffer(dirty->root->node); | ||
412 | kfree(dirty->root); | 437 | kfree(dirty->root); |
413 | kfree(dirty); | 438 | kfree(dirty); |
414 | } | 439 | } |
@@ -514,6 +539,8 @@ static noinline int drop_dirty_roots(struct btrfs_root *tree_root, | |||
514 | ret = btrfs_end_transaction(trans, tree_root); | 539 | ret = btrfs_end_transaction(trans, tree_root); |
515 | BUG_ON(ret); | 540 | BUG_ON(ret); |
516 | 541 | ||
542 | btrfs_remove_leaf_refs(dirty->root); | ||
543 | |||
517 | free_extent_buffer(dirty->root->node); | 544 | free_extent_buffer(dirty->root->node); |
518 | kfree(dirty->root); | 545 | kfree(dirty->root); |
519 | kfree(dirty); | 546 | kfree(dirty); |
@@ -698,6 +725,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | |||
698 | &dirty_fs_roots); | 725 | &dirty_fs_roots); |
699 | BUG_ON(ret); | 726 | BUG_ON(ret); |
700 | 727 | ||
728 | spin_lock(&root->fs_info->ref_cache_lock); | ||
729 | root->fs_info->running_ref_cache_size = 0; | ||
730 | spin_unlock(&root->fs_info->ref_cache_lock); | ||
731 | |||
701 | ret = btrfs_commit_tree_roots(trans, root); | 732 | ret = btrfs_commit_tree_roots(trans, root); |
702 | BUG_ON(ret); | 733 | BUG_ON(ret); |
703 | 734 | ||