Btrfs: Fix the write error into prealloc file
RFC PATCH 1/1 Btrfs: Fix the write error into prealloc file
reproduce:
dd if=/dev/zero of=prealloc_test bs=4K count=1 fallocate -n -o 4K -l 1M prealloc_test
dd if=/dev/zero of=tmpfile1 bs=1M
dd if=/dev/zero of=tmpfile2 bs=4K
dd if=/dev/zero of=prealloc_test seek=1 bs=4K count=2 conv=notrunc
Although the prealloc_test file still has space, the write will be failed. Because the reserve code think the space is full. Before reserve the data space for writing, checking whether the inode has prealloc extent or not. If match the range, it's means we don't need reserve the space. I also use a extra bit EXTENT_PREALLOC to record the NO-need-reserve-space extent_state.
There is a another dangerous that if after we don't reserve the prealloc range, there is a writeback thread change the prealloc range into regular range nicely. The reserve space will wrong, what we do is waiting all the dirty pages in prealloc range to write back.
I made it for RFC because this patch will lead to performance decay also with disk fragment growth.
--- fs/btrfs/ctree.h | 6 ++- fs/btrfs/extent-tree.c | 30 ++++++++++-- fs/btrfs/extent_io.c | 17 +++++++ fs/btrfs/extent_io.h | 6 +++ fs/btrfs/file.c | 2 +- fs/btrfs/inode.c | 114 ++++++++++++++++++++++++++++++++++++++++++++--- fs/btrfs/ioctl.c | 2 +- 7 files changed, 161 insertions(+), 16 deletions(-)
fs/btrfs/ctree.h +5 -1
@@ -2234,7 +2234,8 @@ int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans, struct btrfs_pending_snapshot *pending);int btrfs_delalloc_reserve_metadata (struct inode *inode,u64 num_bytes);void btrfs_delalloc_release_metadata (struct inode *inode,u64 num_bytes); -int btrfs_delalloc_reserve_space (struct inode *inode,u64 num_bytes); +int btrfs_delalloc_reserve_space (struct inode *inode,u64 start, +u64 num_bytes);void btrfs_delalloc_release_space (struct inode *inode,u64 num_bytes);void btrfs_init_block_rsv (struct btrfs_block_rsv *rsv);struct btrfs_block_rsv *btrfs_alloc_block_rsv (struct btrfs_root *root);
@@ -2595,6 +2596,9 @@ int btrfs_prealloc_file_range_trans(struct inode *inode, u64 start, u64 num_bytes, u64 min_size, loff_t actual_len, u64 *alloc_hint);extern const struct dentry_operations btrfs_dentry_operations; +extern int btrfs_search_prealloc_file_range (struct inode *inode, +u64 start,u64 len, +u64 *need_reserve);/* ioctl.c */ long btrfs_ioctl (struct file *file,unsigned int cmd,unsigned long arg);
fs/btrfs/extent-tree.c +25 -5
@@ -4054,17 +4054,37 @@ void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes) to_free); } -int btrfs_delalloc_reserve_space (struct inode *inode,u64 num_bytes) +int btrfs_delalloc_reserve_space (struct inode *inode,u64 start,u64 num_bytes) {int ret; +u64 need_reserve = num_bytes; - ret =btrfs_check_data_free_space (inode, num_bytes); -if (ret) -return ret; +if (BTRFS_I (inode)->flags &BTRFS_INODE_PREALLOC ) { +struct extent_state *cached_state =NULL ; + +lock_extent_bits (&BTRFS_I (inode)->io_tree, start, + start + num_bytes -1 ,0 , + &cached_state,GFP_NOFS ); + + ret =btrfs_search_prealloc_file_range (inode, + start, num_bytes, &need_reserve); + +unlock_extent_cached (&BTRFS_I (inode)->io_tree, start, + start + num_bytes -1 , + &cached_state,GFP_NOFS ); +if (ret) +return ret; + } + +if (need_reserve !=0 ) { + ret =btrfs_check_data_free_space (inode, need_reserve); +if (ret) +return ret; + } ret =btrfs_delalloc_reserve_metadata (inode, num_bytes);if (ret) { -btrfs_free_reserved_data_space (inode, num_bytes); +btrfs_free_reserved_data_space (inode, need_reserve);return ret; }
fs/btrfs/extent_io.c +17 -0
@@ -953,6 +953,20 @@ static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start, cached_state, mask); } +int set_extent_prealloc (struct extent_io_tree *tree,u64 start,u64 end, +gfp_t mask) +{ +return set_extent_bit (tree, start, end,EXTENT_PREALLOC ,0 ,NULL , +NULL , mask); +} + +int clear_extent_prealloc (struct extent_io_tree *tree,u64 start,u64 end, +gfp_t mask) +{ +return clear_extent_bit (tree, start, end,EXTENT_PREALLOC ,0 ,0 , +NULL , mask); +} +/* * either insert or lock state struct between start and end use mask to tell * us if waiting is desired.
@@ -1348,6 +1362,9 @@ int extent_clear_unlock_delalloc(struct inode *inode,if (op &EXTENT_CLEAR_DELALLOC ) clear_bits |=EXTENT_DELALLOC ; +if (op &EXTENT_CLEAR_PREALLOC ) + clear_bits |=EXTENT_PREALLOC ; +clear_extent_bit (tree, start, end, clear_bits,1 ,0 ,NULL ,GFP_NOFS ); if (!(op & (EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_DIRTY | EXTENT_SET_WRITEBACK | EXTENT_END_WRITEBACK |
fs/btrfs/extent_io.h +6 -0
@@ -17,6 +17,7 @@#define EXTENT_NODATASUM (1 << 10)#define EXTENT_DO_ACCOUNTING (1 << 11)#define EXTENT_FIRST_DELALLOC (1 << 12) +#define EXTENT_PREALLOC (1 << 13)#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)#define EXTENT_CTLBITS (EXTENT_DO_ACCOUNTING | EXTENT_FIRST_DELALLOC)
@@ -42,6 +43,7 @@#define EXTENT_END_WRITEBACK 0x20#define EXTENT_SET_PRIVATE2 0x40#define EXTENT_CLEAR_ACCOUNTING 0x80 +#define EXTENT_CLEAR_PREALLOC 0x100/* * page->private values. Every page that is controlled by the extent
@@ -216,6 +218,10 @@ int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask);int set_extent_delalloc (struct extent_io_tree *tree,u64 start,u64 end,struct extent_state **cached_state,gfp_t mask); +int set_extent_prealloc (struct extent_io_tree *tree,u64 start,u64 end, +gfp_t mask); +int clear_extent_prealloc (struct extent_io_tree *tree,u64 start,u64 end, +gfp_t mask);int find_first_extent_bit (struct extent_io_tree *tree,u64 start,u64 *start_ret,u64 *end_ret,int bits); struct extent_state *find_first_extent_bit_state(struct extent_io_tree *tree,
fs/btrfs/file.c +1 -1
@@ -1195,7 +1195,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,break ; } - ret =btrfs_delalloc_reserve_space (inode, + ret =btrfs_delalloc_reserve_space (inode, pos, num_pages <<PAGE_CACHE_SHIFT );if (ret)break ;
fs/btrfs/inode.c +106 -8
155 lines not on this page · raw message
fs/btrfs/ioctl.c +1 -1
@@ -856,7 +856,7 @@ static int cluster_pages_for_defrag(struct inode *inode,return 0 ; file_end = (isize -1 ) >>PAGE_CACHE_SHIFT ; - ret =btrfs_delalloc_reserve_space (inode, + ret =btrfs_delalloc_reserve_space (inode,page_offset (pages[0 ]), num_pages <<PAGE_CACHE_SHIFT );if (ret)return ret;
Signature · 1 line
1.7.3.1