From: Jeff Mahoney <jeffm@suse.com>
Subject: btrfsmaintenance: Make balance, scrub, and trim mutually exclusive tasks
Patch-mainline: Never, upstream uses systemd unit files

When scheduled simultaneously, balance, scrub, and trim can cause the
file system to stall due to balance generating large numbers of transactions
and trim taking the commit root sem across potentially time consuming
operatations, which in turn limits the when transactions can be committed.

The original idea for this was from Oliver Kurz <okurz@suse.com>, who
wrote it for systemd unit files.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs-balance.sh           |   12 +++++++++---
 btrfs-scrub.sh             |    8 +++++++-
 btrfs-trim.sh              |    4 +++-
 common.sh                  |   35 +++++++++++++++++++++++++++++++++++
 sysconfig.btrfsmaintenance |   13 +++++++++++++
 5 files changed, 67 insertions(+), 5 deletions(-)

--- a/btrfs-balance.sh
+++ b/btrfs-balance.sh
@@ -19,8 +19,14 @@ if [ -f /etc/default/btrfsmaintenance ]
     . /etc/default/btrfsmaintenance
 fi
 
+. $(dirname "$(realpath "$0")")/common.sh
+
 LOGIDENTIFIER='btrfs-balance'
 
+btrfs_balance() {
+	run_task btrfs balance "$@"
+}
+
 {
 OIFS="$IFS"
 IFS=:
@@ -33,15 +39,15 @@ for MM in $BTRFS_BALANCE_MOUNTPOINTS; do
 	echo "Before balance of $MM"
 	btrfs filesystem df "$MM"
 	df -H "$MM"
-	btrfs balance start -dusage=0 "$MM"
+	btrfs_balance start -dusage=0 "$MM"
 	for BB in $BTRFS_BALANCE_DUSAGE; do
 		# quick round to clean up the unused block groups
 		btrfs balance start -v -dusage=$BB "$MM"
 	done
-	btrfs balance start -musage=0 "$MM"
+	btrfs_balance start -musage=0 "$MM"
 	for BB in $BTRFS_BALANCE_MUSAGE; do
 		# quick round to clean up the unused block groups
-		btrfs balance start -v -musage="$BB" "$MM"
+		btrfs_balance start -v -musage="$BB" "$MM"
 	done
 	echo "After balance of $MM"
 	btrfs filesystem df "$MM"
--- a/btrfs-scrub.sh
+++ b/btrfs-scrub.sh
@@ -19,6 +19,12 @@ if [ -f /etc/default/btrfsmaintenance ]
     . /etc/default/btrfsmaintenance
 fi
 
+. $(dirname "$(realpath "$0")")/common.sh
+
+btrfs_scrub() {
+	run_task btrfs scrub "$@"
+}
+
 LOGIDENTIFIER='btrfs-scrub'
 
 readonly=
@@ -42,7 +48,7 @@ for MNT in $BTRFS_SCRUB_MOUNTPOINTS; do
 		echo "Path $MNT is not btrfs, skipping"
 		continue
 	fi
-	btrfs scrub start -Bd $ioprio $readonly "$MNT"
+	btrfs_scrub start -Bd $ioprio $readonly "$MNT"
 	if [ "$?" != "0" ]; then
 		echo "Scrub cancelled at $MNT"
 		exit 1
--- a/btrfs-trim.sh
+++ b/btrfs-trim.sh
@@ -19,6 +19,8 @@ if [ -f /etc/default/btrfsmaintenance ]
     . /etc/default/btrfsmaintenance
 fi
 
+. $(dirname "$(realpath "$0")")/common.sh
+
 LOGIDENTIFIER='btrfs-trim'
 
 {
@@ -31,7 +33,7 @@ for MNT in $BTRFS_TRIM_MOUNTPOINTS; do
 		continue
 	fi
 	echo "Running fstrim on $MNT"
-	fstrim "$MNT"
+	run_task fstrim "$MNT"
 done
 
 } | \
--- /dev/null
+++ b/common.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 SUSE LINUX
+#
+# please send bugfixes or comments to http://www.suse.com/feedback.
+
+#
+# paranoia settings
+#
+umask 022
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+export PATH
+
+if [ -f /etc/sysconfig/btrfsmaintenance ] ; then
+    . /etc/sysconfig/btrfsmaintenance
+fi
+
+if [ -f /etc/default/btrfsmaintenance ] ; then
+    . /etc/default/btrfsmaintenance
+fi
+
+btrfs_fsid() {
+	btrfs filesystem show "$1" | sed -n -e '/uuid:/ {s/^.*uuid: //;p }'
+}
+
+run_task() {
+	MNT="${@:$#}"
+	UUID=$(btrfs_fsid "$MNT")
+
+	if test "$BTRFS_ALLOW_CONCURRENCY" = "true"; then
+		"$@"
+	else
+		/usr/bin/flock /run/btrfs-maintenance-running."$UUID" "$@"
+	fi
+}
--- a/sysconfig.btrfsmaintenance
+++ b/sysconfig.btrfsmaintenance
@@ -118,3 +118,16 @@ BTRFS_TRIM_PERIOD="weekly"
 # Which mountpoints/filesystems to trim periodically.
 # (Colon separated paths)
 BTRFS_TRIM_MOUNTPOINTS="/"
+
+## Path:	System/File systems/btrfs
+## Description:	Configuration to allow concurrent jobs
+## Type: 	boolean
+## Default:	"false"
+#
+# These maintenance tasks may compete for resources with each other, blocking
+# out other tasks from using the file systems.  This option will force
+# these jobs to run in FIFO order when scheduled at overlapping times.  This
+# may include tasks scheduled to run when a system resumes or boots when
+# the timer for these tasks(s) elapsed while the system was suspended
+# or powered off.
+BTRFS_ALLOW_CONCURRENCY="false"
