LCOV - code coverage report
Current view: top level - fs/xfs/scrub - quotacheck_repair.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-achx @ Mon Jul 31 20:08:12 PDT 2023 Lines: 85 109 78.0 %
Date: 2023-07-31 20:08:12 Functions: 4 4 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2020-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_log_format.h"
      13             : #include "xfs_trans.h"
      14             : #include "xfs_inode.h"
      15             : #include "xfs_quota.h"
      16             : #include "xfs_qm.h"
      17             : #include "xfs_icache.h"
      18             : #include "xfs_bmap_util.h"
      19             : #include "xfs_iwalk.h"
      20             : #include "xfs_ialloc.h"
      21             : #include "xfs_sb.h"
      22             : #include "scrub/scrub.h"
      23             : #include "scrub/common.h"
      24             : #include "scrub/repair.h"
      25             : #include "scrub/xfile.h"
      26             : #include "scrub/xfarray.h"
      27             : #include "scrub/iscan.h"
      28             : #include "scrub/quotacheck.h"
      29             : #include "scrub/trace.h"
      30             : 
      31             : /*
      32             :  * Live Quotacheck Repair
      33             :  * ======================
      34             :  *
      35             :  * Use the live quota counter information that we collected to replace the
      36             :  * counter values in the incore dquots.  A scrub->repair cycle should have left
      37             :  * the live data and hooks active, so this is safe so long as we make sure the
      38             :  * dquot is locked.
      39             :  */
      40             : 
      41             : /* Commit new counters to a dquot. */
      42             : static int
      43    12100960 : xqcheck_commit_dquot(
      44             :         struct xfs_dquot        *dqp,
      45             :         xfs_dqtype_t            dqtype,
      46             :         void                    *priv)
      47             : {
      48    12100960 :         struct xqcheck_dquot    xcdq;
      49    12100960 :         struct xqcheck          *xqc = priv;
      50    12100960 :         struct xfarray          *counts = xqcheck_counters_for(xqc, dqtype);
      51    12100960 :         int64_t                 delta;
      52    12100960 :         bool                    dirty = false;
      53    12100960 :         int                     error = 0;
      54             : 
      55             :         /* Unlock the dquot just long enough to allocate a transaction. */
      56    12100960 :         xfs_dqunlock(dqp);
      57    12100960 :         error = xchk_trans_alloc(xqc->sc, 0);
      58    12100960 :         xfs_dqlock(dqp);
      59    12100960 :         if (error)
      60             :                 return error;
      61             : 
      62    12100960 :         xfs_trans_dqjoin(xqc->sc->tp, dqp);
      63             : 
      64    12100960 :         if (xchk_iscan_aborted(&xqc->iscan)) {
      65           0 :                 error = -ECANCELED;
      66           0 :                 goto out_cancel;
      67             :         }
      68             : 
      69    12100960 :         mutex_lock(&xqc->lock);
      70    12100960 :         error = xfarray_load_sparse(counts, dqp->q_id, &xcdq);
      71    12100960 :         if (error)
      72           0 :                 goto out_unlock;
      73             : 
      74             :         /* Adjust counters as needed. */
      75    12100960 :         delta = (int64_t)xcdq.icount - dqp->q_ino.count;
      76    12100960 :         if (delta) {
      77           0 :                 dqp->q_ino.reserved += delta;
      78           0 :                 dqp->q_ino.count += delta;
      79           0 :                 dirty = true;
      80             :         }
      81             : 
      82    12100960 :         delta = (int64_t)xcdq.bcount - dqp->q_blk.count;
      83    12100960 :         if (delta) {
      84           0 :                 dqp->q_blk.reserved += delta;
      85           0 :                 dqp->q_blk.count += delta;
      86           0 :                 dirty = true;
      87             :         }
      88             : 
      89    12100960 :         delta = (int64_t)xcdq.rtbcount - dqp->q_rtb.count;
      90    12100960 :         if (delta) {
      91           0 :                 dqp->q_rtb.reserved += delta;
      92           0 :                 dqp->q_rtb.count += delta;
      93           0 :                 dirty = true;
      94             :         }
      95             : 
      96    12100960 :         xcdq.flags |= (XQCHECK_DQUOT_REPAIR_SCANNED | XQCHECK_DQUOT_WRITTEN);
      97    12100960 :         error = xfarray_store(counts, dqp->q_id, &xcdq);
      98    12100960 :         if (error == -EFBIG) {
      99             :                 /*
     100             :                  * EFBIG means we tried to store data at too high a byte offset
     101             :                  * in the sparse array.  IOWs, we cannot complete the repair
     102             :                  * and must cancel the whole operation.  This should never
     103             :                  * happen, but we need to catch it anyway.
     104             :                  */
     105           0 :                 error = -ECANCELED;
     106             :         }
     107    12100960 :         mutex_unlock(&xqc->lock);
     108    12100960 :         if (error || !dirty)
     109    12100960 :                 goto out_cancel;
     110             : 
     111           0 :         trace_xrep_quotacheck_dquot(xqc->sc->mp, dqp->q_type, dqp->q_id);
     112             : 
     113             :         /* Commit the dirty dquot to disk. */
     114           0 :         dqp->q_flags |= XFS_DQFLAG_DIRTY;
     115           0 :         if (dqp->q_id)
     116           0 :                 xfs_qm_adjust_dqtimers(dqp);
     117           0 :         xfs_trans_log_dquot(xqc->sc->tp, dqp);
     118             : 
     119             :         /*
     120             :          * Transaction commit unlocks the dquot, so we must re-lock it so that
     121             :          * the caller can put the reference (which apparently requires a locked
     122             :          * dquot).
     123             :          */
     124           0 :         error = xrep_trans_commit(xqc->sc);
     125           0 :         xfs_dqlock(dqp);
     126           0 :         return error;
     127             : 
     128             : out_unlock:
     129           0 :         mutex_unlock(&xqc->lock);
     130    12100960 : out_cancel:
     131    12100960 :         xchk_trans_cancel(xqc->sc);
     132             : 
     133             :         /* Re-lock the dquot so the caller can put the reference. */
     134    12100960 :         xfs_dqlock(dqp);
     135    12100960 :         return error;
     136             : }
     137             : 
     138             : /* Commit new quota counters for a particular quota type. */
     139             : STATIC int
     140        9312 : xqcheck_commit_dqtype(
     141             :         struct xqcheck          *xqc,
     142             :         unsigned int            dqtype)
     143             : {
     144        9312 :         struct xqcheck_dquot    xcdq;
     145        9312 :         struct xfs_scrub        *sc = xqc->sc;
     146        9312 :         struct xfs_mount        *mp = sc->mp;
     147        9312 :         struct xfarray          *counts = xqcheck_counters_for(xqc, dqtype);
     148        9312 :         struct xfs_dquot        *dqp;
     149        9312 :         xfarray_idx_t           cur = XFARRAY_CURSOR_INIT;
     150        9312 :         int                     error;
     151             : 
     152             :         /*
     153             :          * Update the counters of every dquot that the quota file knows about.
     154             :          */
     155        9312 :         error = xfs_qm_dqiterate(mp, dqtype, xqcheck_commit_dquot, xqc);
     156        9312 :         if (error)
     157             :                 return error;
     158             : 
     159             :         /*
     160             :          * Make a second pass to deal with the dquots that we know about but
     161             :          * the quota file previously did not know about.
     162             :          */
     163        9312 :         mutex_lock(&xqc->lock);
     164    12110272 :         while ((error = xfarray_iter(counts, &cur, &xcdq)) == 1) {
     165    12100960 :                 xfs_dqid_t      id = cur - 1;
     166             : 
     167    12100960 :                 if (xcdq.flags & XQCHECK_DQUOT_REPAIR_SCANNED)
     168        9312 :                         continue;
     169             : 
     170    12091648 :                 mutex_unlock(&xqc->lock);
     171             : 
     172             :                 /*
     173             :                  * Grab the dquot, allowing for dquot block allocation in a
     174             :                  * separate transaction.  We committed the scrub transaction
     175             :                  * in a previous step, so we will not be creating nested
     176             :                  * transactions here.
     177             :                  */
     178    12091648 :                 error = xfs_qm_dqget(mp, id, dqtype, true, &dqp);
     179    12091648 :                 if (error)
     180           0 :                         return error;
     181             : 
     182    12091648 :                 error = xqcheck_commit_dquot(dqp, dqtype, xqc);
     183    12091648 :                 xfs_qm_dqput(dqp);
     184    12091648 :                 if (error)
     185           0 :                         return error;
     186             : 
     187    12091648 :                 mutex_lock(&xqc->lock);
     188             :         }
     189        9312 :         mutex_unlock(&xqc->lock);
     190             : 
     191        9312 :         return error;
     192             : }
     193             : 
     194             : /* Figure out quota CHKD flags for the running quota types. */
     195             : static inline unsigned int
     196        3104 : xqcheck_chkd_flags(
     197             :         struct xfs_mount        *mp)
     198             : {
     199        3104 :         unsigned int            ret = 0;
     200             : 
     201        3104 :         if (XFS_IS_UQUOTA_ON(mp))
     202        3104 :                 ret |= XFS_UQUOTA_CHKD;
     203        3104 :         if (XFS_IS_GQUOTA_ON(mp))
     204        3104 :                 ret |= XFS_GQUOTA_CHKD;
     205        3104 :         if (XFS_IS_PQUOTA_ON(mp))
     206        3104 :                 ret |= XFS_PQUOTA_CHKD;
     207        3104 :         return ret;
     208             : }
     209             : 
     210             : /* Commit the new dquot counters. */
     211             : int
     212        3104 : xrep_quotacheck(
     213             :         struct xfs_scrub        *sc)
     214             : {
     215        3104 :         struct xqcheck          *xqc = sc->buf;
     216        3104 :         unsigned int            qflags = xqcheck_chkd_flags(sc->mp);
     217        3104 :         int                     error;
     218             : 
     219             :         /*
     220             :          * Clear the CHKD flag for the running quota types and commit the scrub
     221             :          * transaction so that we can allocate new quota block mappings if we
     222             :          * have to.  If we crash after this point, the sb still has the CHKD
     223             :          * flags cleared, so mount quotacheck will fix all of this up.
     224             :          */
     225        3104 :         xrep_update_qflags(sc, qflags, 0);
     226        3104 :         error = xrep_trans_commit(sc);
     227        3104 :         if (error)
     228             :                 return error;
     229             : 
     230             :         /* Commit the new counters to the dquots. */
     231        3104 :         if (xqc->ucounts) {
     232        3104 :                 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_USER);
     233        3104 :                 if (error)
     234             :                         return error;
     235             :         }
     236        3104 :         if (xqc->gcounts) {
     237        3104 :                 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_GROUP);
     238        3104 :                 if (error)
     239             :                         return error;
     240             :         }
     241        3104 :         if (xqc->pcounts) {
     242        3104 :                 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_PROJ);
     243        3104 :                 if (error)
     244             :                         return error;
     245             :         }
     246             : 
     247             :         /* Set the CHKD flags now that we've fixed quota counts. */
     248        3104 :         error = xchk_trans_alloc(sc, 0);
     249        3104 :         if (error)
     250             :                 return error;
     251             : 
     252        3104 :         xrep_update_qflags(sc, 0, qflags);
     253        3104 :         return 0;
     254             : }

Generated by: LCOV version 1.14