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-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 85 109 78.0 %
Date: 2023-07-31 20:08:27 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     2502581 : xqcheck_commit_dquot(
      44             :         struct xfs_dquot        *dqp,
      45             :         xfs_dqtype_t            dqtype,
      46             :         void                    *priv)
      47             : {
      48     2502581 :         struct xqcheck_dquot    xcdq;
      49     2502581 :         struct xqcheck          *xqc = priv;
      50     2502581 :         struct xfarray          *counts = xqcheck_counters_for(xqc, dqtype);
      51     2502581 :         int64_t                 delta;
      52     2502581 :         bool                    dirty = false;
      53     2502581 :         int                     error = 0;
      54             : 
      55             :         /* Unlock the dquot just long enough to allocate a transaction. */
      56     2502581 :         xfs_dqunlock(dqp);
      57     2502581 :         error = xchk_trans_alloc(xqc->sc, 0);
      58     2502581 :         xfs_dqlock(dqp);
      59     2502581 :         if (error)
      60             :                 return error;
      61             : 
      62     2502581 :         xfs_trans_dqjoin(xqc->sc->tp, dqp);
      63             : 
      64     2502581 :         if (xchk_iscan_aborted(&xqc->iscan)) {
      65           0 :                 error = -ECANCELED;
      66           0 :                 goto out_cancel;
      67             :         }
      68             : 
      69     2502581 :         mutex_lock(&xqc->lock);
      70     2502581 :         error = xfarray_load_sparse(counts, dqp->q_id, &xcdq);
      71     2502581 :         if (error)
      72           0 :                 goto out_unlock;
      73             : 
      74             :         /* Adjust counters as needed. */
      75     2502581 :         delta = (int64_t)xcdq.icount - dqp->q_ino.count;
      76     2502581 :         if (delta) {
      77           0 :                 dqp->q_ino.reserved += delta;
      78           0 :                 dqp->q_ino.count += delta;
      79           0 :                 dirty = true;
      80             :         }
      81             : 
      82     2502581 :         delta = (int64_t)xcdq.bcount - dqp->q_blk.count;
      83     2502581 :         if (delta) {
      84           0 :                 dqp->q_blk.reserved += delta;
      85           0 :                 dqp->q_blk.count += delta;
      86           0 :                 dirty = true;
      87             :         }
      88             : 
      89     2502581 :         delta = (int64_t)xcdq.rtbcount - dqp->q_rtb.count;
      90     2502581 :         if (delta) {
      91           0 :                 dqp->q_rtb.reserved += delta;
      92           0 :                 dqp->q_rtb.count += delta;
      93           0 :                 dirty = true;
      94             :         }
      95             : 
      96     2502581 :         xcdq.flags |= (XQCHECK_DQUOT_REPAIR_SCANNED | XQCHECK_DQUOT_WRITTEN);
      97     2502581 :         error = xfarray_store(counts, dqp->q_id, &xcdq);
      98     2502581 :         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     2502581 :         mutex_unlock(&xqc->lock);
     108     2502581 :         if (error || !dirty)
     109     2502581 :                 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     2502581 : out_cancel:
     131     2502581 :         xchk_trans_cancel(xqc->sc);
     132             : 
     133             :         /* Re-lock the dquot so the caller can put the reference. */
     134     2502581 :         xfs_dqlock(dqp);
     135     2502581 :         return error;
     136             : }
     137             : 
     138             : /* Commit new quota counters for a particular quota type. */
     139             : STATIC int
     140        5385 : xqcheck_commit_dqtype(
     141             :         struct xqcheck          *xqc,
     142             :         unsigned int            dqtype)
     143             : {
     144        5385 :         struct xqcheck_dquot    xcdq;
     145        5385 :         struct xfs_scrub        *sc = xqc->sc;
     146        5385 :         struct xfs_mount        *mp = sc->mp;
     147        5385 :         struct xfarray          *counts = xqcheck_counters_for(xqc, dqtype);
     148        5385 :         struct xfs_dquot        *dqp;
     149        5385 :         xfarray_idx_t           cur = XFARRAY_CURSOR_INIT;
     150        5385 :         int                     error;
     151             : 
     152             :         /*
     153             :          * Update the counters of every dquot that the quota file knows about.
     154             :          */
     155        5385 :         error = xfs_qm_dqiterate(mp, dqtype, xqcheck_commit_dquot, xqc);
     156        5385 :         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        5385 :         mutex_lock(&xqc->lock);
     164     2507966 :         while ((error = xfarray_iter(counts, &cur, &xcdq)) == 1) {
     165     2502581 :                 xfs_dqid_t      id = cur - 1;
     166             : 
     167     2502581 :                 if (xcdq.flags & XQCHECK_DQUOT_REPAIR_SCANNED)
     168        5385 :                         continue;
     169             : 
     170     2497196 :                 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     2497196 :                 error = xfs_qm_dqget(mp, id, dqtype, true, &dqp);
     179     2497196 :                 if (error)
     180           0 :                         return error;
     181             : 
     182     2497196 :                 error = xqcheck_commit_dquot(dqp, dqtype, xqc);
     183     2497196 :                 xfs_qm_dqput(dqp);
     184     2497196 :                 if (error)
     185           0 :                         return error;
     186             : 
     187     2497196 :                 mutex_lock(&xqc->lock);
     188             :         }
     189        5385 :         mutex_unlock(&xqc->lock);
     190             : 
     191        5385 :         return error;
     192             : }
     193             : 
     194             : /* Figure out quota CHKD flags for the running quota types. */
     195             : static inline unsigned int
     196        1795 : xqcheck_chkd_flags(
     197             :         struct xfs_mount        *mp)
     198             : {
     199        1795 :         unsigned int            ret = 0;
     200             : 
     201        1795 :         if (XFS_IS_UQUOTA_ON(mp))
     202        1795 :                 ret |= XFS_UQUOTA_CHKD;
     203        1795 :         if (XFS_IS_GQUOTA_ON(mp))
     204        1795 :                 ret |= XFS_GQUOTA_CHKD;
     205        1795 :         if (XFS_IS_PQUOTA_ON(mp))
     206        1795 :                 ret |= XFS_PQUOTA_CHKD;
     207        1795 :         return ret;
     208             : }
     209             : 
     210             : /* Commit the new dquot counters. */
     211             : int
     212        1795 : xrep_quotacheck(
     213             :         struct xfs_scrub        *sc)
     214             : {
     215        1795 :         struct xqcheck          *xqc = sc->buf;
     216        1795 :         unsigned int            qflags = xqcheck_chkd_flags(sc->mp);
     217        1795 :         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        1795 :         xrep_update_qflags(sc, qflags, 0);
     226        1795 :         error = xrep_trans_commit(sc);
     227        1795 :         if (error)
     228             :                 return error;
     229             : 
     230             :         /* Commit the new counters to the dquots. */
     231        1795 :         if (xqc->ucounts) {
     232        1795 :                 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_USER);
     233        1795 :                 if (error)
     234             :                         return error;
     235             :         }
     236        1795 :         if (xqc->gcounts) {
     237        1795 :                 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_GROUP);
     238        1795 :                 if (error)
     239             :                         return error;
     240             :         }
     241        1795 :         if (xqc->pcounts) {
     242        1795 :                 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_PROJ);
     243        1795 :                 if (error)
     244             :                         return error;
     245             :         }
     246             : 
     247             :         /* Set the CHKD flags now that we've fixed quota counts. */
     248        1795 :         error = xchk_trans_alloc(sc, 0);
     249        1795 :         if (error)
     250             :                 return error;
     251             : 
     252        1795 :         xrep_update_qflags(sc, 0, qflags);
     253        1795 :         return 0;
     254             : }

Generated by: LCOV version 1.14