diff options
author | Josef Bacik <jbacik@fusionio.com> | 2012-09-11 15:40:07 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@fusionio.com> | 2012-10-01 15:19:20 -0400 |
commit | 69ffb54347a71c4f2a4694e0c1155af8538d55f6 (patch) | |
tree | 34e12adce924817ccfba6ecc7be3b68e4cdcab98 /fs/btrfs/inode.c | |
parent | 6df7881a84013f91405e5e113a4c322dd1804ba6 (diff) |
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>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 9e9754adf7a4..406666cb6156 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -5898,6 +5898,48 @@ static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend, | |||
5898 | return ret; | 5898 | return ret; |
5899 | } | 5899 | } |
5900 | 5900 | ||
5901 | static struct extent_map *create_pinned_em(struct inode *inode, u64 start, | ||
5902 | u64 len, u64 orig_start, | ||
5903 | u64 block_start, u64 block_len, | ||
5904 | int type) | ||
5905 | { | ||
5906 | struct extent_map_tree *em_tree; | ||
5907 | struct extent_map *em; | ||
5908 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
5909 | int ret; | ||
5910 | |||
5911 | em_tree = &BTRFS_I(inode)->extent_tree; | ||
5912 | em = alloc_extent_map(); | ||
5913 | if (!em) | ||
5914 | return ERR_PTR(-ENOMEM); | ||
5915 | |||
5916 | em->start = start; | ||
5917 | em->orig_start = orig_start; | ||
5918 | em->len = len; | ||
5919 | em->block_len = block_len; | ||
5920 | em->block_start = block_start; | ||
5921 | em->bdev = root->fs_info->fs_devices->latest_bdev; | ||
5922 | set_bit(EXTENT_FLAG_PINNED, &em->flags); | ||
5923 | if (type == BTRFS_ORDERED_PREALLOC) | ||
5924 | set_bit(EXTENT_FLAG_PREALLOC, &em->flags); | ||
5925 | |||
5926 | do { | ||
5927 | btrfs_drop_extent_cache(inode, em->start, | ||
5928 | em->start + em->len - 1, 0); | ||
5929 | write_lock(&em_tree->lock); | ||
5930 | ret = add_extent_mapping(em_tree, em); | ||
5931 | write_unlock(&em_tree->lock); | ||
5932 | } while (ret == -EEXIST); | ||
5933 | |||
5934 | if (ret) { | ||
5935 | free_extent_map(em); | ||
5936 | return ERR_PTR(ret); | ||
5937 | } | ||
5938 | |||
5939 | return em; | ||
5940 | } | ||
5941 | |||
5942 | |||
5901 | static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, | 5943 | static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, |
5902 | struct buffer_head *bh_result, int create) | 5944 | struct buffer_head *bh_result, int create) |
5903 | { | 5945 | { |
@@ -6012,6 +6054,19 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, | |||
6012 | goto must_cow; | 6054 | goto must_cow; |
6013 | 6055 | ||
6014 | if (can_nocow_odirect(trans, inode, start, len) == 1) { | 6056 | if (can_nocow_odirect(trans, inode, start, len) == 1) { |
6057 | u64 orig_start = em->start; | ||
6058 | |||
6059 | if (type == BTRFS_ORDERED_PREALLOC) { | ||
6060 | free_extent_map(em); | ||
6061 | em = create_pinned_em(inode, start, len, | ||
6062 | orig_start, | ||
6063 | block_start, len, type); | ||
6064 | if (IS_ERR(em)) { | ||
6065 | btrfs_end_transaction(trans, root); | ||
6066 | goto unlock_err; | ||
6067 | } | ||
6068 | } | ||
6069 | |||
6015 | ret = btrfs_add_ordered_extent_dio(inode, start, | 6070 | ret = btrfs_add_ordered_extent_dio(inode, start, |
6016 | block_start, len, len, type); | 6071 | block_start, len, len, type); |
6017 | btrfs_end_transaction(trans, root); | 6072 | btrfs_end_transaction(trans, root); |