[VOL-4259] OpenOnuAdapter Flow/GemPort removal deadlock if both start concurrently

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I941cde489412b08a78bc3e8bf04df37a1a33f551
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index 4473c7e..3985f00 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -335,10 +335,10 @@
 			"device-id":         oFsm.deviceID})
 
 		// Access critical state with lock
-		oFsm.pUniTechProf.mutexTPState.Lock()
+		oFsm.pUniTechProf.mutexTPState.RLock()
 		oFsm.alloc0ID = oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID
 		mapGemPortParams := oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].mapGemPortParams
-		oFsm.pUniTechProf.mutexTPState.Unlock()
+		oFsm.pUniTechProf.mutexTPState.RUnlock()
 
 		//for all TechProfile set GemIndices
 		for _, gemEntry := range mapGemPortParams {
@@ -760,7 +760,8 @@
 }
 
 func (oFsm *uniPonAniConfigFsm) enterRemovingGemIW(ctx context.Context, e *fsm.Event) {
-	oFsm.pUniTechProf.mutexTPState.Lock()
+	// no need to protect access to oFsm.waitFlowDeleteChannel, only used in synchronized state entries
+	//  or CancelProcessing() that uses separate isWaitingForFlowDelete to write to the channel
 	//flush the waitFlowDeleteChannel - possibly already/still set by some previous activity
 	select {
 	case <-oFsm.waitFlowDeleteChannel:
@@ -769,8 +770,8 @@
 	}
 
 	if oFsm.pDeviceHandler.UniVlanConfigFsmMap[oFsm.pOnuUniPort.uniID] != nil {
+		// ensure mutexTPState not locked before calling some VlanConfigFsm activity (that might already be pending on it)
 		if oFsm.pDeviceHandler.UniVlanConfigFsmMap[oFsm.pOnuUniPort.uniID].IsFlowRemovePending(oFsm.waitFlowDeleteChannel) {
-			oFsm.pUniTechProf.mutexTPState.Unlock()
 			logger.Debugw(ctx, "flow remove pending - wait before processing gem port delete",
 				log.Fields{"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID, "techProfile-id": oFsm.techProfileID})
 			// if flow remove is pending then wait for flow remove to finish first before proceeding with gem port delete
@@ -792,9 +793,10 @@
 			log.Fields{"device-id": oFsm.deviceID, "techProfile-id": oFsm.techProfileID})
 	}
 
+	oFsm.pUniTechProf.mutexTPState.RLock()
 	// get the related GemPort entity Id from pUniTechProf, OMCI Gem* entityID is set to be equal to GemPortId!
 	loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
-	oFsm.pUniTechProf.mutexTPState.Unlock()
+	oFsm.pUniTechProf.mutexTPState.RUnlock()
 	logger.Debugw(ctx, "uniPonAniConfigFsm - start removing one GemIwTP", log.Fields{
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID,
 		"GemIwTp-entity-id": loGemPortID})
@@ -894,9 +896,9 @@
 }
 
 func (oFsm *uniPonAniConfigFsm) enterRemovingGemNCTP(ctx context.Context, e *fsm.Event) {
-	oFsm.pUniTechProf.mutexTPState.Lock()
+	oFsm.pUniTechProf.mutexTPState.RLock()
 	loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
-	oFsm.pUniTechProf.mutexTPState.Unlock()
+	oFsm.pUniTechProf.mutexTPState.RUnlock()
 	logger.Debugw(ctx, "uniPonAniConfigFsm - start removing one GemNCTP", log.Fields{
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID,
 		"GemNCTP-entity-id": loGemPortID})
@@ -928,9 +930,9 @@
 	}
 }
 func (oFsm *uniPonAniConfigFsm) enterRemovingTD(ctx context.Context, e *fsm.Event) {
-	oFsm.pUniTechProf.mutexTPState.Lock()
+	oFsm.pUniTechProf.mutexTPState.RLock()
 	loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
-	oFsm.pUniTechProf.mutexTPState.Unlock()
+	oFsm.pUniTechProf.mutexTPState.RUnlock()
 	logger.Debugw(ctx, "uniPonAniConfigFsm - start removing Traffic Descriptor", log.Fields{
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID,
 		"TD-entity-id": loGemPortID})