[PATCH 3/3] Btrfs: only allocate necessary space when relocating a data block group

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Filipe Manana <fdmanana@xxxxxxxx>

When relocating a data block group we group extents from the block group
into a cluster and when the cluster reaches a certain number of extents
we do the relocation.

The first step is reserving data space and we try to reserve more space
than we need if the block group is not full, when there are gaps between
the collected extents. What happens is we attempt to reserve an amount of
space that corresponds to the different between the end offset of the last
extent and the start offset of the first extent. This can cause us to fail
with -ENOSPC even when we have enough free space for relocating all the
extents. We should skip space reservation for any gaps which always exist
for block groups that are not full. Non full block groups are the ones
which are useful to relocate, therefore a common use case.

So fix this by tracking the total number of bytes used for all the extents
in the cluster and then reserving an amount of space that matches exactly
the sum of the sizes of all the collected extents.

Signed-off-by: Filipe Manana <fdmanana@xxxxxxxx>
---
 fs/btrfs/relocation.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 11d156995446..7ec75229d86f 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -108,6 +108,7 @@ struct tree_block {
 struct file_extent_cluster {
 	u64 start;
 	u64 end;
+	u64 total_bytes;
 	u64 boundary[MAX_EXTENTS];
 	unsigned int nr;
 };
@@ -2583,14 +2584,14 @@ int prealloc_file_extent_cluster(struct inode *inode,
 	int nr = 0;
 	int ret = 0;
 	u64 prealloc_start = cluster->start - offset;
-	u64 prealloc_end = cluster->end - offset;
 	u64 cur_offset;
+	u64 allocated = 0;
 
 	BUG_ON(cluster->start != cluster->boundary[0]);
 	inode_lock(inode);
 
 	ret = btrfs_alloc_data_chunk_ondemand(BTRFS_I(inode),
-					      prealloc_end + 1 - prealloc_start);
+					      cluster->total_bytes);
 	if (ret)
 		goto out;
 
@@ -2604,21 +2605,19 @@ int prealloc_file_extent_cluster(struct inode *inode,
 
 		lock_extent(&BTRFS_I(inode)->io_tree, start, end);
 		num_bytes = end + 1 - start;
-		if (cur_offset < start)
-			btrfs_free_reserved_data_space_noquota(inode,
-						       start - cur_offset);
 		ret = btrfs_prealloc_file_range(inode, 0, start,
 						num_bytes, num_bytes,
 						end + 1, &alloc_hint);
 		cur_offset = end + 1;
+		allocated += num_bytes;
 		unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
 		if (ret)
 			break;
 		nr++;
 	}
-	if (cur_offset < prealloc_end)
+	if (allocated < cluster->total_bytes)
 		btrfs_free_reserved_data_space_noquota(inode,
-					       prealloc_end + 1 - cur_offset);
+				       cluster->total_bytes - allocated);
 out:
 	inode_unlock(inode);
 	return ret;
@@ -2809,6 +2808,7 @@ int relocate_data_extent(struct inode *inode, struct btrfs_key *extent_key,
 		if (ret)
 			return ret;
 		cluster->nr = 0;
+		cluster->total_bytes = 0;
 	}
 
 	if (!cluster->nr)
@@ -2818,12 +2818,14 @@ int relocate_data_extent(struct inode *inode, struct btrfs_key *extent_key,
 	cluster->end = extent_key->objectid + extent_key->offset - 1;
 	cluster->boundary[cluster->nr] = extent_key->objectid;
 	cluster->nr++;
+	cluster->total_bytes += extent_key->offset;
 
 	if (cluster->nr >= MAX_EXTENTS) {
 		ret = relocate_file_extent_cluster(inode, cluster);
 		if (ret)
 			return ret;
 		cluster->nr = 0;
+		cluster->total_bytes = 0;
 	}
 	return 0;
 }
-- 
2.11.0




[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux