diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-04-09 16:28:12 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:01 -0400 |
commit | f188591e987e21b6f7f8864c66a02858b95b530e (patch) | |
tree | 996f04b7c8d1e8a626b123e7a2a217992d705c60 /fs/btrfs/disk-io.c | |
parent | 22c599485b1fdd95e4476a4752596a6cf6c6629a (diff) |
Btrfs: Retry metadata reads in the face of checksum failures
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 | 72 |
1 files changed, 53 insertions, 19 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 94ff87d0eae4..59bdf0474be3 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -156,7 +156,6 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, | |||
156 | memcpy(&found, result, BTRFS_CRC32_SIZE); | 156 | memcpy(&found, result, BTRFS_CRC32_SIZE); |
157 | 157 | ||
158 | read_extent_buffer(buf, &val, 0, BTRFS_CRC32_SIZE); | 158 | read_extent_buffer(buf, &val, 0, BTRFS_CRC32_SIZE); |
159 | WARN_ON(1); | ||
160 | printk("btrfs: %s checksum verify failed on %llu " | 159 | printk("btrfs: %s checksum verify failed on %llu " |
161 | "wanted %X found %X from_this_trans %d " | 160 | "wanted %X found %X from_this_trans %d " |
162 | "level %d\n", | 161 | "level %d\n", |
@@ -171,6 +170,40 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf, | |||
171 | return 0; | 170 | return 0; |
172 | } | 171 | } |
173 | 172 | ||
173 | static int btree_read_extent_buffer_pages(struct btrfs_root *root, | ||
174 | struct extent_buffer *eb, | ||
175 | u64 start) | ||
176 | { | ||
177 | struct extent_io_tree *io_tree; | ||
178 | int ret; | ||
179 | int num_copies = 0; | ||
180 | int mirror_num = 0; | ||
181 | |||
182 | io_tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree; | ||
183 | while (1) { | ||
184 | ret = read_extent_buffer_pages(io_tree, eb, start, 1, | ||
185 | btree_get_extent, mirror_num); | ||
186 | if (!ret) { | ||
187 | if (mirror_num) | ||
188 | printk("good read %Lu mirror %d total %d\n", eb->start, mirror_num, num_copies); | ||
189 | return ret; | ||
190 | } | ||
191 | num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, | ||
192 | eb->start, eb->len); | ||
193 | printk("failed to read %Lu mirror %d total %d\n", eb->start, mirror_num, num_copies); | ||
194 | if (num_copies == 1) { | ||
195 | printk("reading %Lu failed only one copy\n", eb->start); | ||
196 | return ret; | ||
197 | } | ||
198 | mirror_num++; | ||
199 | if (mirror_num > num_copies) { | ||
200 | printk("bailing at mirror %d of %d\n", mirror_num, num_copies); | ||
201 | return ret; | ||
202 | } | ||
203 | } | ||
204 | printk("read extent buffer page last\n"); | ||
205 | return -EIO; | ||
206 | } | ||
174 | 207 | ||
175 | int csum_dirty_buffer(struct btrfs_root *root, struct page *page) | 208 | int csum_dirty_buffer(struct btrfs_root *root, struct page *page) |
176 | { | 209 | { |
@@ -180,6 +213,8 @@ int csum_dirty_buffer(struct btrfs_root *root, struct page *page) | |||
180 | int found_level; | 213 | int found_level; |
181 | unsigned long len; | 214 | unsigned long len; |
182 | struct extent_buffer *eb; | 215 | struct extent_buffer *eb; |
216 | int ret; | ||
217 | |||
183 | tree = &BTRFS_I(page->mapping->host)->io_tree; | 218 | tree = &BTRFS_I(page->mapping->host)->io_tree; |
184 | 219 | ||
185 | if (page->private == EXTENT_PAGE_PRIVATE) | 220 | if (page->private == EXTENT_PAGE_PRIVATE) |
@@ -191,8 +226,8 @@ int csum_dirty_buffer(struct btrfs_root *root, struct page *page) | |||
191 | WARN_ON(1); | 226 | WARN_ON(1); |
192 | } | 227 | } |
193 | eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS); | 228 | eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS); |
194 | read_extent_buffer_pages(tree, eb, start + PAGE_CACHE_SIZE, 1, | 229 | ret = btree_read_extent_buffer_pages(root, eb, start + PAGE_CACHE_SIZE); |
195 | btree_get_extent); | 230 | BUG_ON(ret); |
196 | btrfs_clear_buffer_defrag(eb); | 231 | btrfs_clear_buffer_defrag(eb); |
197 | found_start = btrfs_header_bytenr(eb); | 232 | found_start = btrfs_header_bytenr(eb); |
198 | if (found_start != start) { | 233 | if (found_start != start) { |
@@ -240,7 +275,7 @@ int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, | |||
240 | unsigned long len; | 275 | unsigned long len; |
241 | struct extent_buffer *eb; | 276 | struct extent_buffer *eb; |
242 | struct btrfs_root *root = BTRFS_I(page->mapping->host)->root; | 277 | struct btrfs_root *root = BTRFS_I(page->mapping->host)->root; |
243 | int ret; | 278 | int ret = 0; |
244 | 279 | ||
245 | tree = &BTRFS_I(page->mapping->host)->io_tree; | 280 | tree = &BTRFS_I(page->mapping->host)->io_tree; |
246 | if (page->private == EXTENT_PAGE_PRIVATE) | 281 | if (page->private == EXTENT_PAGE_PRIVATE) |
@@ -252,25 +287,26 @@ int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, | |||
252 | WARN_ON(1); | 287 | WARN_ON(1); |
253 | } | 288 | } |
254 | eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS); | 289 | eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS); |
255 | read_extent_buffer_pages(tree, eb, start + PAGE_CACHE_SIZE, 1, | 290 | |
256 | btree_get_extent); | ||
257 | btrfs_clear_buffer_defrag(eb); | 291 | btrfs_clear_buffer_defrag(eb); |
258 | found_start = btrfs_header_bytenr(eb); | 292 | found_start = btrfs_header_bytenr(eb); |
259 | if (found_start != start) { | 293 | if (found_start != start) { |
260 | printk("warning: eb start incorrect %Lu buffer %Lu len %lu\n", | 294 | printk("bad start on %Lu found %Lu\n", eb->start, found_start); |
261 | start, found_start, len); | 295 | ret = -EIO; |
262 | WARN_ON(1); | ||
263 | goto err; | 296 | goto err; |
264 | } | 297 | } |
265 | if (eb->first_page != page) { | 298 | if (eb->first_page != page) { |
266 | printk("bad first page %lu %lu\n", eb->first_page->index, | 299 | printk("bad first page %lu %lu\n", eb->first_page->index, |
267 | page->index); | 300 | page->index); |
268 | WARN_ON(1); | 301 | WARN_ON(1); |
302 | ret = -EIO; | ||
269 | goto err; | 303 | goto err; |
270 | } | 304 | } |
271 | found_level = btrfs_header_level(eb); | 305 | found_level = btrfs_header_level(eb); |
272 | 306 | ||
273 | ret = csum_tree_block(root, eb, 1); | 307 | ret = csum_tree_block(root, eb, 1); |
308 | if (ret) | ||
309 | ret = -EIO; | ||
274 | 310 | ||
275 | end = min_t(u64, eb->len, PAGE_CACHE_SIZE); | 311 | end = min_t(u64, eb->len, PAGE_CACHE_SIZE); |
276 | end = eb->start + end - 1; | 312 | end = eb->start + end - 1; |
@@ -278,7 +314,7 @@ int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, | |||
278 | err: | 314 | err: |
279 | free_extent_buffer(eb); | 315 | free_extent_buffer(eb); |
280 | out: | 316 | out: |
281 | return 0; | 317 | return ret; |
282 | } | 318 | } |
283 | 319 | ||
284 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23) | 320 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23) |
@@ -329,7 +365,8 @@ int btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio, | |||
329 | return 0; | 365 | return 0; |
330 | } | 366 | } |
331 | 367 | ||
332 | static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio) | 368 | static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio, |
369 | int mirror_num) | ||
333 | { | 370 | { |
334 | struct btrfs_root *root = BTRFS_I(inode)->root; | 371 | struct btrfs_root *root = BTRFS_I(inode)->root; |
335 | u64 offset; | 372 | u64 offset; |
@@ -338,7 +375,7 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio) | |||
338 | offset = bio->bi_sector << 9; | 375 | offset = bio->bi_sector << 9; |
339 | 376 | ||
340 | if (rw & (1 << BIO_RW)) { | 377 | if (rw & (1 << BIO_RW)) { |
341 | return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio); | 378 | return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio, mirror_num); |
342 | } | 379 | } |
343 | 380 | ||
344 | ret = btrfs_bio_wq_end_io(root->fs_info, bio, 1); | 381 | ret = btrfs_bio_wq_end_io(root->fs_info, bio, 1); |
@@ -349,7 +386,7 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio) | |||
349 | submit_bio(rw, bio); | 386 | submit_bio(rw, bio); |
350 | return 0; | 387 | return 0; |
351 | } | 388 | } |
352 | return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio); | 389 | return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio, mirror_num); |
353 | } | 390 | } |
354 | 391 | ||
355 | static int btree_writepage(struct page *page, struct writeback_control *wbc) | 392 | static int btree_writepage(struct page *page, struct writeback_control *wbc) |
@@ -459,7 +496,7 @@ int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize) | |||
459 | if (!buf) | 496 | if (!buf) |
460 | return 0; | 497 | return 0; |
461 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, | 498 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, |
462 | buf, 0, 0, btree_get_extent); | 499 | buf, 0, 0, btree_get_extent, 0); |
463 | free_extent_buffer(buf); | 500 | free_extent_buffer(buf); |
464 | return ret; | 501 | return ret; |
465 | } | 502 | } |
@@ -522,8 +559,7 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, | |||
522 | if (!buf) | 559 | if (!buf) |
523 | return NULL; | 560 | return NULL; |
524 | 561 | ||
525 | ret = read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, 0, | 562 | ret = btree_read_extent_buffer_pages(root, buf, 0); |
526 | 1, btree_get_extent); | ||
527 | 563 | ||
528 | if (ret == 0) { | 564 | if (ret == 0) { |
529 | buf->flags |= EXTENT_UPTODATE; | 565 | buf->flags |= EXTENT_UPTODATE; |
@@ -1366,10 +1402,8 @@ int btrfs_clear_buffer_defrag(struct extent_buffer *buf) | |||
1366 | int btrfs_read_buffer(struct extent_buffer *buf) | 1402 | int btrfs_read_buffer(struct extent_buffer *buf) |
1367 | { | 1403 | { |
1368 | struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root; | 1404 | struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root; |
1369 | struct inode *btree_inode = root->fs_info->btree_inode; | ||
1370 | int ret; | 1405 | int ret; |
1371 | ret = read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, | 1406 | ret = btree_read_extent_buffer_pages(root, buf, 0); |
1372 | buf, 0, 1, btree_get_extent); | ||
1373 | if (ret == 0) { | 1407 | if (ret == 0) { |
1374 | buf->flags |= EXTENT_UPTODATE; | 1408 | buf->flags |= EXTENT_UPTODATE; |
1375 | } | 1409 | } |