From c092c617b75d6523ca90f2c6caf51d0b7228f398 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 1 Aug 2024 10:45:53 +0800 Subject: [PATCH 1/5] xfs: only allow minlen allocations when near ENOSPC maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9VTE3 CVE: NA Reference: https://lore.kernel.org/linux-xfs/20240705162450.3481169-1-john.g.garry@oracle.com -------------------------------- When we are near ENOSPC and don't have enough free space for an args->maxlen allocation, xfs_alloc_space_available() will trim args->maxlen to equal the available space. However, this function has only checked that there is enough contiguous free space for an aligned args->minlen allocation to succeed. Hence there is no guarantee that an args->maxlen allocation will succeed, nor that the available space will allow for correct alignment of an args->maxlen allocation. Further, by trimming args->maxlen arbitrarily, it breaks an assumption made in xfs_alloc_fix_len() that if the caller wants aligned allocation, then args->maxlen will be set to an aligned value. It then skips the tail alignment and so we end up with extents that aren't aligned to extent size hint boundaries as we approach ENOSPC. To avoid this problem, don't reduce args->maxlen by some random, arbitrary amount. If args->maxlen is too large for the available space, reduce the allocation to a minlen allocation as we know we have contiguous free space available for this to succeed and always be correctly aligned. Signed-off-by: Dave Chinner Signed-off-by: John Garry Signed-off-by: Long Li --- fs/xfs/libxfs/xfs_alloc.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 95bfe89651ad..19d89bf19b48 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -2385,14 +2385,23 @@ xfs_alloc_space_available( if (available < (int)max(args->total, alloc_len)) return false; + if (flags & XFS_ALLOC_FLAG_CHECK) + return true; + /* - * Clamp maxlen to the amount of free space available for the actual - * extent allocation. + * If we can't do a maxlen allocation, then we must reduce the size of + * the allocation to match the available free space. We know how big + * the largest contiguous free space we can allocate is, so that's our + * upper bound. However, we don't exaclty know what alignment/size + * constraints have been placed on the allocation, so we can't + * arbitrarily select some new max size. Hence make this a minlen + * allocation as we know that will definitely succeed and match the + * callers alignment constraints. */ - if (available < (int)args->maxlen && !(flags & XFS_ALLOC_FLAG_CHECK)) { - args->maxlen = available; + alloc_len = args->maxlen + (args->alignment - 1) + args->minalignslop; + if (longest < alloc_len) { + args->maxlen = args->minlen; ASSERT(args->maxlen > 0); - ASSERT(args->maxlen >= args->minlen); } return true; -- Gitee From 6522694d2a3f1f463ee9335fb3c7b1f4d0e7cfbc Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 1 Aug 2024 10:45:54 +0800 Subject: [PATCH 2/5] xfs: always tail align maxlen allocations maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9VTE3 CVE: NA Reference: https://lore.kernel.org/linux-xfs/20240705162450.3481169-1-john.g.garry@oracle.com -------------------------------- When we do a large allocation, the core free space allocation code assumes that args->maxlen is aligned to args->prod/args->mod. hence if we get a maximum sized extent allocated, it does not do tail alignment of the extent. However, this assumes that nothing modifies args->maxlen between the original allocation context setup and trimming the selected free space extent to size. This assumption has recently been found to be invalid - xfs_alloc_space_available() modifies args->maxlen in low space situations - and there may be more situations we haven't yet found like this. Force aligned allocation introduces the requirement that extents are correctly tail aligned, resulting in this occasional latent alignment failure to be reclassified from an unimportant curiousity to a must-fix bug. Removing the assumption about args->maxlen allocations always being tail aligned is trivial, and should not impact anything because args->maxlen for inodes with extent size hints configured are already aligned. Hence all this change does it avoid weird corner cases that would have resulted in unaligned extent sizes by always trimming the extent down to an aligned size. Signed-off-by: Dave Chinner Reviewed-by: "Darrick J. Wong" [provisional on v1 series comment] Signed-off-by: John Garry Signed-off-by: Long Li --- fs/xfs/libxfs/xfs_alloc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 19d89bf19b48..23c0e666d2f4 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -408,20 +408,18 @@ xfs_alloc_compute_diff( * Fix up the length, based on mod and prod. * len should be k * prod + mod for some k. * If len is too small it is returned unchanged. - * If len hits maxlen it is left alone. */ -STATIC void +static void xfs_alloc_fix_len( - xfs_alloc_arg_t *args) /* allocation argument structure */ + struct xfs_alloc_arg *args) { - xfs_extlen_t k; - xfs_extlen_t rlen; + xfs_extlen_t k; + xfs_extlen_t rlen = args->len; ASSERT(args->mod < args->prod); - rlen = args->len; ASSERT(rlen >= args->minlen); ASSERT(rlen <= args->maxlen); - if (args->prod <= 1 || rlen < args->mod || rlen == args->maxlen || + if (args->prod <= 1 || rlen < args->mod || (args->mod == 0 && rlen < args->prod)) return; k = rlen % args->prod; -- Gitee From 85b9710988be633016d558719c855ebc98847da7 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 1 Aug 2024 10:45:55 +0800 Subject: [PATCH 3/5] xfs: align args->minlen for forced allocation alignment maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9VTE3 CVE: NA Reference: https://lore.kernel.org/linux-xfs/20240705162450.3481169-1-john.g.garry@oracle.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 Reviewed-by: "Darrick J. Wong" Signed-off-by: John Garry Conflicts: fs/xfs/libxfs/xfs_bmap.c [Conflicts in xfs_bmap_select_minlen()] Signed-off-by: Long Li --- fs/xfs/libxfs/xfs_bmap.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 7682dfe2f701..284cc73b8bef 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -3253,32 +3253,47 @@ xfs_bmap_longest_free_extent( return error; } -static void +static int xfs_bmap_select_minlen( struct xfs_bmalloca *ap, struct xfs_alloc_arg *args, xfs_extlen_t *blen, int notinit) { + xfs_extlen_t nlen = 0; + if (notinit || *blen < ap->minlen) { /* * Since we did a BUF_TRYLOCK above, it is possible that * there is space for this request. */ - args->minlen = ap->minlen; + nlen = ap->minlen; } else if (*blen < args->maxlen) { /* * If the best seen length is less than the request length, * use the best as the minimum. */ - args->minlen = *blen; + + nlen = *blen; } else { /* * Otherwise we've seen an extent as big as maxlen, use that * as the minimum. */ - args->minlen = args->maxlen; + nlen = args->maxlen; } + + if (args->alignment > 1) { + nlen = rounddown(nlen, args->alignment); + if (nlen < ap->minlen) { + if (xfs_inode_forcealign(ap->ip) && + (ap->datatype & XFS_ALLOC_USERDATA)) + return -ENOSPC; + nlen = ap->minlen; + } + } + args->minlen = nlen; + return 0; } STATIC int @@ -3311,8 +3326,8 @@ xfs_bmap_btalloc_nullfb( break; } - xfs_bmap_select_minlen(ap, args, blen, notinit); - return 0; + error = xfs_bmap_select_minlen(ap, args, blen, notinit); + return error; } STATIC int @@ -3349,7 +3364,9 @@ xfs_bmap_btalloc_filestreams( } - xfs_bmap_select_minlen(ap, args, blen, notinit); + error = xfs_bmap_select_minlen(ap, args, blen, notinit); + if (error) + return error; /* * Set the failure fallback case to look in the selected AG as stream -- Gitee From a50b56c949316f754b739dd2d9e2fd52119a9734 Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 1 Aug 2024 10:45:56 +0800 Subject: [PATCH 4/5] xfs: Don't revert allocated offset for forcealign maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9VTE3 CVE: NA Reference: https://lore.kernel.org/linux-xfs/20240705162450.3481169-1-john.g.garry@oracle.com -------------------------------- In xfs_bmap_process_allocated_extent(), for when we found that we could not provide the requested length completely, the mapping is moved so that we can provide as much as possible for the original request. For forcealign, this would mean ignoring alignment guaranteed, so don't do this. Reviewed-by: "Darrick J. Wong" Signed-off-by: John Garry Signed-off-by: Long Li --- fs/xfs/libxfs/xfs_bmap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 284cc73b8bef..35728779b0d6 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -3686,10 +3686,12 @@ xfs_bmap_btalloc( * very fragmented so we're unlikely to be able to satisfy the * hints anyway. */ - if (ap->length <= orig_length) - ap->offset = orig_offset; - else if (ap->offset + ap->length < orig_offset + orig_length) - ap->offset = orig_offset + orig_length - ap->length; + if (!(xfs_inode_forcealign(ap->ip) && align)) { + if (ap->length <= orig_length) + ap->offset = orig_offset; + else if (ap->offset + ap->length < orig_offset + orig_length) + ap->offset = orig_offset + orig_length - ap->length; + } xfs_bmap_btalloc_accounting(ap, &args); } else { ap->blkno = NULLFSBLOCK; -- Gitee From f2221c8fc386a9d57a9676d9e31b75ba6d78ea3e Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 1 Aug 2024 10:45:57 +0800 Subject: [PATCH 5/5] xfs: set minlen to align for forcealign hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9VTE3 CVE: NA -------------------------------- Set the minlen of extent allocation arguments to the alignment size to enforce alignment. This prevents the extent from being misaligned at the tail when we approach a no-space situation. Although xfs_bmap_select_minlen updates args->minlen alignment, args-> minlen may revert to ap->minlen in subsequent extent allocations, which could violate the tail alignment. Signed-off-by: Long Li --- fs/xfs/libxfs/xfs_bmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 35728779b0d6..bf09737cfc71 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -3510,6 +3510,7 @@ xfs_bmap_btalloc( */ if (xfs_inode_forcealign(ap->ip) && align) { args.alignment = align; + args.minlen = ap->minlen = align; if (stripe_align == 0 || stripe_align % align) stripe_align = align; } else { -- Gitee