aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZheng Yan <zheng.yan@oracle.com>2008-09-26 10:04:53 -0400
committerChris Mason <chris.mason@oracle.com>2008-09-26 10:04:53 -0400
commite465768938f95388723b0fd3c50a0ae48173edb9 (patch)
tree0f624a9a98dedfafae902e12b384d27e1100cd3a
parente8569813849b5da394a195c7e76b4faa452b12d1 (diff)
Btrfs: Add shared reference cache
Btrfs has a cache of reference counts in leaves, allowing it to avoid reading tree leaves while deleting snapshots. To reduce contention with multiple subvolumes, this cache is private to each subvolume. This patch adds shared reference cache support. The new space balancing code plays with multiple subvols at the same time, So the old per-subvol reference cache is not well suited. Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r--fs/btrfs/ctree.h19
-rw-r--r--fs/btrfs/disk-io.c2
-rw-r--r--fs/btrfs/extent-tree.c17
-rw-r--r--fs/btrfs/ref-cache.c58
-rw-r--r--fs/btrfs/ref-cache.h7
-rw-r--r--fs/btrfs/transaction.c2
6 files changed, 71 insertions, 34 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index c683aaa925fa..b9f9f815ed09 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -81,6 +81,10 @@ struct btrfs_ordered_sum;
81#define BTRFS_TREE_LOG_OBJECTID -6ULL 81#define BTRFS_TREE_LOG_OBJECTID -6ULL
82#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL 82#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL
83 83
84/* for space balancing */
85#define BTRFS_TREE_RELOC_OBJECTID -8ULL
86#define BTRFS_DATA_RELOC_TREE_OBJECTID -9ULL
87
84/* dummy objectid represents multiple objectids */ 88/* dummy objectid represents multiple objectids */
85#define BTRFS_MULTIPLE_OBJECTIDS -255ULL 89#define BTRFS_MULTIPLE_OBJECTIDS -255ULL
86 90
@@ -539,6 +543,12 @@ struct btrfs_block_group_cache {
539 struct list_head list; 543 struct list_head list;
540}; 544};
541 545
546struct btrfs_leaf_ref_tree {
547 struct rb_root root;
548 struct list_head list;
549 spinlock_t lock;
550};
551
542struct btrfs_device; 552struct btrfs_device;
543struct btrfs_fs_devices; 553struct btrfs_fs_devices;
544struct btrfs_fs_info { 554struct btrfs_fs_info {
@@ -637,6 +647,8 @@ struct btrfs_fs_info {
637 struct task_struct *cleaner_kthread; 647 struct task_struct *cleaner_kthread;
638 int thread_pool_size; 648 int thread_pool_size;
639 649
650 struct btrfs_leaf_ref_tree shared_ref_tree;
651
640 struct kobject super_kobj; 652 struct kobject super_kobj;
641 struct completion kobj_unregister; 653 struct completion kobj_unregister;
642 int do_barriers; 654 int do_barriers;
@@ -670,13 +682,6 @@ struct btrfs_fs_info {
670 void *bdev_holder; 682 void *bdev_holder;
671}; 683};
672 684
673struct btrfs_leaf_ref_tree {
674 struct rb_root root;
675 struct btrfs_leaf_ref *last;
676 struct list_head list;
677 spinlock_t lock;
678};
679
680/* 685/*
681 * in ram representation of the tree. extent_root is used for all allocations 686 * in ram representation of the tree. extent_root is used for all allocations
682 * and for the extent tree extent_root root. 687 * and for the extent tree extent_root root.
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 71e81f3a765b..8969fee23318 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1406,6 +1406,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
1406 fs_info->btree_inode->i_mapping, GFP_NOFS); 1406 fs_info->btree_inode->i_mapping, GFP_NOFS);
1407 fs_info->do_barriers = 1; 1407 fs_info->do_barriers = 1;
1408 1408
1409 btrfs_leaf_ref_tree_init(&fs_info->shared_ref_tree);
1410
1409 BTRFS_I(fs_info->btree_inode)->root = tree_root; 1411 BTRFS_I(fs_info->btree_inode)->root = tree_root;
1410 memset(&BTRFS_I(fs_info->btree_inode)->location, 0, 1412 memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
1411 sizeof(struct btrfs_key)); 1413 sizeof(struct btrfs_key));
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 3e2f969de42d..9ab099bc01a4 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1091,16 +1091,26 @@ out:
1091int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, 1091int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
1092 struct extent_buffer *buf, u32 nr_extents) 1092 struct extent_buffer *buf, u32 nr_extents)
1093{ 1093{
1094 u32 nritems;
1095 struct btrfs_key key; 1094 struct btrfs_key key;
1096 struct btrfs_file_extent_item *fi; 1095 struct btrfs_file_extent_item *fi;
1096 u64 root_gen;
1097 u32 nritems;
1097 int i; 1098 int i;
1098 int level; 1099 int level;
1099 int ret = 0; 1100 int ret = 0;
1101 int shared = 0;
1100 1102
1101 if (!root->ref_cows) 1103 if (!root->ref_cows)
1102 return 0; 1104 return 0;
1103 1105
1106 if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
1107 shared = 0;
1108 root_gen = root->root_key.offset;
1109 } else {
1110 shared = 1;
1111 root_gen = trans->transid - 1;
1112 }
1113
1104 level = btrfs_header_level(buf); 1114 level = btrfs_header_level(buf);
1105 nritems = btrfs_header_nritems(buf); 1115 nritems = btrfs_header_nritems(buf);
1106 1116
@@ -1114,7 +1124,7 @@ int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
1114 goto out; 1124 goto out;
1115 } 1125 }
1116 1126
1117 ref->root_gen = root->root_key.offset; 1127 ref->root_gen = root_gen;
1118 ref->bytenr = buf->start; 1128 ref->bytenr = buf->start;
1119 ref->owner = btrfs_header_owner(buf); 1129 ref->owner = btrfs_header_owner(buf);
1120 ref->generation = btrfs_header_generation(buf); 1130 ref->generation = btrfs_header_generation(buf);
@@ -1143,8 +1153,7 @@ int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
1143 info++; 1153 info++;
1144 } 1154 }
1145 1155
1146 BUG_ON(!root->ref_tree); 1156 ret = btrfs_add_leaf_ref(root, ref, shared);
1147 ret = btrfs_add_leaf_ref(root, ref);
1148 WARN_ON(ret); 1157 WARN_ON(ret);
1149 btrfs_free_leaf_ref(root, ref); 1158 btrfs_free_leaf_ref(root, ref);
1150 } 1159 }
diff --git a/fs/btrfs/ref-cache.c b/fs/btrfs/ref-cache.c
index 272b9890c982..c5809988c875 100644
--- a/fs/btrfs/ref-cache.c
+++ b/fs/btrfs/ref-cache.c
@@ -78,7 +78,6 @@ static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
78 } 78 }
79 79
80 entry = rb_entry(node, struct btrfs_leaf_ref, rb_node); 80 entry = rb_entry(node, struct btrfs_leaf_ref, rb_node);
81 entry->in_tree = 1;
82 rb_link_node(node, parent, p); 81 rb_link_node(node, parent, p);
83 rb_insert_color(node, root); 82 rb_insert_color(node, root);
84 return NULL; 83 return NULL;
@@ -103,23 +102,29 @@ static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
103 return NULL; 102 return NULL;
104} 103}
105 104
106int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen) 105int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen,
106 int shared)
107{ 107{
108 struct btrfs_leaf_ref *ref = NULL; 108 struct btrfs_leaf_ref *ref = NULL;
109 struct btrfs_leaf_ref_tree *tree = root->ref_tree; 109 struct btrfs_leaf_ref_tree *tree = root->ref_tree;
110 110
111 if (shared)
112 tree = &root->fs_info->shared_ref_tree;
111 if (!tree) 113 if (!tree)
112 return 0; 114 return 0;
113 115
114 spin_lock(&tree->lock); 116 spin_lock(&tree->lock);
115 while(!list_empty(&tree->list)) { 117 while(!list_empty(&tree->list)) {
116 ref = list_entry(tree->list.next, struct btrfs_leaf_ref, list); 118 ref = list_entry(tree->list.next, struct btrfs_leaf_ref, list);
117 BUG_ON(!ref->in_tree); 119 BUG_ON(ref->tree != tree);
118 if (ref->root_gen > max_root_gen) 120 if (ref->root_gen > max_root_gen)
119 break; 121 break;
122 if (!xchg(&ref->in_tree, 0)) {
123 cond_resched_lock(&tree->lock);
124 continue;
125 }
120 126
121 rb_erase(&ref->rb_node, &tree->root); 127 rb_erase(&ref->rb_node, &tree->root);
122 ref->in_tree = 0;
123 list_del_init(&ref->list); 128 list_del_init(&ref->list);
124 129
125 spin_unlock(&tree->lock); 130 spin_unlock(&tree->lock);
@@ -137,32 +142,43 @@ struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root,
137 struct rb_node *rb; 142 struct rb_node *rb;
138 struct btrfs_leaf_ref *ref = NULL; 143 struct btrfs_leaf_ref *ref = NULL;
139 struct btrfs_leaf_ref_tree *tree = root->ref_tree; 144 struct btrfs_leaf_ref_tree *tree = root->ref_tree;
140 145again:
141 if (!tree) 146 if (tree) {
142 return NULL; 147 spin_lock(&tree->lock);
143 148 rb = tree_search(&tree->root, bytenr);
144 spin_lock(&tree->lock); 149 if (rb)
145 rb = tree_search(&tree->root, bytenr); 150 ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node);
146 if (rb) 151 if (ref)
147 ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node); 152 atomic_inc(&ref->usage);
148 if (ref) 153 spin_unlock(&tree->lock);
149 atomic_inc(&ref->usage); 154 if (ref)
150 spin_unlock(&tree->lock); 155 return ref;
151 return ref; 156 }
157 if (tree != &root->fs_info->shared_ref_tree) {
158 tree = &root->fs_info->shared_ref_tree;
159 goto again;
160 }
161 return NULL;
152} 162}
153 163
154int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref) 164int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref,
165 int shared)
155{ 166{
156 int ret = 0; 167 int ret = 0;
157 struct rb_node *rb; 168 struct rb_node *rb;
158 struct btrfs_leaf_ref_tree *tree = root->ref_tree; 169 struct btrfs_leaf_ref_tree *tree = root->ref_tree;
159 170
171 if (shared)
172 tree = &root->fs_info->shared_ref_tree;
173
160 spin_lock(&tree->lock); 174 spin_lock(&tree->lock);
161 rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node); 175 rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node);
162 if (rb) { 176 if (rb) {
163 ret = -EEXIST; 177 ret = -EEXIST;
164 } else { 178 } else {
165 atomic_inc(&ref->usage); 179 atomic_inc(&ref->usage);
180 ref->tree = tree;
181 ref->in_tree = 1;
166 list_add_tail(&ref->list, &tree->list); 182 list_add_tail(&ref->list, &tree->list);
167 } 183 }
168 spin_unlock(&tree->lock); 184 spin_unlock(&tree->lock);
@@ -171,13 +187,15 @@ int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
171 187
172int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref) 188int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref)
173{ 189{
174 struct btrfs_leaf_ref_tree *tree = root->ref_tree; 190 struct btrfs_leaf_ref_tree *tree;
191
192 if (!xchg(&ref->in_tree, 0))
193 return 0;
175 194
176 BUG_ON(!ref->in_tree); 195 tree = ref->tree;
177 spin_lock(&tree->lock); 196 spin_lock(&tree->lock);
178 197
179 rb_erase(&ref->rb_node, &tree->root); 198 rb_erase(&ref->rb_node, &tree->root);
180 ref->in_tree = 0;
181 list_del_init(&ref->list); 199 list_del_init(&ref->list);
182 200
183 spin_unlock(&tree->lock); 201 spin_unlock(&tree->lock);
diff --git a/fs/btrfs/ref-cache.h b/fs/btrfs/ref-cache.h
index c361b321c0c3..617564787f52 100644
--- a/fs/btrfs/ref-cache.h
+++ b/fs/btrfs/ref-cache.h
@@ -27,6 +27,7 @@ struct btrfs_extent_info {
27 27
28struct btrfs_leaf_ref { 28struct btrfs_leaf_ref {
29 struct rb_node rb_node; 29 struct rb_node rb_node;
30 struct btrfs_leaf_ref_tree *tree;
30 int in_tree; 31 int in_tree;
31 atomic_t usage; 32 atomic_t usage;
32 33
@@ -64,8 +65,10 @@ struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(struct btrfs_root *root,
64void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref); 65void btrfs_free_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref);
65struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root, 66struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root,
66 u64 bytenr); 67 u64 bytenr);
67int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref); 68int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref,
68int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen); 69 int shared);
70int btrfs_remove_leaf_refs(struct btrfs_root *root, u64 max_root_gen,
71 int shared);
69int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref); 72int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref);
70 73
71#endif 74#endif
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 151b00d52593..656baefa5255 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -650,7 +650,7 @@ static noinline int drop_dirty_roots(struct btrfs_root *tree_root,
650 ret = btrfs_end_transaction(trans, tree_root); 650 ret = btrfs_end_transaction(trans, tree_root);
651 BUG_ON(ret); 651 BUG_ON(ret);
652 652
653 ret = btrfs_remove_leaf_refs(root, max_useless); 653 ret = btrfs_remove_leaf_refs(root, max_useless, 0);
654 BUG_ON(ret); 654 BUG_ON(ret);
655 655
656 free_extent_buffer(dirty->root->node); 656 free_extent_buffer(dirty->root->node);