diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-10-15 16:19:22 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:03:56 -0400 |
commit | 19c00ddcc31ad4bdfb86b57085e06d6135b9b1d7 (patch) | |
tree | a48cbaac47070057912c1b35d270b61b7e14386c /fs/btrfs/disk-io.c | |
parent | 0f82731fc56448c2733f58e1f5db6c2cbfc90652 (diff) |
Btrfs: Add back metadata checksumming
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 | 215 |
1 files changed, 155 insertions, 60 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 16f0260fca66..1176e5420c56 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -60,7 +60,7 @@ struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root, | |||
60 | struct extent_buffer *eb; | 60 | struct extent_buffer *eb; |
61 | 61 | ||
62 | eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->extent_tree, | 62 | eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->extent_tree, |
63 | bytenr, blocksize, GFP_NOFS); | 63 | bytenr, blocksize, NULL, GFP_NOFS); |
64 | return eb; | 64 | return eb; |
65 | } | 65 | } |
66 | 66 | ||
@@ -99,10 +99,102 @@ out: | |||
99 | return em; | 99 | return em; |
100 | } | 100 | } |
101 | 101 | ||
102 | u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len) | ||
103 | { | ||
104 | return crc32c(seed, data, len); | ||
105 | } | ||
106 | |||
107 | void btrfs_csum_final(u32 crc, char *result) | ||
108 | { | ||
109 | *(__le32 *)result = ~cpu_to_le32(crc); | ||
110 | } | ||
111 | |||
112 | static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, | ||
113 | int verify) | ||
114 | { | ||
115 | char result[BTRFS_CRC32_SIZE]; | ||
116 | unsigned long len; | ||
117 | unsigned long cur_len; | ||
118 | unsigned long offset = BTRFS_CSUM_SIZE; | ||
119 | char *map_token = NULL; | ||
120 | char *kaddr; | ||
121 | unsigned long map_start; | ||
122 | unsigned long map_len; | ||
123 | int err; | ||
124 | u32 crc = ~(u32)0; | ||
125 | |||
126 | len = buf->len - offset; | ||
127 | while(len > 0) { | ||
128 | err = map_private_extent_buffer(buf, offset, 32, | ||
129 | &map_token, &kaddr, | ||
130 | &map_start, &map_len, KM_USER0); | ||
131 | if (err) { | ||
132 | printk("failed to map extent buffer! %lu\n", | ||
133 | offset); | ||
134 | return 1; | ||
135 | } | ||
136 | cur_len = min(len, map_len - (offset - map_start)); | ||
137 | crc = btrfs_csum_data(root, kaddr + offset - map_start, | ||
138 | crc, cur_len); | ||
139 | len -= cur_len; | ||
140 | offset += cur_len; | ||
141 | unmap_extent_buffer(buf, map_token, KM_USER0); | ||
142 | } | ||
143 | btrfs_csum_final(crc, result); | ||
144 | |||
145 | if (verify) { | ||
146 | if (memcmp_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE)) { | ||
147 | printk("btrfs: %s checksum verify failed on %llu\n", | ||
148 | root->fs_info->sb->s_id, | ||
149 | buf->start); | ||
150 | return 1; | ||
151 | } | ||
152 | } else { | ||
153 | write_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE); | ||
154 | } | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | |||
159 | int csum_dirty_buffer(struct btrfs_root *root, struct page *page) | ||
160 | { | ||
161 | struct extent_map_tree *tree; | ||
162 | u64 start = page->index << PAGE_CACHE_SHIFT; | ||
163 | u64 found_start; | ||
164 | int found_level; | ||
165 | unsigned long len; | ||
166 | struct extent_buffer *eb; | ||
167 | tree = &BTRFS_I(page->mapping->host)->extent_tree; | ||
168 | |||
169 | if (page->private == EXTENT_PAGE_PRIVATE) | ||
170 | goto out; | ||
171 | if (!page->private) | ||
172 | goto out; | ||
173 | len = page->private >> 2; | ||
174 | if (len == 0) { | ||
175 | WARN_ON(1); | ||
176 | } | ||
177 | eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS); | ||
178 | read_extent_buffer_pages(tree, eb, start + PAGE_CACHE_SIZE, 1); | ||
179 | found_start = btrfs_header_bytenr(eb); | ||
180 | if (found_start != start) { | ||
181 | printk("warning: eb start incorrect %Lu buffer %Lu len %lu\n", | ||
182 | start, found_start, len); | ||
183 | } | ||
184 | found_level = btrfs_header_level(eb); | ||
185 | csum_tree_block(root, eb, 0); | ||
186 | free_extent_buffer(eb); | ||
187 | out: | ||
188 | return 0; | ||
189 | } | ||
190 | |||
102 | static int btree_writepage(struct page *page, struct writeback_control *wbc) | 191 | static int btree_writepage(struct page *page, struct writeback_control *wbc) |
103 | { | 192 | { |
104 | struct extent_map_tree *tree; | 193 | struct extent_map_tree *tree; |
194 | struct btrfs_root *root = BTRFS_I(page->mapping->host)->root; | ||
105 | tree = &BTRFS_I(page->mapping->host)->extent_tree; | 195 | tree = &BTRFS_I(page->mapping->host)->extent_tree; |
196 | |||
197 | csum_dirty_buffer(root, page); | ||
106 | return extent_write_full_page(tree, page, btree_get_extent, wbc); | 198 | return extent_write_full_page(tree, page, btree_get_extent, wbc); |
107 | } | 199 | } |
108 | int btree_readpage(struct file *file, struct page *page) | 200 | int btree_readpage(struct file *file, struct page *page) |
@@ -117,7 +209,6 @@ static int btree_releasepage(struct page *page, gfp_t unused_gfp_flags) | |||
117 | struct extent_map_tree *tree; | 209 | struct extent_map_tree *tree; |
118 | int ret; | 210 | int ret; |
119 | 211 | ||
120 | BUG_ON(page->private != 1); | ||
121 | tree = &BTRFS_I(page->mapping->host)->extent_tree; | 212 | tree = &BTRFS_I(page->mapping->host)->extent_tree; |
122 | ret = try_release_extent_mapping(tree, page); | 213 | ret = try_release_extent_mapping(tree, page); |
123 | if (ret == 1) { | 214 | if (ret == 1) { |
@@ -136,46 +227,6 @@ static void btree_invalidatepage(struct page *page, unsigned long offset) | |||
136 | btree_releasepage(page, GFP_NOFS); | 227 | btree_releasepage(page, GFP_NOFS); |
137 | } | 228 | } |
138 | 229 | ||
139 | int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len, | ||
140 | char *result) | ||
141 | { | ||
142 | return 0; | ||
143 | #if 0 | ||
144 | u32 crc; | ||
145 | crc = crc32c(0, data, len); | ||
146 | memcpy(result, &crc, BTRFS_CRC32_SIZE); | ||
147 | return 0; | ||
148 | #endif | ||
149 | } | ||
150 | |||
151 | #if 0 | ||
152 | static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, | ||
153 | int verify) | ||
154 | { | ||
155 | return 0; | ||
156 | char result[BTRFS_CRC32_SIZE]; | ||
157 | int ret; | ||
158 | struct btrfs_node *node; | ||
159 | |||
160 | ret = btrfs_csum_data(root, bh->b_data + BTRFS_CSUM_SIZE, | ||
161 | bh->b_size - BTRFS_CSUM_SIZE, result); | ||
162 | if (ret) | ||
163 | return ret; | ||
164 | if (verify) { | ||
165 | if (memcmp(bh->b_data, result, BTRFS_CRC32_SIZE)) { | ||
166 | printk("btrfs: %s checksum verify failed on %llu\n", | ||
167 | root->fs_info->sb->s_id, | ||
168 | (unsigned long long)bh_blocknr(bh)); | ||
169 | return 1; | ||
170 | } | ||
171 | } else { | ||
172 | node = btrfs_buffer_node(bh); | ||
173 | memcpy(node->header.csum, result, BTRFS_CRC32_SIZE); | ||
174 | } | ||
175 | return 0; | ||
176 | } | ||
177 | #endif | ||
178 | |||
179 | #if 0 | 230 | #if 0 |
180 | static int btree_writepage(struct page *page, struct writeback_control *wbc) | 231 | static int btree_writepage(struct page *page, struct writeback_control *wbc) |
181 | { | 232 | { |
@@ -215,7 +266,7 @@ int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize) | |||
215 | if (!buf) | 266 | if (!buf) |
216 | return 0; | 267 | return 0; |
217 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->extent_tree, | 268 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->extent_tree, |
218 | buf, 0); | 269 | buf, 0, 0); |
219 | free_extent_buffer(buf); | 270 | free_extent_buffer(buf); |
220 | return ret; | 271 | return ret; |
221 | } | 272 | } |
@@ -225,12 +276,29 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, | |||
225 | { | 276 | { |
226 | struct extent_buffer *buf = NULL; | 277 | struct extent_buffer *buf = NULL; |
227 | struct inode *btree_inode = root->fs_info->btree_inode; | 278 | struct inode *btree_inode = root->fs_info->btree_inode; |
279 | struct extent_map_tree *extent_tree; | ||
280 | int ret; | ||
281 | |||
282 | extent_tree = &BTRFS_I(btree_inode)->extent_tree; | ||
228 | 283 | ||
229 | buf = btrfs_find_create_tree_block(root, bytenr, blocksize); | 284 | buf = btrfs_find_create_tree_block(root, bytenr, blocksize); |
230 | if (!buf) | 285 | if (!buf) |
231 | return NULL; | 286 | return NULL; |
232 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->extent_tree, | 287 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->extent_tree, |
233 | buf, 1); | 288 | buf, 0, 1); |
289 | if (buf->flags & EXTENT_CSUM) { | ||
290 | return buf; | ||
291 | } | ||
292 | if (test_range_bit(extent_tree, buf->start, buf->start + buf->len - 1, | ||
293 | EXTENT_CSUM, 1)) { | ||
294 | buf->flags |= EXTENT_CSUM; | ||
295 | return buf; | ||
296 | } | ||
297 | ret = csum_tree_block(root, buf, 1); | ||
298 | set_extent_bits(extent_tree, buf->start, | ||
299 | buf->start + buf->len - 1, | ||
300 | EXTENT_CSUM, GFP_NOFS); | ||
301 | buf->flags |= EXTENT_CSUM; | ||
234 | return buf; | 302 | return buf; |
235 | } | 303 | } |
236 | 304 | ||
@@ -251,13 +319,6 @@ int wait_on_tree_block_writeback(struct btrfs_root *root, | |||
251 | return 0; | 319 | return 0; |
252 | } | 320 | } |
253 | 321 | ||
254 | int set_tree_block_dirty(struct btrfs_root *root, struct extent_buffer *buf) | ||
255 | { | ||
256 | struct inode *btree_inode = root->fs_info->btree_inode; | ||
257 | set_extent_buffer_dirty(&BTRFS_I(btree_inode)->extent_tree, buf); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, | 322 | static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, |
262 | struct btrfs_root *root, | 323 | struct btrfs_root *root, |
263 | struct btrfs_fs_info *fs_info, | 324 | struct btrfs_fs_info *fs_info, |
@@ -416,7 +477,24 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, | |||
416 | 477 | ||
417 | return root; | 478 | return root; |
418 | } | 479 | } |
419 | 480 | #if 0 | |
481 | static int add_hasher(struct btrfs_fs_info *info, char *type) { | ||
482 | struct btrfs_hasher *hasher; | ||
483 | |||
484 | hasher = kmalloc(sizeof(*hasher), GFP_NOFS); | ||
485 | if (!hasher) | ||
486 | return -ENOMEM; | ||
487 | hasher->hash_tfm = crypto_alloc_hash(type, 0, CRYPTO_ALG_ASYNC); | ||
488 | if (!hasher->hash_tfm) { | ||
489 | kfree(hasher); | ||
490 | return -EINVAL; | ||
491 | } | ||
492 | spin_lock(&info->hash_lock); | ||
493 | list_add(&hasher->list, &info->hashers); | ||
494 | spin_unlock(&info->hash_lock); | ||
495 | return 0; | ||
496 | } | ||
497 | #endif | ||
420 | struct btrfs_root *open_ctree(struct super_block *sb) | 498 | struct btrfs_root *open_ctree(struct super_block *sb) |
421 | { | 499 | { |
422 | u32 sectorsize; | 500 | u32 sectorsize; |
@@ -440,6 +518,9 @@ struct btrfs_root *open_ctree(struct super_block *sb) | |||
440 | INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_NOFS); | 518 | INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_NOFS); |
441 | INIT_LIST_HEAD(&fs_info->trans_list); | 519 | INIT_LIST_HEAD(&fs_info->trans_list); |
442 | INIT_LIST_HEAD(&fs_info->dead_roots); | 520 | INIT_LIST_HEAD(&fs_info->dead_roots); |
521 | INIT_LIST_HEAD(&fs_info->hashers); | ||
522 | spin_lock_init(&fs_info->hash_lock); | ||
523 | |||
443 | memset(&fs_info->super_kobj, 0, sizeof(fs_info->super_kobj)); | 524 | memset(&fs_info->super_kobj, 0, sizeof(fs_info->super_kobj)); |
444 | init_completion(&fs_info->kobj_unregister); | 525 | init_completion(&fs_info->kobj_unregister); |
445 | sb_set_blocksize(sb, 4096); | 526 | sb_set_blocksize(sb, 4096); |
@@ -479,6 +560,14 @@ struct btrfs_root *open_ctree(struct super_block *sb) | |||
479 | mutex_init(&fs_info->trans_mutex); | 560 | mutex_init(&fs_info->trans_mutex); |
480 | mutex_init(&fs_info->fs_mutex); | 561 | mutex_init(&fs_info->fs_mutex); |
481 | 562 | ||
563 | #if 0 | ||
564 | ret = add_hasher(fs_info, "crc32c"); | ||
565 | if (ret) { | ||
566 | printk("btrfs: failed hash setup, modprobe cryptomgr?\n"); | ||
567 | err = -ENOMEM; | ||
568 | goto fail_iput; | ||
569 | } | ||
570 | #endif | ||
482 | __setup_root(512, 512, 512, tree_root, | 571 | __setup_root(512, 512, 512, tree_root, |
483 | fs_info, BTRFS_ROOT_TREE_OBJECTID); | 572 | fs_info, BTRFS_ROOT_TREE_OBJECTID); |
484 | 573 | ||
@@ -509,25 +598,21 @@ struct btrfs_root *open_ctree(struct super_block *sb) | |||
509 | i_size_write(fs_info->btree_inode, | 598 | i_size_write(fs_info->btree_inode, |
510 | btrfs_super_total_bytes(disk_super)); | 599 | btrfs_super_total_bytes(disk_super)); |
511 | 600 | ||
512 | |||
513 | if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC, | 601 | if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC, |
514 | sizeof(disk_super->magic))) { | 602 | sizeof(disk_super->magic))) { |
515 | printk("btrfs: valid FS not found on %s\n", sb->s_id); | 603 | printk("btrfs: valid FS not found on %s\n", sb->s_id); |
516 | goto fail_sb_buffer; | 604 | goto fail_sb_buffer; |
517 | } | 605 | } |
606 | |||
518 | blocksize = btrfs_level_size(tree_root, | 607 | blocksize = btrfs_level_size(tree_root, |
519 | btrfs_super_root_level(disk_super)); | 608 | btrfs_super_root_level(disk_super)); |
609 | |||
520 | tree_root->node = read_tree_block(tree_root, | 610 | tree_root->node = read_tree_block(tree_root, |
521 | btrfs_super_root(disk_super), | 611 | btrfs_super_root(disk_super), |
522 | blocksize); | 612 | blocksize); |
523 | if (!tree_root->node) | 613 | if (!tree_root->node) |
524 | goto fail_sb_buffer; | 614 | goto fail_sb_buffer; |
525 | 615 | ||
526 | #if 0 | ||
527 | btrfs_print_leaf(tree_root, tree_root->node); | ||
528 | err = -EIO; | ||
529 | goto fail_tree_root; | ||
530 | #endif | ||
531 | mutex_lock(&fs_info->fs_mutex); | 616 | mutex_lock(&fs_info->fs_mutex); |
532 | 617 | ||
533 | ret = find_and_setup_root(tree_root, fs_info, | 618 | ret = find_and_setup_root(tree_root, fs_info, |
@@ -634,9 +719,19 @@ int close_ctree(struct btrfs_root *root) | |||
634 | 719 | ||
635 | btrfs_free_block_groups(root->fs_info); | 720 | btrfs_free_block_groups(root->fs_info); |
636 | del_fs_roots(fs_info); | 721 | del_fs_roots(fs_info); |
637 | extent_map_tree_cleanup(&BTRFS_I(fs_info->btree_inode)->extent_tree); | 722 | extent_map_tree_empty_lru(&BTRFS_I(fs_info->btree_inode)->extent_tree); |
638 | truncate_inode_pages(fs_info->btree_inode->i_mapping, 0); | 723 | truncate_inode_pages(fs_info->btree_inode->i_mapping, 0); |
639 | iput(fs_info->btree_inode); | 724 | iput(fs_info->btree_inode); |
725 | #if 0 | ||
726 | while(!list_empty(&fs_info->hashers)) { | ||
727 | struct btrfs_hasher *hasher; | ||
728 | hasher = list_entry(fs_info->hashers.next, struct btrfs_hasher, | ||
729 | hashers); | ||
730 | list_del(&hasher->hashers); | ||
731 | crypto_free_hash(&fs_info->hash_tfm); | ||
732 | kfree(hasher); | ||
733 | } | ||
734 | #endif | ||
640 | kfree(fs_info->extent_root); | 735 | kfree(fs_info->extent_root); |
641 | kfree(fs_info->tree_root); | 736 | kfree(fs_info->tree_root); |
642 | return 0; | 737 | return 0; |
@@ -733,5 +828,5 @@ int btrfs_read_buffer(struct extent_buffer *buf) | |||
733 | struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root; | 828 | struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root; |
734 | struct inode *btree_inode = root->fs_info->btree_inode; | 829 | struct inode *btree_inode = root->fs_info->btree_inode; |
735 | return read_extent_buffer_pages(&BTRFS_I(btree_inode)->extent_tree, | 830 | return read_extent_buffer_pages(&BTRFS_I(btree_inode)->extent_tree, |
736 | buf, 1); | 831 | buf, 0, 1); |
737 | } | 832 | } |