Skip to content

Commit 9a0ab4f

Browse files
Darrick J. Wonggregkh
authored andcommitted
xfs: restrict when we try to align cow fork delalloc to cowextsz hints
commit 288e1f6 upstream. xfs/205 produces the following failure when always_cow is enabled: # --- a/tests/xfs/205.out 2024-02-28 16:20:24.437887970 -0800 # +++ b/tests/xfs/205.out.bad 2024-06-03 21:13:40.584000000 -0700 # @@ -1,4 +1,5 @@ # QA output created by 205 # *** one file # + !!! disk full (expected) # *** one file, a few bytes at a time # *** done This is the result of overly aggressive attempts to align cow fork delalloc reservations to the CoW extent size hint. Looking at the trace data, we're trying to append a single fsblock to the "fred" file. Trying to create a speculative post-eof reservation fails because there's not enough space. We then set @prealloc_blocks to zero and try again, but the cowextsz alignment code triggers, which expands our request for a 1-fsblock reservation into a 39-block reservation. There's not enough space for that, so the whole write fails with ENOSPC even though there's sufficient space in the filesystem to allocate the single block that we need to land the write. There are two things wrong here -- first, we shouldn't be attempting speculative preallocations beyond what was requested when we're low on space. Second, if we've already computed a posteof preallocation, we shouldn't bother trying to align that to the cowextsize hint. Fix both of these problems by adding a flag that only enables the expansion of the delalloc reservation to the cowextsize if we're doing a non-extending write, and only if we're not doing an ENOSPC retry. This requires us to move the ENOSPC retry logic to xfs_bmapi_reserve_delalloc. I probably should have caught this six years ago when 6ca3072 was being reviewed, but oh well. Update the comments to reflect what the code does now. Fixes: 6ca3072 ("xfs: bmap code cleanup") Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Chandan Babu R <[email protected]> Signed-off-by: Catherine Hoang <[email protected]> Acked-by: Darrick J. Wong <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 3eeac33 commit 9a0ab4f

File tree

2 files changed

+39
-26
lines changed

2 files changed

+39
-26
lines changed

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3974,20 +3974,32 @@ xfs_bmapi_reserve_delalloc(
39743974
xfs_extlen_t alen;
39753975
xfs_extlen_t indlen;
39763976
int error;
3977-
xfs_fileoff_t aoff = off;
3977+
xfs_fileoff_t aoff;
3978+
bool use_cowextszhint =
3979+
whichfork == XFS_COW_FORK && !prealloc;
39783980

3981+
retry:
39793982
/*
39803983
* Cap the alloc length. Keep track of prealloc so we know whether to
39813984
* tag the inode before we return.
39823985
*/
3986+
aoff = off;
39833987
alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN);
39843988
if (!eof)
39853989
alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
39863990
if (prealloc && alen >= len)
39873991
prealloc = alen - len;
39883992

3989-
/* Figure out the extent size, adjust alen */
3990-
if (whichfork == XFS_COW_FORK) {
3993+
/*
3994+
* If we're targetting the COW fork but aren't creating a speculative
3995+
* posteof preallocation, try to expand the reservation to align with
3996+
* the COW extent size hint if there's sufficient free space.
3997+
*
3998+
* Unlike the data fork, the CoW cancellation functions will free all
3999+
* the reservations at inactivation, so we don't require that every
4000+
* delalloc reservation have a dirty pagecache.
4001+
*/
4002+
if (use_cowextszhint) {
39914003
struct xfs_bmbt_irec prev;
39924004
xfs_extlen_t extsz = xfs_get_cowextsz_hint(ip);
39934005

@@ -4006,7 +4018,7 @@ xfs_bmapi_reserve_delalloc(
40064018
*/
40074019
error = xfs_quota_reserve_blkres(ip, alen);
40084020
if (error)
4009-
return error;
4021+
goto out;
40104022

40114023
/*
40124024
* Split changing sb for alen and indlen since they could be coming
@@ -4051,6 +4063,17 @@ xfs_bmapi_reserve_delalloc(
40514063
out_unreserve_quota:
40524064
if (XFS_IS_QUOTA_ON(mp))
40534065
xfs_quota_unreserve_blkres(ip, alen);
4066+
out:
4067+
if (error == -ENOSPC || error == -EDQUOT) {
4068+
trace_xfs_delalloc_enospc(ip, off, len);
4069+
4070+
if (prealloc || use_cowextszhint) {
4071+
/* retry without any preallocation */
4072+
use_cowextszhint = false;
4073+
prealloc = 0;
4074+
goto retry;
4075+
}
4076+
}
40544077
return error;
40554078
}
40564079

fs/xfs/xfs_iomap.c

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,33 +1127,23 @@ xfs_buffered_write_iomap_begin(
11271127
}
11281128
}
11291129

1130-
retry:
1131-
error = xfs_bmapi_reserve_delalloc(ip, allocfork, offset_fsb,
1132-
end_fsb - offset_fsb, prealloc_blocks,
1133-
allocfork == XFS_DATA_FORK ? &imap : &cmap,
1134-
allocfork == XFS_DATA_FORK ? &icur : &ccur,
1135-
allocfork == XFS_DATA_FORK ? eof : cow_eof);
1136-
switch (error) {
1137-
case 0:
1138-
break;
1139-
case -ENOSPC:
1140-
case -EDQUOT:
1141-
/* retry without any preallocation */
1142-
trace_xfs_delalloc_enospc(ip, offset, count);
1143-
if (prealloc_blocks) {
1144-
prealloc_blocks = 0;
1145-
goto retry;
1146-
}
1147-
fallthrough;
1148-
default:
1149-
goto out_unlock;
1150-
}
1151-
11521130
if (allocfork == XFS_COW_FORK) {
1131+
error = xfs_bmapi_reserve_delalloc(ip, allocfork, offset_fsb,
1132+
end_fsb - offset_fsb, prealloc_blocks, &cmap,
1133+
&ccur, cow_eof);
1134+
if (error)
1135+
goto out_unlock;
1136+
11531137
trace_xfs_iomap_alloc(ip, offset, count, allocfork, &cmap);
11541138
goto found_cow;
11551139
}
11561140

1141+
error = xfs_bmapi_reserve_delalloc(ip, allocfork, offset_fsb,
1142+
end_fsb - offset_fsb, prealloc_blocks, &imap, &icur,
1143+
eof);
1144+
if (error)
1145+
goto out_unlock;
1146+
11571147
/*
11581148
* Flag newly allocated delalloc blocks with IOMAP_F_NEW so we punch
11591149
* them out if the write happens to fail.

0 commit comments

Comments
 (0)