LCOV - code coverage report
Current view: top level - fs/xfs/scrub - cow_repair.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-acha @ Mon Jul 31 20:08:06 PDT 2023 Lines: 178 229 77.7 %
Date: 2023-07-31 20:08:07 Functions: 11 12 91.7 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2022-2023 Oracle.  All Rights Reserved.
       4             :  * Author: Darrick J. Wong <djwong@kernel.org>
       5             :  */
       6             : #include "xfs.h"
       7             : #include "xfs_fs.h"
       8             : #include "xfs_shared.h"
       9             : #include "xfs_format.h"
      10             : #include "xfs_trans_resv.h"
      11             : #include "xfs_mount.h"
      12             : #include "xfs_defer.h"
      13             : #include "xfs_btree.h"
      14             : #include "xfs_log_format.h"
      15             : #include "xfs_trans.h"
      16             : #include "xfs_inode.h"
      17             : #include "xfs_inode_fork.h"
      18             : #include "xfs_alloc.h"
      19             : #include "xfs_bmap.h"
      20             : #include "xfs_rmap.h"
      21             : #include "xfs_refcount.h"
      22             : #include "xfs_quota.h"
      23             : #include "xfs_ialloc.h"
      24             : #include "xfs_ag.h"
      25             : #include "xfs_error.h"
      26             : #include "xfs_errortag.h"
      27             : #include "xfs_icache.h"
      28             : #include "xfs_refcount_btree.h"
      29             : #include "scrub/xfs_scrub.h"
      30             : #include "scrub/scrub.h"
      31             : #include "scrub/common.h"
      32             : #include "scrub/trace.h"
      33             : #include "scrub/repair.h"
      34             : #include "scrub/bitmap.h"
      35             : #include "scrub/reap.h"
      36             : 
      37             : /*
      38             :  * CoW Fork Mapping Repair
      39             :  * =======================
      40             :  *
      41             :  * Although CoW staging extents are owned by incore CoW inode forks, on disk
      42             :  * they are owned by the refcount btree.  The ondisk metadata does not record
      43             :  * any ownership information, which limits what we can do to repair the
      44             :  * mappings in the CoW fork.  At most, we can replace ifork mappings that lack
      45             :  * an entry in the refcount btree or are described by a reverse mapping record
      46             :  * whose owner is not OWN_COW.
      47             :  *
      48             :  * Replacing extents is also tricky -- we can't touch written CoW fork extents
      49             :  * since they are undergoing writeback, and delalloc extents do not require
      50             :  * repair since they only exist incore.  Hence the most we can do is find the
      51             :  * bad parts of unwritten mappings, allocate a replacement set of blocks, and
      52             :  * replace the incore mapping.  We use the regular reaping process to unmap
      53             :  * or free the discarded blocks, as appropriate.
      54             :  */
      55             : struct xrep_cow {
      56             :         struct xfs_scrub        *sc;
      57             : 
      58             :         /* Bitmap of file offset ranges that need replacing. */
      59             :         struct xbitmap          bad_fileoffs;
      60             : 
      61             :         /* Bitmap of fsblocks that were removed from the CoW fork. */
      62             :         struct xfsb_bitmap      old_cowfork_fsblocks;
      63             : 
      64             :         /* CoW fork mappings used to scan for bad CoW staging extents. */
      65             :         struct xfs_bmbt_irec    irec;
      66             : 
      67             :         /* refcount btree block number of irec.br_startblock */
      68             :         unsigned int            irec_startbno;
      69             : 
      70             :         /* refcount btree block number of the next refcount record we expect */
      71             :         unsigned int            next_bno;
      72             : };
      73             : 
      74             : /* CoW staging extent. */
      75             : struct xrep_cow_extent {
      76             :         xfs_fsblock_t           fsbno;
      77             :         xfs_extlen_t            len;
      78             : };
      79             : 
      80             : /*
      81             :  * Mark the part of the file range that corresponds to the given physical
      82             :  * space.  Caller must ensure that the physical range is within xc->irec.
      83             :  */
      84             : STATIC int
      85        1410 : xrep_cow_mark_file_range(
      86             :         struct xrep_cow         *xc,
      87             :         xfs_fsblock_t           startblock,
      88             :         xfs_filblks_t           blockcount)
      89             : {
      90        1410 :         xfs_fileoff_t           startoff;
      91             : 
      92        1410 :         startoff = xc->irec.br_startoff +
      93        1410 :                                 (startblock - xc->irec.br_startblock);
      94             : 
      95        1410 :         trace_xrep_cow_mark_file_range(xc->sc->ip, startblock, startoff,
      96             :                         blockcount);
      97             : 
      98        1410 :         return xbitmap_set(&xc->bad_fileoffs, startoff, blockcount);
      99             : }
     100             : 
     101             : /*
     102             :  * Trim @src to fit within the CoW fork mapping being examined, and put the
     103             :  * result in @dst.
     104             :  */
     105             : static inline void
     106        1410 : xrep_cow_trim_refcount(
     107             :         struct xrep_cow                 *xc,
     108             :         struct xfs_refcount_irec        *dst,
     109             :         const struct xfs_refcount_irec  *src)
     110             : {
     111        1410 :         unsigned int                    adj;
     112             : 
     113        2820 :         memcpy(dst, src, sizeof(*dst));
     114             : 
     115        1410 :         if (dst->rc_startblock < xc->irec_startbno) {
     116         577 :                 adj = xc->irec_startbno - dst->rc_startblock;
     117         577 :                 dst->rc_blockcount -= adj;
     118         577 :                 dst->rc_startblock += adj;
     119             :         }
     120             : 
     121        1410 :         if (dst->rc_startblock + dst->rc_blockcount >
     122        1410 :             xc->irec_startbno + xc->irec.br_blockcount) {
     123         578 :                 adj = (dst->rc_startblock + dst->rc_blockcount) -
     124         578 :                       (xc->irec_startbno + xc->irec.br_blockcount);
     125         578 :                 dst->rc_blockcount -= adj;
     126             :         }
     127        1410 : }
     128             : 
     129             : /* Mark any shared CoW staging extents. */
     130             : STATIC int
     131           0 : xrep_cow_mark_shared_staging(
     132             :         struct xfs_btree_cur            *cur,
     133             :         const struct xfs_refcount_irec  *rec,
     134             :         void                            *priv)
     135             : {
     136           0 :         struct xrep_cow                 *xc = priv;
     137           0 :         struct xfs_refcount_irec        rrec;
     138           0 :         xfs_fsblock_t                   fsbno;
     139             : 
     140           0 :         if (!xfs_refcount_check_domain(rec) ||
     141           0 :             rec->rc_domain != XFS_REFC_DOMAIN_SHARED)
     142             :                 return -EFSCORRUPTED;
     143             : 
     144           0 :         xrep_cow_trim_refcount(xc, &rrec, rec);
     145             : 
     146           0 :         fsbno = XFS_AGB_TO_FSB(xc->sc->mp, cur->bc_ag.pag->pag_agno,
     147             :                         rrec.rc_startblock);
     148           0 :         return xrep_cow_mark_file_range(xc, fsbno, rrec.rc_blockcount);
     149             : }
     150             : 
     151             : /*
     152             :  * Mark any portion of the CoW fork file offset range where there is not a CoW
     153             :  * staging extent record in the refcountbt, and keep a record of where we did
     154             :  * find correct refcountbt records.  Staging records are always cleaned out at
     155             :  * mount time, so any two inodes trying to map the same staging area would have
     156             :  * already taken the fs down due to refcount btree verifier errors.  Hence this
     157             :  * inode should be the sole creator of the staging extent records ondisk.
     158             :  */
     159             : STATIC int
     160        1410 : xrep_cow_mark_missing_staging(
     161             :         struct xfs_btree_cur            *cur,
     162             :         const struct xfs_refcount_irec  *rec,
     163             :         void                            *priv)
     164             : {
     165        1410 :         struct xrep_cow                 *xc = priv;
     166        1410 :         struct xfs_refcount_irec        rrec;
     167        1410 :         int                             error;
     168             : 
     169        1410 :         if (!xfs_refcount_check_domain(rec) ||
     170        1410 :             rec->rc_domain != XFS_REFC_DOMAIN_COW)
     171             :                 return -EFSCORRUPTED;
     172             : 
     173        1410 :         xrep_cow_trim_refcount(xc, &rrec, rec);
     174             : 
     175        1410 :         if (xc->next_bno >= rrec.rc_startblock)
     176        1410 :                 goto next;
     177             : 
     178           0 :         error = xrep_cow_mark_file_range(xc,
     179           0 :                         XFS_AGB_TO_FSB(xc->sc->mp, cur->bc_ag.pag->pag_agno,
     180             :                                        xc->next_bno),
     181           0 :                         rrec.rc_startblock - xc->next_bno);
     182           0 :         if (error)
     183             :                 return error;
     184             : 
     185           0 : next:
     186        1410 :         xc->next_bno = rrec.rc_startblock + rrec.rc_blockcount;
     187        1410 :         return 0;
     188             : }
     189             : 
     190             : /*
     191             :  * Mark any area that does not correspond to a CoW staging rmap.  These are
     192             :  * cross-linked areas that must be avoided.
     193             :  */
     194             : STATIC int
     195        1410 : xrep_cow_mark_missing_staging_rmap(
     196             :         struct xfs_btree_cur            *cur,
     197             :         const struct xfs_rmap_irec      *rec,
     198             :         void                            *priv)
     199             : {
     200        1410 :         struct xrep_cow                 *xc = priv;
     201        1410 :         xfs_fsblock_t                   fsbno;
     202        1410 :         xfs_agblock_t                   rec_bno;
     203        1410 :         xfs_extlen_t                    rec_len;
     204        1410 :         unsigned int                    adj;
     205             : 
     206        1410 :         if (rec->rm_owner == XFS_RMAP_OWN_COW)
     207             :                 return 0;
     208             : 
     209           0 :         rec_bno = rec->rm_startblock;
     210           0 :         rec_len = rec->rm_blockcount;
     211           0 :         if (rec_bno < xc->irec_startbno) {
     212           0 :                 adj = xc->irec_startbno - rec_bno;
     213           0 :                 rec_len -= adj;
     214           0 :                 rec_bno += adj;
     215             :         }
     216             : 
     217           0 :         if (rec_bno + rec_len > xc->irec_startbno + xc->irec.br_blockcount) {
     218           0 :                 adj = (rec_bno + rec_len) -
     219           0 :                       (xc->irec_startbno + xc->irec.br_blockcount);
     220           0 :                 rec_len -= adj;
     221             :         }
     222             : 
     223           0 :         fsbno = XFS_AGB_TO_FSB(xc->sc->mp, cur->bc_ag.pag->pag_agno, rec_bno);
     224           0 :         return xrep_cow_mark_file_range(xc, fsbno, rec_len);
     225             : }
     226             : 
     227             : /*
     228             :  * Find any part of the CoW fork mapping that isn't a single-owner CoW staging
     229             :  * extent and mark the corresponding part of the file range in the bitmap.
     230             :  */
     231             : STATIC int
     232        1410 : xrep_cow_find_bad(
     233             :         struct xrep_cow                 *xc)
     234             : {
     235        1410 :         struct xfs_refcount_irec        rc_low = { 0 };
     236        1410 :         struct xfs_refcount_irec        rc_high = { 0 };
     237        1410 :         struct xfs_rmap_irec            rm_low = { 0 };
     238        1410 :         struct xfs_rmap_irec            rm_high = { 0 };
     239        1410 :         struct xfs_perag                *pag;
     240        1410 :         struct xfs_scrub                *sc = xc->sc;
     241        1410 :         xfs_agnumber_t                  agno;
     242        1410 :         int                             error;
     243             : 
     244        1410 :         agno = XFS_FSB_TO_AGNO(sc->mp, xc->irec.br_startblock);
     245        1410 :         xc->irec_startbno = XFS_FSB_TO_AGBNO(sc->mp, xc->irec.br_startblock);
     246             : 
     247        1410 :         pag = xfs_perag_get(sc->mp, agno);
     248        1410 :         if (!pag)
     249             :                 return -EFSCORRUPTED;
     250             : 
     251        1410 :         error = xrep_ag_init(sc, pag, &sc->sa);
     252        1410 :         if (error)
     253           0 :                 goto out_pag;
     254             : 
     255             :         /* Mark any CoW fork extents that are shared. */
     256        1410 :         rc_low.rc_startblock = xc->irec_startbno;
     257        1410 :         rc_high.rc_startblock = xc->irec_startbno + xc->irec.br_blockcount - 1;
     258        1410 :         rc_low.rc_domain = rc_high.rc_domain = XFS_REFC_DOMAIN_SHARED;
     259        1410 :         error = xfs_refcount_query_range(sc->sa.refc_cur, &rc_low, &rc_high,
     260             :                         xrep_cow_mark_shared_staging, xc);
     261        1410 :         if (error)
     262           0 :                 goto out_sa;
     263             : 
     264             :         /* Make sure there are CoW staging extents for the whole mapping. */
     265        1410 :         rc_low.rc_startblock = xc->irec_startbno;
     266        1410 :         rc_high.rc_startblock = xc->irec_startbno + xc->irec.br_blockcount - 1;
     267        1410 :         rc_low.rc_domain = rc_high.rc_domain = XFS_REFC_DOMAIN_COW;
     268        1410 :         xc->next_bno = xc->irec_startbno;
     269        1410 :         error = xfs_refcount_query_range(sc->sa.refc_cur, &rc_low, &rc_high,
     270             :                         xrep_cow_mark_missing_staging, xc);
     271        1410 :         if (error)
     272           0 :                 goto out_sa;
     273             : 
     274        1410 :         if (xc->next_bno < xc->irec_startbno + xc->irec.br_blockcount) {
     275           0 :                 error = xrep_cow_mark_file_range(xc,
     276           0 :                                 XFS_AGB_TO_FSB(sc->mp, pag->pag_agno,
     277             :                                                xc->next_bno),
     278             :                                 xc->irec_startbno + xc->irec.br_blockcount -
     279             :                                 xc->next_bno);
     280           0 :                 if (error)
     281           0 :                         goto out_sa;
     282             :         }
     283             : 
     284             :         /* Mark any area has an rmap that isn't a COW staging extent. */
     285        1410 :         rm_low.rm_startblock = xc->irec_startbno;
     286        1410 :         memset(&rm_high, 0xFF, sizeof(rm_high));
     287        1410 :         rm_high.rm_startblock = xc->irec_startbno + xc->irec.br_blockcount - 1;
     288        1410 :         error = xfs_rmap_query_range(sc->sa.rmap_cur, &rm_low, &rm_high,
     289             :                         xrep_cow_mark_missing_staging_rmap, xc);
     290        1410 :         if (error)
     291           0 :                 goto out_sa;
     292             : 
     293             :         /*
     294             :          * If userspace is forcing us to rebuild the CoW fork or someone turned
     295             :          * on the debugging knob, replace everything in the CoW fork.
     296             :          */
     297        1410 :         if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
     298           0 :             XFS_TEST_ERROR(false, sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) {
     299        1410 :                 error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
     300             :                                 xc->irec.br_blockcount);
     301        1410 :                 if (error)
     302             :                         return error;
     303             :         }
     304             : 
     305        1410 : out_sa:
     306        1410 :         xchk_ag_free(sc, &sc->sa);
     307        1410 : out_pag:
     308        1410 :         xfs_perag_put(pag);
     309        1410 :         return 0;
     310             : }
     311             : 
     312             : /*
     313             :  * Allocate a replacement CoW staging extent of up to the given number of
     314             :  * blocks, and fill out the mapping.
     315             :  */
     316             : STATIC int
     317        1724 : xrep_cow_alloc(
     318             :         struct xfs_scrub        *sc,
     319             :         xfs_filblks_t           maxlen,
     320             :         struct xrep_cow_extent  *repl)
     321             : {
     322        1724 :         struct xfs_alloc_arg    args = {
     323        1724 :                 .tp             = sc->tp,
     324        1724 :                 .mp             = sc->mp,
     325             :                 .oinfo          = XFS_RMAP_OINFO_SKIP_UPDATE,
     326             :                 .minlen         = 1,
     327             :                 .maxlen         = maxlen,
     328             :                 .prod           = 1,
     329             :                 .resv           = XFS_AG_RESV_NONE,
     330             :                 .datatype       = XFS_ALLOC_USERDATA,
     331             :         };
     332        1724 :         int                     error;
     333             : 
     334        1724 :         error = xfs_trans_reserve_more(sc->tp, maxlen, 0);
     335        1724 :         if (error)
     336             :                 return error;
     337             : 
     338        1724 :         error = xfs_alloc_vextent_start_ag(&args,
     339        1724 :                         XFS_INO_TO_FSB(sc->mp, sc->ip->i_ino));
     340        1724 :         if (error)
     341             :                 return error;
     342        1724 :         if (args.fsbno == NULLFSBLOCK)
     343             :                 return -ENOSPC;
     344             : 
     345        1724 :         xfs_refcount_alloc_cow_extent(sc->tp, args.fsbno, args.len);
     346             : 
     347        1724 :         repl->fsbno = args.fsbno;
     348        1724 :         repl->len = args.len;
     349        1724 :         return 0;
     350             : }
     351             : 
     352             : /*
     353             :  * Look up the current CoW fork mapping so that we only allocate enough to
     354             :  * replace a single mapping.  If we don't find a mapping that covers the start
     355             :  * of the file range, or we find a delalloc or written extent, something is
     356             :  * seriously wrong, since we didn't drop the ILOCK.
     357             :  */
     358             : static inline int
     359        1724 : xrep_cow_find_mapping(
     360             :         struct xrep_cow         *xc,
     361             :         struct xfs_iext_cursor  *icur,
     362             :         xfs_fileoff_t           startoff,
     363             :         struct xfs_bmbt_irec    *got)
     364             : {
     365        1724 :         struct xfs_inode        *ip = xc->sc->ip;
     366        1724 :         struct xfs_ifork        *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
     367             : 
     368        1724 :         if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got))
     369           0 :                 goto bad;
     370             : 
     371        1724 :         if (got->br_startoff > startoff)
     372           0 :                 goto bad;
     373             : 
     374        1724 :         if (got->br_blockcount == 0)
     375           0 :                 goto bad;
     376             : 
     377        1724 :         if (isnullstartblock(got->br_startblock))
     378           0 :                 goto bad;
     379             : 
     380        1724 :         if (xfs_bmap_is_written_extent(got))
     381           0 :                 goto bad;
     382             : 
     383             :         return 0;
     384           0 : bad:
     385           0 :         ASSERT(0);
     386           0 :         return -EFSCORRUPTED;
     387             : }
     388             : 
     389             : #define REPLACE_LEFT_SIDE       (1U << 0)
     390             : #define REPLACE_RIGHT_SIDE      (1U << 1)
     391             : 
     392             : /*
     393             :  * Given a CoW fork mapping @got and a replacement mapping @repl, remap the
     394             :  * beginning of @got with the space described by @rep.
     395             :  */
     396             : static inline void
     397        1724 : xrep_cow_replace_mapping(
     398             :         struct xfs_inode                *ip,
     399             :         struct xfs_iext_cursor          *icur,
     400             :         const struct xfs_bmbt_irec      *got,
     401             :         const struct xrep_cow_extent    *repl)
     402             : {
     403        1724 :         struct xfs_bmbt_irec            new = *got; /* struct copy */
     404             : 
     405        1724 :         ASSERT(repl->len > 0);
     406        1724 :         ASSERT(!isnullstartblock(got->br_startblock));
     407             : 
     408        1724 :         trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len);
     409             : 
     410        1724 :         if (got->br_blockcount == repl->len) {
     411             :                 /*
     412             :                  * The new extent is a complete replacement for the existing
     413             :                  * extent.  Update the COW fork record.
     414             :                  */
     415        1410 :                 new.br_startblock = repl->fsbno;
     416        1410 :                 xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
     417        1410 :                 return;
     418             :         }
     419             : 
     420             :         /*
     421             :          * The new extent can replace the beginning of the COW fork record.
     422             :          * Move the left side of @got upwards, then insert the new record.
     423             :          */
     424         314 :         new.br_startoff += repl->len;
     425         314 :         new.br_startblock += repl->len;
     426         314 :         new.br_blockcount -= repl->len;
     427         314 :         xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
     428             : 
     429         314 :         new.br_startoff = got->br_startoff;
     430         314 :         new.br_startblock = repl->fsbno;
     431         314 :         new.br_blockcount = repl->len;
     432         314 :         xfs_iext_insert(ip, icur, &new, BMAP_COWFORK);
     433             : }
     434             : 
     435             : /*
     436             :  * Replace the unwritten CoW staging extent backing the given file range with a
     437             :  * new space extent that isn't as problematic.
     438             :  */
     439             : STATIC int
     440        1724 : xrep_cow_replace_range(
     441             :         struct xrep_cow         *xc,
     442             :         xfs_fileoff_t           startoff,
     443             :         xfs_extlen_t            *blockcount)
     444             : {
     445        1724 :         struct xfs_iext_cursor  icur;
     446        1724 :         struct xrep_cow_extent  repl;
     447        1724 :         struct xfs_bmbt_irec    got;
     448        1724 :         struct xfs_scrub        *sc = xc->sc;
     449        1724 :         xfs_fileoff_t           nextoff;
     450        1724 :         int                     error;
     451             : 
     452             :         /*
     453             :          * Put the existing CoW fork mapping in @got.  If @got ends before
     454             :          * @rep, truncate @rep so we only replace one extent mapping at a time.
     455             :          */
     456        1724 :         error = xrep_cow_find_mapping(xc, &icur, startoff, &got);
     457        1724 :         if (error)
     458             :                 return error;
     459        1724 :         nextoff = min(startoff + *blockcount,
     460             :                       got.br_startoff + got.br_blockcount);
     461             : 
     462             :         /*
     463             :          * Allocate a replacement extent.  If we don't fill all the blocks,
     464             :          * shorten the quantity that will be deleted in this step.
     465             :          */
     466        1724 :         error = xrep_cow_alloc(sc, nextoff - startoff, &repl);
     467        1724 :         if (error)
     468             :                 return error;
     469             : 
     470             :         /*
     471             :          * Replace the old mapping with the new one, and commit the metadata
     472             :          * changes made so far.
     473             :          */
     474        1724 :         xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl);
     475             : 
     476        1724 :         xfs_inode_set_cowblocks_tag(sc->ip);
     477        1724 :         error = xfs_defer_finish(&sc->tp);
     478        1724 :         if (error)
     479             :                 return error;
     480             : 
     481             :         /* Note the old CoW staging extents; we'll reap them all later. */
     482        1724 :         error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, got.br_startblock,
     483        1724 :                         repl.len);
     484        1724 :         if (error)
     485             :                 return error;
     486             : 
     487        1724 :         *blockcount = repl.len;
     488        1724 :         return 0;
     489             : }
     490             : 
     491             : /*
     492             :  * Replace a bad part of an unwritten CoW staging extent with a fresh delalloc
     493             :  * reservation.
     494             :  */
     495             : STATIC int
     496         341 : xrep_cow_replace(
     497             :         uint64_t                startoff,
     498             :         uint64_t                blockcount,
     499             :         void                    *priv)
     500             : {
     501         341 :         struct xrep_cow         *xc = priv;
     502         341 :         int                     error = 0;
     503             : 
     504        2065 :         while (blockcount > 0) {
     505        1724 :                 xfs_extlen_t    len = min_t(xfs_filblks_t, blockcount,
     506             :                                             XFS_MAX_BMBT_EXTLEN);
     507             : 
     508        1724 :                 error = xrep_cow_replace_range(xc, startoff, &len);
     509        1724 :                 if (error)
     510             :                         break;
     511             : 
     512        1724 :                 blockcount -= len;
     513        1724 :                 startoff += len;
     514             :         }
     515             : 
     516         341 :         return error;
     517             : }
     518             : 
     519             : /*
     520             :  * Repair an inode's CoW fork.  The CoW fork is an in-core structure, so
     521             :  * there's no btree to rebuid.  Instead, we replace any mappings that are
     522             :  * cross-linked or lack ondisk CoW fork records in the refcount btree.
     523             :  */
     524             : int
     525       30821 : xrep_bmap_cow(
     526             :         struct xfs_scrub        *sc)
     527             : {
     528       30821 :         struct xrep_cow         *xc;
     529       30821 :         struct xfs_iext_cursor  icur;
     530       30821 :         struct xfs_ifork        *ifp = xfs_ifork_ptr(sc->ip, XFS_COW_FORK);
     531       30821 :         int                     error;
     532             : 
     533       30821 :         if (!xfs_has_rmapbt(sc->mp) || !xfs_has_reflink(sc->mp))
     534             :                 return -EOPNOTSUPP;
     535             : 
     536       30821 :         if (!ifp)
     537             :                 return 0;
     538             : 
     539             :         /* realtime files aren't supported yet */
     540       30821 :         if (XFS_IS_REALTIME_INODE(sc->ip))
     541             :                 return -EOPNOTSUPP;
     542             : 
     543             :         /*
     544             :          * If we're somehow not in extents format, then reinitialize it to
     545             :          * an empty extent mapping fork and exit.
     546             :          */
     547       30821 :         if (ifp->if_format != XFS_DINODE_FMT_EXTENTS) {
     548           0 :                 ifp->if_format = XFS_DINODE_FMT_EXTENTS;
     549           0 :                 ifp->if_nextents = 0;
     550           0 :                 return 0;
     551             :         }
     552             : 
     553       30821 :         xc = kzalloc(sizeof(struct xrep_cow), XCHK_GFP_FLAGS);
     554       30821 :         if (!xc)
     555             :                 return -ENOMEM;
     556             : 
     557       30821 :         xfs_trans_ijoin(sc->tp, sc->ip, 0);
     558             : 
     559       30821 :         xc->sc = sc;
     560       30821 :         xbitmap_init(&xc->bad_fileoffs);
     561       30821 :         xfsb_bitmap_init(&xc->old_cowfork_fsblocks);
     562             : 
     563       32231 :         for_each_xfs_iext(ifp, &icur, &xc->irec) {
     564        1410 :                 if (xchk_should_terminate(sc, &error))
     565           0 :                         goto out_bitmap;
     566             : 
     567             :                 /*
     568             :                  * delalloc reservations only exist incore, so there is no
     569             :                  * ondisk metadata that we can examine.  Hence we leave them
     570             :                  * alone.
     571             :                  */
     572        1410 :                 if (isnullstartblock(xc->irec.br_startblock))
     573           0 :                         continue;
     574             : 
     575             :                 /*
     576             :                  * COW fork extents are only in the written state if writeback
     577             :                  * is actively writing to disk.  We cannot restart the write
     578             :                  * at a different disk address since we've already issued the
     579             :                  * IO, so we leave these alone and hope for the best.
     580             :                  */
     581        1410 :                 if (xfs_bmap_is_written_extent(&xc->irec))
     582           0 :                         continue;
     583             : 
     584        1410 :                 error = xrep_cow_find_bad(xc);
     585        1410 :                 if (error)
     586           0 :                         goto out_bitmap;
     587             :         }
     588             : 
     589             :         /* Replace any bad unwritten mappings with fresh reservations. */
     590       30821 :         error = xbitmap_walk(&xc->bad_fileoffs, xrep_cow_replace, xc);
     591       30821 :         if (error)
     592           0 :                 goto out_bitmap;
     593             : 
     594             :         /*
     595             :          * Reap as many of the old CoW blocks as we can.  They are owned ondisk
     596             :          * by the refcount btree, not the inode, so it is correct to treat them
     597             :          * like inode metadata.
     598             :          */
     599       30821 :         error = xrep_reap_fsblocks(sc, &xc->old_cowfork_fsblocks,
     600             :                         &XFS_RMAP_OINFO_COW);
     601       30821 :         if (error)
     602             :                 goto out_bitmap;
     603             : 
     604       30821 : out_bitmap:
     605       30821 :         xfsb_bitmap_destroy(&xc->old_cowfork_fsblocks);
     606       30821 :         xbitmap_destroy(&xc->bad_fileoffs);
     607       30821 :         kmem_free(xc);
     608       30821 :         return error;
     609             : }

Generated by: LCOV version 1.14