Skip to contentSkip to conversation

Btrfs: Fix the write error into prealloc file

RFC PATCH New WuBo 1 participant 1 message Sep 29, 2011 Btrfs file system

lore
Share & tools
lore

WuBo

author
#0 lore raw

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
index 8b99c79..030cd28 100644
@@ -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
index 80d6148..2a37571 100644
@@ -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
index 8491712..b872a04 100644
@@ -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
index 7b2f0c3..dec16a4 100644
@@ -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
index 15e5a1c..ffc72fb 100644
@@ -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
index b3d249d..bb16613 100644
@@ -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