* [PATCH 1/8] xfs: don't replace the wrong part of the cow fork
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
@ 2026-07-10 5:21 ` Darrick J. Wong
2026-07-10 5:37 ` Christoph Hellwig
2026-07-10 5:22 ` [PATCH 2/8] xfs: make cow repair somewhat flaky when debugging knob enabled Darrick J. Wong
` (6 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:21 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: stable, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
LOLLM points out that xfs_iext_lookup_extent can return a @got where
got->br_startoff < startoff. In this case, xrep_cow_replace_range
replaces the entire mapping instead of just the part that had been
marked bad in the bitmap, but advances the bitmap cursor in
xrep_cow_replace by the amount replaced. As a result, we fail to
replace the end of the bad range, and replace part of the good range.
Fix this by rewriting the replace method to handle replacing the middle
of a cow fork mapping. This we do by returning both the current mapping
as @got, and the subset of the mapping that we want to replace as @rep,
using @rep to store the results of the new allocation, and comparing
@rep to @got to figure out the exact transformations needed.
Cc: <stable@vger.kernel.org> # v6.8
Fixes: dbbdbd0086320a ("xfs: repair problems in CoW forks")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
---
fs/xfs/scrub/trace.h | 28 ++++--
fs/xfs/scrub/cow_repair.c | 203 +++++++++++++++++++++++++++++----------------
2 files changed, 148 insertions(+), 83 deletions(-)
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 1b7d9e07a27d3d..d48a6db5b5f308 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -2672,9 +2672,9 @@ TRACE_EVENT(xrep_cow_mark_file_range,
);
TRACE_EVENT(xrep_cow_replace_mapping,
- TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec,
- xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount),
- TP_ARGS(ip, irec, new_startblock, new_blockcount),
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
+ const struct xfs_bmbt_irec *rep),
+ TP_ARGS(ip, got, rep),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
@@ -2682,28 +2682,34 @@ TRACE_EVENT(xrep_cow_replace_mapping,
__field(xfs_fileoff_t, startoff)
__field(xfs_filblks_t, blockcount)
__field(xfs_exntst_t, state)
+ __field(xfs_fileoff_t, new_startoff)
__field(xfs_fsblock_t, new_startblock)
__field(xfs_extlen_t, new_blockcount)
+ __field(xfs_exntst_t, new_state)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = I_INO(ip);
- __entry->startoff = irec->br_startoff;
- __entry->startblock = irec->br_startblock;
- __entry->blockcount = irec->br_blockcount;
- __entry->state = irec->br_state;
- __entry->new_startblock = new_startblock;
- __entry->new_blockcount = new_blockcount;
+ __entry->startoff = got->br_startoff;
+ __entry->startblock = got->br_startblock;
+ __entry->blockcount = got->br_blockcount;
+ __entry->state = got->br_state;
+ __entry->new_startoff = rep->br_startoff;
+ __entry->new_startblock = rep->br_startblock;
+ __entry->new_blockcount = rep->br_blockcount;
+ __entry->new_state = rep->br_state;
),
- TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x",
+ TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->startoff,
__entry->startblock,
__entry->blockcount,
__entry->state,
+ __entry->new_startoff,
__entry->new_startblock,
- __entry->new_blockcount)
+ __entry->new_blockcount,
+ __entry->new_state)
);
TRACE_EVENT(xrep_cow_free_staging,
diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c
index 0075b6d5a1b5ff..ca3405a26b641c 100644
--- a/fs/xfs/scrub/cow_repair.c
+++ b/fs/xfs/scrub/cow_repair.c
@@ -80,12 +80,6 @@ struct xrep_cow {
unsigned int next_bno;
};
-/* CoW staging extent. */
-struct xrep_cow_extent {
- xfs_fsblock_t fsbno;
- xfs_extlen_t len;
-};
-
/*
* Mark the part of the file range that corresponds to the given physical
* space. Caller must ensure that the physical range is within xc->irec.
@@ -401,22 +395,21 @@ xrep_cow_find_bad_rt(
STATIC int
xrep_cow_alloc(
struct xfs_scrub *sc,
- xfs_extlen_t maxlen,
- struct xrep_cow_extent *repl)
+ struct xfs_bmbt_irec *del)
{
struct xfs_alloc_arg args = {
.tp = sc->tp,
.mp = sc->mp,
.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE,
.minlen = 1,
- .maxlen = maxlen,
+ .maxlen = del->br_blockcount,
.prod = 1,
.resv = XFS_AG_RESV_NONE,
.datatype = XFS_ALLOC_USERDATA,
};
int error;
- error = xfs_trans_reserve_more(sc->tp, maxlen, 0);
+ error = xfs_trans_reserve_more(sc->tp, del->br_blockcount, 0);
if (error)
return error;
@@ -428,8 +421,8 @@ xrep_cow_alloc(
xfs_refcount_alloc_cow_extent(sc->tp, false, args.fsbno, args.len);
- repl->fsbno = args.fsbno;
- repl->len = args.len;
+ del->br_startblock = args.fsbno;
+ del->br_blockcount = args.len;
return 0;
}
@@ -440,10 +433,12 @@ xrep_cow_alloc(
STATIC int
xrep_cow_alloc_rt(
struct xfs_scrub *sc,
- xfs_extlen_t maxlen,
- struct xrep_cow_extent *repl)
+ struct xfs_bmbt_irec *del)
{
- xfs_rtxlen_t maxrtx = xfs_rtb_to_rtx(sc->mp, maxlen);
+ xfs_fsblock_t fsbno;
+ xfs_rtxlen_t maxrtx =
+ min(U32_MAX, xfs_blen_to_rtbxlen(sc->mp, del->br_blockcount));
+ xfs_extlen_t len;
int error;
error = xfs_trans_reserve_more(sc->tp, 0, maxrtx);
@@ -451,11 +446,14 @@ xrep_cow_alloc_rt(
return error;
error = xfs_rtallocate_rtgs(sc->tp, NULLRTBLOCK, 1, maxrtx, 1, false,
- false, &repl->fsbno, &repl->len);
+ false, &fsbno, &len);
if (error)
return error;
- xfs_refcount_alloc_cow_extent(sc->tp, true, repl->fsbno, repl->len);
+ xfs_refcount_alloc_cow_extent(sc->tp, true, fsbno, len);
+
+ del->br_startblock = fsbno;
+ del->br_blockcount = len;
return 0;
}
@@ -469,19 +467,19 @@ static inline int
xrep_cow_find_mapping(
struct xrep_cow *xc,
struct xfs_iext_cursor *icur,
- xfs_fileoff_t startoff,
- struct xfs_bmbt_irec *got)
+ xfs_fileoff_t badoff,
+ xfs_extlen_t badlen,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
{
struct xfs_inode *ip = xc->sc->ip;
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
- if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got))
+ if (!xfs_iext_lookup_extent(ip, ifp, badoff, icur, got))
goto bad;
+ memcpy(rep, got, sizeof(*rep));
- if (got->br_startoff > startoff)
- goto bad;
-
- if (got->br_blockcount == 0)
+ if (got->br_startoff > badoff)
goto bad;
if (isnullstartblock(got->br_startblock))
@@ -490,6 +488,24 @@ xrep_cow_find_mapping(
if (xfs_bmap_is_written_extent(got))
goto bad;
+ if (got->br_startoff < badoff) {
+ const int64_t delta = badoff - got->br_startoff;
+
+ rep->br_blockcount -= delta;
+ rep->br_startoff += delta;
+ rep->br_startblock += delta;
+ }
+
+ if (got->br_startoff + got->br_blockcount > badoff + badlen) {
+ const int64_t delta = (got->br_startoff + got->br_blockcount) -
+ (badoff + badlen);
+
+ rep->br_blockcount -= delta;
+ }
+
+ if (got->br_blockcount == 0)
+ goto bad;
+
return 0;
bad:
ASSERT(0);
@@ -500,46 +516,92 @@ xrep_cow_find_mapping(
#define REPLACE_RIGHT_SIDE (1U << 1)
/*
- * Given a CoW fork mapping @got and a replacement mapping @repl, remap the
- * beginning of @got with the space described by @rep.
+ * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
+ * described by @rep into the cow fork, pushing aside @got as necessary. @icur
+ * must point to iext tree leaf containing @got.
*/
static inline void
xrep_cow_replace_mapping(
- struct xfs_inode *ip,
- struct xfs_iext_cursor *icur,
- const struct xfs_bmbt_irec *got,
- const struct xrep_cow_extent *repl)
+ struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
{
- struct xfs_bmbt_irec new = *got; /* struct copy */
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
+ xfs_fileoff_t rep_endoff =
+ rep->br_startoff + rep->br_blockcount;
+ xfs_fileoff_t got_endoff =
+ got->br_startoff + got->br_blockcount;
+ uint32_t state = BMAP_COWFORK;
- ASSERT(repl->len > 0);
+ ASSERT(rep->br_blockcount > 0);
ASSERT(!isnullstartblock(got->br_startblock));
+ ASSERT(got->br_startoff <= rep->br_startoff);
+ ASSERT(got_endoff >= rep_endoff);
- trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len);
+ trace_xrep_cow_replace_mapping(ip, got, rep);
- if (got->br_blockcount == repl->len) {
+ if (got->br_startoff == rep->br_startoff)
+ state |= BMAP_LEFT_FILLING;
+ if (got_endoff == rep_endoff)
+ state |= BMAP_RIGHT_FILLING;
+
+ switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+ case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
+ /*
+ * Replacement matches the whole mapping, update the record.
+ */
+ xfs_iext_update_extent(ip, state, icur, rep);
+ break;
+ case BMAP_LEFT_FILLING:
+ /*
+ * Replace the first part of the mapping: Update the cursor
+ * position with the new mapping, then add a record with the
+ * tail of the old mapping.
+ */
+ got->br_startoff = rep_endoff;
+ got->br_blockcount -= rep->br_blockcount;
+ got->br_startblock += rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, rep);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, got, state);
+ break;
+ case BMAP_RIGHT_FILLING:
/*
- * The new extent is a complete replacement for the existing
- * extent. Update the COW fork record.
+ * Replacing the last part of the mapping. Shorten the current
+ * mapping then add a record with the new mapping.
*/
- new.br_startblock = repl->fsbno;
- xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
- return;
+ got->br_blockcount -= rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ break;
+ case 0:
+ /*
+ * Replacing the middle of the extent. Shorten the current
+ * mapping, add a new record with the new mapping, and add a
+ * second new record with the tail of the old mapping.
+ */
+ got->br_blockcount = rep->br_startoff - got->br_startoff;
+
+ struct xfs_bmbt_irec new = {
+ .br_startoff = rep_endoff,
+ .br_blockcount = got_endoff - rep_endoff,
+ .br_state = got->br_state,
+ .br_startblock = got->br_startblock +
+ rep->br_blockcount +
+ got->br_blockcount,
+ };
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, &new, state);
+ break;
}
-
- /*
- * The new extent can replace the beginning of the COW fork record.
- * Move the left side of @got upwards, then insert the new record.
- */
- new.br_startoff += repl->len;
- new.br_startblock += repl->len;
- new.br_blockcount -= repl->len;
- xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
-
- new.br_startoff = got->br_startoff;
- new.br_startblock = repl->fsbno;
- new.br_blockcount = repl->len;
- xfs_iext_insert(ip, icur, &new, BMAP_COWFORK);
}
/*
@@ -553,33 +615,30 @@ xrep_cow_replace_range(
xfs_extlen_t *blockcount)
{
struct xfs_iext_cursor icur;
- struct xrep_cow_extent repl;
- struct xfs_bmbt_irec got;
+ struct xfs_bmbt_irec got, rep;
struct xfs_scrub *sc = xc->sc;
- xfs_fileoff_t nextoff;
- xfs_extlen_t alloc_len;
+ xfs_fsblock_t old_fsbno;
int error;
/*
- * Put the existing CoW fork mapping in @got. If @got ends before
- * @rep, truncate @rep so we only replace one extent mapping at a time.
+ * Put the existing CoW fork mapping in @got, and put in @rep the
+ * contents of @got trimmed to @startoff/@blockcount. We only want
+ * to replace the bad region, and only one mapping at a time.
*/
- error = xrep_cow_find_mapping(xc, &icur, startoff, &got);
+ error = xrep_cow_find_mapping(xc, &icur, startoff, *blockcount, &got,
+ &rep);
if (error)
return error;
- nextoff = min(startoff + *blockcount,
- got.br_startoff + got.br_blockcount);
+ old_fsbno = rep.br_startblock;
/*
* Allocate a replacement extent. If we don't fill all the blocks,
* shorten the quantity that will be deleted in this step.
*/
- alloc_len = min_t(xfs_fileoff_t, XFS_MAX_BMBT_EXTLEN,
- nextoff - startoff);
if (XFS_IS_REALTIME_INODE(sc->ip))
- error = xrep_cow_alloc_rt(sc, alloc_len, &repl);
+ error = xrep_cow_alloc_rt(sc, &rep);
else
- error = xrep_cow_alloc(sc, alloc_len, &repl);
+ error = xrep_cow_alloc(sc, &rep);
if (error)
return error;
@@ -587,7 +646,7 @@ xrep_cow_replace_range(
* Replace the old mapping with the new one, and commit the metadata
* changes made so far.
*/
- xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl);
+ xrep_cow_replace_mapping(sc->ip, &icur, &got, &rep);
xfs_inode_set_cowblocks_tag(sc->ip);
error = xfs_defer_finish(&sc->tp);
@@ -596,15 +655,15 @@ xrep_cow_replace_range(
/* Note the old CoW staging extents; we'll reap them all later. */
if (XFS_IS_REALTIME_INODE(sc->ip))
- error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks,
- got.br_startblock, repl.len);
+ error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, old_fsbno,
+ rep.br_blockcount);
else
- error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks,
- got.br_startblock, repl.len);
+ error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, old_fsbno,
+ rep.br_blockcount);
if (error)
return error;
- *blockcount = repl.len;
+ *blockcount = rep.br_blockcount;
return 0;
}
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 1/8] xfs: don't replace the wrong part of the cow fork
2026-07-10 5:21 ` [PATCH 1/8] xfs: don't replace the wrong part of the cow fork Darrick J. Wong
@ 2026-07-10 5:37 ` Christoph Hellwig
0 siblings, 0 replies; 13+ messages in thread
From: Christoph Hellwig @ 2026-07-10 5:37 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: cem, hch, stable, linux-xfs
On Thu, Jul 09, 2026 at 10:21:56PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> LOLLM points out that xfs_iext_lookup_extent can return a @got where
> got->br_startoff < startoff. In this case, xrep_cow_replace_range
> replaces the entire mapping instead of just the part that had been
> marked bad in the bitmap, but advances the bitmap cursor in
> xrep_cow_replace by the amount replaced. As a result, we fail to
> replace the end of the bad range, and replace part of the good range.
>
> Fix this by rewriting the replace method to handle replacing the middle
> of a cow fork mapping. This we do by returning both the current mapping
> as @got, and the subset of the mapping that we want to replace as @rep,
> using @rep to store the results of the new allocation, and comparing
> @rep to @got to figure out the exact transformations needed.
>
> Cc: <stable@vger.kernel.org> # v6.8
> Fixes: dbbdbd0086320a ("xfs: repair problems in CoW forks")
> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> Assisted-by: LOLLM # finding obvious bugs
> ---
> fs/xfs/scrub/trace.h | 28 ++++--
> fs/xfs/scrub/cow_repair.c | 203 +++++++++++++++++++++++++++++----------------
> 2 files changed, 148 insertions(+), 83 deletions(-)
>
>
> diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
> index 1b7d9e07a27d3d..d48a6db5b5f308 100644
> --- a/fs/xfs/scrub/trace.h
> +++ b/fs/xfs/scrub/trace.h
> @@ -2672,9 +2672,9 @@ TRACE_EVENT(xrep_cow_mark_file_range,
> );
>
> TRACE_EVENT(xrep_cow_replace_mapping,
> - TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec,
> - xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount),
> - TP_ARGS(ip, irec, new_startblock, new_blockcount),
> + TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
> + const struct xfs_bmbt_irec *rep),
> + TP_ARGS(ip, got, rep),
> TP_STRUCT__entry(
> __field(dev_t, dev)
> __field(xfs_ino_t, ino)
> @@ -2682,28 +2682,34 @@ TRACE_EVENT(xrep_cow_replace_mapping,
> __field(xfs_fileoff_t, startoff)
> __field(xfs_filblks_t, blockcount)
> __field(xfs_exntst_t, state)
> + __field(xfs_fileoff_t, new_startoff)
> __field(xfs_fsblock_t, new_startblock)
> __field(xfs_extlen_t, new_blockcount)
> + __field(xfs_exntst_t, new_state)
> ),
> TP_fast_assign(
> __entry->dev = ip->i_mount->m_super->s_dev;
> __entry->ino = I_INO(ip);
> - __entry->startoff = irec->br_startoff;
> - __entry->startblock = irec->br_startblock;
> - __entry->blockcount = irec->br_blockcount;
> - __entry->state = irec->br_state;
> - __entry->new_startblock = new_startblock;
> - __entry->new_blockcount = new_blockcount;
> + __entry->startoff = got->br_startoff;
> + __entry->startblock = got->br_startblock;
> + __entry->blockcount = got->br_blockcount;
> + __entry->state = got->br_state;
> + __entry->new_startoff = rep->br_startoff;
> + __entry->new_startblock = rep->br_startblock;
> + __entry->new_blockcount = rep->br_blockcount;
> + __entry->new_state = rep->br_state;
> ),
> - TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x",
> + TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
> MAJOR(__entry->dev), MINOR(__entry->dev),
> __entry->ino,
> __entry->startoff,
> __entry->startblock,
> __entry->blockcount,
> __entry->state,
> + __entry->new_startoff,
> __entry->new_startblock,
> - __entry->new_blockcount)
> + __entry->new_blockcount,
> + __entry->new_state)
> );
>
> TRACE_EVENT(xrep_cow_free_staging,
> diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c
> index 0075b6d5a1b5ff..ca3405a26b641c 100644
> --- a/fs/xfs/scrub/cow_repair.c
> +++ b/fs/xfs/scrub/cow_repair.c
> @@ -80,12 +80,6 @@ struct xrep_cow {
> unsigned int next_bno;
> };
>
> -/* CoW staging extent. */
> -struct xrep_cow_extent {
> - xfs_fsblock_t fsbno;
> - xfs_extlen_t len;
> -};
> -
> /*
> * Mark the part of the file range that corresponds to the given physical
> * space. Caller must ensure that the physical range is within xc->irec.
> @@ -401,22 +395,21 @@ xrep_cow_find_bad_rt(
> STATIC int
> xrep_cow_alloc(
> struct xfs_scrub *sc,
> - xfs_extlen_t maxlen,
> - struct xrep_cow_extent *repl)
> + struct xfs_bmbt_irec *del)
> {
> struct xfs_alloc_arg args = {
> .tp = sc->tp,
> .mp = sc->mp,
> .oinfo = XFS_RMAP_OINFO_SKIP_UPDATE,
> .minlen = 1,
> - .maxlen = maxlen,
> + .maxlen = del->br_blockcount,
> .prod = 1,
> .resv = XFS_AG_RESV_NONE,
> .datatype = XFS_ALLOC_USERDATA,
> };
> int error;
>
> - error = xfs_trans_reserve_more(sc->tp, maxlen, 0);
> + error = xfs_trans_reserve_more(sc->tp, del->br_blockcount, 0);
> if (error)
> return error;
>
> @@ -428,8 +421,8 @@ xrep_cow_alloc(
>
> xfs_refcount_alloc_cow_extent(sc->tp, false, args.fsbno, args.len);
>
> - repl->fsbno = args.fsbno;
> - repl->len = args.len;
> + del->br_startblock = args.fsbno;
> + del->br_blockcount = args.len;
> return 0;
> }
>
> @@ -440,10 +433,12 @@ xrep_cow_alloc(
> STATIC int
> xrep_cow_alloc_rt(
> struct xfs_scrub *sc,
> - xfs_extlen_t maxlen,
> - struct xrep_cow_extent *repl)
> + struct xfs_bmbt_irec *del)
> {
> - xfs_rtxlen_t maxrtx = xfs_rtb_to_rtx(sc->mp, maxlen);
> + xfs_fsblock_t fsbno;
> + xfs_rtxlen_t maxrtx =
> + min(U32_MAX, xfs_blen_to_rtbxlen(sc->mp, del->br_blockcount));
> + xfs_extlen_t len;
> int error;
>
> error = xfs_trans_reserve_more(sc->tp, 0, maxrtx);
> @@ -451,11 +446,14 @@ xrep_cow_alloc_rt(
> return error;
>
> error = xfs_rtallocate_rtgs(sc->tp, NULLRTBLOCK, 1, maxrtx, 1, false,
> - false, &repl->fsbno, &repl->len);
> + false, &fsbno, &len);
> if (error)
> return error;
>
> - xfs_refcount_alloc_cow_extent(sc->tp, true, repl->fsbno, repl->len);
> + xfs_refcount_alloc_cow_extent(sc->tp, true, fsbno, len);
> +
> + del->br_startblock = fsbno;
> + del->br_blockcount = len;
> return 0;
> }
>
> @@ -469,19 +467,19 @@ static inline int
> xrep_cow_find_mapping(
> struct xrep_cow *xc,
> struct xfs_iext_cursor *icur,
> - xfs_fileoff_t startoff,
> - struct xfs_bmbt_irec *got)
> + xfs_fileoff_t badoff,
> + xfs_extlen_t badlen,
> + struct xfs_bmbt_irec *got,
> + struct xfs_bmbt_irec *rep)
> {
> struct xfs_inode *ip = xc->sc->ip;
> struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
>
> - if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got))
> + if (!xfs_iext_lookup_extent(ip, ifp, badoff, icur, got))
> goto bad;
> + memcpy(rep, got, sizeof(*rep));
>
> - if (got->br_startoff > startoff)
> - goto bad;
> -
> - if (got->br_blockcount == 0)
> + if (got->br_startoff > badoff)
> goto bad;
>
> if (isnullstartblock(got->br_startblock))
> @@ -490,6 +488,24 @@ xrep_cow_find_mapping(
> if (xfs_bmap_is_written_extent(got))
> goto bad;
>
> + if (got->br_startoff < badoff) {
> + const int64_t delta = badoff - got->br_startoff;
> +
> + rep->br_blockcount -= delta;
> + rep->br_startoff += delta;
> + rep->br_startblock += delta;
> + }
> +
> + if (got->br_startoff + got->br_blockcount > badoff + badlen) {
> + const int64_t delta = (got->br_startoff + got->br_blockcount) -
> + (badoff + badlen);
> +
> + rep->br_blockcount -= delta;
Overly long line here. Although I'm not even sure what the point is in
this local delta variable that is only used once anyay.
Otherwise looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/8] xfs: make cow repair somewhat flaky when debugging knob enabled
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
2026-07-10 5:21 ` [PATCH 1/8] xfs: don't replace the wrong part of the cow fork Darrick J. Wong
@ 2026-07-10 5:22 ` Darrick J. Wong
2026-07-10 5:37 ` Christoph Hellwig
2026-07-10 5:22 ` [PATCH 3/8] xfs: move cow_replace_mapping to xfs_bmap_util.c Darrick J. Wong
` (5 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:22 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Introduce a new behavior for the cow fork repair code: if the debugging
knob is enabled, we'll pick a random subrange of each cow fork mapping
to mark as bad. This will exercise the xrep_cow_replace_mapping more
thoroughly.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/xfs/scrub/cow_repair.c | 35 +++++++++++++++++++++++++++++++----
1 file changed, 31 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c
index ca3405a26b641c..8507a1185b2b45 100644
--- a/fs/xfs/scrub/cow_repair.c
+++ b/fs/xfs/scrub/cow_repair.c
@@ -224,6 +224,31 @@ xrep_cow_mark_missing_staging_rmap(
xfs_gbno_to_fsb(cur->bc_group, rec_bno), rec_len);
}
+#ifdef DEBUG
+/*
+ * Trim the start and end of the current mapping by up to 1/4 of the length
+ * and mark that as "bad" to test the cow fork repair mechanism.
+ */
+static inline int
+xrep_cow_debug_replacement(
+ struct xrep_cow *xc)
+{
+ xfs_fsblock_t fsbno = xc->irec.br_startblock;
+ xfs_extlen_t len = xc->irec.br_blockcount;
+ uint32_t trim;
+
+ /* get_random_u32_below requires a nonzero argument */
+ trim = len > 4 ? get_random_u32_below(len / 4) : 0;
+ len -= trim;
+
+ trim = len > 4 ? get_random_u32_below(len / 4) : 0;
+ fsbno += trim;
+ len -= trim;
+
+ return xrep_cow_mark_file_range(xc, fsbno, len);
+}
+#endif
+
/*
* Find any part of the CoW fork mapping that isn't a single-owner CoW staging
* extent and mark the corresponding part of the file range in the bitmap.
@@ -293,8 +318,9 @@ xrep_cow_find_bad(
* If userspace is forcing us to rebuild the CoW fork or someone turned
* on the debugging knob, replace everything in the CoW fork.
*/
- if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
- XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ error = xrep_cow_debug_replacement(xc);
+ else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
xc->irec.br_blockcount);
@@ -375,8 +401,9 @@ xrep_cow_find_bad_rt(
* turned on the debugging knob, replace everything in the
* CoW fork and then scan for staging extents in the refcountbt.
*/
- if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
- XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ if (XFS_TEST_ERROR(sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
+ error = xrep_cow_debug_replacement(xc);
+ else if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD)
error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
xc->irec.br_blockcount);
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 3/8] xfs: move cow_replace_mapping to xfs_bmap_util.c
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
2026-07-10 5:21 ` [PATCH 1/8] xfs: don't replace the wrong part of the cow fork Darrick J. Wong
2026-07-10 5:22 ` [PATCH 2/8] xfs: make cow repair somewhat flaky when debugging knob enabled Darrick J. Wong
@ 2026-07-10 5:22 ` Darrick J. Wong
2026-07-10 5:37 ` Christoph Hellwig
2026-07-10 5:22 ` [PATCH 4/8] xfs: don't wrap around quota ids in dqiterate Darrick J. Wong
` (4 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:22 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move the actual details of (partially) replacing a COW fork mapping to
xfs_bmap_util.c so that all the code doing hairy operations on subsets
of bmbt_irecs are kept together.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/xfs/scrub/trace.h | 41 -------------------
fs/xfs/xfs_bmap_util.h | 4 ++
fs/xfs/xfs_trace.h | 41 +++++++++++++++++++
fs/xfs/scrub/cow_repair.c | 95 +--------------------------------------------
fs/xfs/xfs_bmap_util.c | 89 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 136 insertions(+), 134 deletions(-)
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index d48a6db5b5f308..d5d39d82749e5c 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -2671,47 +2671,6 @@ TRACE_EVENT(xrep_cow_mark_file_range,
__entry->blockcount)
);
-TRACE_EVENT(xrep_cow_replace_mapping,
- TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
- const struct xfs_bmbt_irec *rep),
- TP_ARGS(ip, got, rep),
- TP_STRUCT__entry(
- __field(dev_t, dev)
- __field(xfs_ino_t, ino)
- __field(xfs_fsblock_t, startblock)
- __field(xfs_fileoff_t, startoff)
- __field(xfs_filblks_t, blockcount)
- __field(xfs_exntst_t, state)
- __field(xfs_fileoff_t, new_startoff)
- __field(xfs_fsblock_t, new_startblock)
- __field(xfs_extlen_t, new_blockcount)
- __field(xfs_exntst_t, new_state)
- ),
- TP_fast_assign(
- __entry->dev = ip->i_mount->m_super->s_dev;
- __entry->ino = I_INO(ip);
- __entry->startoff = got->br_startoff;
- __entry->startblock = got->br_startblock;
- __entry->blockcount = got->br_blockcount;
- __entry->state = got->br_state;
- __entry->new_startoff = rep->br_startoff;
- __entry->new_startblock = rep->br_startblock;
- __entry->new_blockcount = rep->br_blockcount;
- __entry->new_state = rep->br_state;
- ),
- TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
- MAJOR(__entry->dev), MINOR(__entry->dev),
- __entry->ino,
- __entry->startoff,
- __entry->startblock,
- __entry->blockcount,
- __entry->state,
- __entry->new_startoff,
- __entry->new_startblock,
- __entry->new_blockcount,
- __entry->new_state)
-);
-
TRACE_EVENT(xrep_cow_free_staging,
TP_PROTO(const struct xfs_perag *pag, xfs_agblock_t agbno,
xfs_extlen_t blockcount),
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index c477b336163040..eaaf094154b9f9 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -81,4 +81,8 @@ int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
+void xfs_bmap_replace_cow_mapping(struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur, struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep);
+
#endif /* __XFS_BMAP_UTIL_H__ */
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index d478693674f952..aeb89ac53bf190 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -6439,6 +6439,47 @@ TRACE_EVENT(xfs_verify_media_error,
__entry->error)
);
+TRACE_EVENT(xfs_bmap_replace_cow_mapping,
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
+ const struct xfs_bmbt_irec *rep),
+ TP_ARGS(ip, got, rep),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(xfs_fsblock_t, startblock)
+ __field(xfs_fileoff_t, startoff)
+ __field(xfs_filblks_t, blockcount)
+ __field(xfs_exntst_t, state)
+ __field(xfs_fileoff_t, new_startoff)
+ __field(xfs_fsblock_t, new_startblock)
+ __field(xfs_extlen_t, new_blockcount)
+ __field(xfs_exntst_t, new_state)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = I_INO(ip);
+ __entry->startoff = got->br_startoff;
+ __entry->startblock = got->br_startblock;
+ __entry->blockcount = got->br_blockcount;
+ __entry->state = got->br_state;
+ __entry->new_startoff = rep->br_startoff;
+ __entry->new_startblock = rep->br_startblock;
+ __entry->new_blockcount = rep->br_blockcount;
+ __entry->new_state = rep->br_state;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->startoff,
+ __entry->startblock,
+ __entry->blockcount,
+ __entry->state,
+ __entry->new_startoff,
+ __entry->new_startblock,
+ __entry->new_blockcount,
+ __entry->new_state)
+);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c
index 8507a1185b2b45..1153e6a603fccf 100644
--- a/fs/xfs/scrub/cow_repair.c
+++ b/fs/xfs/scrub/cow_repair.c
@@ -29,6 +29,7 @@
#include "xfs_rtalloc.h"
#include "xfs_rtbitmap.h"
#include "xfs_rtgroup.h"
+#include "xfs_bmap_util.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -539,98 +540,6 @@ xrep_cow_find_mapping(
return -EFSCORRUPTED;
}
-#define REPLACE_LEFT_SIDE (1U << 0)
-#define REPLACE_RIGHT_SIDE (1U << 1)
-
-/*
- * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
- * described by @rep into the cow fork, pushing aside @got as necessary. @icur
- * must point to iext tree leaf containing @got.
- */
-static inline void
-xrep_cow_replace_mapping(
- struct xfs_inode *ip,
- struct xfs_iext_cursor *icur,
- struct xfs_bmbt_irec *got,
- struct xfs_bmbt_irec *rep)
-{
- struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
- xfs_fileoff_t rep_endoff =
- rep->br_startoff + rep->br_blockcount;
- xfs_fileoff_t got_endoff =
- got->br_startoff + got->br_blockcount;
- uint32_t state = BMAP_COWFORK;
-
- ASSERT(rep->br_blockcount > 0);
- ASSERT(!isnullstartblock(got->br_startblock));
- ASSERT(got->br_startoff <= rep->br_startoff);
- ASSERT(got_endoff >= rep_endoff);
-
- trace_xrep_cow_replace_mapping(ip, got, rep);
-
- if (got->br_startoff == rep->br_startoff)
- state |= BMAP_LEFT_FILLING;
- if (got_endoff == rep_endoff)
- state |= BMAP_RIGHT_FILLING;
-
- switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
- case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
- /*
- * Replacement matches the whole mapping, update the record.
- */
- xfs_iext_update_extent(ip, state, icur, rep);
- break;
- case BMAP_LEFT_FILLING:
- /*
- * Replace the first part of the mapping: Update the cursor
- * position with the new mapping, then add a record with the
- * tail of the old mapping.
- */
- got->br_startoff = rep_endoff;
- got->br_blockcount -= rep->br_blockcount;
- got->br_startblock += rep->br_blockcount;
-
- xfs_iext_update_extent(ip, state, icur, rep);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, got, state);
- break;
- case BMAP_RIGHT_FILLING:
- /*
- * Replacing the last part of the mapping. Shorten the current
- * mapping then add a record with the new mapping.
- */
- got->br_blockcount -= rep->br_blockcount;
-
- xfs_iext_update_extent(ip, state, icur, got);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, rep, state);
- break;
- case 0:
- /*
- * Replacing the middle of the extent. Shorten the current
- * mapping, add a new record with the new mapping, and add a
- * second new record with the tail of the old mapping.
- */
- got->br_blockcount = rep->br_startoff - got->br_startoff;
-
- struct xfs_bmbt_irec new = {
- .br_startoff = rep_endoff,
- .br_blockcount = got_endoff - rep_endoff,
- .br_state = got->br_state,
- .br_startblock = got->br_startblock +
- rep->br_blockcount +
- got->br_blockcount,
- };
-
- xfs_iext_update_extent(ip, state, icur, got);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, rep, state);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, &new, state);
- break;
- }
-}
-
/*
* Replace the unwritten CoW staging extent backing the given file range with a
* new space extent that isn't as problematic.
@@ -673,7 +582,7 @@ xrep_cow_replace_range(
* Replace the old mapping with the new one, and commit the metadata
* changes made so far.
*/
- xrep_cow_replace_mapping(sc->ip, &icur, &got, &rep);
+ xfs_bmap_replace_cow_mapping(sc->ip, &icur, &got, &rep);
xfs_inode_set_cowblocks_tag(sc->ip);
error = xfs_defer_finish(&sc->tp);
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 3b9f262f8e9128..c88b9ade7389dd 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1744,3 +1744,92 @@ xfs_swap_extents(
xfs_trans_cancel(tp);
goto out_unlock_ilock;
}
+
+/*
+ * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
+ * described by @rep into the cow fork, pushing aside @got as necessary. @icur
+ * must point to iext tree leaf containing @got.
+ */
+void
+xfs_bmap_replace_cow_mapping(
+ struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
+{
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
+ xfs_fileoff_t rep_endoff =
+ rep->br_startoff + rep->br_blockcount;
+ xfs_fileoff_t got_endoff =
+ got->br_startoff + got->br_blockcount;
+ uint32_t state = BMAP_COWFORK;
+
+ ASSERT(rep->br_blockcount > 0);
+ ASSERT(!isnullstartblock(got->br_startblock));
+ ASSERT(got->br_startoff <= rep->br_startoff);
+ ASSERT(got_endoff >= rep_endoff);
+
+ trace_xfs_bmap_replace_cow_mapping(ip, got, rep);
+
+ if (got->br_startoff == rep->br_startoff)
+ state |= BMAP_LEFT_FILLING;
+ if (got_endoff == rep_endoff)
+ state |= BMAP_RIGHT_FILLING;
+
+ switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+ case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
+ /*
+ * Replacement matches the whole mapping, update the record.
+ */
+ xfs_iext_update_extent(ip, state, icur, rep);
+ break;
+ case BMAP_LEFT_FILLING:
+ /*
+ * Replace the first part of the mapping: Update the cursor
+ * position with the new mapping, then add a record with the
+ * tail of the old mapping.
+ */
+ got->br_startoff = rep_endoff;
+ got->br_blockcount -= rep->br_blockcount;
+ got->br_startblock += rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, rep);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, got, state);
+ break;
+ case BMAP_RIGHT_FILLING:
+ /*
+ * Replacing the last part of the mapping. Shorten the current
+ * mapping then add a record with the new mapping.
+ */
+ got->br_blockcount -= rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ break;
+ case 0:
+ /*
+ * Replacing the middle of the extent. Shorten the current
+ * mapping, add a new record with the new mapping, and add a
+ * second new record with the tail of the old mapping.
+ */
+ got->br_blockcount = rep->br_startoff - got->br_startoff;
+
+ struct xfs_bmbt_irec new = {
+ .br_startoff = rep_endoff,
+ .br_blockcount = got_endoff - rep_endoff,
+ .br_state = got->br_state,
+ .br_startblock = got->br_startblock +
+ rep->br_blockcount +
+ got->br_blockcount,
+ };
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, &new, state);
+ break;
+ }
+}
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 4/8] xfs: don't wrap around quota ids in dqiterate
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
` (2 preceding siblings ...)
2026-07-10 5:22 ` [PATCH 3/8] xfs: move cow_replace_mapping to xfs_bmap_util.c Darrick J. Wong
@ 2026-07-10 5:22 ` Darrick J. Wong
2026-07-10 5:22 ` [PATCH 5/8] xfs: use rtrefcount btree cursor in xchk_xref_is_rt_cow_staging Darrick J. Wong
` (3 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:22 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: stable, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
LOLLM noticed that q_id is an unsigned 32-bit variable. If it happens
to be set to XFS_DQ_ID_MAX due to a filesystem that actually has a dquot
for ID_MAX, then this addition will truncate to zero and the iteration
starts over. Fix this by casting to u64.
Cc: <stable@vger.kernel.org> # v6.8
Fixes: 21d7500929c8a0 ("xfs: improve dquot iteration for scrub")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/scrub/dqiterate.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/xfs/scrub/dqiterate.c b/fs/xfs/scrub/dqiterate.c
index 10950e4bd4c3c0..079dc4e691a01a 100644
--- a/fs/xfs/scrub/dqiterate.c
+++ b/fs/xfs/scrub/dqiterate.c
@@ -205,7 +205,7 @@ xchk_dquot_iter(
if (error)
return error;
- cursor->id = dq->q_id + 1;
+ cursor->id = (uint64_t)dq->q_id + 1;
*dqpp = dq;
return 1;
}
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 5/8] xfs: use rtrefcount btree cursor in xchk_xref_is_rt_cow_staging
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
` (3 preceding siblings ...)
2026-07-10 5:22 ` [PATCH 4/8] xfs: don't wrap around quota ids in dqiterate Darrick J. Wong
@ 2026-07-10 5:22 ` Darrick J. Wong
2026-07-10 5:23 ` [PATCH 6/8] xfs: use the rt version of the cow staging checker Darrick J. Wong
` (2 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:22 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: stable, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
LOLLM points out that we pass the wrong btree cursor here. We want the
rtrefcount btree cursor, not the non-rt one. This is fairly benign
since it only affects tracing data.
Cc: <stable@vger.kernel.org> # v6.14
Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/scrub/rtrefcount.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c
index 0d10ce2910c2cb..4e7c540c8d2307 100644
--- a/fs/xfs/scrub/rtrefcount.c
+++ b/fs/xfs/scrub/rtrefcount.c
@@ -607,7 +607,7 @@ xchk_xref_is_rt_cow_staging(
/* CoW lookup returned a shared extent record? */
if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
- xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+ xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
/* Must be at least as long as what was passed in */
if (rc.rc_blockcount < len)
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 6/8] xfs: use the rt version of the cow staging checker
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
` (4 preceding siblings ...)
2026-07-10 5:22 ` [PATCH 5/8] xfs: use rtrefcount btree cursor in xchk_xref_is_rt_cow_staging Darrick J. Wong
@ 2026-07-10 5:23 ` Darrick J. Wong
2026-07-10 5:23 ` [PATCH 7/8] xfs: write the rg superblock when fixing it Darrick J. Wong
2026-07-10 5:23 ` [PATCH 8/8] xfs: grab rtrmap btree when checking rgsuper Darrick J. Wong
7 siblings, 0 replies; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:23 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: stable, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
LOLLM also noticed that xchk_rtrmapbt_xref ought to be using the rtdev
version of the "is this a cow extent?" helper function, not the datadev
one.
Cc: <stable@vger.kernel.org> # v6.14
Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/scrub/rtrmap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c
index 043be93c714884..b3b2cf17ba2c08 100644
--- a/fs/xfs/scrub/rtrmap.c
+++ b/fs/xfs/scrub/rtrmap.c
@@ -209,7 +209,7 @@ xchk_rtrmapbt_xref(
xfs_rgbno_to_rtb(sc->sr.rtg, irec->rm_startblock),
irec->rm_blockcount);
if (irec->rm_owner == XFS_RMAP_OWN_COW)
- xchk_xref_is_cow_staging(sc, irec->rm_startblock,
+ xchk_xref_is_rt_cow_staging(sc, irec->rm_startblock,
irec->rm_blockcount);
else
xchk_rtrmapbt_xref_rtrefc(sc, irec);
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 7/8] xfs: write the rg superblock when fixing it
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
` (5 preceding siblings ...)
2026-07-10 5:23 ` [PATCH 6/8] xfs: use the rt version of the cow staging checker Darrick J. Wong
@ 2026-07-10 5:23 ` Darrick J. Wong
2026-07-10 5:23 ` [PATCH 8/8] xfs: grab rtrmap btree when checking rgsuper Darrick J. Wong
7 siblings, 0 replies; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:23 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: stable, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
The rtgroup superblock fixer should write the rtgroup superblock.
LOLLM noticed this, oops. :/
Cc: <stable@vger.kernel.org> # v6.13
Fixes: 1433f8f9cead37 ("xfs: repair realtime group superblock")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
---
fs/xfs/scrub/rgsuper.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c
index 482f899a518a85..a52d37c33ca15a 100644
--- a/fs/xfs/scrub/rgsuper.c
+++ b/fs/xfs/scrub/rgsuper.c
@@ -80,9 +80,13 @@ int
xrep_rgsuperblock(
struct xfs_scrub *sc)
{
+ struct xfs_buf *sb_bp;
+
ASSERT(rtg_rgno(sc->sr.rtg) == 0);
xfs_log_sb(sc->tp);
+ sb_bp = xfs_trans_getsb(sc->tp);
+ xfs_log_rtsb(sc->tp, sb_bp);
return 0;
}
#endif /* CONFIG_XFS_ONLINE_REPAIR */
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 8/8] xfs: grab rtrmap btree when checking rgsuper
2026-07-10 5:21 [PATCHSET v2] xfs: LOLLM-inspired bug fixes, part 1 Darrick J. Wong
` (6 preceding siblings ...)
2026-07-10 5:23 ` [PATCH 7/8] xfs: write the rg superblock when fixing it Darrick J. Wong
@ 2026-07-10 5:23 ` Darrick J. Wong
7 siblings, 0 replies; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-10 5:23 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: stable, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
LOLLM noticed that we aren't grabbing the rtrmap btree when we check the
realtime group superblock. As a result, none of the cross-referencing
checks have ever run. Fix this.
Cc: <stable@vger.kernel.org> # v6.14
Fixes: 428e4884656db9 ("xfs: allow queued realtime intents to drain before scrubbing")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/scrub/rgsuper.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c
index a52d37c33ca15a..c92212d8eb11b0 100644
--- a/fs/xfs/scrub/rgsuper.c
+++ b/fs/xfs/scrub/rgsuper.c
@@ -23,6 +23,8 @@ int
xchk_setup_rgsuperblock(
struct xfs_scrub *sc)
{
+ if (xchk_need_intent_drain(sc))
+ xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
return xchk_trans_alloc(sc, 0);
}
@@ -43,6 +45,7 @@ xchk_rgsuperblock(
struct xfs_scrub *sc)
{
xfs_rgnumber_t rgno = sc->sm->sm_agno;
+ unsigned int flags;
int error;
/*
@@ -63,7 +66,12 @@ xchk_rgsuperblock(
if (!xchk_xref_process_error(sc, 0, 0, &error))
return error;
- error = xchk_rtgroup_lock(sc, &sc->sr, XFS_RTGLOCK_BITMAP_SHARED);
+ if (xfs_has_rtrmapbt(sc->mp))
+ flags = XFS_RTGLOCK_BITMAP | XFS_RTGLOCK_RMAP;
+ else
+ flags = XFS_RTGLOCK_BITMAP_SHARED;
+
+ error = xchk_rtgroup_lock(sc, &sc->sr, flags);
if (error)
return error;
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 3/8] xfs: move cow_replace_mapping to xfs_bmap_util.c
2026-07-14 6:03 [PATCHSET v3 1/2] xfs: LLM-inspired bug fixes, part 1 Darrick J. Wong
@ 2026-07-14 6:04 ` Darrick J. Wong
0 siblings, 0 replies; 13+ messages in thread
From: Darrick J. Wong @ 2026-07-14 6:04 UTC (permalink / raw)
To: djwong, cem, hch; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Move the actual details of (partially) replacing a COW fork mapping to
xfs_bmap_util.c so that all the code doing hairy operations on subsets
of bmbt_irecs are kept together.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/scrub/trace.h | 41 -------------------
fs/xfs/xfs_bmap_util.h | 4 ++
fs/xfs/xfs_trace.h | 41 +++++++++++++++++++
fs/xfs/scrub/cow_repair.c | 95 +--------------------------------------------
fs/xfs/xfs_bmap_util.c | 89 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 136 insertions(+), 134 deletions(-)
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index d48a6db5b5f308..d5d39d82749e5c 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -2671,47 +2671,6 @@ TRACE_EVENT(xrep_cow_mark_file_range,
__entry->blockcount)
);
-TRACE_EVENT(xrep_cow_replace_mapping,
- TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
- const struct xfs_bmbt_irec *rep),
- TP_ARGS(ip, got, rep),
- TP_STRUCT__entry(
- __field(dev_t, dev)
- __field(xfs_ino_t, ino)
- __field(xfs_fsblock_t, startblock)
- __field(xfs_fileoff_t, startoff)
- __field(xfs_filblks_t, blockcount)
- __field(xfs_exntst_t, state)
- __field(xfs_fileoff_t, new_startoff)
- __field(xfs_fsblock_t, new_startblock)
- __field(xfs_extlen_t, new_blockcount)
- __field(xfs_exntst_t, new_state)
- ),
- TP_fast_assign(
- __entry->dev = ip->i_mount->m_super->s_dev;
- __entry->ino = I_INO(ip);
- __entry->startoff = got->br_startoff;
- __entry->startblock = got->br_startblock;
- __entry->blockcount = got->br_blockcount;
- __entry->state = got->br_state;
- __entry->new_startoff = rep->br_startoff;
- __entry->new_startblock = rep->br_startblock;
- __entry->new_blockcount = rep->br_blockcount;
- __entry->new_state = rep->br_state;
- ),
- TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
- MAJOR(__entry->dev), MINOR(__entry->dev),
- __entry->ino,
- __entry->startoff,
- __entry->startblock,
- __entry->blockcount,
- __entry->state,
- __entry->new_startoff,
- __entry->new_startblock,
- __entry->new_blockcount,
- __entry->new_state)
-);
-
TRACE_EVENT(xrep_cow_free_staging,
TP_PROTO(const struct xfs_perag *pag, xfs_agblock_t agbno,
xfs_extlen_t blockcount),
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index c477b336163040..eaaf094154b9f9 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -81,4 +81,8 @@ int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
+void xfs_bmap_replace_cow_mapping(struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur, struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep);
+
#endif /* __XFS_BMAP_UTIL_H__ */
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index d478693674f952..aeb89ac53bf190 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -6439,6 +6439,47 @@ TRACE_EVENT(xfs_verify_media_error,
__entry->error)
);
+TRACE_EVENT(xfs_bmap_replace_cow_mapping,
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
+ const struct xfs_bmbt_irec *rep),
+ TP_ARGS(ip, got, rep),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(xfs_fsblock_t, startblock)
+ __field(xfs_fileoff_t, startoff)
+ __field(xfs_filblks_t, blockcount)
+ __field(xfs_exntst_t, state)
+ __field(xfs_fileoff_t, new_startoff)
+ __field(xfs_fsblock_t, new_startblock)
+ __field(xfs_extlen_t, new_blockcount)
+ __field(xfs_exntst_t, new_state)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = I_INO(ip);
+ __entry->startoff = got->br_startoff;
+ __entry->startblock = got->br_startblock;
+ __entry->blockcount = got->br_blockcount;
+ __entry->state = got->br_state;
+ __entry->new_startoff = rep->br_startoff;
+ __entry->new_startblock = rep->br_startblock;
+ __entry->new_blockcount = rep->br_blockcount;
+ __entry->new_state = rep->br_state;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->startoff,
+ __entry->startblock,
+ __entry->blockcount,
+ __entry->state,
+ __entry->new_startoff,
+ __entry->new_startblock,
+ __entry->new_blockcount,
+ __entry->new_state)
+);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c
index 511a761dca9fe3..8dd9c0266e2169 100644
--- a/fs/xfs/scrub/cow_repair.c
+++ b/fs/xfs/scrub/cow_repair.c
@@ -29,6 +29,7 @@
#include "xfs_rtalloc.h"
#include "xfs_rtbitmap.h"
#include "xfs_rtgroup.h"
+#include "xfs_bmap_util.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -537,98 +538,6 @@ xrep_cow_find_mapping(
return -EFSCORRUPTED;
}
-#define REPLACE_LEFT_SIDE (1U << 0)
-#define REPLACE_RIGHT_SIDE (1U << 1)
-
-/*
- * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
- * described by @rep into the cow fork, pushing aside @got as necessary. @icur
- * must point to iext tree leaf containing @got.
- */
-static inline void
-xrep_cow_replace_mapping(
- struct xfs_inode *ip,
- struct xfs_iext_cursor *icur,
- struct xfs_bmbt_irec *got,
- struct xfs_bmbt_irec *rep)
-{
- struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
- xfs_fileoff_t rep_endoff =
- rep->br_startoff + rep->br_blockcount;
- xfs_fileoff_t got_endoff =
- got->br_startoff + got->br_blockcount;
- uint32_t state = BMAP_COWFORK;
-
- ASSERT(rep->br_blockcount > 0);
- ASSERT(!isnullstartblock(got->br_startblock));
- ASSERT(got->br_startoff <= rep->br_startoff);
- ASSERT(got_endoff >= rep_endoff);
-
- trace_xrep_cow_replace_mapping(ip, got, rep);
-
- if (got->br_startoff == rep->br_startoff)
- state |= BMAP_LEFT_FILLING;
- if (got_endoff == rep_endoff)
- state |= BMAP_RIGHT_FILLING;
-
- switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
- case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
- /*
- * Replacement matches the whole mapping, update the record.
- */
- xfs_iext_update_extent(ip, state, icur, rep);
- break;
- case BMAP_LEFT_FILLING:
- /*
- * Replace the first part of the mapping: Update the cursor
- * position with the new mapping, then add a record with the
- * tail of the old mapping.
- */
- got->br_startoff = rep_endoff;
- got->br_blockcount -= rep->br_blockcount;
- got->br_startblock += rep->br_blockcount;
-
- xfs_iext_update_extent(ip, state, icur, rep);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, got, state);
- break;
- case BMAP_RIGHT_FILLING:
- /*
- * Replacing the last part of the mapping. Shorten the current
- * mapping then add a record with the new mapping.
- */
- got->br_blockcount -= rep->br_blockcount;
-
- xfs_iext_update_extent(ip, state, icur, got);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, rep, state);
- break;
- case 0:
- /*
- * Replacing the middle of the extent. Shorten the current
- * mapping, add a new record with the new mapping, and add a
- * second new record with the tail of the old mapping.
- */
- got->br_blockcount = rep->br_startoff - got->br_startoff;
-
- struct xfs_bmbt_irec new = {
- .br_startoff = rep_endoff,
- .br_blockcount = got_endoff - rep_endoff,
- .br_state = got->br_state,
- .br_startblock = got->br_startblock +
- rep->br_blockcount +
- got->br_blockcount,
- };
-
- xfs_iext_update_extent(ip, state, icur, got);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, rep, state);
- xfs_iext_next(ifp, icur);
- xfs_iext_insert(ip, icur, &new, state);
- break;
- }
-}
-
/*
* Replace the unwritten CoW staging extent backing the given file range with a
* new space extent that isn't as problematic.
@@ -671,7 +580,7 @@ xrep_cow_replace_range(
* Replace the old mapping with the new one, and commit the metadata
* changes made so far.
*/
- xrep_cow_replace_mapping(sc->ip, &icur, &got, &rep);
+ xfs_bmap_replace_cow_mapping(sc->ip, &icur, &got, &rep);
xfs_inode_set_cowblocks_tag(sc->ip);
error = xfs_defer_finish(&sc->tp);
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 3b9f262f8e9128..c88b9ade7389dd 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1744,3 +1744,92 @@ xfs_swap_extents(
xfs_trans_cancel(tp);
goto out_unlock_ilock;
}
+
+/*
+ * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
+ * described by @rep into the cow fork, pushing aside @got as necessary. @icur
+ * must point to iext tree leaf containing @got.
+ */
+void
+xfs_bmap_replace_cow_mapping(
+ struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
+{
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
+ xfs_fileoff_t rep_endoff =
+ rep->br_startoff + rep->br_blockcount;
+ xfs_fileoff_t got_endoff =
+ got->br_startoff + got->br_blockcount;
+ uint32_t state = BMAP_COWFORK;
+
+ ASSERT(rep->br_blockcount > 0);
+ ASSERT(!isnullstartblock(got->br_startblock));
+ ASSERT(got->br_startoff <= rep->br_startoff);
+ ASSERT(got_endoff >= rep_endoff);
+
+ trace_xfs_bmap_replace_cow_mapping(ip, got, rep);
+
+ if (got->br_startoff == rep->br_startoff)
+ state |= BMAP_LEFT_FILLING;
+ if (got_endoff == rep_endoff)
+ state |= BMAP_RIGHT_FILLING;
+
+ switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+ case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
+ /*
+ * Replacement matches the whole mapping, update the record.
+ */
+ xfs_iext_update_extent(ip, state, icur, rep);
+ break;
+ case BMAP_LEFT_FILLING:
+ /*
+ * Replace the first part of the mapping: Update the cursor
+ * position with the new mapping, then add a record with the
+ * tail of the old mapping.
+ */
+ got->br_startoff = rep_endoff;
+ got->br_blockcount -= rep->br_blockcount;
+ got->br_startblock += rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, rep);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, got, state);
+ break;
+ case BMAP_RIGHT_FILLING:
+ /*
+ * Replacing the last part of the mapping. Shorten the current
+ * mapping then add a record with the new mapping.
+ */
+ got->br_blockcount -= rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ break;
+ case 0:
+ /*
+ * Replacing the middle of the extent. Shorten the current
+ * mapping, add a new record with the new mapping, and add a
+ * second new record with the tail of the old mapping.
+ */
+ got->br_blockcount = rep->br_startoff - got->br_startoff;
+
+ struct xfs_bmbt_irec new = {
+ .br_startoff = rep_endoff,
+ .br_blockcount = got_endoff - rep_endoff,
+ .br_state = got->br_state,
+ .br_startblock = got->br_startblock +
+ rep->br_blockcount +
+ got->br_blockcount,
+ };
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, &new, state);
+ break;
+ }
+}
^ permalink raw reply related [flat|nested] 13+ messages in thread