Btrfs: Retry metadata reads in the face of checksum failures

Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
Chris Mason 2008-04-09 16:28:12 -04:00
parent 22c599485b
commit f188591e98
7 changed files with 138 additions and 52 deletions

View file

@ -569,7 +569,16 @@ static int check_leaf(struct btrfs_root *root, struct btrfs_path *path,
static int noinline check_block(struct btrfs_root *root, static int noinline check_block(struct btrfs_root *root,
struct btrfs_path *path, int level) struct btrfs_path *path, int level)
{ {
return 0; u64 found_start;
if (btrfs_header_level(path->nodes[level]) != level)
printk("warning: bad level %Lu wanted %d found %d\n",
path->nodes[level]->start, level,
btrfs_header_level(path->nodes[level]));
found_start = btrfs_header_bytenr(path->nodes[level]);
if (found_start != path->nodes[level]->start) {
printk("warning: bad bytentr %Lu found %Lu\n",
path->nodes[level]->start, found_start);
}
#if 0 #if 0
struct extent_buffer *buf = path->nodes[level]; struct extent_buffer *buf = path->nodes[level];

View file

@ -156,7 +156,6 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
memcpy(&found, result, BTRFS_CRC32_SIZE); memcpy(&found, result, BTRFS_CRC32_SIZE);
read_extent_buffer(buf, &val, 0, BTRFS_CRC32_SIZE); read_extent_buffer(buf, &val, 0, BTRFS_CRC32_SIZE);
WARN_ON(1);
printk("btrfs: %s checksum verify failed on %llu " printk("btrfs: %s checksum verify failed on %llu "
"wanted %X found %X from_this_trans %d " "wanted %X found %X from_this_trans %d "
"level %d\n", "level %d\n",
@ -171,6 +170,40 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
return 0; return 0;
} }
static int btree_read_extent_buffer_pages(struct btrfs_root *root,
struct extent_buffer *eb,
u64 start)
{
struct extent_io_tree *io_tree;
int ret;
int num_copies = 0;
int mirror_num = 0;
io_tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
while (1) {
ret = read_extent_buffer_pages(io_tree, eb, start, 1,
btree_get_extent, mirror_num);
if (!ret) {
if (mirror_num)
printk("good read %Lu mirror %d total %d\n", eb->start, mirror_num, num_copies);
return ret;
}
num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
eb->start, eb->len);
printk("failed to read %Lu mirror %d total %d\n", eb->start, mirror_num, num_copies);
if (num_copies == 1) {
printk("reading %Lu failed only one copy\n", eb->start);
return ret;
}
mirror_num++;
if (mirror_num > num_copies) {
printk("bailing at mirror %d of %d\n", mirror_num, num_copies);
return ret;
}
}
printk("read extent buffer page last\n");
return -EIO;
}
int csum_dirty_buffer(struct btrfs_root *root, struct page *page) int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
{ {
@ -180,6 +213,8 @@ int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
int found_level; int found_level;
unsigned long len; unsigned long len;
struct extent_buffer *eb; struct extent_buffer *eb;
int ret;
tree = &BTRFS_I(page->mapping->host)->io_tree; tree = &BTRFS_I(page->mapping->host)->io_tree;
if (page->private == EXTENT_PAGE_PRIVATE) if (page->private == EXTENT_PAGE_PRIVATE)
@ -191,8 +226,8 @@ int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
WARN_ON(1); WARN_ON(1);
} }
eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS); eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS);
read_extent_buffer_pages(tree, eb, start + PAGE_CACHE_SIZE, 1, ret = btree_read_extent_buffer_pages(root, eb, start + PAGE_CACHE_SIZE);
btree_get_extent); BUG_ON(ret);
btrfs_clear_buffer_defrag(eb); btrfs_clear_buffer_defrag(eb);
found_start = btrfs_header_bytenr(eb); found_start = btrfs_header_bytenr(eb);
if (found_start != start) { if (found_start != start) {
@ -240,7 +275,7 @@ int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
unsigned long len; unsigned long len;
struct extent_buffer *eb; struct extent_buffer *eb;
struct btrfs_root *root = BTRFS_I(page->mapping->host)->root; struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
int ret; int ret = 0;
tree = &BTRFS_I(page->mapping->host)->io_tree; tree = &BTRFS_I(page->mapping->host)->io_tree;
if (page->private == EXTENT_PAGE_PRIVATE) if (page->private == EXTENT_PAGE_PRIVATE)
@ -252,25 +287,26 @@ int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
WARN_ON(1); WARN_ON(1);
} }
eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS); eb = alloc_extent_buffer(tree, start, len, page, GFP_NOFS);
read_extent_buffer_pages(tree, eb, start + PAGE_CACHE_SIZE, 1,
btree_get_extent);
btrfs_clear_buffer_defrag(eb); btrfs_clear_buffer_defrag(eb);
found_start = btrfs_header_bytenr(eb); found_start = btrfs_header_bytenr(eb);
if (found_start != start) { if (found_start != start) {
printk("warning: eb start incorrect %Lu buffer %Lu len %lu\n", printk("bad start on %Lu found %Lu\n", eb->start, found_start);
start, found_start, len); ret = -EIO;
WARN_ON(1);
goto err; goto err;
} }
if (eb->first_page != page) { if (eb->first_page != page) {
printk("bad first page %lu %lu\n", eb->first_page->index, printk("bad first page %lu %lu\n", eb->first_page->index,
page->index); page->index);
WARN_ON(1); WARN_ON(1);
ret = -EIO;
goto err; goto err;
} }
found_level = btrfs_header_level(eb); found_level = btrfs_header_level(eb);
ret = csum_tree_block(root, eb, 1); ret = csum_tree_block(root, eb, 1);
if (ret)
ret = -EIO;
end = min_t(u64, eb->len, PAGE_CACHE_SIZE); end = min_t(u64, eb->len, PAGE_CACHE_SIZE);
end = eb->start + end - 1; end = eb->start + end - 1;
@ -278,7 +314,7 @@ int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
err: err:
free_extent_buffer(eb); free_extent_buffer(eb);
out: out:
return 0; return ret;
} }
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23) #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,
return 0; return 0;
} }
static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio) static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
int mirror_num)
{ {
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
u64 offset; u64 offset;
@ -338,7 +375,7 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio)
offset = bio->bi_sector << 9; offset = bio->bi_sector << 9;
if (rw & (1 << BIO_RW)) { if (rw & (1 << BIO_RW)) {
return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio); return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio, mirror_num);
} }
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 1); 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)
submit_bio(rw, bio); submit_bio(rw, bio);
return 0; return 0;
} }
return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio); return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio, mirror_num);
} }
static int btree_writepage(struct page *page, struct writeback_control *wbc) 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)
if (!buf) if (!buf)
return 0; return 0;
read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree,
buf, 0, 0, btree_get_extent); buf, 0, 0, btree_get_extent, 0);
free_extent_buffer(buf); free_extent_buffer(buf);
return ret; return ret;
} }
@ -522,8 +559,7 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
if (!buf) if (!buf)
return NULL; return NULL;
ret = read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, 0, ret = btree_read_extent_buffer_pages(root, buf, 0);
1, btree_get_extent);
if (ret == 0) { if (ret == 0) {
buf->flags |= EXTENT_UPTODATE; buf->flags |= EXTENT_UPTODATE;
@ -1366,10 +1402,8 @@ int btrfs_clear_buffer_defrag(struct extent_buffer *buf)
int btrfs_read_buffer(struct extent_buffer *buf) int btrfs_read_buffer(struct extent_buffer *buf)
{ {
struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root; struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root;
struct inode *btree_inode = root->fs_info->btree_inode;
int ret; int ret;
ret = read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, ret = btree_read_extent_buffer_pages(root, buf, 0);
buf, 0, 1, btree_get_extent);
if (ret == 0) { if (ret == 0) {
buf->flags |= EXTENT_UPTODATE; buf->flags |= EXTENT_UPTODATE;
} }

View file

@ -1546,7 +1546,7 @@ static int end_bio_extent_readpage(struct bio *bio,
!(state->state & EXTENT_LOCKED)) !(state->state & EXTENT_LOCKED))
state = NULL; state = NULL;
} }
if (!state) { if (!state && uptodate) {
spin_unlock_irqrestore(&tree->lock, flags); spin_unlock_irqrestore(&tree->lock, flags);
set_extent_uptodate(tree, start, end, set_extent_uptodate(tree, start, end,
GFP_ATOMIC); GFP_ATOMIC);
@ -1567,8 +1567,10 @@ static int end_bio_extent_readpage(struct bio *bio,
} else { } else {
state = NULL; state = NULL;
} }
set_state_cb(tree, clear, EXTENT_UPTODATE); if (uptodate) {
clear->state |= EXTENT_UPTODATE; set_state_cb(tree, clear, EXTENT_UPTODATE);
clear->state |= EXTENT_UPTODATE;
}
clear_state_bit(tree, clear, EXTENT_LOCKED, clear_state_bit(tree, clear, EXTENT_LOCKED,
1, 0); 1, 0);
if (cur == start) if (cur == start)
@ -1685,7 +1687,7 @@ extent_bio_alloc(struct block_device *bdev, u64 first_sector, int nr_vecs,
return bio; return bio;
} }
static int submit_one_bio(int rw, struct bio *bio) static int submit_one_bio(int rw, struct bio *bio, int mirror_num)
{ {
u64 maxsector; u64 maxsector;
int ret = 0; int ret = 0;
@ -1722,7 +1724,8 @@ static int submit_one_bio(int rw, struct bio *bio)
WARN_ON(1); WARN_ON(1);
} }
if (tree->ops && tree->ops->submit_bio_hook) if (tree->ops && tree->ops->submit_bio_hook)
tree->ops->submit_bio_hook(page->mapping->host, rw, bio); tree->ops->submit_bio_hook(page->mapping->host, rw, bio,
mirror_num);
else else
submit_bio(rw, bio); submit_bio(rw, bio);
if (bio_flagged(bio, BIO_EOPNOTSUPP)) if (bio_flagged(bio, BIO_EOPNOTSUPP))
@ -1737,7 +1740,8 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree,
struct block_device *bdev, struct block_device *bdev,
struct bio **bio_ret, struct bio **bio_ret,
unsigned long max_pages, unsigned long max_pages,
bio_end_io_t end_io_func) bio_end_io_t end_io_func,
int mirror_num)
{ {
int ret = 0; int ret = 0;
struct bio *bio; struct bio *bio;
@ -1749,7 +1753,7 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree,
(tree->ops && tree->ops->merge_bio_hook && (tree->ops && tree->ops->merge_bio_hook &&
tree->ops->merge_bio_hook(page, offset, size, bio)) || tree->ops->merge_bio_hook(page, offset, size, bio)) ||
bio_add_page(bio, page, size, offset) < size) { bio_add_page(bio, page, size, offset) < size) {
ret = submit_one_bio(rw, bio); ret = submit_one_bio(rw, bio, mirror_num);
bio = NULL; bio = NULL;
} else { } else {
return 0; return 0;
@ -1769,7 +1773,7 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree,
if (bio_ret) { if (bio_ret) {
*bio_ret = bio; *bio_ret = bio;
} else { } else {
ret = submit_one_bio(rw, bio); ret = submit_one_bio(rw, bio, mirror_num);
} }
return ret; return ret;
@ -1798,7 +1802,7 @@ void set_page_extent_head(struct page *page, unsigned long len)
static int __extent_read_full_page(struct extent_io_tree *tree, static int __extent_read_full_page(struct extent_io_tree *tree,
struct page *page, struct page *page,
get_extent_t *get_extent, get_extent_t *get_extent,
struct bio **bio) struct bio **bio, int mirror_num)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
u64 start = (u64)page->index << PAGE_CACHE_SHIFT; u64 start = (u64)page->index << PAGE_CACHE_SHIFT;
@ -1901,7 +1905,7 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
ret = submit_extent_page(READ, tree, page, ret = submit_extent_page(READ, tree, page,
sector, iosize, page_offset, sector, iosize, page_offset,
bdev, bio, nr, bdev, bio, nr,
end_bio_extent_readpage); end_bio_extent_readpage, mirror_num);
} }
if (ret) if (ret)
SetPageError(page); SetPageError(page);
@ -1923,9 +1927,9 @@ int extent_read_full_page(struct extent_io_tree *tree, struct page *page,
struct bio *bio = NULL; struct bio *bio = NULL;
int ret; int ret;
ret = __extent_read_full_page(tree, page, get_extent, &bio); ret = __extent_read_full_page(tree, page, get_extent, &bio, 0);
if (bio) if (bio)
submit_one_bio(READ, bio); submit_one_bio(READ, bio, 0);
return ret; return ret;
} }
EXPORT_SYMBOL(extent_read_full_page); EXPORT_SYMBOL(extent_read_full_page);
@ -2077,7 +2081,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
ret = submit_extent_page(WRITE, tree, page, sector, ret = submit_extent_page(WRITE, tree, page, sector,
iosize, page_offset, bdev, iosize, page_offset, bdev,
&epd->bio, max_nr, &epd->bio, max_nr,
end_bio_extent_writepage); end_bio_extent_writepage, 0);
if (ret) if (ret)
SetPageError(page); SetPageError(page);
} }
@ -2244,7 +2248,7 @@ int extent_write_full_page(struct extent_io_tree *tree, struct page *page,
write_cache_pages(mapping, &wbc_writepages, __extent_writepage, &epd); write_cache_pages(mapping, &wbc_writepages, __extent_writepage, &epd);
if (epd.bio) { if (epd.bio) {
submit_one_bio(WRITE, epd.bio); submit_one_bio(WRITE, epd.bio, 0);
} }
return ret; return ret;
} }
@ -2265,7 +2269,7 @@ int extent_writepages(struct extent_io_tree *tree,
ret = write_cache_pages(mapping, wbc, __extent_writepage, &epd); ret = write_cache_pages(mapping, wbc, __extent_writepage, &epd);
if (epd.bio) { if (epd.bio) {
submit_one_bio(WRITE, epd.bio); submit_one_bio(WRITE, epd.bio, 0);
} }
return ret; return ret;
} }
@ -2297,7 +2301,8 @@ int extent_readpages(struct extent_io_tree *tree,
page_cache_get(page); page_cache_get(page);
if (!pagevec_add(&pvec, page)) if (!pagevec_add(&pvec, page))
__pagevec_lru_add(&pvec); __pagevec_lru_add(&pvec);
__extent_read_full_page(tree, page, get_extent, &bio); __extent_read_full_page(tree, page, get_extent,
&bio, 0);
} }
page_cache_release(page); page_cache_release(page);
} }
@ -2305,7 +2310,7 @@ int extent_readpages(struct extent_io_tree *tree,
__pagevec_lru_add(&pvec); __pagevec_lru_add(&pvec);
BUG_ON(!list_empty(pages)); BUG_ON(!list_empty(pages));
if (bio) if (bio)
submit_one_bio(READ, bio); submit_one_bio(READ, bio, 0);
return 0; return 0;
} }
EXPORT_SYMBOL(extent_readpages); EXPORT_SYMBOL(extent_readpages);
@ -2430,7 +2435,7 @@ int extent_prepare_write(struct extent_io_tree *tree,
ret = submit_extent_page(READ, tree, page, ret = submit_extent_page(READ, tree, page,
sector, iosize, page_offset, em->bdev, sector, iosize, page_offset, em->bdev,
NULL, 1, NULL, 1,
end_bio_extent_preparewrite); end_bio_extent_preparewrite, 0);
iocount++; iocount++;
block_start = block_start + iosize; block_start = block_start + iosize;
} else { } else {
@ -2696,6 +2701,7 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
mark_page_accessed(page0); mark_page_accessed(page0);
set_page_extent_mapped(page0); set_page_extent_mapped(page0);
set_page_extent_head(page0, len); set_page_extent_head(page0, len);
uptodate = PageUptodate(page0);
} else { } else {
i = 0; i = 0;
} }
@ -3006,7 +3012,7 @@ EXPORT_SYMBOL(extent_buffer_uptodate);
int read_extent_buffer_pages(struct extent_io_tree *tree, int read_extent_buffer_pages(struct extent_io_tree *tree,
struct extent_buffer *eb, struct extent_buffer *eb,
u64 start, int wait, u64 start, int wait,
get_extent_t *get_extent) get_extent_t *get_extent, int mirror_num)
{ {
unsigned long i; unsigned long i;
unsigned long start_i; unsigned long start_i;
@ -3062,8 +3068,10 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
if (start_i == 0) if (start_i == 0)
inc_all_pages = 1; inc_all_pages = 1;
ClearPageError(page);
err = __extent_read_full_page(tree, page, err = __extent_read_full_page(tree, page,
get_extent, &bio); get_extent, &bio,
mirror_num);
if (err) { if (err) {
ret = err; ret = err;
} }
@ -3073,7 +3081,7 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
} }
if (bio) if (bio)
submit_one_bio(READ, bio); submit_one_bio(READ, bio, mirror_num);
if (ret || !wait) { if (ret || !wait) {
return ret; return ret;

View file

@ -27,7 +27,8 @@ struct extent_state;
struct extent_io_ops { struct extent_io_ops {
int (*fill_delalloc)(struct inode *inode, u64 start, u64 end); int (*fill_delalloc)(struct inode *inode, u64 start, u64 end);
int (*writepage_io_hook)(struct page *page, u64 start, u64 end); int (*writepage_io_hook)(struct page *page, u64 start, u64 end);
int (*submit_bio_hook)(struct inode *inode, int rw, struct bio *bio); int (*submit_bio_hook)(struct inode *inode, int rw, struct bio *bio,
int mirror_num);
int (*merge_bio_hook)(struct page *page, unsigned long offset, int (*merge_bio_hook)(struct page *page, unsigned long offset,
size_t size, struct bio *bio); size_t size, struct bio *bio);
int (*readpage_io_hook)(struct page *page, u64 start, u64 end); int (*readpage_io_hook)(struct page *page, u64 start, u64 end);
@ -172,7 +173,7 @@ struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
void free_extent_buffer(struct extent_buffer *eb); void free_extent_buffer(struct extent_buffer *eb);
int read_extent_buffer_pages(struct extent_io_tree *tree, int read_extent_buffer_pages(struct extent_io_tree *tree,
struct extent_buffer *eb, u64 start, int wait, struct extent_buffer *eb, u64 start, int wait,
get_extent_t *get_extent); get_extent_t *get_extent, int mirror_num);
static inline void extent_buffer_get(struct extent_buffer *eb) static inline void extent_buffer_get(struct extent_buffer *eb)
{ {

View file

@ -314,7 +314,7 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
map_tree = &root->fs_info->mapping_tree; map_tree = &root->fs_info->mapping_tree;
map_length = length; map_length = length;
ret = btrfs_map_block(map_tree, READ, logical, ret = btrfs_map_block(map_tree, READ, logical,
&map_length, NULL); &map_length, NULL, 0);
if (map_length < length + size) { if (map_length < length + size) {
return 1; return 1;
@ -322,7 +322,8 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
return 0; return 0;
} }
int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio) int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
int mirror_num)
{ {
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
@ -347,7 +348,7 @@ int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio)
BUG_ON(ret); BUG_ON(ret);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
mapit: mapit:
return btrfs_map_bio(root, rw, bio); return btrfs_map_bio(root, rw, bio, mirror_num);
} }
int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end) int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end)

View file

@ -788,9 +788,31 @@ void btrfs_mapping_tree_free(struct btrfs_mapping_tree *tree)
} }
} }
int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len)
{
struct extent_map *em;
struct map_lookup *map;
struct extent_map_tree *em_tree = &map_tree->map_tree;
int ret;
spin_lock(&em_tree->lock);
em = lookup_extent_mapping(em_tree, logical, len);
BUG_ON(!em);
BUG_ON(em->start > logical || em->start + em->len < logical);
map = (struct map_lookup *)em->bdev;
if (map->type & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1))
ret = map->num_stripes;
else
ret = 1;
free_extent_map(em);
spin_unlock(&em_tree->lock);
return ret;
}
int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
u64 logical, u64 *length, u64 logical, u64 *length,
struct btrfs_multi_bio **multi_ret) struct btrfs_multi_bio **multi_ret, int mirror_num)
{ {
struct extent_map *em; struct extent_map *em;
struct map_lookup *map; struct map_lookup *map;
@ -822,6 +844,9 @@ again:
map = (struct map_lookup *)em->bdev; map = (struct map_lookup *)em->bdev;
offset = logical - em->start; offset = logical - em->start;
if (mirror_num > map->num_stripes)
mirror_num = 0;
/* if our multi bio struct is too small, back off and try again */ /* if our multi bio struct is too small, back off and try again */
if (multi_ret && (rw & (1 << BIO_RW)) && if (multi_ret && (rw & (1 << BIO_RW)) &&
stripes_allocated < map->num_stripes && stripes_allocated < map->num_stripes &&
@ -862,7 +887,9 @@ again:
if (map->type & BTRFS_BLOCK_GROUP_RAID1) { if (map->type & BTRFS_BLOCK_GROUP_RAID1) {
if (rw & (1 << BIO_RW)) if (rw & (1 << BIO_RW))
multi->num_stripes = map->num_stripes; multi->num_stripes = map->num_stripes;
else { else if (mirror_num) {
stripe_index = mirror_num - 1;
} else {
int i; int i;
u64 least = (u64)-1; u64 least = (u64)-1;
struct btrfs_device *cur; struct btrfs_device *cur;
@ -880,6 +907,8 @@ again:
} else if (map->type & BTRFS_BLOCK_GROUP_DUP) { } else if (map->type & BTRFS_BLOCK_GROUP_DUP) {
if (rw & (1 << BIO_RW)) if (rw & (1 << BIO_RW))
multi->num_stripes = map->num_stripes; multi->num_stripes = map->num_stripes;
else if (mirror_num)
stripe_index = mirror_num - 1;
} else { } else {
/* /*
* after this do_div call, stripe_nr is the number of stripes * after this do_div call, stripe_nr is the number of stripes
@ -938,7 +967,8 @@ static int end_bio_multi_stripe(struct bio *bio,
#endif #endif
} }
int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio) int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
int mirror_num)
{ {
struct btrfs_mapping_tree *map_tree; struct btrfs_mapping_tree *map_tree;
struct btrfs_device *dev; struct btrfs_device *dev;
@ -960,7 +990,8 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio)
map_tree = &root->fs_info->mapping_tree; map_tree = &root->fs_info->mapping_tree;
map_length = length; map_length = length;
ret = btrfs_map_block(map_tree, rw, logical, &map_length, &multi); ret = btrfs_map_block(map_tree, rw, logical, &map_length, &multi,
mirror_num);
BUG_ON(ret); BUG_ON(ret);
total_devs = multi->num_stripes; total_devs = multi->num_stripes;

View file

@ -93,7 +93,7 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
u64 owner, u64 num_bytes, u64 *start); u64 owner, u64 num_bytes, u64 *start);
int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
u64 logical, u64 *length, u64 logical, u64 *length,
struct btrfs_multi_bio **multi_ret); struct btrfs_multi_bio **multi_ret, int mirror_num);
int btrfs_read_sys_array(struct btrfs_root *root); int btrfs_read_sys_array(struct btrfs_root *root);
int btrfs_read_chunk_tree(struct btrfs_root *root); int btrfs_read_chunk_tree(struct btrfs_root *root);
int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
@ -101,7 +101,8 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
u64 *num_bytes, u64 type); u64 *num_bytes, u64 type);
void btrfs_mapping_init(struct btrfs_mapping_tree *tree); void btrfs_mapping_init(struct btrfs_mapping_tree *tree);
void btrfs_mapping_tree_free(struct btrfs_mapping_tree *tree); void btrfs_mapping_tree_free(struct btrfs_mapping_tree *tree);
int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio); int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
int mirror_num);
int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf); int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf);
int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
int flags, void *holder); int flags, void *holder);
@ -112,4 +113,5 @@ int btrfs_add_device(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_device *device); struct btrfs_device *device);
int btrfs_cleanup_fs_uuids(void); int btrfs_cleanup_fs_uuids(void);
int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len);
#endif #endif