Skip to content

Commit 75d6536

Browse files
Brian Fosterdjwong
authored andcommitted
xfs: split indlen reservations fairly when under reserved
Certain workoads that punch holes into speculative preallocation can cause delalloc indirect reservation splits when the delalloc extent is split in two. If further splits occur, an already short-handed extent can be split into two in a manner that leaves zero indirect blocks for one of the two new extents. This occurs because the shortage is large enough that the xfs_bmap_split_indlen() algorithm completely drains the requested indlen of one of the extents before it honors the existing reservation. This ultimately results in a warning from xfs_bmap_del_extent(). This has been observed during file copies of large, sparse files using 'cp --sparse=always.' To avoid this problem, update xfs_bmap_split_indlen() to explicitly apply the reservation shortage fairly between both extents. This smooths out the overall indlen shortage and defers the situation where we end up with a delalloc extent with zero indlen reservation to extreme circumstances. Reported-by: Patrick Dung <mpatdung@gmail.com> Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
1 parent 0e339ef commit 75d6536

File tree

1 file changed

+43
-18
lines changed

1 file changed

+43
-18
lines changed

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4795,34 +4795,59 @@ xfs_bmap_split_indlen(
47954795
xfs_filblks_t len2 = *indlen2;
47964796
xfs_filblks_t nres = len1 + len2; /* new total res. */
47974797
xfs_filblks_t stolen = 0;
4798+
xfs_filblks_t resfactor;
47984799

47994800
/*
48004801
* Steal as many blocks as we can to try and satisfy the worst case
48014802
* indlen for both new extents.
48024803
*/
4803-
while (nres > ores && avail) {
4804-
nres--;
4805-
avail--;
4806-
stolen++;
4807-
}
4804+
if (ores < nres && avail)
4805+
stolen = XFS_FILBLKS_MIN(nres - ores, avail);
4806+
ores += stolen;
4807+
4808+
/* nothing else to do if we've satisfied the new reservation */
4809+
if (ores >= nres)
4810+
return stolen;
4811+
4812+
/*
4813+
* We can't meet the total required reservation for the two extents.
4814+
* Calculate the percent of the overall shortage between both extents
4815+
* and apply this percentage to each of the requested indlen values.
4816+
* This distributes the shortage fairly and reduces the chances that one
4817+
* of the two extents is left with nothing when extents are repeatedly
4818+
* split.
4819+
*/
4820+
resfactor = (ores * 100);
4821+
do_div(resfactor, nres);
4822+
len1 *= resfactor;
4823+
do_div(len1, 100);
4824+
len2 *= resfactor;
4825+
do_div(len2, 100);
4826+
ASSERT(len1 + len2 <= ores);
4827+
ASSERT(len1 < *indlen1 && len2 < *indlen2);
48084828

48094829
/*
4810-
* The only blocks available are those reserved for the original
4811-
* extent and what we can steal from the extent being removed.
4812-
* If this still isn't enough to satisfy the combined
4813-
* requirements for the two new extents, skim blocks off of each
4814-
* of the new reservations until they match what is available.
4830+
* Hand out the remainder to each extent. If one of the two reservations
4831+
* is zero, we want to make sure that one gets a block first. The loop
4832+
* below starts with len1, so hand len2 a block right off the bat if it
4833+
* is zero.
48154834
*/
4816-
while (nres > ores) {
4817-
if (len1) {
4818-
len1--;
4819-
nres--;
4835+
ores -= (len1 + len2);
4836+
ASSERT((*indlen1 - len1) + (*indlen2 - len2) >= ores);
4837+
if (ores && !len2 && *indlen2) {
4838+
len2++;
4839+
ores--;
4840+
}
4841+
while (ores) {
4842+
if (len1 < *indlen1) {
4843+
len1++;
4844+
ores--;
48204845
}
4821-
if (nres == ores)
4846+
if (!ores)
48224847
break;
4823-
if (len2) {
4824-
len2--;
4825-
nres--;
4848+
if (len2 < *indlen2) {
4849+
len2++;
4850+
ores--;
48264851
}
48274852
}
48284853

0 commit comments

Comments
 (0)