xfs_scrub: serialize the scan-happy repair functions

Some of the metadata repair functions require full scans of the
filesystem.  Use a rwlock to make sure that these repair functions only
run one at a time.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
diff --git a/scrub/scrub.c b/scrub/scrub.c
index 365e441..2a4db6b 100644
--- a/scrub/scrub.c
+++ b/scrub/scrub.c
@@ -690,6 +690,24 @@
 
 /* General repair routines. */
 
+/*
+ * Decide if this repair item should be run one at a time.  The only types
+ * requiring serialization are the ones that need to freeze the filesystem
+ * and the ones that have to use trylocking to avoid ABBA deadlocks.
+ */
+static inline bool
+repair_needs_excl(const struct xfs_scrub_metadata *meta)
+{
+	switch (meta->sm_type) {
+	case XFS_SCRUB_TYPE_PARENT:
+	case XFS_SCRUB_TYPE_RMAPBT:
+	case XFS_SCRUB_TYPE_RTRMAPBT:
+		return true;
+	}
+
+	return false;
+}
+
 /* Repair some metadata. */
 enum check_outcome
 xfs_repair_metadata(
@@ -732,7 +750,19 @@
 		str_info(ctx, descr_render(&dsc),
 				_("Attempting optimization."));
 retry:
+	/*
+	 * Certain types of repairs involve full filesystem scans and
+	 * trylocking.  These repair activities are substantially more likely
+	 * to succeed if they don't have to compete with other activity.  Use a
+	 * exclusive lock to serialize the repair functions that require it,
+	 * and a shared lock for those that can run concurrently.
+	 */
+	if (repair_needs_excl(&meta))
+		pthread_rwlock_wrlock(&ctx->repair_rwlock);
+	else
+		pthread_rwlock_rdlock(&ctx->repair_rwlock);
 	error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
+	pthread_rwlock_unlock(&ctx->repair_rwlock);
 	switch (error) {
 	case 0:
 		/* No operational errors encountered. */
diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c
index 1dde402..538e16d 100644
--- a/scrub/xfs_scrub.c
+++ b/scrub/xfs_scrub.c
@@ -618,6 +618,7 @@
 	textdomain(PACKAGE);
 
 	pthread_mutex_init(&ctx.lock, NULL);
+	pthread_rwlock_init(&ctx.repair_rwlock, NULL);
 	ctx.mode = SCRUB_MODE_REPAIR;
 	ctx.error_action = ERRORS_CONTINUE;
 	ctx.freeze_ok = true;
diff --git a/scrub/xfs_scrub.h b/scrub/xfs_scrub.h
index 8e4ca16..d6c9286 100644
--- a/scrub/xfs_scrub.h
+++ b/scrub/xfs_scrub.h
@@ -85,6 +85,9 @@
 	unsigned long long	slow_ops_skipped;
 	bool			scrub_setup_succeeded;
 	bool			preen_triggers[XFS_SCRUB_TYPE_NR];
+
+	/* Used to serialize certain scan-happy repair operations. */
+	pthread_rwlock_t	repair_rwlock;
 };
 
 /* Phase helper functions */