From: Dave Chinner <dchin...@redhat.com>

If args->minlen is not aligned to the constraints of forced
alignment, we may do minlen allocations that are not aligned when we
approach ENOSPC. Avoid this by always aligning args->minlen
appropriately. If alignment of minlen results in a value smaller
than the alignment constraint, fail the allocation immediately.

Signed-off-by: Dave Chinner <dchin...@redhat.com>
Signed-off-by: John Garry <john.g.ga...@oracle.com>
---
 fs/xfs/libxfs/xfs_bmap.c | 45 +++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 9131ba8113a6..c9cf138e13c4 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -3278,33 +3278,48 @@ xfs_bmap_longest_free_extent(
        return 0;
 }
 
-static xfs_extlen_t
+static int
 xfs_bmap_select_minlen(
        struct xfs_bmalloca     *ap,
        struct xfs_alloc_arg    *args,
        xfs_extlen_t            blen)
 {
-
        /* Adjust best length for extent start alignment. */
        if (blen > args->alignment)
                blen -= args->alignment;
 
        /*
         * Since we used XFS_ALLOC_FLAG_TRYLOCK in _longest_free_extent(), it is
-        * possible that there is enough contiguous free space for this request.
+        * possible that there is enough contiguous free space for this request
+        * even if best length is less that the minimum length we need.
+        *
+        * If the best length won't satisfy the maximum length we requested,
+        * then use it as the minimum length so we get as large an allocation
+        * as possible.
         */
        if (blen < ap->minlen)
-               return ap->minlen;
+               blen = ap->minlen;
+       else if (blen > args->maxlen)
+               blen = args->maxlen;
 
        /*
-        * If the best seen length is less than the request length,
-        * use the best as the minimum, otherwise we've got the maxlen we
-        * were asked for.
+        * If we have alignment constraints, round the minlen down to match the
+        * constraint so that alignment will be attempted. This may reduce the
+        * allocation to smaller than was requested, so clamp the minimum to
+        * ap->minlen to allow unaligned allocation to succeed. If we are forced
+        * to align the allocation, return ENOSPC at this point because we don't
+        * have enough contiguous free space to guarantee aligned allocation.
         */
-       if (blen < args->maxlen)
-               return blen;
-       return args->maxlen;
-
+       if (args->alignment > 1) {
+               blen = rounddown(blen, args->alignment);
+               if (blen < ap->minlen) {
+                       if (args->datatype & XFS_ALLOC_FORCEALIGN)
+                               return -ENOSPC;
+                       blen = ap->minlen;
+               }
+       }
+       args->minlen = blen;
+       return 0;
 }
 
 static int
@@ -3340,8 +3355,7 @@ xfs_bmap_btalloc_select_lengths(
        if (pag)
                xfs_perag_rele(pag);
 
-       args->minlen = xfs_bmap_select_minlen(ap, args, blen);
-       return error;
+       return xfs_bmap_select_minlen(ap, args, blen);
 }
 
 /* Update all inode and quota accounting for the allocation we just did. */
@@ -3661,7 +3675,10 @@ xfs_bmap_btalloc_filestreams(
                goto out_low_space;
        }
 
-       args->minlen = xfs_bmap_select_minlen(ap, args, blen);
+       error = xfs_bmap_select_minlen(ap, args, blen);
+       if (error)
+               goto out_low_space;
+
        if (ap->aeof && ap->offset)
                error = xfs_bmap_btalloc_at_eof(ap, args);
 
-- 
2.31.1

Reply via email to