diff mbox

Btrfs: try harder when we merge holes

Message ID 1350299031-2854-2-git-send-email-Jerry87905@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Li Dongyang Oct. 15, 2012, 11:03 a.m. UTC
We should look at path->slots[0] rather than path->slots[0]+1 while trying to
merge with the hole behind us.
Also this patch will delete the the latter one if we can merge with both front
and back, leaving one hole covers all three.

Signed-off-by: Li Dongyang <Jerry87905@gmail.com>
---
 fs/btrfs/file.c | 133 ++++++++++++++++++++++++++++++++------------------------
 1 file changed, 76 insertions(+), 57 deletions(-)
diff mbox

Patch

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 9ab1bed..d41805a 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1663,89 +1663,108 @@  static int btrfs_file_mmap(struct file	*filp, struct vm_area_struct *vma)
 	return 0;
 }
 
-static int hole_mergeable(struct inode *inode, struct extent_buffer *leaf,
-			  int slot, u64 start, u64 end)
-{
-	struct btrfs_file_extent_item *fi;
-	struct btrfs_key key;
-
-	if (slot < 0 || slot >= btrfs_header_nritems(leaf))
-		return 0;
-
-	btrfs_item_key_to_cpu(leaf, &key, slot);
-	if (key.objectid != btrfs_ino(inode) ||
-	    key.type != BTRFS_EXTENT_DATA_KEY)
-		return 0;
-
-	fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
-
-	if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG)
-		return 0;
-
-	if (btrfs_file_extent_disk_bytenr(leaf, fi))
-		return 0;
-
-	if (key.offset == end)
-		return 1;
-	if (key.offset + btrfs_file_extent_num_bytes(leaf, fi) == start)
-		return 1;
-	return 0;
-}
-
-static int fill_holes(struct btrfs_trans_handle *trans, struct inode *inode,
-		      struct btrfs_path *path, u64 offset, u64 end)
+static int merge_holes(struct btrfs_trans_handle *trans, struct inode *inode,
+		       struct btrfs_path *path, u64 offset, u64 end)
 {
 	struct btrfs_root *root = BTRFS_I(inode)->root;
 	struct extent_buffer *leaf;
-	struct btrfs_file_extent_item *fi;
-	struct extent_map *hole_em;
-	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
+	struct btrfs_file_extent_item *back_fi, *front_fi;
 	struct btrfs_key key;
 	int ret;
+	bool front_mergeable = false;
+	bool back_mergeable = false;
 
 	key.objectid = btrfs_ino(inode);
 	key.type = BTRFS_EXTENT_DATA_KEY;
 	key.offset = offset;
 
-
 	ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
 	if (ret < 0)
 		return ret;
 	BUG_ON(!ret);
 
 	leaf = path->nodes[0];
-	if (hole_mergeable(inode, leaf, path->slots[0]-1, offset, end)) {
-		u64 num_bytes;
+	if (path->slots[0] != 0) {
+		btrfs_item_key_to_cpu(leaf, &key, path->slots[0]-1);
+		if (key.objectid == btrfs_ino(inode) &&
+		    key.type == BTRFS_EXTENT_DATA_KEY) {
+			front_fi = btrfs_item_ptr(leaf, path->slots[0]-1,
+						struct btrfs_file_extent_item);
+			if (btrfs_file_extent_type(leaf, front_fi) ==
+			    BTRFS_FILE_EXTENT_REG &&
+			    btrfs_file_extent_disk_bytenr(leaf,
+							  front_fi) == 0 &&
+			    key.offset +
+			    btrfs_file_extent_num_bytes(leaf, front_fi) ==
+			    offset) {
+				front_mergeable = true;
+			}
+		}
+	}
 
-		path->slots[0]--;
-		fi = btrfs_item_ptr(leaf, path->slots[0],
-				    struct btrfs_file_extent_item);
-		num_bytes = btrfs_file_extent_num_bytes(leaf, fi) +
-			end - offset;
-		btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
-		btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
-		btrfs_set_file_extent_offset(leaf, fi, 0);
-		btrfs_mark_buffer_dirty(leaf);
-		goto out;
+	btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+	if (key.objectid == btrfs_ino(inode) &&
+	    key.type == BTRFS_EXTENT_DATA_KEY) {
+		back_fi = btrfs_item_ptr(leaf, path->slots[0],
+					 struct btrfs_file_extent_item);
+		if (btrfs_file_extent_type(leaf, back_fi) ==
+		    BTRFS_FILE_EXTENT_REG &&
+		    btrfs_file_extent_disk_bytenr(leaf, back_fi) == 0 &&
+		    key.offset == end) {
+			back_mergeable = true;
+		}
 	}
 
-	if (hole_mergeable(inode, leaf, path->slots[0]+1, offset, end)) {
+	if (front_mergeable) {
+		u64 num_bytes = 0;
+
+		if (back_mergeable) {
+			num_bytes = btrfs_file_extent_num_bytes(leaf, back_fi);
+
+			ret = btrfs_del_item(trans, root, path);
+			if (ret)
+				goto out;
+		}
+
+		num_bytes += btrfs_file_extent_num_bytes(leaf, front_fi) +
+			end - offset;
+		btrfs_set_file_extent_num_bytes(leaf, front_fi, num_bytes);
+		btrfs_set_file_extent_ram_bytes(leaf, front_fi, num_bytes);
+		btrfs_set_file_extent_offset(leaf, front_fi, 0);
+		btrfs_mark_buffer_dirty(leaf);
+		ret = 0;
+	} else if (back_mergeable) {
 		u64 num_bytes;
 
-		path->slots[0]++;
 		key.offset = offset;
 		btrfs_set_item_key_safe(trans, root, path, &key);
-		fi = btrfs_item_ptr(leaf, path->slots[0],
-				    struct btrfs_file_extent_item);
-		num_bytes = btrfs_file_extent_num_bytes(leaf, fi) + end -
-			offset;
-		btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
-		btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
-		btrfs_set_file_extent_offset(leaf, fi, 0);
+		num_bytes = btrfs_file_extent_num_bytes(leaf, back_fi) +
+			end - offset;
+		btrfs_set_file_extent_num_bytes(leaf, back_fi, num_bytes);
+		btrfs_set_file_extent_ram_bytes(leaf, back_fi, num_bytes);
+		btrfs_set_file_extent_offset(leaf, back_fi, 0);
 		btrfs_mark_buffer_dirty(leaf);
-		goto out;
+		ret = 0;
 	}
+
+out:
 	btrfs_release_path(path);
+	return ret;
+}
+
+static int fill_holes(struct btrfs_trans_handle *trans, struct inode *inode,
+		      struct btrfs_path *path, u64 offset, u64 end)
+{
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+	struct extent_map *hole_em;
+	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
+	int ret;
+
+	ret = merge_holes(trans, inode, path, offset, end);
+	if (ret < 0)
+		return ret;
+	if (!ret)
+		goto out;
 
 	ret = btrfs_insert_file_extent(trans, root, btrfs_ino(inode), offset,
 				       0, 0, end - offset, 0, end - offset,