Commit 4b384318 authored by Mark Fasheh's avatar Mark Fasheh Committed by Chris Mason

btrfs: Introduce extent_read_full_page_nolock()

We want this for btrfs_extent_same. Basically readpage and friends do their
own extent locking but for the purposes of dedupe, we want to have both
files locked down across a set of readpage operations (so that we can
compare data). Introduce this variant and a flag which can be set for
extent_read_full_page() to indicate that we are already locked.

Partial credit for this patch goes to Gabriel de Perthuis <g2p.code@gmail.com>
as I have included a fix from him to the original patch which avoids a
deadlock on compressed extents.
Signed-off-by: default avatarMark Fasheh <mfasheh@suse.de>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
Signed-off-by: default avatarChris Mason <chris.mason@fusionio.com>
parent 32b7c687
...@@ -639,7 +639,11 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, ...@@ -639,7 +639,11 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
faili = nr_pages - 1; faili = nr_pages - 1;
cb->nr_pages = nr_pages; cb->nr_pages = nr_pages;
add_ra_bio_pages(inode, em_start + em_len, cb); /* In the parent-locked case, we only locked the range we are
* interested in. In all other cases, we can opportunistically
* cache decompressed data that goes beyond the requested range. */
if (!(bio_flags & EXTENT_BIO_PARENT_LOCKED))
add_ra_bio_pages(inode, em_start + em_len, cb);
/* include any pages we added in add_ra-bio_pages */ /* include any pages we added in add_ra-bio_pages */
uncompressed_len = bio->bi_vcnt * PAGE_CACHE_SIZE; uncompressed_len = bio->bi_vcnt * PAGE_CACHE_SIZE;
......
...@@ -2764,11 +2764,12 @@ static int __do_readpage(struct extent_io_tree *tree, ...@@ -2764,11 +2764,12 @@ static int __do_readpage(struct extent_io_tree *tree,
struct block_device *bdev; struct block_device *bdev;
int ret; int ret;
int nr = 0; int nr = 0;
int parent_locked = *bio_flags & EXTENT_BIO_PARENT_LOCKED;
size_t pg_offset = 0; size_t pg_offset = 0;
size_t iosize; size_t iosize;
size_t disk_io_size; size_t disk_io_size;
size_t blocksize = inode->i_sb->s_blocksize; size_t blocksize = inode->i_sb->s_blocksize;
unsigned long this_bio_flag = 0; unsigned long this_bio_flag = *bio_flags & EXTENT_BIO_PARENT_LOCKED;
set_page_extent_mapped(page); set_page_extent_mapped(page);
...@@ -2807,15 +2808,18 @@ static int __do_readpage(struct extent_io_tree *tree, ...@@ -2807,15 +2808,18 @@ static int __do_readpage(struct extent_io_tree *tree,
kunmap_atomic(userpage); kunmap_atomic(userpage);
set_extent_uptodate(tree, cur, cur + iosize - 1, set_extent_uptodate(tree, cur, cur + iosize - 1,
&cached, GFP_NOFS); &cached, GFP_NOFS);
unlock_extent_cached(tree, cur, cur + iosize - 1, if (!parent_locked)
&cached, GFP_NOFS); unlock_extent_cached(tree, cur,
cur + iosize - 1,
&cached, GFP_NOFS);
break; break;
} }
em = __get_extent_map(inode, page, pg_offset, cur, em = __get_extent_map(inode, page, pg_offset, cur,
end - cur + 1, get_extent, em_cached); end - cur + 1, get_extent, em_cached);
if (IS_ERR_OR_NULL(em)) { if (IS_ERR_OR_NULL(em)) {
SetPageError(page); SetPageError(page);
unlock_extent(tree, cur, end); if (!parent_locked)
unlock_extent(tree, cur, end);
break; break;
} }
extent_offset = cur - em->start; extent_offset = cur - em->start;
...@@ -2823,7 +2827,7 @@ static int __do_readpage(struct extent_io_tree *tree, ...@@ -2823,7 +2827,7 @@ static int __do_readpage(struct extent_io_tree *tree,
BUG_ON(end < cur); BUG_ON(end < cur);
if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) { if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
this_bio_flag = EXTENT_BIO_COMPRESSED; this_bio_flag |= EXTENT_BIO_COMPRESSED;
extent_set_compress_type(&this_bio_flag, extent_set_compress_type(&this_bio_flag,
em->compress_type); em->compress_type);
} }
...@@ -2867,7 +2871,8 @@ static int __do_readpage(struct extent_io_tree *tree, ...@@ -2867,7 +2871,8 @@ static int __do_readpage(struct extent_io_tree *tree,
if (test_range_bit(tree, cur, cur_end, if (test_range_bit(tree, cur, cur_end,
EXTENT_UPTODATE, 1, NULL)) { EXTENT_UPTODATE, 1, NULL)) {
check_page_uptodate(tree, page); check_page_uptodate(tree, page);
unlock_extent(tree, cur, cur + iosize - 1); if (!parent_locked)
unlock_extent(tree, cur, cur + iosize - 1);
cur = cur + iosize; cur = cur + iosize;
pg_offset += iosize; pg_offset += iosize;
continue; continue;
...@@ -2877,7 +2882,8 @@ static int __do_readpage(struct extent_io_tree *tree, ...@@ -2877,7 +2882,8 @@ static int __do_readpage(struct extent_io_tree *tree,
*/ */
if (block_start == EXTENT_MAP_INLINE) { if (block_start == EXTENT_MAP_INLINE) {
SetPageError(page); SetPageError(page);
unlock_extent(tree, cur, cur + iosize - 1); if (!parent_locked)
unlock_extent(tree, cur, cur + iosize - 1);
cur = cur + iosize; cur = cur + iosize;
pg_offset += iosize; pg_offset += iosize;
continue; continue;
...@@ -2895,7 +2901,8 @@ static int __do_readpage(struct extent_io_tree *tree, ...@@ -2895,7 +2901,8 @@ static int __do_readpage(struct extent_io_tree *tree,
*bio_flags = this_bio_flag; *bio_flags = this_bio_flag;
} else { } else {
SetPageError(page); SetPageError(page);
unlock_extent(tree, cur, cur + iosize - 1); if (!parent_locked)
unlock_extent(tree, cur, cur + iosize - 1);
} }
cur = cur + iosize; cur = cur + iosize;
pg_offset += iosize; pg_offset += iosize;
...@@ -3021,6 +3028,20 @@ int extent_read_full_page(struct extent_io_tree *tree, struct page *page, ...@@ -3021,6 +3028,20 @@ int extent_read_full_page(struct extent_io_tree *tree, struct page *page,
return ret; return ret;
} }
int extent_read_full_page_nolock(struct extent_io_tree *tree, struct page *page,
get_extent_t *get_extent, int mirror_num)
{
struct bio *bio = NULL;
unsigned long bio_flags = EXTENT_BIO_PARENT_LOCKED;
int ret;
ret = __do_readpage(tree, page, get_extent, NULL, &bio, mirror_num,
&bio_flags, READ);
if (bio)
ret = submit_one_bio(READ, bio, mirror_num, bio_flags);
return ret;
}
static noinline void update_nr_written(struct page *page, static noinline void update_nr_written(struct page *page,
struct writeback_control *wbc, struct writeback_control *wbc,
unsigned long nr_written) unsigned long nr_written)
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
*/ */
#define EXTENT_BIO_COMPRESSED 1 #define EXTENT_BIO_COMPRESSED 1
#define EXTENT_BIO_TREE_LOG 2 #define EXTENT_BIO_TREE_LOG 2
#define EXTENT_BIO_PARENT_LOCKED 4
#define EXTENT_BIO_FLAG_SHIFT 16 #define EXTENT_BIO_FLAG_SHIFT 16
/* these are bit numbers for test/set bit */ /* these are bit numbers for test/set bit */
...@@ -199,6 +200,8 @@ int unlock_extent_cached(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -199,6 +200,8 @@ int unlock_extent_cached(struct extent_io_tree *tree, u64 start, u64 end,
int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end); int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end);
int extent_read_full_page(struct extent_io_tree *tree, struct page *page, int extent_read_full_page(struct extent_io_tree *tree, struct page *page,
get_extent_t *get_extent, int mirror_num); get_extent_t *get_extent, int mirror_num);
int extent_read_full_page_nolock(struct extent_io_tree *tree, struct page *page,
get_extent_t *get_extent, int mirror_num);
int __init extent_io_init(void); int __init extent_io_init(void);
void extent_io_exit(void); void extent_io_exit(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment