diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-06-25 16:01:31 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:03 -0400 |
commit | e7a84565bcdb239caad29ccbe559ef978090ac7e (patch) | |
tree | aaeba005e713cde2030c27451d98847accff116d | |
parent | a74a4b97b61beede185b4b3ad359d7d378b0d312 (diff) |
Btrfs: Add btree locking to the tree defragmentation code
The online btree defragger is simplified and rewritten to use
standard btree searches instead of a walk up / down mechanism.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/ctree.c | 46 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 2 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 7 | ||||
-rw-r--r-- | fs/btrfs/tree-defrag.c | 239 |
4 files changed, 93 insertions, 201 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 40f0e0cb804b..7f4cc2b88d09 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -354,7 +354,6 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, | |||
354 | struct btrfs_key *progress) | 354 | struct btrfs_key *progress) |
355 | { | 355 | { |
356 | struct extent_buffer *cur; | 356 | struct extent_buffer *cur; |
357 | struct extent_buffer *tmp; | ||
358 | u64 blocknr; | 357 | u64 blocknr; |
359 | u64 gen; | 358 | u64 gen; |
360 | u64 search_start = *last_ret; | 359 | u64 search_start = *last_ret; |
@@ -370,9 +369,6 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, | |||
370 | int progress_passed = 0; | 369 | int progress_passed = 0; |
371 | struct btrfs_disk_key disk_key; | 370 | struct btrfs_disk_key disk_key; |
372 | 371 | ||
373 | /* FIXME this code needs locking */ | ||
374 | return 0; | ||
375 | |||
376 | parent_level = btrfs_header_level(parent); | 372 | parent_level = btrfs_header_level(parent); |
377 | if (cache_only && parent_level != 1) | 373 | if (cache_only && parent_level != 1) |
378 | return 0; | 374 | return 0; |
@@ -454,20 +450,23 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, | |||
454 | if (search_start == 0) | 450 | if (search_start == 0) |
455 | search_start = last_block; | 451 | search_start = last_block; |
456 | 452 | ||
453 | btrfs_tree_lock(cur); | ||
457 | err = __btrfs_cow_block(trans, root, cur, parent, i, | 454 | err = __btrfs_cow_block(trans, root, cur, parent, i, |
458 | &tmp, search_start, | 455 | &cur, search_start, |
459 | min(16 * blocksize, | 456 | min(16 * blocksize, |
460 | (end_slot - i) * blocksize)); | 457 | (end_slot - i) * blocksize)); |
461 | if (err) { | 458 | if (err) { |
459 | btrfs_tree_unlock(cur); | ||
462 | free_extent_buffer(cur); | 460 | free_extent_buffer(cur); |
463 | break; | 461 | break; |
464 | } | 462 | } |
465 | search_start = tmp->start; | 463 | search_start = cur->start; |
466 | last_block = tmp->start; | 464 | last_block = cur->start; |
467 | *last_ret = search_start; | 465 | *last_ret = search_start; |
468 | if (parent_level == 1) | 466 | if (parent_level == 1) |
469 | btrfs_clear_buffer_defrag(tmp); | 467 | btrfs_clear_buffer_defrag(cur); |
470 | free_extent_buffer(tmp); | 468 | btrfs_tree_unlock(cur); |
469 | free_extent_buffer(cur); | ||
471 | } | 470 | } |
472 | if (parent->map_token) { | 471 | if (parent->map_token) { |
473 | unmap_extent_buffer(parent, parent->map_token, | 472 | unmap_extent_buffer(parent, parent->map_token, |
@@ -2970,6 +2969,35 @@ int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path) | |||
2970 | return 1; | 2969 | return 1; |
2971 | } | 2970 | } |
2972 | 2971 | ||
2972 | int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path, | ||
2973 | struct btrfs_key *key, int lowest_level) | ||
2974 | { | ||
2975 | int level = lowest_level; | ||
2976 | int slot; | ||
2977 | struct extent_buffer *c; | ||
2978 | |||
2979 | while(level < BTRFS_MAX_LEVEL) { | ||
2980 | if (!path->nodes[level]) | ||
2981 | return 1; | ||
2982 | |||
2983 | slot = path->slots[level] + 1; | ||
2984 | c = path->nodes[level]; | ||
2985 | if (slot >= btrfs_header_nritems(c)) { | ||
2986 | level++; | ||
2987 | if (level == BTRFS_MAX_LEVEL) { | ||
2988 | return 1; | ||
2989 | } | ||
2990 | continue; | ||
2991 | } | ||
2992 | if (level == 0) | ||
2993 | btrfs_item_key_to_cpu(c, key, slot); | ||
2994 | else | ||
2995 | btrfs_node_key_to_cpu(c, key, slot); | ||
2996 | return 0; | ||
2997 | } | ||
2998 | return 1; | ||
2999 | } | ||
3000 | |||
2973 | /* | 3001 | /* |
2974 | * search the tree again to find a leaf with greater keys | 3002 | * search the tree again to find a leaf with greater keys |
2975 | * returns 0 if it found something or 1 if there are no greater leaves. | 3003 | * returns 0 if it found something or 1 if there are no greater leaves. |
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 244fe86bcc55..ca8e6f15859e 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1411,6 +1411,8 @@ int btrfs_previous_item(struct btrfs_root *root, | |||
1411 | 1411 | ||
1412 | struct extent_buffer *btrfs_root_node(struct btrfs_root *root); | 1412 | struct extent_buffer *btrfs_root_node(struct btrfs_root *root); |
1413 | struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root); | 1413 | struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root); |
1414 | int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path, | ||
1415 | struct btrfs_key *key, int lowest_level); | ||
1414 | 1416 | ||
1415 | int btrfs_cow_block(struct btrfs_trans_handle *trans, | 1417 | int btrfs_cow_block(struct btrfs_trans_handle *trans, |
1416 | struct btrfs_root *root, struct extent_buffer *buf, | 1418 | struct btrfs_root *root, struct extent_buffer *buf, |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 89cc4f611869..a9b3a25a45b7 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -2201,6 +2201,7 @@ int drop_snap_lookup_refcount(struct btrfs_root *root, u64 start, u64 len, | |||
2201 | { | 2201 | { |
2202 | mutex_unlock(&root->fs_info->alloc_mutex); | 2202 | mutex_unlock(&root->fs_info->alloc_mutex); |
2203 | lookup_extent_ref(NULL, root, start, len, refs); | 2203 | lookup_extent_ref(NULL, root, start, len, refs); |
2204 | cond_resched(); | ||
2204 | mutex_lock(&root->fs_info->alloc_mutex); | 2205 | mutex_lock(&root->fs_info->alloc_mutex); |
2205 | return lookup_extent_ref(NULL, root, start, len, refs); | 2206 | return lookup_extent_ref(NULL, root, start, len, refs); |
2206 | } | 2207 | } |
@@ -2280,6 +2281,7 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans, | |||
2280 | 2281 | ||
2281 | next = read_tree_block(root, bytenr, blocksize, | 2282 | next = read_tree_block(root, bytenr, blocksize, |
2282 | ptr_gen); | 2283 | ptr_gen); |
2284 | cond_resched(); | ||
2283 | mutex_lock(&root->fs_info->alloc_mutex); | 2285 | mutex_lock(&root->fs_info->alloc_mutex); |
2284 | 2286 | ||
2285 | /* we've dropped the lock, double check */ | 2287 | /* we've dropped the lock, double check */ |
@@ -2329,6 +2331,7 @@ out: | |||
2329 | *level += 1; | 2331 | *level += 1; |
2330 | BUG_ON(ret); | 2332 | BUG_ON(ret); |
2331 | mutex_unlock(&root->fs_info->alloc_mutex); | 2333 | mutex_unlock(&root->fs_info->alloc_mutex); |
2334 | cond_resched(); | ||
2332 | return 0; | 2335 | return 0; |
2333 | } | 2336 | } |
2334 | 2337 | ||
@@ -2448,6 +2451,10 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
2448 | break; | 2451 | break; |
2449 | if (wret < 0) | 2452 | if (wret < 0) |
2450 | ret = wret; | 2453 | ret = wret; |
2454 | if (trans->transaction->in_commit) { | ||
2455 | ret = -EAGAIN; | ||
2456 | break; | ||
2457 | } | ||
2451 | } | 2458 | } |
2452 | for (i = 0; i <= orig_level; i++) { | 2459 | for (i = 0; i <= orig_level; i++) { |
2453 | if (path->nodes[i]) { | 2460 | if (path->nodes[i]) { |
diff --git a/fs/btrfs/tree-defrag.c b/fs/btrfs/tree-defrag.c index fab851d85383..1677e4edaf6f 100644 --- a/fs/btrfs/tree-defrag.c +++ b/fs/btrfs/tree-defrag.c | |||
@@ -21,167 +21,26 @@ | |||
21 | #include "disk-io.h" | 21 | #include "disk-io.h" |
22 | #include "print-tree.h" | 22 | #include "print-tree.h" |
23 | #include "transaction.h" | 23 | #include "transaction.h" |
24 | 24 | #include "locking.h" | |
25 | static void reada_defrag(struct btrfs_root *root, | ||
26 | struct extent_buffer *node) | ||
27 | { | ||
28 | int i; | ||
29 | u32 nritems; | ||
30 | u64 bytenr; | ||
31 | u64 gen; | ||
32 | u32 blocksize; | ||
33 | int ret; | ||
34 | |||
35 | blocksize = btrfs_level_size(root, btrfs_header_level(node) - 1); | ||
36 | nritems = btrfs_header_nritems(node); | ||
37 | for (i = 0; i < nritems; i++) { | ||
38 | bytenr = btrfs_node_blockptr(node, i); | ||
39 | gen = btrfs_node_ptr_generation(node, i); | ||
40 | ret = readahead_tree_block(root, bytenr, blocksize, gen); | ||
41 | if (ret) | ||
42 | break; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | static int defrag_walk_down(struct btrfs_trans_handle *trans, | ||
47 | struct btrfs_root *root, | ||
48 | struct btrfs_path *path, int *level, | ||
49 | int cache_only, u64 *last_ret) | ||
50 | { | ||
51 | struct extent_buffer *next; | ||
52 | struct extent_buffer *cur; | ||
53 | u64 bytenr; | ||
54 | u64 ptr_gen; | ||
55 | int ret = 0; | ||
56 | int is_extent = 0; | ||
57 | |||
58 | WARN_ON(*level < 0); | ||
59 | WARN_ON(*level >= BTRFS_MAX_LEVEL); | ||
60 | |||
61 | if (root->fs_info->extent_root == root) | ||
62 | is_extent = 1; | ||
63 | |||
64 | if (*level == 1 && cache_only && path->nodes[1] && | ||
65 | !btrfs_buffer_defrag(path->nodes[1])) { | ||
66 | goto out; | ||
67 | } | ||
68 | while(*level > 0) { | ||
69 | WARN_ON(*level < 0); | ||
70 | WARN_ON(*level >= BTRFS_MAX_LEVEL); | ||
71 | cur = path->nodes[*level]; | ||
72 | |||
73 | if (!cache_only && *level > 1 && path->slots[*level] == 0) | ||
74 | reada_defrag(root, cur); | ||
75 | |||
76 | if (btrfs_header_level(cur) != *level) | ||
77 | WARN_ON(1); | ||
78 | |||
79 | if (path->slots[*level] >= | ||
80 | btrfs_header_nritems(cur)) | ||
81 | break; | ||
82 | |||
83 | if (*level == 1) { | ||
84 | WARN_ON(btrfs_header_generation(path->nodes[*level]) != | ||
85 | trans->transid); | ||
86 | ret = btrfs_realloc_node(trans, root, | ||
87 | path->nodes[*level], | ||
88 | path->slots[*level], | ||
89 | cache_only, last_ret, | ||
90 | &root->defrag_progress); | ||
91 | if (is_extent) | ||
92 | btrfs_extent_post_op(trans, root); | ||
93 | |||
94 | break; | ||
95 | } | ||
96 | bytenr = btrfs_node_blockptr(cur, path->slots[*level]); | ||
97 | ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]); | ||
98 | |||
99 | if (cache_only) { | ||
100 | next = btrfs_find_tree_block(root, bytenr, | ||
101 | btrfs_level_size(root, *level - 1)); | ||
102 | if (!next || !btrfs_buffer_uptodate(next, ptr_gen) || | ||
103 | !btrfs_buffer_defrag(next)) { | ||
104 | free_extent_buffer(next); | ||
105 | path->slots[*level]++; | ||
106 | continue; | ||
107 | } | ||
108 | } else { | ||
109 | next = read_tree_block(root, bytenr, | ||
110 | btrfs_level_size(root, *level - 1), | ||
111 | ptr_gen); | ||
112 | } | ||
113 | ret = btrfs_cow_block(trans, root, next, path->nodes[*level], | ||
114 | path->slots[*level], &next); | ||
115 | BUG_ON(ret); | ||
116 | if (is_extent) | ||
117 | btrfs_extent_post_op(trans, root); | ||
118 | |||
119 | WARN_ON(*level <= 0); | ||
120 | if (path->nodes[*level-1]) | ||
121 | free_extent_buffer(path->nodes[*level-1]); | ||
122 | path->nodes[*level-1] = next; | ||
123 | *level = btrfs_header_level(next); | ||
124 | path->slots[*level] = 0; | ||
125 | } | ||
126 | WARN_ON(*level < 0); | ||
127 | WARN_ON(*level >= BTRFS_MAX_LEVEL); | ||
128 | |||
129 | btrfs_clear_buffer_defrag(path->nodes[*level]); | ||
130 | out: | ||
131 | free_extent_buffer(path->nodes[*level]); | ||
132 | path->nodes[*level] = NULL; | ||
133 | *level += 1; | ||
134 | WARN_ON(ret && ret != -EAGAIN); | ||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | static int defrag_walk_up(struct btrfs_trans_handle *trans, | ||
139 | struct btrfs_root *root, | ||
140 | struct btrfs_path *path, int *level, | ||
141 | int cache_only) | ||
142 | { | ||
143 | int i; | ||
144 | int slot; | ||
145 | struct extent_buffer *node; | ||
146 | |||
147 | for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { | ||
148 | slot = path->slots[i]; | ||
149 | if (slot < btrfs_header_nritems(path->nodes[i]) - 1) { | ||
150 | path->slots[i]++; | ||
151 | *level = i; | ||
152 | node = path->nodes[i]; | ||
153 | WARN_ON(i == 0); | ||
154 | btrfs_node_key_to_cpu(node, &root->defrag_progress, | ||
155 | path->slots[i]); | ||
156 | root->defrag_level = i; | ||
157 | return 0; | ||
158 | } else { | ||
159 | btrfs_clear_buffer_defrag(path->nodes[*level]); | ||
160 | free_extent_buffer(path->nodes[*level]); | ||
161 | path->nodes[*level] = NULL; | ||
162 | *level = i + 1; | ||
163 | } | ||
164 | } | ||
165 | return 1; | ||
166 | } | ||
167 | 25 | ||
168 | int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, | 26 | int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, |
169 | struct btrfs_root *root, int cache_only) | 27 | struct btrfs_root *root, int cache_only) |
170 | { | 28 | { |
171 | struct btrfs_path *path = NULL; | 29 | struct btrfs_path *path = NULL; |
172 | struct extent_buffer *tmp; | 30 | struct btrfs_key key; |
173 | int ret = 0; | 31 | int ret = 0; |
174 | int wret; | 32 | int wret; |
175 | int level; | 33 | int level; |
176 | int orig_level; | 34 | int orig_level; |
177 | int i; | 35 | int i; |
178 | int is_extent = 0; | 36 | int is_extent = 0; |
37 | int next_key_ret = 0; | ||
179 | u64 last_ret = 0; | 38 | u64 last_ret = 0; |
180 | 39 | ||
181 | if (root->fs_info->extent_root == root) | 40 | if (root->fs_info->extent_root == root) { |
41 | mutex_lock(&root->fs_info->alloc_mutex); | ||
182 | is_extent = 1; | 42 | is_extent = 1; |
183 | 43 | } | |
184 | goto out; | ||
185 | 44 | ||
186 | if (root->ref_cows == 0 && !is_extent) | 45 | if (root->ref_cows == 0 && !is_extent) |
187 | goto out; | 46 | goto out; |
@@ -200,67 +59,63 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, | |||
200 | goto out; | 59 | goto out; |
201 | } | 60 | } |
202 | if (root->defrag_progress.objectid == 0) { | 61 | if (root->defrag_progress.objectid == 0) { |
62 | struct extent_buffer *root_node; | ||
203 | u32 nritems; | 63 | u32 nritems; |
204 | 64 | ||
205 | nritems = btrfs_header_nritems(root->node); | 65 | root_node = btrfs_lock_root_node(root); |
66 | nritems = btrfs_header_nritems(root_node); | ||
206 | root->defrag_max.objectid = 0; | 67 | root->defrag_max.objectid = 0; |
207 | /* from above we know this is not a leaf */ | 68 | /* from above we know this is not a leaf */ |
208 | btrfs_node_key_to_cpu(root->node, &root->defrag_max, | 69 | btrfs_node_key_to_cpu(root_node, &root->defrag_max, |
209 | nritems - 1); | 70 | nritems - 1); |
210 | extent_buffer_get(root->node); | 71 | btrfs_tree_unlock(root_node); |
211 | ret = btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp); | 72 | free_extent_buffer(root_node); |
212 | BUG_ON(ret); | 73 | memset(&key, 0, sizeof(key)); |
213 | path->nodes[level] = root->node; | ||
214 | path->slots[level] = 0; | ||
215 | if (is_extent) | ||
216 | btrfs_extent_post_op(trans, root); | ||
217 | } else { | 74 | } else { |
218 | level = root->defrag_level; | 75 | memcpy(&key, &root->defrag_progress, sizeof(key)); |
219 | path->lowest_level = level; | ||
220 | wret = btrfs_search_slot(trans, root, &root->defrag_progress, | ||
221 | path, 0, 1); | ||
222 | |||
223 | if (is_extent) | ||
224 | btrfs_extent_post_op(trans, root); | ||
225 | |||
226 | if (wret < 0) { | ||
227 | ret = wret; | ||
228 | goto out; | ||
229 | } | ||
230 | |||
231 | while(level > 0 && !path->nodes[level]) | ||
232 | level--; | ||
233 | |||
234 | if (!path->nodes[level]) { | ||
235 | ret = 0; | ||
236 | goto out; | ||
237 | } | ||
238 | } | 76 | } |
239 | 77 | ||
240 | while(1) { | 78 | path->lowest_level = 1; |
241 | wret = defrag_walk_down(trans, root, path, &level, cache_only, | 79 | path->keep_locks = 1; |
242 | &last_ret); | 80 | wret = btrfs_search_slot(trans, root, &key, path, 0, 1); |
243 | if (wret > 0) | ||
244 | break; | ||
245 | if (wret < 0) | ||
246 | ret = wret; | ||
247 | 81 | ||
248 | wret = defrag_walk_up(trans, root, path, &level, cache_only); | 82 | if (wret < 0) { |
249 | if (wret > 0) | 83 | ret = wret; |
250 | break; | 84 | goto out; |
251 | if (wret < 0) | 85 | } |
252 | ret = wret; | 86 | if (!path->nodes[1]) { |
253 | else | 87 | ret = 0; |
254 | ret = -EAGAIN; | 88 | goto out; |
255 | break; | 89 | } |
90 | path->slots[1] = btrfs_header_nritems(path->nodes[1]); | ||
91 | next_key_ret = btrfs_find_next_key(root, path, &key, 1); | ||
92 | ret = btrfs_realloc_node(trans, root, | ||
93 | path->nodes[1], 0, | ||
94 | cache_only, &last_ret, | ||
95 | &root->defrag_progress); | ||
96 | WARN_ON(ret && ret != -EAGAIN); | ||
97 | if (next_key_ret == 0) { | ||
98 | memcpy(&root->defrag_progress, &key, sizeof(key)); | ||
99 | ret = -EAGAIN; | ||
256 | } | 100 | } |
257 | for (i = 0; i <= orig_level; i++) { | 101 | |
102 | for (i = 1; i < BTRFS_MAX_LEVEL; i++) { | ||
103 | if (path->locks[i]) { | ||
104 | btrfs_tree_unlock(path->nodes[i]); | ||
105 | path->locks[i] = 0; | ||
106 | } | ||
258 | if (path->nodes[i]) { | 107 | if (path->nodes[i]) { |
259 | free_extent_buffer(path->nodes[i]); | 108 | free_extent_buffer(path->nodes[i]); |
260 | path->nodes[i] = NULL; | 109 | path->nodes[i] = NULL; |
261 | } | 110 | } |
262 | } | 111 | } |
112 | if (is_extent) | ||
113 | btrfs_extent_post_op(trans, root); | ||
114 | |||
263 | out: | 115 | out: |
116 | if (is_extent) | ||
117 | mutex_unlock(&root->fs_info->alloc_mutex); | ||
118 | |||
264 | if (path) | 119 | if (path) |
265 | btrfs_free_path(path); | 120 | btrfs_free_path(path); |
266 | if (ret == -EAGAIN) { | 121 | if (ret == -EAGAIN) { |