diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-04-11 15:53:25 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-04-11 15:53:25 -0400 |
commit | 7eccb903a817e890c947ba4bc90c6a9af9b4219a (patch) | |
tree | 0e8553fb8f1dfd701d4e8eacd618ec72a8ec8720 /fs/btrfs/disk-io.c | |
parent | 0bd93ba022f5c1ddb4e5b722b02f3c4a4a8865ad (diff) |
Btrfs: create a logical->phsyical block number mapping scheme
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/disk-io.c')
-rw-r--r-- | fs/btrfs/disk-io.c | 140 |
1 files changed, 102 insertions, 38 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index e09233262af6..c872a7e67abd 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -10,10 +10,30 @@ | |||
10 | #include "transaction.h" | 10 | #include "transaction.h" |
11 | #include "btrfs_inode.h" | 11 | #include "btrfs_inode.h" |
12 | 12 | ||
13 | struct dev_lookup { | ||
14 | u64 block_start; | ||
15 | u64 num_blocks; | ||
16 | struct block_device *bdev; | ||
17 | }; | ||
18 | |||
19 | u64 bh_blocknr(struct buffer_head *bh) | ||
20 | { | ||
21 | int blkbits = bh->b_page->mapping->host->i_blkbits; | ||
22 | u64 blocknr = bh->b_page->index << (PAGE_CACHE_SHIFT - blkbits); | ||
23 | unsigned long offset; | ||
24 | |||
25 | if (PageHighMem(bh->b_page)) | ||
26 | offset = (unsigned long)bh->b_data; | ||
27 | else | ||
28 | offset = bh->b_data - (char *)page_address(bh->b_page); | ||
29 | blocknr += offset >> (PAGE_CACHE_SHIFT - blkbits); | ||
30 | return blocknr; | ||
31 | } | ||
32 | |||
13 | static int check_tree_block(struct btrfs_root *root, struct buffer_head *buf) | 33 | static int check_tree_block(struct btrfs_root *root, struct buffer_head *buf) |
14 | { | 34 | { |
15 | struct btrfs_node *node = btrfs_buffer_node(buf); | 35 | struct btrfs_node *node = btrfs_buffer_node(buf); |
16 | if (buf->b_blocknr != btrfs_header_blocknr(&node->header)) { | 36 | if (bh_blocknr(buf) != btrfs_header_blocknr(&node->header)) { |
17 | BUG(); | 37 | BUG(); |
18 | } | 38 | } |
19 | return 0; | 39 | return 0; |
@@ -40,7 +60,7 @@ struct buffer_head *btrfs_find_tree_block(struct btrfs_root *root, u64 blocknr) | |||
40 | head = page_buffers(page); | 60 | head = page_buffers(page); |
41 | bh = head; | 61 | bh = head; |
42 | do { | 62 | do { |
43 | if (buffer_mapped(bh) && bh->b_blocknr == blocknr) { | 63 | if (buffer_mapped(bh) && bh_blocknr(bh) == blocknr) { |
44 | ret = bh; | 64 | ret = bh; |
45 | get_bh(bh); | 65 | get_bh(bh); |
46 | goto out_unlock; | 66 | goto out_unlock; |
@@ -56,6 +76,33 @@ out_unlock: | |||
56 | return ret; | 76 | return ret; |
57 | } | 77 | } |
58 | 78 | ||
79 | static int map_bh_to_logical(struct btrfs_root *root, struct buffer_head *bh, | ||
80 | u64 logical) | ||
81 | { | ||
82 | struct dev_lookup *lookup[2]; | ||
83 | char b[BDEVNAME_SIZE]; | ||
84 | |||
85 | int ret; | ||
86 | |||
87 | root = root->fs_info->dev_root; | ||
88 | ret = radix_tree_gang_lookup(&root->fs_info->dev_radix, | ||
89 | (void **)lookup, | ||
90 | (unsigned long)logical, | ||
91 | ARRAY_SIZE(lookup)); | ||
92 | if (ret == 0 || lookup[0]->block_start > logical || | ||
93 | lookup[0]->block_start + lookup[0]->num_blocks <= logical) { | ||
94 | ret = -ENOENT; | ||
95 | goto out; | ||
96 | } | ||
97 | bh->b_bdev = lookup[0]->bdev; | ||
98 | bh->b_blocknr = logical - lookup[0]->block_start; | ||
99 | printk("logical mapping %Lu to %lu bdev %s\n", logical, bh->b_blocknr, bdevname(bh->b_bdev, b)); | ||
100 | set_buffer_mapped(bh); | ||
101 | ret = 0; | ||
102 | out: | ||
103 | return ret; | ||
104 | } | ||
105 | |||
59 | struct buffer_head *btrfs_find_create_tree_block(struct btrfs_root *root, | 106 | struct buffer_head *btrfs_find_create_tree_block(struct btrfs_root *root, |
60 | u64 blocknr) | 107 | u64 blocknr) |
61 | { | 108 | { |
@@ -66,6 +113,7 @@ struct buffer_head *btrfs_find_create_tree_block(struct btrfs_root *root, | |||
66 | struct buffer_head *bh; | 113 | struct buffer_head *bh; |
67 | struct buffer_head *head; | 114 | struct buffer_head *head; |
68 | struct buffer_head *ret = NULL; | 115 | struct buffer_head *ret = NULL; |
116 | int err; | ||
69 | u64 first_block = index << (PAGE_CACHE_SHIFT - blockbits); | 117 | u64 first_block = index << (PAGE_CACHE_SHIFT - blockbits); |
70 | 118 | ||
71 | page = grab_cache_page(mapping, index); | 119 | page = grab_cache_page(mapping, index); |
@@ -78,11 +126,10 @@ struct buffer_head *btrfs_find_create_tree_block(struct btrfs_root *root, | |||
78 | bh = head; | 126 | bh = head; |
79 | do { | 127 | do { |
80 | if (!buffer_mapped(bh)) { | 128 | if (!buffer_mapped(bh)) { |
81 | bh->b_bdev = root->fs_info->sb->s_bdev; | 129 | err = map_bh_to_logical(root, bh, first_block); |
82 | bh->b_blocknr = first_block; | 130 | BUG_ON(err); |
83 | set_buffer_mapped(bh); | ||
84 | } | 131 | } |
85 | if (bh->b_blocknr == blocknr) { | 132 | if (bh_blocknr(bh) == blocknr) { |
86 | ret = bh; | 133 | ret = bh; |
87 | get_bh(bh); | 134 | get_bh(bh); |
88 | goto out_unlock; | 135 | goto out_unlock; |
@@ -98,38 +145,13 @@ out_unlock: | |||
98 | return ret; | 145 | return ret; |
99 | } | 146 | } |
100 | 147 | ||
101 | static sector_t max_block(struct block_device *bdev) | ||
102 | { | ||
103 | sector_t retval = ~((sector_t)0); | ||
104 | loff_t sz = i_size_read(bdev->bd_inode); | ||
105 | |||
106 | if (sz) { | ||
107 | unsigned int size = block_size(bdev); | ||
108 | unsigned int sizebits = blksize_bits(size); | ||
109 | retval = (sz >> sizebits); | ||
110 | } | ||
111 | return retval; | ||
112 | } | ||
113 | |||
114 | static int btree_get_block(struct inode *inode, sector_t iblock, | 148 | static int btree_get_block(struct inode *inode, sector_t iblock, |
115 | struct buffer_head *bh, int create) | 149 | struct buffer_head *bh, int create) |
116 | { | 150 | { |
117 | if (iblock >= max_block(inode->i_sb->s_bdev)) { | 151 | int err; |
118 | if (create) | 152 | struct btrfs_root *root = BTRFS_I(bh->b_page->mapping->host)->root; |
119 | return -EIO; | 153 | err = map_bh_to_logical(root, bh, iblock); |
120 | 154 | return err; | |
121 | /* | ||
122 | * for reads, we're just trying to fill a partial page. | ||
123 | * return a hole, they will have to call get_block again | ||
124 | * before they can fill it, and they will get -EIO at that | ||
125 | * time | ||
126 | */ | ||
127 | return 0; | ||
128 | } | ||
129 | bh->b_bdev = inode->i_sb->s_bdev; | ||
130 | bh->b_blocknr = iblock; | ||
131 | set_buffer_mapped(bh); | ||
132 | return 0; | ||
133 | } | 155 | } |
134 | 156 | ||
135 | int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len, | 157 | int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len, |
@@ -164,8 +186,8 @@ static int csum_tree_block(struct btrfs_root *root, struct buffer_head *bh, | |||
164 | return ret; | 186 | return ret; |
165 | if (verify) { | 187 | if (verify) { |
166 | if (memcmp(bh->b_data, result, BTRFS_CSUM_SIZE)) { | 188 | if (memcmp(bh->b_data, result, BTRFS_CSUM_SIZE)) { |
167 | printk("checksum verify failed on %lu\n", | 189 | printk("checksum verify failed on %Lu\n", |
168 | bh->b_blocknr); | 190 | bh_blocknr(bh)); |
169 | return 1; | 191 | return 1; |
170 | } | 192 | } |
171 | } else { | 193 | } else { |
@@ -386,10 +408,12 @@ struct btrfs_root *open_ctree(struct super_block *sb) | |||
386 | GFP_NOFS); | 408 | GFP_NOFS); |
387 | int ret; | 409 | int ret; |
388 | struct btrfs_super_block *disk_super; | 410 | struct btrfs_super_block *disk_super; |
411 | struct dev_lookup *dev_lookup; | ||
389 | 412 | ||
390 | init_bit_radix(&fs_info->pinned_radix); | 413 | init_bit_radix(&fs_info->pinned_radix); |
391 | init_bit_radix(&fs_info->pending_del_radix); | 414 | init_bit_radix(&fs_info->pending_del_radix); |
392 | INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_NOFS); | 415 | INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_NOFS); |
416 | INIT_RADIX_TREE(&fs_info->dev_radix, GFP_NOFS); | ||
393 | sb_set_blocksize(sb, 4096); | 417 | sb_set_blocksize(sb, 4096); |
394 | fs_info->running_transaction = NULL; | 418 | fs_info->running_transaction = NULL; |
395 | fs_info->tree_root = tree_root; | 419 | fs_info->tree_root = tree_root; |
@@ -422,6 +446,13 @@ struct btrfs_root *open_ctree(struct super_block *sb) | |||
422 | 446 | ||
423 | __setup_root(sb->s_blocksize, tree_root, | 447 | __setup_root(sb->s_blocksize, tree_root, |
424 | fs_info, BTRFS_ROOT_TREE_OBJECTID); | 448 | fs_info, BTRFS_ROOT_TREE_OBJECTID); |
449 | |||
450 | dev_lookup = kmalloc(sizeof(*dev_lookup), GFP_NOFS); | ||
451 | dev_lookup->block_start = 0; | ||
452 | dev_lookup->num_blocks = (u32)-2; | ||
453 | dev_lookup->bdev = sb->s_bdev; | ||
454 | ret = radix_tree_insert(&fs_info->dev_radix, (u32)-2, dev_lookup); | ||
455 | BUG_ON(ret); | ||
425 | fs_info->sb_buffer = read_tree_block(tree_root, | 456 | fs_info->sb_buffer = read_tree_block(tree_root, |
426 | BTRFS_SUPER_INFO_OFFSET / | 457 | BTRFS_SUPER_INFO_OFFSET / |
427 | sb->s_blocksize); | 458 | sb->s_blocksize); |
@@ -432,6 +463,14 @@ struct btrfs_root *open_ctree(struct super_block *sb) | |||
432 | if (!btrfs_super_root(disk_super)) | 463 | if (!btrfs_super_root(disk_super)) |
433 | return NULL; | 464 | return NULL; |
434 | 465 | ||
466 | radix_tree_delete(&fs_info->dev_radix, (u32)-2); | ||
467 | dev_lookup->block_start = btrfs_super_device_block_start(disk_super); | ||
468 | dev_lookup->num_blocks = btrfs_super_device_num_blocks(disk_super); | ||
469 | ret = radix_tree_insert(&fs_info->dev_radix, | ||
470 | dev_lookup->block_start + | ||
471 | dev_lookup->num_blocks, dev_lookup); | ||
472 | BUG_ON(ret); | ||
473 | |||
435 | fs_info->disk_super = disk_super; | 474 | fs_info->disk_super = disk_super; |
436 | dev_root->node = read_tree_block(tree_root, | 475 | dev_root->node = read_tree_block(tree_root, |
437 | btrfs_super_device_root(disk_super)); | 476 | btrfs_super_device_root(disk_super)); |
@@ -459,7 +498,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root | |||
459 | struct buffer_head *bh = root->fs_info->sb_buffer; | 498 | struct buffer_head *bh = root->fs_info->sb_buffer; |
460 | 499 | ||
461 | btrfs_set_super_root(root->fs_info->disk_super, | 500 | btrfs_set_super_root(root->fs_info->disk_super, |
462 | root->fs_info->tree_root->node->b_blocknr); | 501 | bh_blocknr(root->fs_info->tree_root->node)); |
463 | lock_buffer(bh); | 502 | lock_buffer(bh); |
464 | WARN_ON(atomic_read(&bh->b_count) < 1); | 503 | WARN_ON(atomic_read(&bh->b_count) < 1); |
465 | clear_buffer_dirty(bh); | 504 | clear_buffer_dirty(bh); |
@@ -506,6 +545,29 @@ int del_fs_roots(struct btrfs_fs_info *fs_info) | |||
506 | } | 545 | } |
507 | return 0; | 546 | return 0; |
508 | } | 547 | } |
548 | static int free_dev_radix(struct btrfs_fs_info *fs_info) | ||
549 | { | ||
550 | struct dev_lookup *lookup[8]; | ||
551 | struct block_device *super_bdev = fs_info->sb->s_bdev; | ||
552 | int ret; | ||
553 | int i; | ||
554 | while(1) { | ||
555 | ret = radix_tree_gang_lookup(&fs_info->dev_radix, | ||
556 | (void **)lookup, 0, | ||
557 | ARRAY_SIZE(lookup)); | ||
558 | if (!ret) | ||
559 | break; | ||
560 | for (i = 0; i < ret; i++) { | ||
561 | if (lookup[i]->bdev != super_bdev) | ||
562 | close_bdev_excl(lookup[i]->bdev); | ||
563 | radix_tree_delete(&fs_info->dev_radix, | ||
564 | lookup[i]->block_start + | ||
565 | lookup[i]->num_blocks); | ||
566 | kfree(lookup[i]); | ||
567 | } | ||
568 | } | ||
569 | return 0; | ||
570 | } | ||
509 | 571 | ||
510 | int close_ctree(struct btrfs_root *root) | 572 | int close_ctree(struct btrfs_root *root) |
511 | { | 573 | { |
@@ -537,6 +599,8 @@ int close_ctree(struct btrfs_root *root) | |||
537 | crypto_free_hash(fs_info->hash_tfm); | 599 | crypto_free_hash(fs_info->hash_tfm); |
538 | truncate_inode_pages(fs_info->btree_inode->i_mapping, 0); | 600 | truncate_inode_pages(fs_info->btree_inode->i_mapping, 0); |
539 | iput(fs_info->btree_inode); | 601 | iput(fs_info->btree_inode); |
602 | |||
603 | free_dev_radix(fs_info); | ||
540 | del_fs_roots(fs_info); | 604 | del_fs_roots(fs_info); |
541 | kfree(fs_info->extent_root); | 605 | kfree(fs_info->extent_root); |
542 | kfree(fs_info->tree_root); | 606 | kfree(fs_info->tree_root); |