All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <darrick.wong@oracle.com>
To: darrick.wong@oracle.com
Cc: linux-xfs@vger.kernel.org
Subject: [PATCH 4/5] xfs: implement live quotacheck as part of quota repair
Date: Tue, 31 Dec 2019 17:10:34 -0800	[thread overview]
Message-ID: <157784103458.1364003.10041166419649712004.stgit@magnolia> (raw)
In-Reply-To: <157784100871.1364003.10658176827446969836.stgit@magnolia>

From: Darrick J. Wong <darrick.wong@oracle.com>

Use the fs freezing mechanism we developed for the rmapbt repair to
freeze the fs, this time to scan the fs for a live quotacheck.  We add a
new dqget variant to use the existing scrub transaction to allocate an
on-disk dquot block if it is missing.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/quota.c        |   22 ++++++-
 fs/xfs/scrub/quota_repair.c |  139 +++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_qm.c             |   94 ++++++++++++++++++-----------
 fs/xfs/xfs_qm.h             |    3 +
 4 files changed, 221 insertions(+), 37 deletions(-)


diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c
index bab55b6cd723..64e24fe5dcb2 100644
--- a/fs/xfs/scrub/quota.c
+++ b/fs/xfs/scrub/quota.c
@@ -16,6 +16,7 @@
 #include "xfs_qm.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
+#include "scrub/repair.h"
 
 /* Convert a scrub type code to a DQ flag, or return 0 if error. */
 uint
@@ -53,12 +54,31 @@ xchk_setup_quota(
 	mutex_lock(&sc->mp->m_quotainfo->qi_quotaofflock);
 	if (!xfs_this_quota_on(sc->mp, dqtype))
 		return -ENOENT;
+
+	/*
+	 * Freeze out anything that can alter an inode because we reconstruct
+	 * the quota counts by iterating all the inodes in the system.
+	 */
+	if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
+	    ((sc->flags & XCHK_TRY_HARDER) || XFS_QM_NEED_QUOTACHECK(sc->mp))) {
+		error = xchk_fs_freeze(sc);
+		if (error)
+			return error;
+	}
+
 	error = xchk_setup_fs(sc, ip);
 	if (error)
 		return error;
 	sc->ip = xfs_quota_inode(sc->mp, dqtype);
-	xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
 	sc->ilock_flags = XFS_ILOCK_EXCL;
+	/*
+	 * Pretend to be an ILOCK parent to shut up lockdep if we're going to
+	 * do a full inode scan of the fs.  Quota inodes do not count towards
+	 * quota accounting, so we shouldn't deadlock on ourselves.
+	 */
+	if (sc->flags & XCHK_FS_FROZEN)
+		sc->ilock_flags |= XFS_ILOCK_PARENT;
+	xfs_ilock(sc->ip, sc->ilock_flags);
 	return 0;
 }
 
diff --git a/fs/xfs/scrub/quota_repair.c b/fs/xfs/scrub/quota_repair.c
index 5f76c4f4db1a..61d7e43ba56b 100644
--- a/fs/xfs/scrub/quota_repair.c
+++ b/fs/xfs/scrub/quota_repair.c
@@ -23,6 +23,11 @@
 #include "xfs_qm.h"
 #include "xfs_dquot.h"
 #include "xfs_dquot_item.h"
+#include "xfs_trans_space.h"
+#include "xfs_error.h"
+#include "xfs_errortag.h"
+#include "xfs_health.h"
+#include "xfs_iwalk.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
@@ -37,6 +42,11 @@
  * verifiers complain about, cap any counters or limits that make no sense,
  * and schedule a quotacheck if we had to fix anything.  We also repair any
  * data fork extent records that don't apply to metadata files.
