diff options
author | Chris Mason <chris.mason@oracle.com> | 2012-05-06 07:23:47 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2012-05-06 07:23:47 -0400 |
commit | b9fab919b748c7b39c19ff236ed6c5682c266dde (patch) | |
tree | 49e5a6f8041a7f0a9be0c1a39cd9088e3faa1df2 /fs/btrfs | |
parent | ea9947b4395fa34666086b2fa6f686e94903e047 (diff) |
Btrfs: avoid sleeping in verify_parent_transid while atomic
verify_parent_transid needs to lock the extent range to make
sure no IO is underway, and so it can safely clear the
uptodate bits if our checks fail.
But, a few callers are using it with spinlocks held. Most
of the time, the generation numbers are going to match, and
we don't want to switch to a blocking lock just for the error
case. This adds an atomic flag to verify_parent_transid,
and changes it to return EAGAIN if it needs to block to
properly verifiy things.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ctree.c | 26 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 18 | ||||
-rw-r--r-- | fs/btrfs/disk-io.h | 3 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 2 | ||||
-rw-r--r-- | fs/btrfs/tree-log.c | 2 |
5 files changed, 34 insertions, 17 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 086303b9be64..4106264fbc65 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -725,7 +725,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, | |||
725 | 725 | ||
726 | cur = btrfs_find_tree_block(root, blocknr, blocksize); | 726 | cur = btrfs_find_tree_block(root, blocknr, blocksize); |
727 | if (cur) | 727 | if (cur) |
728 | uptodate = btrfs_buffer_uptodate(cur, gen); | 728 | uptodate = btrfs_buffer_uptodate(cur, gen, 0); |
729 | else | 729 | else |
730 | uptodate = 0; | 730 | uptodate = 0; |
731 | if (!cur || !uptodate) { | 731 | if (!cur || !uptodate) { |
@@ -1360,7 +1360,12 @@ static noinline int reada_for_balance(struct btrfs_root *root, | |||
1360 | block1 = btrfs_node_blockptr(parent, slot - 1); | 1360 | block1 = btrfs_node_blockptr(parent, slot - 1); |
1361 | gen = btrfs_node_ptr_generation(parent, slot - 1); | 1361 | gen = btrfs_node_ptr_generation(parent, slot - 1); |
1362 | eb = btrfs_find_tree_block(root, block1, blocksize); | 1362 | eb = btrfs_find_tree_block(root, block1, blocksize); |
1363 | if (eb && btrfs_buffer_uptodate(eb, gen)) | 1363 | /* |
1364 | * if we get -eagain from btrfs_buffer_uptodate, we | ||
1365 | * don't want to return eagain here. That will loop | ||
1366 | * forever | ||
1367 | */ | ||
1368 | if (eb && btrfs_buffer_uptodate(eb, gen, 1) != 0) | ||
1364 | block1 = 0; | 1369 | block1 = 0; |
1365 | free_extent_buffer(eb); | 1370 | free_extent_buffer(eb); |
1366 | } | 1371 | } |
@@ -1368,7 +1373,7 @@ static noinline int reada_for_balance(struct btrfs_root *root, | |||
1368 | block2 = btrfs_node_blockptr(parent, slot + 1); | 1373 | block2 = btrfs_node_blockptr(parent, slot + 1); |
1369 | gen = btrfs_node_ptr_generation(parent, slot + 1); | 1374 | gen = btrfs_node_ptr_generation(parent, slot + 1); |
1370 | eb = btrfs_find_tree_block(root, block2, blocksize); | 1375 | eb = btrfs_find_tree_block(root, block2, blocksize); |
1371 | if (eb && btrfs_buffer_uptodate(eb, gen)) | 1376 | if (eb && btrfs_buffer_uptodate(eb, gen, 1) != 0) |
1372 | block2 = 0; | 1377 | block2 = 0; |
1373 | free_extent_buffer(eb); | 1378 | free_extent_buffer(eb); |
1374 | } | 1379 | } |
@@ -1506,8 +1511,9 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
1506 | 1511 | ||
1507 | tmp = btrfs_find_tree_block(root, blocknr, blocksize); | 1512 | tmp = btrfs_find_tree_block(root, blocknr, blocksize); |
1508 | if (tmp) { | 1513 | if (tmp) { |
1509 | if (btrfs_buffer_uptodate(tmp, 0)) { | 1514 | /* first we do an atomic uptodate check */ |
1510 | if (btrfs_buffer_uptodate(tmp, gen)) { | 1515 | if (btrfs_buffer_uptodate(tmp, 0, 1) > 0) { |
1516 | if (btrfs_buffer_uptodate(tmp, gen, 1) > 0) { | ||
1511 | /* | 1517 | /* |
1512 | * we found an up to date block without | 1518 | * we found an up to date block without |
1513 | * sleeping, return | 1519 | * sleeping, return |
@@ -1525,8 +1531,9 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
1525 | free_extent_buffer(tmp); | 1531 | free_extent_buffer(tmp); |
1526 | btrfs_set_path_blocking(p); | 1532 | btrfs_set_path_blocking(p); |
1527 | 1533 | ||
1534 | /* now we're allowed to do a blocking uptodate check */ | ||
1528 | tmp = read_tree_block(root, blocknr, blocksize, gen); | 1535 | tmp = read_tree_block(root, blocknr, blocksize, gen); |
1529 | if (tmp && btrfs_buffer_uptodate(tmp, gen)) { | 1536 | if (tmp && btrfs_buffer_uptodate(tmp, gen, 0) > 0) { |
1530 | *eb_ret = tmp; | 1537 | *eb_ret = tmp; |
1531 | return 0; | 1538 | return 0; |
1532 | } | 1539 | } |
@@ -1561,7 +1568,7 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
1561 | * and give up so that our caller doesn't loop forever | 1568 | * and give up so that our caller doesn't loop forever |
1562 | * on our EAGAINs. | 1569 | * on our EAGAINs. |
1563 | */ | 1570 | */ |
1564 | if (!btrfs_buffer_uptodate(tmp, 0)) | 1571 | if (!btrfs_buffer_uptodate(tmp, 0, 0)) |
1565 | ret = -EIO; | 1572 | ret = -EIO; |
1566 | free_extent_buffer(tmp); | 1573 | free_extent_buffer(tmp); |
1567 | } | 1574 | } |
@@ -4045,7 +4052,7 @@ again: | |||
4045 | tmp = btrfs_find_tree_block(root, blockptr, | 4052 | tmp = btrfs_find_tree_block(root, blockptr, |
4046 | btrfs_level_size(root, level - 1)); | 4053 | btrfs_level_size(root, level - 1)); |
4047 | 4054 | ||
4048 | if (tmp && btrfs_buffer_uptodate(tmp, gen)) { | 4055 | if (tmp && btrfs_buffer_uptodate(tmp, gen, 1) > 0) { |
4049 | free_extent_buffer(tmp); | 4056 | free_extent_buffer(tmp); |
4050 | break; | 4057 | break; |
4051 | } | 4058 | } |
@@ -4168,7 +4175,8 @@ next: | |||
4168 | struct extent_buffer *cur; | 4175 | struct extent_buffer *cur; |
4169 | cur = btrfs_find_tree_block(root, blockptr, | 4176 | cur = btrfs_find_tree_block(root, blockptr, |
4170 | btrfs_level_size(root, level - 1)); | 4177 | btrfs_level_size(root, level - 1)); |
4171 | if (!cur || !btrfs_buffer_uptodate(cur, gen)) { | 4178 | if (!cur || |
4179 | btrfs_buffer_uptodate(cur, gen, 1) <= 0) { | ||
4172 | slot++; | 4180 | slot++; |
4173 | if (cur) | 4181 | if (cur) |
4174 | free_extent_buffer(cur); | 4182 | free_extent_buffer(cur); |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index d0c969beaad4..a7ffc88a7dbe 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -323,7 +323,8 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, | |||
323 | * in the wrong place. | 323 | * in the wrong place. |
324 | */ | 324 | */ |
325 | static int verify_parent_transid(struct extent_io_tree *io_tree, | 325 | static int verify_parent_transid(struct extent_io_tree *io_tree, |
326 | struct extent_buffer *eb, u64 parent_transid) | 326 | struct extent_buffer *eb, u64 parent_transid, |
327 | int atomic) | ||
327 | { | 328 | { |
328 | struct extent_state *cached_state = NULL; | 329 | struct extent_state *cached_state = NULL; |
329 | int ret; | 330 | int ret; |
@@ -331,6 +332,9 @@ static int verify_parent_transid(struct extent_io_tree *io_tree, | |||
331 | if (!parent_transid || btrfs_header_generation(eb) == parent_transid) | 332 | if (!parent_transid || btrfs_header_generation(eb) == parent_transid) |
332 | return 0; | 333 | return 0; |
333 | 334 | ||
335 | if (atomic) | ||
336 | return -EAGAIN; | ||
337 | |||
334 | lock_extent_bits(io_tree, eb->start, eb->start + eb->len - 1, | 338 | lock_extent_bits(io_tree, eb->start, eb->start + eb->len - 1, |
335 | 0, &cached_state); | 339 | 0, &cached_state); |
336 | if (extent_buffer_uptodate(eb) && | 340 | if (extent_buffer_uptodate(eb) && |
@@ -372,7 +376,8 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root, | |||
372 | ret = read_extent_buffer_pages(io_tree, eb, start, | 376 | ret = read_extent_buffer_pages(io_tree, eb, start, |
373 | WAIT_COMPLETE, | 377 | WAIT_COMPLETE, |
374 | btree_get_extent, mirror_num); | 378 | btree_get_extent, mirror_num); |
375 | if (!ret && !verify_parent_transid(io_tree, eb, parent_transid)) | 379 | if (!ret && !verify_parent_transid(io_tree, eb, |
380 | parent_transid, 0)) | ||
376 | break; | 381 | break; |
377 | 382 | ||
378 | /* | 383 | /* |
@@ -1202,7 +1207,7 @@ static int __must_check find_and_setup_root(struct btrfs_root *tree_root, | |||
1202 | root->commit_root = NULL; | 1207 | root->commit_root = NULL; |
1203 | root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), | 1208 | root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), |
1204 | blocksize, generation); | 1209 | blocksize, generation); |
1205 | if (!root->node || !btrfs_buffer_uptodate(root->node, generation)) { | 1210 | if (!root->node || !btrfs_buffer_uptodate(root->node, generation, 0)) { |
1206 | free_extent_buffer(root->node); | 1211 | free_extent_buffer(root->node); |
1207 | root->node = NULL; | 1212 | root->node = NULL; |
1208 | return -EIO; | 1213 | return -EIO; |
@@ -3143,7 +3148,8 @@ int close_ctree(struct btrfs_root *root) | |||
3143 | return 0; | 3148 | return 0; |
3144 | } | 3149 | } |
3145 | 3150 | ||
3146 | int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid) | 3151 | int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid, |
3152 | int atomic) | ||
3147 | { | 3153 | { |
3148 | int ret; | 3154 | int ret; |
3149 | struct inode *btree_inode = buf->pages[0]->mapping->host; | 3155 | struct inode *btree_inode = buf->pages[0]->mapping->host; |
@@ -3153,7 +3159,9 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid) | |||
3153 | return ret; | 3159 | return ret; |
3154 | 3160 | ||
3155 | ret = verify_parent_transid(&BTRFS_I(btree_inode)->io_tree, buf, | 3161 | ret = verify_parent_transid(&BTRFS_I(btree_inode)->io_tree, buf, |
3156 | parent_transid); | 3162 | parent_transid, atomic); |
3163 | if (ret == -EAGAIN) | ||
3164 | return ret; | ||
3157 | return !ret; | 3165 | return !ret; |
3158 | } | 3166 | } |
3159 | 3167 | ||
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index a7ace1a2dd12..ab1830aaf0ed 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h | |||
@@ -66,7 +66,8 @@ void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr); | |||
66 | void __btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr); | 66 | void __btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr); |
67 | void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root); | 67 | void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root); |
68 | void btrfs_mark_buffer_dirty(struct extent_buffer *buf); | 68 | void btrfs_mark_buffer_dirty(struct extent_buffer *buf); |
69 | int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid); | 69 | int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid, |
70 | int atomic); | ||
70 | int btrfs_set_buffer_uptodate(struct extent_buffer *buf); | 71 | int btrfs_set_buffer_uptodate(struct extent_buffer *buf); |
71 | int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid); | 72 | int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid); |
72 | u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); | 73 | u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len); |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6fc2e6f5aab8..49fd7b66d57b 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -6568,7 +6568,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, | |||
6568 | goto skip; | 6568 | goto skip; |
6569 | } | 6569 | } |
6570 | 6570 | ||
6571 | if (!btrfs_buffer_uptodate(next, generation)) { | 6571 | if (!btrfs_buffer_uptodate(next, generation, 0)) { |
6572 | btrfs_tree_unlock(next); | 6572 | btrfs_tree_unlock(next); |
6573 | free_extent_buffer(next); | 6573 | free_extent_buffer(next); |
6574 | next = NULL; | 6574 | next = NULL; |
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index d017283ae6f5..eb1ae908582c 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -279,7 +279,7 @@ static int process_one_buffer(struct btrfs_root *log, | |||
279 | log->fs_info->extent_root, | 279 | log->fs_info->extent_root, |
280 | eb->start, eb->len); | 280 | eb->start, eb->len); |
281 | 281 | ||
282 | if (btrfs_buffer_uptodate(eb, gen)) { | 282 | if (btrfs_buffer_uptodate(eb, gen, 0)) { |
283 | if (wc->write) | 283 | if (wc->write) |
284 | btrfs_write_tree_block(eb); | 284 | btrfs_write_tree_block(eb); |
285 | if (wc->wait) | 285 | if (wc->wait) |