From 1be82afccba6f4dc7e41f9ec3ceb36acc7480a94 Mon Sep 17 00:00:00 2001
From: Mingming Cao <mmc@linux.vnet.ibm.com>
Date: Sun, 14 Mar 2021 07:16:23 -0700
Subject: [PATCH] Fix ofpathname race with udev rename

References: bsc#1183496 ltc#191534
Upstream: submitted https://github.com/ibm-power-utilities/powerpc-utils/pull/57
Git-commit: 1be82afccba6f4dc7e41f9ec3ceb36acc7480a94

When hcncfgdrc is called to configure an sr-iov or a backend vdice, drmgr
has already returned from DR add. However sometimes the two commands happens too
fast that the OS has not complete its work to get the device ready.
We had to put some wait time to wait for sysfs is ready. However
there is still small race window between udev rename and ofpathname lookup
which leads to ofpathname grabbed a staled devname
while udev is in the middle of rename device to something else.
This cause hcncfgdrc failed to enslave the sr-iov or backend vdevice silently.
And later causing Live Parition Migration failed due to missing backend slave
for primary sr-iov device

First we need to catch the failure of enslave vdevice at the time of
calling do_config_vdevice to enslave the device, second, in the case of failure
waiting for udev settle to complete udev events, then try ofpathename and enslave
again.

Signed-off-by: Mingming Cao <mmc@linux.vnet.ibm.com>
---
 scripts/hcnmgr | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/scripts/hcnmgr b/scripts/hcnmgr
index 30d31e7faef5..f80429d6340c 100644
--- a/scripts/hcnmgr
+++ b/scripts/hcnmgr
@@ -37,6 +37,7 @@ DRC_INDEX=0
 DEVNAME=""
 MODE=""
 PHYSLOC=""
+DEVPATH=""
 VIO_TYPE=""
 VNIC_SPT=""
 
@@ -235,6 +236,7 @@ get_dev_hcn() {
 	HCNID=$(xxd -l 4 -p "$dev"/ibm,hcn-id)
 	MODE=$(tr -d '\0' <"$dev"/ibm,hcn-mode)
 	PHYSLOC=$(tr -d '\0' <"$dev"/ibm,loc-code)
+	DEVPATH=$1
 
 	# Get the device name. After migration, it may take some time for
 	# sysfs interface up or OFPATHENAME command to translate to device name.
@@ -253,7 +255,7 @@ get_dev_hcn() {
 		((wait--))
 		if [[ $wait == 0 ]]; then
 			hcnlog DEBUG "get_dev_hcn: couldn't get dev name"
-			hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC"
+			hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC DEVPATH $DEVPATH"
 			hcnlog DEBUG "get_dev_hcn: exit"
 			if [[ $HCNCMD == "hcnscan" ]]; then
 				return $E_SUCCESS
@@ -328,7 +330,10 @@ do_config_vdevice() {
 
 	# Add device to the bond
 	hcnlog INFO "nmcli con add type ethernet ifname $DEVNAME master $BONDNAME"
-	nmcli con add type ethernet con-name "$BONDNAME-$DEVNAME" ifname "$DEVNAME" master "$BONDNAME"
+	if ! nmcli con add type ethernet con-name "$BONDNAME-$DEVNAME" ifname "$DEVNAME" master "$BONDNAME"; then
+		hcnlog DEBUG "enslave $DEVNAME failed, /sys/class/net/$DEVNAME might be moved by udev"
+		return $E_ENODEV
+	fi
 	hcnlog DEBUG "Bring up the $DEVNAME interface"
 	nmcli con up "$BONDNAME-$DEVNAME"
 
@@ -350,13 +355,33 @@ do_config_vdevice() {
 # $1 DRC_INDEX of the hybrid network device
 #
 cfghcn() {
+	local retry=3
+
 	hcnlog DEBUG "cfghcn: enter $1"
 	search_dev "$1"
 	if [[ $VIO_TYPE == "VNIC" && $VNIC_SPT == "OFF" ]]; then
 		hcnlog WARN "Backing device $VIO_TYPE for Migratable VF is not supported in hcnmgr version $VERSION"
 		err $E_INVAL_DEV
 	fi
-	do_config_vdevice
+	while [ $retry != 0 ]; do
+		hcnlog DEBUG "cfg_hcn: calling do_confi_vdevice to enslave $DEVNAME to HNV"
+		if do_config_vdevice; then
+			break
+		fi
+
+		hcnlog DEBUG "cfg_hcn: do_confi_vdevice enslave $DEVNAME failed, might race with udev rename"
+		hcnlog DEBUG "cfg_hcn: wait for udev events complete, udevadm settle"
+		udevadm settle
+
+		hcnlog DEBUG "cfg_hcn: calling get_dev_hcn retrive device $DEVPATH name again, retry $retry"
+		get_dev_hcn $DEVPATH
+		hcnlog DEBUG "cfg_hcn: calling get_dev_hcn get  $DEVNAME"
+		((retry--))
+		if [[ $retry == 0 ]]; then
+			err $E_ENODEV
+		fi
+	done
+
 	return $E_SUCCESS
 }
 #
@@ -414,7 +439,7 @@ qrydev() {
 	hcnlog DEBUG "check if the network interface for this SR_IOV is not up, return success"
 	if ! nmcli -f DEVICE con show --active | grep -q "$DEVNAME"; then
 		hcnlog DEBUG "network connection $BONDNAME-$DEVNAME is inactive or nonexist"
-		hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC"
+		hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC DEVPATH $DEVPATH"
 		hcnlog DEBUG "qryhcn: exit"
 		# In this case, tell HMC to do rmdev and okay to migrate
 		return $E_SUCCESS
@@ -423,7 +448,7 @@ qrydev() {
 	hcnlog DEBUG "check if there is bond for this $HCNID"
 	if [ ! -d "$BOND_PATH" ]; then
 		hcnlog DEBUG "bond $BONDNAME is inactive or nonexist"
-		hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC"
+		hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC DEVPATH $DEVPATH"
 		# In this case, tell HMC to do rmdev and okay to migrate
 		hcnlog DEBUG "qryhcn: exit"
 		return $E_SUCCESS
@@ -440,7 +465,7 @@ qrydev() {
 	done <"$BOND_PATH"/slaves
 
 	hcnlog DEBUG "Couldn't find active backup device for $DEVNAME"
-	hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC"
+	hcnlog DEBUG "HCNID $HCNID devname $DEVNAME mode $MODE physloc $PHYSLOC DEVPATH $DEVPATH"
 	hcnlog DEBUG "qryhcn: exit"
 	err $E_BUSY
 }
-- 
2.26.2