+ *
+ * Online quotacheck is fairly straightforward.  We engage a repair freeze,
+ * zero all the dquots, and scan every inode in the system to recalculate the
+ * appropriate quota charges.  Finally, we log all the dquots to disk and
+ * set the _CHKD flags.
  */
 
 struct xrep_quota_info {
@@ -312,6 +322,116 @@ xrep_quota_data_fork(
 	return error;
 }
 
+/* Online Quotacheck */
+
+/*
+ * Zero a dquot prior to regenerating the counts.  We skip flushing the dirty
+ * dquots to disk because we've already cleared the CHKD flags in the ondisk
+ * superblock so if we crash we'll just rerun quotacheck.
+ */
+static int
+xrep_quotacheck_zero_dquot(
+	struct xfs_dquot	*dq,
+	uint			dqtype,
+	void			*priv)
+{
+	dq->q_res_bcount -= be64_to_cpu(dq->q_core.d_bcount);
+	dq->q_core.d_bcount = 0;
+	dq->q_res_icount -= be64_to_cpu(dq->q_core.d_icount);
+	dq->q_core.d_icount = 0;
+	dq->q_res_rtbcount -= be64_to_cpu(dq->q_core.d_rtbcount);
+	dq->q_core.d_rtbcount = 0;
+	dq->dq_flags |= XFS_DQ_DIRTY;
+	return 0;
+}
+
+/* Execute an online quotacheck. */
+STATIC int
+xrep_quotacheck(
+	struct xfs_scrub	*sc)
+{
+	LIST_HEAD		(buffer_list);
+	struct xfs_mount	*mp = sc->mp;
+	uint			qflag = 0;
+	int			error;
+
+	/*
+	 * We can rebuild all the quota information, so we need to be able to
+	 * update both the health status and the CHKD flags.
+	 */
+	if (XFS_IS_UQUOTA_ON(mp)) {
+		sc->sick_mask |= XFS_SICK_FS_UQUOTA;
+		qflag |= XFS_UQUOTA_CHKD;
+	}
+	if (XFS_IS_GQUOTA_ON(mp)) {
+		sc->sick_mask |= XFS_SICK_FS_GQUOTA;
+		qflag |= XFS_GQUOTA_CHKD;
+	}
+	if (XFS_IS_PQUOTA_ON(mp)) {
+		sc->sick_mask |= XFS_SICK_FS_PQUOTA;
+		qflag |= XFS_PQUOTA_CHKD;
+	}
+
+	/* Clear the CHKD flags. */
+	spin_lock(&sc->mp->m_sb_lock);
+	sc->mp->m_qflags &= ~qflag;
+	sc->mp->m_sb.sb_qflags &= ~qflag;
+	spin_unlock(&sc->mp->m_sb_lock);
+	xfs_log_sb(sc->tp);
+
+	/*
+	 * Commit the transaction so that we can allocate new quota ip
+	 * mappings if we have to.  If we crash after this point, the sb
+	 * still has the CHKD flags cleared, so mount quotacheck will fix
+	 * all of this up.
+	 */
+	error = xfs_trans_commit(sc->tp);
+	sc->tp = NULL;
+	if (error)
+		return error;
+
+	/*
+	 * Zero all the dquots, and remember that we rebuild all three quota
+	 * types.  We hold the quotaoff lock, so these won't change.
+	 */
+	if (XFS_IS_UQUOTA_ON(mp)) {
+		error = xfs_qm_dqiterate(mp, XFS_DQ_USER,
+				xrep_quotacheck_zero_dquot, NULL);
+		if (error)
+			goto out;
+	}
+	if (XFS_IS_GQUOTA_ON(mp)) {
+		error = xfs_qm_dqiterate(mp, XFS_DQ_GROUP,
+				xrep_quotacheck_zero_dquot, NULL);
+		if (error)
+			goto out;
+	}
+	if (XFS_IS_PQUOTA_ON(mp)) {
+		error = xfs_qm_dqiterate(mp, XFS_DQ_PROJ,
+				xrep_quotacheck_zero_dquot, NULL);
+		if (error)
+			goto out;
+	}
+
+	/* Walk the inodes and reset the dquots. */
+	error = xfs_qm_quotacheck_walk_and_flush(mp, true, &buffer_list);
+	if (error)
+		goto out;
+
+	/* Set quotachecked flag. */
+	error = xchk_trans_alloc(sc, 0);
+	if (error)
+		goto out;
+
+	spin_lock(&sc->mp->m_sb_lock);
+	sc->mp->m_qflags |= qflag;
+	sc->mp->m_sb.sb_qflags |= qflag;
+	spin_unlock(&sc->mp->m_sb_lock);
+	xfs_log_sb(sc->tp);
+out:
+	return error;
+}
+
 /*
  * Go fix anything in the quota items that we could have been mad about.  Now
  * that we've checked the quota inode data fork we have to drop ILOCK_EXCL to
@@ -332,8 +452,10 @@ xrep_quota_problems(
 		return error;
 
 	/* Make a quotacheck happen. */
-	if (rqi.need_quotacheck)
+	if (rqi.need_quotacheck ||
+	    XFS_TEST_ERROR(false, sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
 		xrep_force_quotacheck(sc, dqtype);
+
 	return 0;
 }
 
@@ -343,6 +465,7 @@ xrep_quota(
 	struct xfs_scrub	*sc)
 {
 	uint			dqtype;
+	uint			flag;
 	int			error;
 
 	dqtype = xchk_quota_to_dqtype(sc);
@@ -358,6 +481,20 @@ xrep_quota(
 
 	/* Fix anything the dquot verifiers complain about. */
 	error = xrep_quota_problems(sc, dqtype);
+	if (error)
+		goto out;
+
+	/* Do we need a quotacheck?  Did we need one? */
+	flag = xfs_quota_chkd_flag(dqtype);
+	if (!(flag & sc->mp->m_qflags)) {
+		/* We need to freeze the fs before we can scan inodes. */
+		if (!(sc->flags & XCHK_FS_FROZEN)) {
+			error = -EDEADLOCK;
+			goto out;
+		}
+
+		error = xrep_quotacheck(sc);
+	}
 out:
 	return error;
 }
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index fc3898f5e27d..0ce334c51d73 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -1140,11 +1140,12 @@ xfs_qm_dqusage_adjust(
 	struct xfs_mount	*mp,
 	struct xfs_trans	*tp,
 	xfs_ino_t		ino,
-	void			*data)
+	void			*need_ilocks)
 {
 	struct xfs_inode	*ip;
 	xfs_qcnt_t		nblks;
 	xfs_filblks_t		rtblks = 0;	/* total rt blks */
+	uint			ilock_flags = 0;
 	int			error;
 
 	ASSERT(XFS_IS_QUOTA_RUNNING(mp));
@@ -1156,16 +1157,19 @@ xfs_qm_dqusage_adjust(
 	if (xfs_is_quota_inode(&mp->m_sb, ino))
 		return 0;
 
-	/*
-	 * We don't _need_ to take the ilock EXCL here because quotacheck runs
-	 * at mount time and therefore nobody will be racing chown/chproj.
-	 */
+	/* Grab inode and lock it if needed. */
 	error = xfs_iget(mp, tp, ino, XFS_IGET_DONTCACHE, 0, &ip);
 	if (error == -EINVAL || error == -ENOENT)
 		return 0;
 	if (error)
 		return error;
 
+	if (need_ilocks) {
+		ilock_flags = XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED;
+		xfs_ilock(ip, ilock_flags);
+		ilock_flags |= xfs_ilock_data_map_shared(ip);
+	}
+
 	ASSERT(ip->i_delayed_blks == 0);
 
 	if (XFS_IS_REALTIME_INODE(ip)) {
@@ -1216,6 +1220,8 @@ xfs_qm_dqusage_adjust(
 	}
 
 error0:
+	if (ilock_flags)
+		xfs_iunlock(ip, ilock_flags);
 	xfs_irele(ip);
 	return error;
 }
@@ -1272,17 +1278,61 @@ xfs_qm_flush_one(
 	return error;
 }
 
+/*
+ * Walk the inodes and adjust quota usage.  Caller must have previously
+ * zeroed all dquots.
+ */
+int
+xfs_qm_quotacheck_walk_and_flush(
+	struct xfs_mount	*mp,
+	bool			need_ilocks,
+	struct list_head	*buffer_list)
+{
+	int			error, error2;
+
+	error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0,
+			!need_ilocks, NULL);
+	if (error)
+		return error;
+
+	/*
+	 * We've made all the changes that we need to make incore.  Flush them
+	 * down to disk buffers if everything was updated successfully.
+	 */
+	if (XFS_IS_UQUOTA_ON(mp)) {
+		error = xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_flush_one,
+					  buffer_list);
+	}
+	if (XFS_IS_GQUOTA_ON(mp)) {
+		error2 = xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_flush_one,
+					   buffer_list);
+		if (!error)
+			error = error2;
+	}
+	if (XFS_IS_PQUOTA_ON(mp)) {
+		error2 = xfs_qm_dquot_walk(mp, XFS_DQ_PROJ, xfs_qm_flush_one,
+					   buffer_list);
+		if (!error)
+			error = error2;
+	}
+
+	error2 = xfs_buf_delwri_submit(buffer_list);
+	if (!error)
+		error = error2;
+	return error;
+}
+
 /*
  * Walk thru all the filesystem inodes and construct a consistent view
  * of the disk quota world. If the quotacheck fails, disable quotas.
  */
 STATIC int
 xfs_qm_quotacheck(
-	xfs_mount_t	*mp)
+	struct xfs_mount	*mp)
 {
-	int			error, error2;
-	uint			flags;
+	int			error;
 	LIST_HEAD		(buffer_list);
+	uint			flags;
 	struct xfs_inode	*uip = mp->m_quotainfo->qi_uquotaip;
 	struct xfs_inode	*gip = mp->m_quotainfo->qi_gquotaip;
 	struct xfs_inode	*pip = mp->m_quotainfo->qi_pquotaip;
@@ -1323,36 +1373,10 @@ xfs_qm_quotacheck(
 		flags |= XFS_PQUOTA_CHKD;
 	}
 
-	error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true,
-			NULL);
+	error = xfs_qm_quotacheck_walk_and_flush(mp, false, &buffer_list);
 	if (error)
 		goto error_return;
 
