Commit f0322c7c authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: measure all contiguous previous extents for prealloc size

When we're estimating a new speculative preallocation length for an
extending write, we should walk backwards through the extent list to
determine the number of number of blocks that are physically and
logically contiguous with the write offset, and use that as an input to
the preallocation size computation.

This way, preallocation length is truly measured by the effectiveness of
the allocator in giving us contiguous allocations without being
influenced by the state of a given extent.  This fixes both the problem
where ZERO_RANGE within an EOF can reduce preallocation, and prevents
the unnecessary shrinkage of preallocation when delalloc extents are
turned into unwritten extents.

This was found as a regression in xfs/014 after changing delalloc writes
to create unwritten extents during writeback.
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarBrian Foster <bfoster@redhat.com>
parent 1edd2c05
...@@ -377,15 +377,17 @@ xfs_iomap_prealloc_size( ...@@ -377,15 +377,17 @@ xfs_iomap_prealloc_size(
loff_t count, loff_t count,
struct xfs_iext_cursor *icur) struct xfs_iext_cursor *icur)
{ {
struct xfs_iext_cursor ncur = *icur;
struct xfs_bmbt_irec prev, got;
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset); xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
struct xfs_bmbt_irec prev;
int shift = 0;
int64_t freesp; int64_t freesp;
xfs_fsblock_t qblocks; xfs_fsblock_t qblocks;
int qshift = 0;
xfs_fsblock_t alloc_blocks = 0; xfs_fsblock_t alloc_blocks = 0;
xfs_extlen_t plen;
int shift = 0;
int qshift = 0;
if (offset + count <= XFS_ISIZE(ip)) if (offset + count <= XFS_ISIZE(ip))
return 0; return 0;
...@@ -400,7 +402,7 @@ xfs_iomap_prealloc_size( ...@@ -400,7 +402,7 @@ xfs_iomap_prealloc_size(
*/ */
if ((mp->m_flags & XFS_MOUNT_ALLOCSIZE) || if ((mp->m_flags & XFS_MOUNT_ALLOCSIZE) ||
XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign) || XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign) ||
!xfs_iext_peek_prev_extent(ifp, icur, &prev) || !xfs_iext_prev_extent(ifp, &ncur, &prev) ||
prev.br_startoff + prev.br_blockcount < offset_fsb) prev.br_startoff + prev.br_blockcount < offset_fsb)
return mp->m_allocsize_blocks; return mp->m_allocsize_blocks;
...@@ -413,16 +415,28 @@ xfs_iomap_prealloc_size( ...@@ -413,16 +415,28 @@ xfs_iomap_prealloc_size(
* preallocation size. * preallocation size.
* *
* If the extent is a hole, then preallocation is essentially disabled. * If the extent is a hole, then preallocation is essentially disabled.
* Otherwise we take the size of the preceding data extent as the basis * Otherwise we take the size of the preceding data extents as the basis
* for the preallocation size. If the size of the extent is greater than * for the preallocation size. Note that we don't care if the previous
* half the maximum extent length, then use the current offset as the * extents are written or not.
* basis. This ensures that for large files the preallocation size *
* always extends to MAXEXTLEN rather than falling short due to things * If the size of the extents is greater than half the maximum extent
* like stripe unit/width alignment of real extents. * length, then use the current offset as the basis. This ensures that
* for large files the preallocation size always extends to MAXEXTLEN
* rather than falling short due to things like stripe unit/width
* alignment of real extents.
*/ */
if (prev.br_blockcount <= (MAXEXTLEN >> 1)) plen = prev.br_blockcount;
alloc_blocks = prev.br_blockcount << 1; while (xfs_iext_prev_extent(ifp, &ncur, &got)) {
else if (plen > MAXEXTLEN / 2 ||
isnullstartblock(got.br_startblock) ||
got.br_startoff + got.br_blockcount != prev.br_startoff ||
got.br_startblock + got.br_blockcount != prev.br_startblock)
break;
plen += got.br_blockcount;
prev = got;
}
alloc_blocks = plen * 2;
if (alloc_blocks > MAXEXTLEN)
alloc_blocks = XFS_B_TO_FSB(mp, offset); alloc_blocks = XFS_B_TO_FSB(mp, offset);
if (!alloc_blocks) if (!alloc_blocks)
goto check_writeio; goto check_writeio;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment