diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-07-28 15:32:51 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:05 -0400 |
commit | 017e5369eb353559d68a11d4a718faa634533821 (patch) | |
tree | c339f2f4a59e403c7f9bfa8d137663c6bf260537 /fs/btrfs/ref-cache.c | |
parent | 31153d81284934601d08110ac7698fd9a535e4c0 (diff) |
Btrfs: Leaf reference cache update
This changes the reference cache to make a single cache per root
instead of one cache per transaction, and to key by the byte number
of the disk block instead of the keys inside.
This makes it much less likely to have cache misses if a snapshot
or something has an extra reference on a higher node or a leaf while
the first transaction that added the leaf into the cache is dropping.
Some throttling is added to functions that free blocks heavily so they
wait for old transactions to drop.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/ref-cache.c')
-rw-r--r-- | fs/btrfs/ref-cache.c | 71 |
1 files changed, 15 insertions, 56 deletions
diff --git a/fs/btrfs/ref-cache.c b/fs/btrfs/ref-cache.c index 95a9faeb9dc4..ec9587784a3d 100644 --- a/fs/btrfs/ref-cache.c +++ b/fs/btrfs/ref-cache.c | |||
@@ -29,6 +29,7 @@ struct btrfs_leaf_ref *btrfs_alloc_leaf_ref(int nr_extents) | |||
29 | if (ref) { | 29 | if (ref) { |
30 | memset(ref, 0, sizeof(*ref)); | 30 | memset(ref, 0, sizeof(*ref)); |
31 | atomic_set(&ref->usage, 1); | 31 | atomic_set(&ref->usage, 1); |
32 | INIT_LIST_HEAD(&ref->list); | ||
32 | } | 33 | } |
33 | return ref; | 34 | return ref; |
34 | } | 35 | } |
@@ -44,40 +45,21 @@ void btrfs_free_leaf_ref(struct btrfs_leaf_ref *ref) | |||
44 | } | 45 | } |
45 | } | 46 | } |
46 | 47 | ||
47 | static int comp_keys(struct btrfs_key *k1, struct btrfs_key *k2) | 48 | static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr, |
48 | { | ||
49 | if (k1->objectid > k2->objectid) | ||
50 | return 1; | ||
51 | if (k1->objectid < k2->objectid) | ||
52 | return -1; | ||
53 | if (k1->type > k2->type) | ||
54 | return 1; | ||
55 | if (k1->type < k2->type) | ||
56 | return -1; | ||
57 | if (k1->offset > k2->offset) | ||
58 | return 1; | ||
59 | if (k1->offset < k2->offset) | ||
60 | return -1; | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static struct rb_node *tree_insert(struct rb_root *root, struct btrfs_key *key, | ||
65 | struct rb_node *node) | 49 | struct rb_node *node) |
66 | { | 50 | { |
67 | struct rb_node ** p = &root->rb_node; | 51 | struct rb_node ** p = &root->rb_node; |
68 | struct rb_node * parent = NULL; | 52 | struct rb_node * parent = NULL; |
69 | struct btrfs_leaf_ref *entry; | 53 | struct btrfs_leaf_ref *entry; |
70 | int ret; | ||
71 | 54 | ||
72 | while(*p) { | 55 | while(*p) { |
73 | parent = *p; | 56 | parent = *p; |
74 | entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node); | 57 | entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node); |
75 | WARN_ON(!entry->in_tree); | 58 | WARN_ON(!entry->in_tree); |
76 | 59 | ||
77 | ret = comp_keys(key, &entry->key); | 60 | if (bytenr < entry->bytenr) |
78 | if (ret < 0) | ||
79 | p = &(*p)->rb_left; | 61 | p = &(*p)->rb_left; |
80 | else if (ret > 0) | 62 | else if (bytenr > entry->bytenr) |
81 | p = &(*p)->rb_right; | 63 | p = &(*p)->rb_right; |
82 | else | 64 | else |
83 | return parent; | 65 | return parent; |
@@ -90,20 +72,18 @@ static struct rb_node *tree_insert(struct rb_root *root, struct btrfs_key *key, | |||
90 | return NULL; | 72 | return NULL; |
91 | } | 73 | } |
92 | 74 | ||
93 | static struct rb_node *tree_search(struct rb_root *root, struct btrfs_key *key) | 75 | static struct rb_node *tree_search(struct rb_root *root, u64 bytenr) |
94 | { | 76 | { |
95 | struct rb_node * n = root->rb_node; | 77 | struct rb_node * n = root->rb_node; |
96 | struct btrfs_leaf_ref *entry; | 78 | struct btrfs_leaf_ref *entry; |
97 | int ret; | ||
98 | 79 | ||
99 | while(n) { | 80 | while(n) { |
100 | entry = rb_entry(n, struct btrfs_leaf_ref, rb_node); | 81 | entry = rb_entry(n, struct btrfs_leaf_ref, rb_node); |
101 | WARN_ON(!entry->in_tree); | 82 | WARN_ON(!entry->in_tree); |
102 | 83 | ||
103 | ret = comp_keys(key, &entry->key); | 84 | if (bytenr < entry->bytenr) |
104 | if (ret < 0) | ||
105 | n = n->rb_left; | 85 | n = n->rb_left; |
106 | else if (ret > 0) | 86 | else if (bytenr > entry->bytenr) |
107 | n = n->rb_right; | 87 | n = n->rb_right; |
108 | else | 88 | else |
109 | return n; | 89 | return n; |
@@ -122,11 +102,11 @@ int btrfs_remove_leaf_refs(struct btrfs_root *root) | |||
122 | 102 | ||
123 | spin_lock(&tree->lock); | 103 | spin_lock(&tree->lock); |
124 | while(!btrfs_leaf_ref_tree_empty(tree)) { | 104 | while(!btrfs_leaf_ref_tree_empty(tree)) { |
125 | tree->last = NULL; | ||
126 | rb = rb_first(&tree->root); | 105 | rb = rb_first(&tree->root); |
127 | ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node); | 106 | ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node); |
128 | rb_erase(&ref->rb_node, &tree->root); | 107 | rb_erase(&ref->rb_node, &tree->root); |
129 | ref->in_tree = 0; | 108 | ref->in_tree = 0; |
109 | list_del_init(&ref->list); | ||
130 | 110 | ||
131 | spin_unlock(&tree->lock); | 111 | spin_unlock(&tree->lock); |
132 | 112 | ||
@@ -140,7 +120,7 @@ int btrfs_remove_leaf_refs(struct btrfs_root *root) | |||
140 | } | 120 | } |
141 | 121 | ||
142 | struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root, | 122 | struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root, |
143 | struct btrfs_key *key) | 123 | u64 bytenr) |
144 | { | 124 | { |
145 | struct rb_node *rb; | 125 | struct rb_node *rb; |
146 | struct btrfs_leaf_ref *ref = NULL; | 126 | struct btrfs_leaf_ref *ref = NULL; |
@@ -150,15 +130,9 @@ struct btrfs_leaf_ref *btrfs_lookup_leaf_ref(struct btrfs_root *root, | |||
150 | return NULL; | 130 | return NULL; |
151 | 131 | ||
152 | spin_lock(&tree->lock); | 132 | spin_lock(&tree->lock); |
153 | if (tree->last && comp_keys(key, &tree->last->key) == 0) { | 133 | rb = tree_search(&tree->root, bytenr); |
154 | ref = tree->last; | 134 | if (rb) |
155 | } else { | 135 | ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node); |
156 | rb = tree_search(&tree->root, key); | ||
157 | if (rb) { | ||
158 | ref = rb_entry(rb, struct btrfs_leaf_ref, rb_node); | ||
159 | tree->last = ref; | ||
160 | } | ||
161 | } | ||
162 | if (ref) | 136 | if (ref) |
163 | atomic_inc(&ref->usage); | 137 | atomic_inc(&ref->usage); |
164 | spin_unlock(&tree->lock); | 138 | spin_unlock(&tree->lock); |
@@ -171,21 +145,17 @@ int btrfs_add_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref) | |||
171 | struct rb_node *rb; | 145 | struct rb_node *rb; |
172 | size_t size = btrfs_leaf_ref_size(ref->nritems); | 146 | size_t size = btrfs_leaf_ref_size(ref->nritems); |
173 | struct btrfs_leaf_ref_tree *tree = root->ref_tree; | 147 | struct btrfs_leaf_ref_tree *tree = root->ref_tree; |
174 | struct btrfs_transaction *trans = root->fs_info->running_transaction; | ||
175 | 148 | ||
176 | spin_lock(&tree->lock); | 149 | spin_lock(&tree->lock); |
177 | rb = tree_insert(&tree->root, &ref->key, &ref->rb_node); | 150 | rb = tree_insert(&tree->root, ref->bytenr, &ref->rb_node); |
178 | if (rb) { | 151 | if (rb) { |
179 | ret = -EEXIST; | 152 | ret = -EEXIST; |
180 | } else { | 153 | } else { |
181 | spin_lock(&root->fs_info->ref_cache_lock); | 154 | spin_lock(&root->fs_info->ref_cache_lock); |
182 | root->fs_info->total_ref_cache_size += size; | 155 | root->fs_info->total_ref_cache_size += size; |
183 | if (trans && tree->generation == trans->transid) | ||
184 | root->fs_info->running_ref_cache_size += size; | ||
185 | spin_unlock(&root->fs_info->ref_cache_lock); | 156 | spin_unlock(&root->fs_info->ref_cache_lock); |
186 | |||
187 | tree->last = ref; | ||
188 | atomic_inc(&ref->usage); | 157 | atomic_inc(&ref->usage); |
158 | list_add_tail(&ref->list, &tree->list); | ||
189 | } | 159 | } |
190 | spin_unlock(&tree->lock); | 160 | spin_unlock(&tree->lock); |
191 | return ret; | 161 | return ret; |
@@ -195,28 +165,17 @@ int btrfs_remove_leaf_ref(struct btrfs_root *root, struct btrfs_leaf_ref *ref) | |||
195 | { | 165 | { |
196 | size_t size = btrfs_leaf_ref_size(ref->nritems); | 166 | size_t size = btrfs_leaf_ref_size(ref->nritems); |
197 | struct btrfs_leaf_ref_tree *tree = root->ref_tree; | 167 | struct btrfs_leaf_ref_tree *tree = root->ref_tree; |
198 | struct btrfs_transaction *trans = root->fs_info->running_transaction; | ||
199 | 168 | ||
200 | BUG_ON(!ref->in_tree); | 169 | BUG_ON(!ref->in_tree); |
201 | spin_lock(&tree->lock); | 170 | spin_lock(&tree->lock); |
202 | 171 | ||
203 | spin_lock(&root->fs_info->ref_cache_lock); | 172 | spin_lock(&root->fs_info->ref_cache_lock); |
204 | root->fs_info->total_ref_cache_size -= size; | 173 | root->fs_info->total_ref_cache_size -= size; |
205 | if (trans && tree->generation == trans->transid) | ||
206 | root->fs_info->running_ref_cache_size -= size; | ||
207 | spin_unlock(&root->fs_info->ref_cache_lock); | 174 | spin_unlock(&root->fs_info->ref_cache_lock); |
208 | 175 | ||
209 | if (tree->last == ref) { | ||
210 | struct rb_node *next = rb_next(&ref->rb_node); | ||
211 | if (next) { | ||
212 | tree->last = rb_entry(next, struct btrfs_leaf_ref, | ||
213 | rb_node); | ||
214 | } else | ||
215 | tree->last = NULL; | ||
216 | } | ||
217 | |||
218 | rb_erase(&ref->rb_node, &tree->root); | 176 | rb_erase(&ref->rb_node, &tree->root); |
219 | ref->in_tree = 0; | 177 | ref->in_tree = 0; |
178 | list_del_init(&ref->list); | ||
220 | 179 | ||
221 | spin_unlock(&tree->lock); | 180 | spin_unlock(&tree->lock); |
222 | 181 | ||