-	/*
-	 * We've made all the changes that we need to make incore.  Flush them
-	 * down to disk buffers if everything was updated successfully.
-	 */
-	if (XFS_IS_UQUOTA_ON(mp)) {
-		error = xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_flush_one,
-					  &buffer_list);
-	}
-	if (XFS_IS_GQUOTA_ON(mp)) {
-		error2 = xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_flush_one,
-					   &buffer_list);
-		if (!error)
-			error = error2;
-	}
-	if (XFS_IS_PQUOTA_ON(mp)) {
-		error2 = xfs_qm_dquot_walk(mp, XFS_DQ_PROJ, xfs_qm_flush_one,
-					   &buffer_list);
-		if (!error)
-			error = error2;
-	}
-
-	error2 = xfs_buf_delwri_submit(&buffer_list);
-	if (!error)
-		error = error2;
-
 	/*
 	 * We can get this error if we couldn't do a dquot allocation inside
 	 * xfs_qm_dqusage_adjust (via bulkstat). We don't care about the
diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h
index 7823af39008b..a3d9932f2e65 100644
--- a/fs/xfs/xfs_qm.h
+++ b/fs/xfs/xfs_qm.h
@@ -179,4 +179,7 @@ xfs_get_defquota(struct xfs_dquot *dqp, struct xfs_quotainfo *qi)
 	return defq;
 }
 
+int xfs_qm_quotacheck_walk_and_flush(struct xfs_mount *mp, bool need_ilocks,
+		struct list_head *buffer_list);
+
 #endif /* __XFS_QM_H__ */


  parent reply	other threads:[~2020-01-01  1:12 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-01  1:10 [PATCH v22 0/5] xfs: online repair of rmap/quota/summary counters Darrick J. Wong
2020-01-01  1:10 ` [PATCH 1/5] xfs: introduce online scrub freeze Darrick J. Wong
2020-01-01  1:10 ` [PATCH 2/5] xfs: make xfile io asynchronous Darrick J. Wong
2020-01-01  1:10 ` [PATCH 3/5] xfs: repair the rmapbt Darrick J. Wong
2020-01-01  1:10 ` Darrick J. Wong [this message]
2020-01-01  1:10 ` [PATCH 5/5] xfs: repair summary counters Darrick J. Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=157784103458.1364003.10041166419649712004.stgit@magnolia \
    --to=darrick.wong@oracle.com \
    --cc=linux-xfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.