Skip to content

Commit 6be73ce

Browse files
Darrick J. Wongdchinner
authored andcommitted
xfs: fix broken logic when detecting mergeable bmap records
Commit 6bc6c99a944c was a well-intentioned effort to initiate consolidation of adjacent bmbt mapping records by setting the PREEN flag. Consolidation can only happen if the length of the combined record doesn't overflow the 21-bit blockcount field of the bmbt recordset. Unfortunately, the length test is inverted, leading to it triggering on data forks like these: EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL 0: [0..16777207]: 76110848..92888055 0 (76110848..92888055) 16777208 1: [16777208..20639743]: 92888056..96750591 0 (92888056..96750591) 3862536 Note that record 0 has a length of 16777208 512b blocks. This corresponds to 2097151 4k fsblocks, which is the maximum. Hence the two records cannot be merged. However, the logic is still wrong even if we change the in-loop comparison, because the scope of our examination isn't broad enough inside the loop to detect mappings like this: 0: [0..9]: 76110838..76110847 0 (76110838..76110847) 10 1: [10..16777217]: 76110848..92888055 0 (76110848..92888055) 16777208 2: [16777218..20639753]: 92888056..96750591 0 (92888056..96750591) 3862536 These three records could be merged into two, but one cannot determine this purely from looking at records 0-1 or 1-2 in isolation. Hoist the mergability detection outside the loop, and base its decision making on whether or not a merged mapping could be expressed in fewer bmbt records. While we're at it, fix the incorrect return type of the iter function. Fixes: 336642f ("xfs: alert the user about data/attr fork mappings that could be merged") Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Dave Chinner <[email protected]> Signed-off-by: Dave Chinner <[email protected]>
1 parent 4320f34 commit 6be73ce

File tree

1 file changed

+13
-12
lines changed

1 file changed

+13
-12
lines changed

fs/xfs/scrub/bmap.c

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -769,14 +769,14 @@ xchk_are_bmaps_contiguous(
769769
* mapping or false if there are no more mappings. Caller must ensure that
770770
* @info.icur is zeroed before the first call.
771771
*/
772-
static int
772+
static bool
773773
xchk_bmap_iext_iter(
774774
struct xchk_bmap_info *info,
775775
struct xfs_bmbt_irec *irec)
776776
{
777777
struct xfs_bmbt_irec got;
778778
struct xfs_ifork *ifp;
779-
xfs_filblks_t prev_len;
779+
unsigned int nr = 0;
780780

781781
ifp = xfs_ifork_ptr(info->sc->ip, info->whichfork);
782782

@@ -790,12 +790,12 @@ xchk_bmap_iext_iter(
790790
irec->br_startoff);
791791
return false;
792792
}
793+
nr++;
793794

794795
/*
795796
* Iterate subsequent iextent records and merge them with the one
796797
* that we just read, if possible.
797798
*/
798-
prev_len = irec->br_blockcount;
799799
while (xfs_iext_peek_next_extent(ifp, &info->icur, &got)) {
800800
if (!xchk_are_bmaps_contiguous(irec, &got))
801801
break;
@@ -805,20 +805,21 @@ xchk_bmap_iext_iter(
805805
got.br_startoff);
806806
return false;
807807
}
808-
809-
/*
810-
* Notify the user of mergeable records in the data or attr
811-
* forks. CoW forks only exist in memory so we ignore them.
812-
*/
813-
if (info->whichfork != XFS_COW_FORK &&
814-
prev_len + got.br_blockcount > BMBT_BLOCKCOUNT_MASK)
815-
xchk_ino_set_preen(info->sc, info->sc->ip->i_ino);
808+
nr++;
816809

817810
irec->br_blockcount += got.br_blockcount;
818-
prev_len = got.br_blockcount;
819811
xfs_iext_next(ifp, &info->icur);
820812
}
821813

814+
/*
815+
* If the merged mapping could be expressed with fewer bmbt records
816+
* than we actually found, notify the user that this fork could be
817+
* optimized. CoW forks only exist in memory so we ignore them.
818+
*/
819+
if (nr > 1 && info->whichfork != XFS_COW_FORK &&
820+
howmany_64(irec->br_blockcount, XFS_MAX_BMBT_EXTLEN) < nr)
821+
xchk_ino_set_preen(info->sc, info->sc->ip->i_ino);
822+
822823
return true;
823824
}
824825

0 commit comments

Comments
 (0)