7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Darrick J. Wong commit d7c204d4ea7ef0cf0dac908388984c2936f1f94e upstream. LOLLM notices that the extents stored in a xrtb_bitmap (aka xfs_rtblock bitmap) can span multiple rtgroups due to two circumstances. The first is that the size of an rtgroup is an exact power of two, which means that rtgroups are adjacent in the segmented xfs_rtblock_t address space. This is fairly common to reduce the amount of multiplication and division needed to handle space on the rt volume. The second is that (unlike in the original rtgroups design), rtgroups do not have fixed-location metadata like AGs do, which means that there's nothing to force a break between rtgroups. Therefore, we must loop through the rtgroups in xreap_rtmeta_extent to avoid running off the end of an rtgroup while reaping. Cc: stable@vger.kernel.org # v6.14 Fixes: fd97fe1112088c ("xfs: fix CoW forks for realtime files") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/reap.c | 59 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 11 deletions(-) --- a/fs/xfs/scrub/reap.c +++ b/fs/xfs/scrub/reap.c @@ -1080,26 +1080,23 @@ xreap_configure_rgcow_limits( XFS_RTGLOCK_RMAP | \ XFS_RTGLOCK_REFCOUNT) -/* - * Break a rt file metadata extent into sub-extents by fate (crosslinked, not - * crosslinked), and dispose of each sub-extent separately. The extent must - * be aligned to a realtime extent. - */ -STATIC int -xreap_rtmeta_extent( - uint64_t rtbno, +static int +xreap_rtgroup_extent( + struct xreap_state *rs, + xfs_rtblock_t rtbno, uint64_t len, - void *priv) + xfs_rgblock_t *done) { - struct xreap_state *rs = priv; struct xfs_scrub *sc = rs->sc; xfs_rgblock_t rgbno = xfs_rtb_to_rgbno(sc->mp, rtbno); - xfs_rgblock_t rgbno_next = rgbno + len; + xfs_rgblock_t rgbno_next; int error = 0; ASSERT(sc->ip != NULL); ASSERT(!sc->sr.rtg); + *done = 0; + /* * We're reaping blocks after repairing file metadata, which means that * we have to init the xchk_ag structure ourselves. @@ -1110,6 +1107,7 @@ xreap_rtmeta_extent( xfs_rtgroup_lock(sc->sr.rtg, XREAP_RTGLOCK_ALL); + rgbno_next = rgbno + min(len, (uint64_t)rtg_blocks(sc->sr.rtg) - rgbno); while (rgbno < rgbno_next) { xfs_extlen_t rglen; bool crosslinked; @@ -1136,6 +1134,7 @@ xreap_rtmeta_extent( } rgbno += rglen; + *done += rglen; } out_unlock: @@ -1146,6 +1145,44 @@ out_unlock: } /* + * Break a rt file metadata extent into sub-extents by fate (crosslinked, not + * crosslinked), and dispose of each sub-extent separately. The extent must + * be aligned to a realtime extent. + */ +STATIC int +xreap_rtmeta_extent( + uint64_t rtbno, + uint64_t len, + void *priv) +{ + struct xreap_state *rs = priv; + int error; + + /* + * Space from adjacent rtgroups can be stored as a single bitmap extent + * because rtgroups do not have fixed-location metadata such as a + * per-group superblock that cannot be reaped. Handle these overlaps + * by only reaping within a single rtgroup at a time. + * + * In the somewhat unlikely event that an rtgroup size is not a power + * of two, the segmented nature of xfs_rtblock_t prevents space in + * adjacent rtgroups from merging in the bitmap. + */ + while (len > 0) { + xfs_rgblock_t done = 0; + + error = xreap_rtgroup_extent(rs, rtbno, len, &done); + if (error) + return error; + + rtbno += done; + len -= done; + } + + return 0; +} + +/* * Dispose of every block of every rt metadata extent in the bitmap. * Do not use this to dispose of the mappings in an ondisk inode fork. */