Btrfs: create a pinned em when writing to a prealloc range in DIO
Wade Cline reported a problem where he was getting garbage and warnings when writing to a preallocated range via O_DIRECT. This is because we weren't creating our normal pinned extent_map for the range we were writing to, which was causing all sorts of issues. This patch fixes the problem and makes his testcase much happier. Thanks, Reported-by: Wade Cline <clinew@linux.vnet.ibm.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
This commit is contained in:
parent
6df7881a84
commit
69ffb54347
1 changed files with 55 additions and 0 deletions
|
@ -5898,6 +5898,48 @@ static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct extent_map *create_pinned_em(struct inode *inode, u64 start,
|
||||
u64 len, u64 orig_start,
|
||||
u64 block_start, u64 block_len,
|
||||
int type)
|
||||
{
|
||||
struct extent_map_tree *em_tree;
|
||||
struct extent_map *em;
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
int ret;
|
||||
|
||||
em_tree = &BTRFS_I(inode)->extent_tree;
|
||||
em = alloc_extent_map();
|
||||
if (!em)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
em->start = start;
|
||||
em->orig_start = orig_start;
|
||||
em->len = len;
|
||||
em->block_len = block_len;
|
||||
em->block_start = block_start;
|
||||
em->bdev = root->fs_info->fs_devices->latest_bdev;
|
||||
set_bit(EXTENT_FLAG_PINNED, &em->flags);
|
||||
if (type == BTRFS_ORDERED_PREALLOC)
|
||||
set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
|
||||
|
||||
do {
|
||||
btrfs_drop_extent_cache(inode, em->start,
|
||||
em->start + em->len - 1, 0);
|
||||
write_lock(&em_tree->lock);
|
||||
ret = add_extent_mapping(em_tree, em);
|
||||
write_unlock(&em_tree->lock);
|
||||
} while (ret == -EEXIST);
|
||||
|
||||
if (ret) {
|
||||
free_extent_map(em);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return em;
|
||||
}
|
||||
|
||||
|
||||
static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create)
|
||||
{
|
||||
|
@ -6012,6 +6054,19 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
|
|||
goto must_cow;
|
||||
|
||||
if (can_nocow_odirect(trans, inode, start, len) == 1) {
|
||||
u64 orig_start = em->start;
|
||||
|
||||
if (type == BTRFS_ORDERED_PREALLOC) {
|
||||
free_extent_map(em);
|
||||
em = create_pinned_em(inode, start, len,
|
||||
orig_start,
|
||||
block_start, len, type);
|
||||
if (IS_ERR(em)) {
|
||||
btrfs_end_transaction(trans, root);
|
||||
goto unlock_err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btrfs_add_ordered_extent_dio(inode, start,
|
||||
block_start, len, len, type);
|
||||
btrfs_end_transaction(trans, root);
|
||||
|
|
Loading…
Reference in a new issue