@@ -332,14 +332,72 @@ xchk_refcountbt_xref(
332332 xchk_refcountbt_xref_rmap (sc , irec );
333333}
334334
335+ struct xchk_refcbt_records {
336+ /* The next AG block where we aren't expecting shared extents. */
337+ xfs_agblock_t next_unshared_agbno ;
338+
339+ /* Number of CoW blocks we expect. */
340+ xfs_agblock_t cow_blocks ;
341+
342+ /* Was the last record a shared or CoW staging extent? */
343+ enum xfs_refc_domain prev_domain ;
344+ };
345+
346+ STATIC int
347+ xchk_refcountbt_rmap_check_gap (
348+ struct xfs_btree_cur * cur ,
349+ const struct xfs_rmap_irec * rec ,
350+ void * priv )
351+ {
352+ xfs_agblock_t * next_bno = priv ;
353+
354+ if (* next_bno != NULLAGBLOCK && rec -> rm_startblock < * next_bno )
355+ return - ECANCELED ;
356+
357+ * next_bno = rec -> rm_startblock + rec -> rm_blockcount ;
358+ return 0 ;
359+ }
360+
361+ /*
362+ * Make sure that a gap in the reference count records does not correspond to
363+ * overlapping records (i.e. shared extents) in the reverse mappings.
364+ */
365+ static inline void
366+ xchk_refcountbt_xref_gaps (
367+ struct xfs_scrub * sc ,
368+ struct xchk_refcbt_records * rrc ,
369+ xfs_agblock_t bno )
370+ {
371+ struct xfs_rmap_irec low ;
372+ struct xfs_rmap_irec high ;
373+ xfs_agblock_t next_bno = NULLAGBLOCK ;
374+ int error ;
375+
376+ if (bno <= rrc -> next_unshared_agbno || !sc -> sa .rmap_cur ||
377+ xchk_skip_xref (sc -> sm ))
378+ return ;
379+
380+ memset (& low , 0 , sizeof (low ));
381+ low .rm_startblock = rrc -> next_unshared_agbno ;
382+ memset (& high , 0xFF , sizeof (high ));
383+ high .rm_startblock = bno - 1 ;
384+
385+ error = xfs_rmap_query_range (sc -> sa .rmap_cur , & low , & high ,
386+ xchk_refcountbt_rmap_check_gap , & next_bno );
387+ if (error == - ECANCELED )
388+ xchk_btree_xref_set_corrupt (sc , sc -> sa .rmap_cur , 0 );
389+ else
390+ xchk_should_check_xref (sc , & error , & sc -> sa .rmap_cur );
391+ }
392+
335393/* Scrub a refcountbt record. */
336394STATIC int
337395xchk_refcountbt_rec (
338396 struct xchk_btree * bs ,
339397 const union xfs_btree_rec * rec )
340398{
341399 struct xfs_refcount_irec irec ;
342- xfs_agblock_t * cow_blocks = bs -> private ;
400+ struct xchk_refcbt_records * rrc = bs -> private ;
343401
344402 xfs_refcount_btrec_to_irec (rec , & irec );
345403 if (xfs_refcount_check_irec (bs -> cur , & irec ) != NULL ) {
@@ -348,10 +406,27 @@ xchk_refcountbt_rec(
348406 }
349407
350408 if (irec .rc_domain == XFS_REFC_DOMAIN_COW )
351- (* cow_blocks ) += irec .rc_blockcount ;
409+ rrc -> cow_blocks += irec .rc_blockcount ;
410+
411+ /* Shared records always come before CoW records. */
412+ if (irec .rc_domain == XFS_REFC_DOMAIN_SHARED &&
413+ rrc -> prev_domain == XFS_REFC_DOMAIN_COW )
414+ xchk_btree_set_corrupt (bs -> sc , bs -> cur , 0 );
415+ rrc -> prev_domain = irec .rc_domain ;
352416
353417 xchk_refcountbt_xref (bs -> sc , & irec );
354418
419+ /*
420+ * If this is a record for a shared extent, check that all blocks
421+ * between the previous record and this one have at most one reverse
422+ * mapping.
423+ */
424+ if (irec .rc_domain == XFS_REFC_DOMAIN_SHARED ) {
425+ xchk_refcountbt_xref_gaps (bs -> sc , rrc , irec .rc_startblock );
426+ rrc -> next_unshared_agbno = irec .rc_startblock +
427+ irec .rc_blockcount ;
428+ }
429+
355430 return 0 ;
356431}
357432
@@ -393,15 +468,25 @@ int
393468xchk_refcountbt (
394469 struct xfs_scrub * sc )
395470{
396- xfs_agblock_t cow_blocks = 0 ;
471+ struct xchk_refcbt_records rrc = {
472+ .cow_blocks = 0 ,
473+ .next_unshared_agbno = 0 ,
474+ .prev_domain = XFS_REFC_DOMAIN_SHARED ,
475+ };
397476 int error ;
398477
399478 error = xchk_btree (sc , sc -> sa .refc_cur , xchk_refcountbt_rec ,
400- & XFS_RMAP_OINFO_REFC , & cow_blocks );
479+ & XFS_RMAP_OINFO_REFC , & rrc );
401480 if (error )
402481 return error ;
403482
404- xchk_refcount_xref_rmap (sc , cow_blocks );
483+ /*
484+ * Check that all blocks between the last refcount > 1 record and the
485+ * end of the AG have at most one reverse mapping.
486+ */
487+ xchk_refcountbt_xref_gaps (sc , & rrc , sc -> mp -> m_sb .sb_agblocks );
488+
489+ xchk_refcount_xref_rmap (sc , rrc .cow_blocks );
405490
406491 return 0 ;
407492}
0 commit comments