Blame SOURCES/bz1465822-OCF-improve-locking.patch

bb196a
From 738577dd30b782104057496bf01f09e28216892b Mon Sep 17 00:00:00 2001
bb196a
From: Dejan Muhamedagic <dejan@hello-penguin.com>
bb196a
Date: Mon, 26 Jun 2017 15:56:01 +0200
bb196a
Subject: [PATCH 1/2] Medium: ocf-shellfuncs: improve locking (ocf_take_lock)
bb196a
bb196a
This change improves locking by ocf_take_lock(). It uses mkdir(1)
bb196a
to prevent two instances from creating the same directory (named
bb196a
by the lock).
bb196a
bb196a
The major difficulty is to prevent a race when a stale lock is
bb196a
discovered. If two processes try to remove the stale lock at
bb196a
about the same time, the one which runs slightly later can remove
bb196a
the lock which just got created by the one which run slightly
bb196a
earlier. The probability of this race is significantly reduced by
bb196a
testing for stale lock twice with a random sleep in between.
bb196a
bb196a
Though this change does not exclude a race entirely, it makes it
bb196a
extremely improbable. In addition, stale locks are result of only
bb196a
abnormal circumstances and occur seldom.
bb196a
bb196a
The function providing random numbers has been modified to use
bb196a
either /dev/urandom or awk (with the process pid as the seed).
bb196a
bb196a
It was thoroughly tested with both stale lock simulation and
bb196a
without, by running 64 instances of processes trying to get the
bb196a
lock on a workstation with 4 cpus.
bb196a
---
bb196a
 heartbeat/ocf-shellfuncs.in | 74 ++++++++++++++++++++++++++++++++++-----------
bb196a
 1 file changed, 57 insertions(+), 17 deletions(-)
bb196a
bb196a
diff --git a/heartbeat/ocf-shellfuncs.in b/heartbeat/ocf-shellfuncs.in
bb196a
index ebc221d5f..615f5b4b8 100644
bb196a
--- a/heartbeat/ocf-shellfuncs.in
bb196a
+++ b/heartbeat/ocf-shellfuncs.in
bb196a
@@ -72,10 +72,11 @@ ocf_is_root() {
bb196a
 }
bb196a
 
bb196a
 ocf_maybe_random() {
bb196a
-	local rnd="$RANDOM"
bb196a
-	# Something sane-ish in case a shell doesn't support $RANDOM
bb196a
-	[ -n "$rnd" ] || rnd=$$
bb196a
-	echo $rnd
bb196a
+	if test -c /dev/urandom; then
bb196a
+		od -An -N4 -tu4 /dev/urandom | tr -d '[:space:]'
bb196a
+	else
bb196a
+		awk -v pid=$$ 'BEGIN{srand(pid); print rand()}' | sed 's/^.*[.]//'
bb196a
+	fi
bb196a
 }
bb196a
 
bb196a
 # Portability comments:
bb196a
@@ -465,24 +466,63 @@ ocf_pidfile_status() {
bb196a
     return 1
bb196a
 }
bb196a
 
