82 lines
3.1 KiB
Diff
82 lines
3.1 KiB
Diff
From c63d4a0865e8e7549e1305cc67b88a355a4a9a02 Mon Sep 17 00:00:00 2001
|
|
From: Dave Chinner <dchinner@redhat.com>
|
|
Date: Thu, 1 May 2025 09:27:24 +1000
|
|
Subject: xfs: don't assume perags are initialised when trimming AGs
|
|
|
|
When running fstrim immediately after mounting a V4 filesystem,
|
|
the fstrim fails to trim all the free space in the filesystem. It
|
|
only trims the first extent in the by-size free space tree in each
|
|
AG and then returns. If a second fstrim is then run, it runs
|
|
correctly and the entire free space in the filesystem is iterated
|
|
and discarded correctly.
|
|
|
|
The problem lies in the setup of the trim cursor - it assumes that
|
|
pag->pagf_longest is valid without either reading the AGF first or
|
|
checking if xfs_perag_initialised_agf(pag) is true or not.
|
|
|
|
As a result, when a filesystem is mounted without reading the AGF
|
|
(e.g. a clean mount on a v4 filesystem) and the first operation is a
|
|
fstrim call, pag->pagf_longest is zero and so the free extent search
|
|
starts at the wrong end of the by-size btree and exits after
|
|
discarding the first record in the tree.
|
|
|
|
Fix this by deferring the initialisation of tcur->count to after
|
|
we have locked the AGF and guaranteed that the perag is properly
|
|
initialised. We trigger this on tcur->count == 0 after locking the
|
|
AGF, as this will only occur on the first call to
|
|
xfs_trim_gather_extents() for each AG. If we need to iterate,
|
|
tcur->count will be set to the length of the record we need to
|
|
restart at, so we can use this to ensure we only sample a valid
|
|
pag->pagf_longest value for the iteration.
|
|
|
|
Signed-off-by: Dave Chinner <dchinner@redhat.com>
|
|
Reviewed-by: Bill O'Donnell <bodonnel@redhat.com>
|
|
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
|
|
Fixes: 89cfa899608f ("xfs: reduce AGF hold times during fstrim operations")
|
|
Cc: <stable@vger.kernel.org> # v6.6
|
|
Signed-off-by: Carlos Maiolino <cem@kernel.org>
|
|
---
|
|
fs/xfs/xfs_discard.c | 17 ++++++++++++++++-
|
|
1 file changed, 16 insertions(+), 1 deletion(-)
|
|
|
|
--- a/fs/xfs/xfs_discard.c
|
|
+++ b/fs/xfs/xfs_discard.c
|
|
@@ -167,6 +167,14 @@ xfs_discard_extents(
|
|
return error;
|
|
}
|
|
|
|
+/*
|
|
+ * Care must be taken setting up the trim cursor as the perags may not have been
|
|
+ * initialised when the cursor is initialised. e.g. a clean mount which hasn't
|
|
+ * read in AGFs and the first operation run on the mounted fs is a trim. This
|
|
+ * can result in perag fields that aren't initialised until
|
|
+ * xfs_trim_gather_extents() calls xfs_alloc_read_agf() to lock down the AG for
|
|
+ * the free space search.
|
|
+ */
|
|
struct xfs_trim_cur {
|
|
xfs_agblock_t start;
|
|
xfs_extlen_t count;
|
|
@@ -204,6 +212,14 @@ xfs_trim_gather_extents(
|
|
if (error)
|
|
goto out_trans_cancel;
|
|
|
|
+ /*
|
|
+ * First time through tcur->count will not have been initialised as
|
|
+ * pag->pagf_longest is not guaranteed to be valid before we read
|
|
+ * the AGF buffer above.
|
|
+ */
|
|
+ if (!tcur->count)
|
|
+ tcur->count = pag->pagf_longest;
|
|
+
|
|
if (tcur->by_bno) {
|
|
/* sub-AG discard request always starts at tcur->start */
|
|
cur = xfs_bnobt_init_cursor(mp, tp, agbp, pag);
|
|
@@ -350,7 +366,6 @@ xfs_trim_perag_extents(
|
|
{
|
|
struct xfs_trim_cur tcur = {
|
|
.start = start,
|
|
- .count = pag->pagf_longest,
|
|
.end = end,
|
|
.minlen = minlen,
|
|
};
|