[PATCH 2/2] xfs: don't skip free extents of the longest length in a whole-AG FITRIM

From: Daejun Park via B4 Relay

Date: Sun Oct 25 2026 - 21:54:23 EST


From: Daejun Park <daejun7.park@xxxxxxxxxxx>

Since commit a66d636385d6 ("xfs: fix fstrim offset calculations"), the
by-length walk of an AG in FITRIM starts with
xfs_alloc_lookup_ge(cur, 0, pagf_longest) and walks down. The lookup
lands on the longest free extent with the lowest block number. The
other free extents of that length sort after it and are never
discarded.

Start every by-length batch with xfs_alloc_lookup_le() at restart_bno.

Suggested-by: Dave Chinner <dgc@xxxxxxxxxx>
Link: https://lore.kernel.org/r/ZgiJWxVcVwag0fIP@xxxxxxxxxxxxxxxxxxx
Cc: stable@xxxxxxxxxxxxxxx
Fixes: a66d636385d6 ("xfs: fix fstrim offset calculations")
Signed-off-by: Daejun Park <daejun7.park@xxxxxxxxxxx>
---
In xfs/842, 6 of 15 free extents that held data were not discarded
without this patch, and none with it.

On v6.10 and later this needs the previous patch. Older stable trees
need a small backport, which I can send:
- 6.6.y: always use xfs_alloc_lookup_le(cur, tcur->ar_startblock,
tcur->ar_blockcount, &i), and backport 23be716b1c4f ("xfs: don't
assume perags are initialised when trimming AGs") with it.
- 5.10.y to 6.1.y: replace xfs_alloc_lookup_ge(cur, 0, agf_longest)
with xfs_alloc_lookup_le(cur, NULLAGBLOCK, agf_longest).
---
fs/xfs/xfs_discard.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c
index 8120f136af..cdf660344c 100644
--- a/fs/xfs/xfs_discard.c
+++ b/fs/xfs/xfs_discard.c
@@ -200,12 +200,12 @@ xfs_trim_gather_extents(
error = xfs_alloc_lookup_le(cur, tcur->start, 0, &i);
if (!error && !i)
error = xfs_alloc_lookup_ge(cur, tcur->start, 0, &i);
- } else if (tcur->restart_bno == NULLAGBLOCK) {
- /* first time through a by-len starts with max length */
- cur = xfs_cntbt_init_cursor(mp, tp, agbp, pag);
- error = xfs_alloc_lookup_ge(cur, 0, tcur->count, &i);
} else {
- /* nth time through a by-len starts where we left off */
+ /*
+ * A by-len walk starts at the last record of the longest
+ * length (restart_bno is NULLAGBLOCK until the first batch
+ * ends) and then where the previous batch stopped.
+ */
cur = xfs_cntbt_init_cursor(mp, tp, agbp, pag);
error = xfs_alloc_lookup_le(cur, tcur->restart_bno,
tcur->count, &i);

--
2.43.0