bb196a
-ocf_take_lock() {
bb196a
-    local lockfile=$1
bb196a
-    local rnd=$(ocf_maybe_random)
bb196a
+# mkdir(1) based locking
bb196a
+# first the directory is created with the name given as $1
bb196a
+# then a file named "pid" is created within that directory with
bb196a
+# the process PID
bb196a
 
bb196a
-    sleep 0.$rnd
bb196a
-    while 
bb196a
-	ocf_pidfile_status $lockfile
bb196a
-    do
bb196a
-	ocf_log info "Sleeping until $lockfile is released..."
bb196a
-	sleep 0.$rnd
bb196a
-    done
bb196a
-    echo $$ > $lockfile
bb196a
+ocf_get_stale_pid() {
bb196a
+	local piddir=$1
bb196a
+	local pid
bb196a
+	[ -z "$piddir" ] && return 2
bb196a
+	pid=`cat $piddir/pid 2>/dev/null`
bb196a
+	[ -z "$pid" ] && return 1 # no process
bb196a
+	kill -0 $pid >/dev/null 2>&1 && return 1 # not stale
bb196a
+	echo $pid
bb196a
 }
bb196a
 
bb196a
+# There is a race when the following two functions to manage the
bb196a
+# lock file (mk and rm) are invoked in parallel by different
bb196a
+# instances. It is up to the caller to reduce probability of that
bb196a
+# taking place (see ocf_take_lock() below).
bb196a
+
bb196a
+ocf_mk_pid() {
bb196a
+	mkdir $1 2>/dev/null && echo $$ > $1/pid
bb196a
+}
bb196a
+ocf_rm_pid() {
bb196a
+	rm -f $1/pid
bb196a
+	rmdir $1 2>/dev/null
bb196a
+}
bb196a
+
bb196a
+# Testing and subsequently removing a stale lock (containing the
bb196a
+# process pid) is inherently difficult to do in such a way as to
bb196a
+# prevent a race between creating a pid file and removing it and
bb196a
+# its directory. We reduce the probability of that happening by
bb196a
+# checking if the stale lock persists over a random period of
bb196a
+# time.
bb196a
+
bb196a
+ocf_take_lock() {
bb196a
+	local lockdir=$1
bb196a
+	local rnd
bb196a
+	local stale_pid
bb196a
+
bb196a
+	# we don't want it too short, so strip leading zeros
bb196a
+	rnd=$(ocf_maybe_random | sed 's/^0*//')
bb196a
+	stale_pid=`ocf_get_stale_pid $lockdir`
bb196a
+	if [ -n "$stale_pid" ]; then
bb196a
+		sleep 0.$rnd
bb196a
+		# remove "stale pid" only if it persists
bb196a
+		[ "$stale_pid" = "`ocf_get_stale_pid $lockdir`" ] &&
bb196a
+			ocf_rm_pid $lockdir
bb196a
+	fi
bb196a
+	while ! ocf_mk_pid $lockdir; do
bb196a
+		ocf_log info "Sleeping until $lockdir is released..."
bb196a
+		sleep 0.$rnd
bb196a
+	done
bb196a
+}
bb196a
 
bb196a
 ocf_release_lock_on_exit() {
bb196a
-    local lockfile=$1
bb196a
-    trap "rm -f $lockfile" EXIT
bb196a
+	trap "ocf_rm_pid $1" EXIT
bb196a
 }
bb196a
 
bb196a
 # returns true if the CRM is currently running a probe. A probe is
bb196a
bb196a
From 46e6f1d0e736e68c7a48c94083d7037e590365b4 Mon Sep 17 00:00:00 2001
bb196a
From: Dejan Muhamedagic <dejan@hello-penguin.com>
bb196a
Date: Mon, 26 Jun 2017 20:29:06 +0200
bb196a
Subject: [PATCH 2/2] Dev: ocf-shellfuncs: handle empty lock directories
bb196a
bb196a
---
bb196a
 heartbeat/ocf-shellfuncs.in | 34 ++++++++++++++++++++++++++++------
bb196a
 1 file changed, 28 insertions(+), 6 deletions(-)
bb196a
bb196a
diff --git a/heartbeat/ocf-shellfuncs.in b/heartbeat/ocf-shellfuncs.in
bb196a
index 615f5b4b8..817b2a557 100644
bb196a
--- a/heartbeat/ocf-shellfuncs.in
bb196a
+++ b/heartbeat/ocf-shellfuncs.in
bb196a
@@ -470,15 +470,37 @@ ocf_pidfile_status() {
bb196a
 # first the directory is created with the name given as $1
bb196a
 # then a file named "pid" is created within that directory with
bb196a
 # the process PID
bb196a
-
bb196a
+# stale locks are handled carefully, the inode of a directory
bb196a
+# needs to match before and after test if the process is running
bb196a
+# empty directories are also handled appropriately
bb196a
+# we relax (sleep) occasionally to allow for other processes to
bb196a
+# finish managing the lock in case they are in the middle of the
bb196a
+# business
bb196a
+
bb196a
+relax() { sleep 0.5; }
bb196a
 ocf_get_stale_pid() {
bb196a
-	local piddir=$1
bb196a
-	local pid
bb196a
+	local piddir pid dir_inode
bb196a
+
bb196a
+	piddir="$1"
bb196a
 	[ -z "$piddir" ] && return 2
bb196a
+	dir_inode="`ls -di $piddir 2>/dev/null`"
bb196a
+	[ -z "$dir_inode" ] && return 1
bb196a
 	pid=`cat $piddir/pid 2>/dev/null`
bb196a
-	[ -z "$pid" ] && return 1 # no process
bb196a
-	kill -0 $pid >/dev/null 2>&1 && return 1 # not stale
bb196a
-	echo $pid
bb196a
+	if [ -z "$pid" ]; then
bb196a
+		# empty directory?
bb196a
+		relax
bb196a
+		if [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
bb196a
+			echo $dir_inode
bb196a
+		else
bb196a
+			return 1
bb196a
+		fi
bb196a
+	elif kill -0 $pid >/dev/null 2>&1; then
bb196a
+		return 1
bb196a
+	elif relax && [ -e "$piddir/pid" ] && [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
bb196a
+		echo $pid
bb196a
+	else
bb196a
+		return 1
bb196a
+	fi
bb196a
 }
bb196a
 
bb196a
 # There is a race when the following two functions to manage the