VOL-4058 - TCONT allocation changes in ani configuration

Change-Id: If2e9c6b58ad62b5c1aa3b0771282058f2f2f5067
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index c60f28f..fe64bf2 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -408,8 +408,10 @@
 		logger.Errorw(ctx, "error-parsing-tpid-from-tppath", log.Fields{"err": err, "tp-path": techProfMsg.Path})
 		return err
 	}
+	logger.Debugw(ctx, "unmarshal-techprof-msg-body", log.Fields{"uniID": uniID, "tp-path": techProfMsg.Path, "tpID": tpID})
 
 	if bTpModify := pDevEntry.updateOnuUniTpPath(ctx, uniID, uint8(tpID), techProfMsg.Path); bTpModify {
+		logger.Debugw(ctx, "onu-uni-tp-path-modified", log.Fields{"uniID": uniID, "tp-path": techProfMsg.Path, "tpID": tpID})
 		//	if there has been some change for some uni TechProfilePath
 		//in order to allow concurrent calls to other dh instances we do not wait for execution here
 		//but doing so we can not indicate problems to the caller (who does what with that then?)
@@ -423,18 +425,30 @@
 		dctx, cancel := context.WithDeadline(context.Background(), deadline)
 
 		dh.pOnuTP.resetTpProcessingErrorIndication(uniID, tpID)
-		pDevEntry.resetKvProcessingErrorIndication()
 
 		var wg sync.WaitGroup
-		wg.Add(2) // for the 2 go routines to finish
+		wg.Add(1) // for the 1 go routine to finish
 		// attention: deadline completion check and wg.Done is to be done in both routines
 		go dh.pOnuTP.configureUniTp(log.WithSpanFromContext(dctx, ctx), uniID, techProfMsg.Path, &wg)
-		go pDevEntry.updateOnuKvStore(log.WithSpanFromContext(dctx, ctx), &wg)
 		dh.waitForCompletion(ctx, cancel, &wg, "TechProfDwld") //wait for background process to finish
-
-		return dh.combineErrorStrings(dh.pOnuTP.getTpProcessingErrorIndication(uniID, tpID), pDevEntry.getKvProcessingErrorIndication())
+		if tpErr := dh.pOnuTP.getTpProcessingErrorIndication(uniID, tpID); tpErr != nil {
+			logger.Errorw(ctx, "error-processing-tp", log.Fields{"device-id": dh.deviceID, "err": tpErr, "tp-path": techProfMsg.Path})
+			return tpErr
+		}
+		deadline = time.Now().Add(dh.pOpenOnuAc.maxTimeoutInterAdapterComm) //allowed run time to finish before execution
+		dctx2, cancel2 := context.WithDeadline(context.Background(), deadline)
+		pDevEntry.resetKvProcessingErrorIndication()
+		wg.Add(1) // for the 1 go routine to finish
+		go pDevEntry.updateOnuKvStore(log.WithSpanFromContext(dctx2, ctx), &wg)
+		dh.waitForCompletion(ctx, cancel2, &wg, "TechProfDwld") //wait for background process to finish
+		if kvErr := pDevEntry.getKvProcessingErrorIndication(); kvErr != nil {
+			logger.Errorw(ctx, "error-updating-KV", log.Fields{"device-id": dh.deviceID, "err": kvErr, "tp-path": techProfMsg.Path})
+			return kvErr
+		}
+		return nil
 	}
 	// no change, nothing really to do - return success
+	logger.Debugw(ctx, "onu-uni-tp-path-not-modified", log.Fields{"uniID": uniID, "tp-path": techProfMsg.Path, "tpID": tpID})
 	return nil
 }
 
@@ -539,6 +553,7 @@
 	}
 
 	if bTpModify := pDevEntry.updateOnuUniTpPath(ctx, uniID, tpID, ""); bTpModify {
+		pDevEntry.freeTcont(ctx, uint16(delTcontMsg.AllocId))
 		// deadline context to ensure completion of background routines waited for
 		deadline := time.Now().Add(dh.pOpenOnuAc.maxTimeoutInterAdapterComm) //allowed run time to finish before execution
 		dctx, cancel := context.WithDeadline(context.Background(), deadline)
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index ae70353..5701985 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -85,10 +85,6 @@
 )
 const cAniFsmIdleState = aniStConfigDone
 
-const (
-	tpIDOffset = 64
-)
-
 type ponAniGemPortAttribs struct {
 	gemPortID      uint16
 	upQueueID      uint16
@@ -133,6 +129,7 @@
 	requestEventOffset       uint8 //used to indicate ConfigDone or Removed using successor (enum)
 	isWaitingForFlowDelete   bool
 	waitFlowDeleteChannel    chan bool
+	tcontSetBefore           bool
 }
 
 //newUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
@@ -149,6 +146,7 @@
 		techProfileID:  aTechProfileID,
 		requestEvent:   aRequestEvent,
 		chanSet:        false,
+		tcontSetBefore: false,
 	}
 	instFsm.uniTpKey = uniTP{uniID: apUniPort.uniID, tpID: aTechProfileID}
 	instFsm.waitFlowDeleteChannel = make(chan bool)
@@ -298,8 +296,6 @@
 //TODO:visit here for refactoring for gocyclo
 func (oFsm *uniPonAniConfigFsm) prepareAndEnterConfigState(ctx context.Context, aPAFsm *AdapterFsm) {
 	if aPAFsm != nil && aPAFsm.pFsm != nil {
-		//stick to pythonAdapter numbering scheme
-		//index 0 in naming refers to possible usage of multiple instances (later)
 		var err error
 		oFsm.mapperSP0ID, err = generateIeeMaperServiceProfileEID(uint16(oFsm.pOnuUniPort.macBpNo), uint16(oFsm.techProfileID))
 		if err != nil {
@@ -316,66 +312,29 @@
 		logger.Debugw(ctx, "generated ids for ani config", log.Fields{"mapperSP0ID": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
 			"macBPCD0ID": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16), "device-id": oFsm.deviceID,
 			"macBpNo": oFsm.pOnuUniPort.macBpNo, "techProfileID": oFsm.techProfileID})
-		if tcontInstKeys := oFsm.pOnuDB.getSortedInstKeys(ctx, me.TContClassID); len(tcontInstKeys) > 0 {
-
-			// FIXME: Ideally the ME configurations on the ONU should constantly be MIB Synced back to the ONU DB
-			// So, as soon as we use up a TCONT Entity on the ONU, the DB at ONU adapter should know that the TCONT
-			// entity is used up via MIB Sync procedure and it will not use it for subsequent TCONT on that ONU.
-			// But, it seems, due to the absence of the constant mib-sync procedure, the TCONT Entities show up as
-			// free even though they are already reserved on the ONU. It seems the mib is synced only once, initially
-			// when the ONU is discovered.
-			/*
-				for _, tcontInstID := range tcontInstKeys {
-					tconInst := oFsm.pOnuDB.GetMe(me.TContClassID, tcontInstID)
-					returnVal := tconInst["AllocId"]
-					if returnVal != nil {
-						if allocID, err := oFsm.pOnuDB.getUint16Attrib(returnVal); err == nil {
-							// If the TCONT Instance ID is set to 0xff or 0xffff, it means it is free to use.
-							if allocID == 0xff || allocID == 0xffff {
-								foundFreeTcontInstID = true
-								oFsm.tcont0ID = uint16(tcontInstID)
-								logger.Debugw("Used TcontId:", log.Fields{"TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
-									"device-id": oFsm.deviceID})
-								break
-							}
-						} else {
-							logger.Errorw("error-converting-alloc-id-to-uint16", log.Fields{"device-id": oFsm.deviceID, "tcont-inst": tcontInstID})
-						}
-					} else {
-						logger.Errorw("error-extracting-alloc-id-attribute", log.Fields{"device-id": oFsm.deviceID, "tcont-inst": tcontInstID})
-					}
-				}
-			*/
-
-			// Ensure that the techProfileID is in a valid range so that we can allocate a free Tcont for it.
-			if oFsm.techProfileID >= tpIDOffset && oFsm.techProfileID < uint8(tpIDOffset+len(tcontInstKeys)) {
-				// For now, as a dirty workaround, use the tpIDOffset to index the TcontEntityID to be used.
-				// The first TP ID for the ONU will get the first TcontEntityID, the next will get second and so on.
-				// Here the assumption is TP ID will always start from 64 (this is also true to Technology Profile Specification) and the
-				// TP ID will increment in single digit
-				oFsm.tcont0ID = tcontInstKeys[oFsm.techProfileID-tpIDOffset]
-				logger.Debugw(ctx, "Used TcontId:", log.Fields{"TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
-					"device-id": oFsm.deviceID})
-			} else {
-				logger.Errorw(ctx, "tech profile id not in valid range", log.Fields{"device-id": oFsm.deviceID, "tp-id": oFsm.techProfileID, "num-tcont": len(tcontInstKeys)})
-				if oFsm.isChanSet() {
-					// indicate processing error/abort to the caller
-					oFsm.chSuccess <- 0
-					oFsm.setChanSet(false) //reset the internal channel state
-				}
-				//reset the state machine to enable usage on subsequent requests
-				_ = aPAFsm.pFsm.Event(aniEvReset)
-				return
-			}
-		} else {
-			logger.Errorw(ctx, "No TCont instances found", log.Fields{"device-id": oFsm.deviceID})
+		pDevEntry := oFsm.pDeviceHandler.getOnuDeviceEntry(ctx, false)
+		if pDevEntry == nil {
+			logger.Errorw(ctx, "No valid OnuDevice - aborting", log.Fields{"device-id": oFsm.deviceID})
 			return
 		}
-		/*
-			if !foundFreeTcontInstID {
-				// This should never happen. If it does, the behavior is unpredictable.
-				logger.Warnw("No free TCONT instances found", log.Fields{"device-id": oFsm.deviceID})
-			}*/
+		tcontInstID, tcontAlreadyExist, err := pDevEntry.allocateFreeTcont(ctx, oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID)
+		if err != nil {
+			logger.Errorw(ctx, "No TCont instances found", log.Fields{"device-id": oFsm.deviceID, "err": err})
+			if oFsm.chanSet {
+				// indicate processing error/abort to the caller
+				oFsm.chSuccess <- 0
+				oFsm.chanSet = false //reset the internal channel state
+			}
+			//reset the state machine to enable usage on subsequent requests
+			_ = aPAFsm.pFsm.Event(aniEvReset)
+			return
+		}
+		oFsm.tcont0ID = tcontInstID
+		oFsm.tcontSetBefore = tcontAlreadyExist
+		logger.Debugw(ctx, "used-tcont-instance-id", log.Fields{"tcont-inst-id": oFsm.tcont0ID,
+			"alloc-id":          oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID,
+			"tcontAlreadyExist": tcontAlreadyExist,
+			"device-id":         oFsm.deviceID})
 
 		// Access critical state with lock
 		oFsm.pUniTechProf.mutexTPState.Lock()
@@ -590,7 +549,17 @@
 	logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::Tcont", log.Fields{
 		"EntitytId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
 		"AllocId":   strconv.FormatInt(int64(oFsm.alloc0ID), 16),
-		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID,
+		"tcontExist": oFsm.tcontSetBefore})
+	//If tcont was set before, then no need to set it again. Let state machine to proceed.
+	if oFsm.tcontSetBefore {
+		go func(aPAFsm *AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.pFsm != nil {
+				_ = aPAFsm.pFsm.Event(aniEvRxTcontsResp)
+			}
+		}(oFsm.pAdaptFsm)
+		return
+	}
 	meParams := me.ParamData{
 		EntityID: oFsm.tcont0ID,
 		Attributes: me.AttributeValueMap{
@@ -1219,6 +1188,11 @@
 		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?",
 			log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
 		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+
+		//FIXME: If setting TCONT fails we need to revert the DB back. Because of the concurency,
+		//doing it here may cause a data inconsistency. To fix this problem we need to think on running
+		//the FSMs of different UNIs sequentially instead of running them concurrently.
+
 		return
 	}
 	oFsm.mutexPLastTxMeInstance.RLock()
@@ -1523,6 +1497,19 @@
 }
 
 func (oFsm *uniPonAniConfigFsm) performSettingPQs(ctx context.Context) {
+	//If upstream PQs were set before, then no need to set them again. Let state machine to proceed.
+	if oFsm.tcontSetBefore {
+		logger.Debugw(ctx, "No need to set PQs again.", log.Fields{
+			"device-id": oFsm.deviceID, "tcont": oFsm.alloc0ID,
+			"uni-id":         oFsm.pOnuUniPort.uniID,
+			"techProfile-id": oFsm.techProfileID})
+		go func(aPAFsm *AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.pFsm != nil {
+				_ = aPAFsm.pFsm.Event(aniEvRxPrioqsResp)
+			}
+		}(oFsm.pAdaptFsm)
+		return
+	}
 	const cu16StrictPrioWeight uint16 = 0xFFFF
 	//find all upstream PrioQueues related to this T-Cont
 	loQueueMap := ordered_map.NewOrderedMap()
diff --git a/internal/pkg/onuadaptercore/onu_device_db.go b/internal/pkg/onuadaptercore/onu_device_db.go
index 7cc1cd4..46638e1 100644
--- a/internal/pkg/onuadaptercore/onu_device_db.go
+++ b/internal/pkg/onuadaptercore/onu_device_db.go
@@ -22,6 +22,7 @@
 	"fmt"
 	"reflect"
 	"sort"
+	"sync"
 
 	me "github.com/opencord/omci-lib-go/generated"
 	"github.com/opencord/voltha-lib-go/v4/pkg/log"
@@ -34,6 +35,7 @@
 	ctx             context.Context
 	pOnuDeviceEntry *OnuDeviceEntry
 	meDb            meDbMap
+	meDbLock        sync.RWMutex
 }
 
 //newOnuDeviceDB returns a new instance for a specific ONU_Device_Entry
@@ -48,7 +50,8 @@
 }
 
 func (onuDeviceDB *onuDeviceDB) PutMe(ctx context.Context, meClassID me.ClassID, meEntityID uint16, meAttributes me.AttributeValueMap) {
-
+	onuDeviceDB.meDbLock.Lock()
+	defer onuDeviceDB.meDbLock.Unlock()
 	//filter out the OnuData
 	if me.OnuDataClassID == meClassID {
 		return
@@ -84,7 +87,8 @@
 }
 
 func (onuDeviceDB *onuDeviceDB) GetMe(meClassID me.ClassID, meEntityID uint16) me.AttributeValueMap {
-
+	onuDeviceDB.meDbLock.RLock()
+	defer onuDeviceDB.meDbLock.RUnlock()
 	if meAttributes, present := onuDeviceDB.meDb[meClassID][meEntityID]; present {
 		/* verbose logging, avoid in >= debug level
 		logger.Debugw(ctx,"ME found:", log.Fields{"meClassID": meClassID, "meEntityID": meEntityID, "meAttributes": meAttributes,
@@ -123,7 +127,8 @@
 func (onuDeviceDB *onuDeviceDB) getSortedInstKeys(ctx context.Context, meClassID me.ClassID) []uint16 {
 
 	var meInstKeys []uint16
-
+	onuDeviceDB.meDbLock.RLock()
+	defer onuDeviceDB.meDbLock.RUnlock()
 	meInstMap := onuDeviceDB.meDb[meClassID]
 
 	for k := range meInstMap {
diff --git a/internal/pkg/onuadaptercore/onu_device_entry.go b/internal/pkg/onuadaptercore/onu_device_entry.go
index 4ada5d9..d3e7238 100644
--- a/internal/pkg/onuadaptercore/onu_device_entry.go
+++ b/internal/pkg/onuadaptercore/onu_device_entry.go
@@ -245,22 +245,23 @@
 }
 
 type onuPersistentData struct {
-	PersOnuID              uint32          `json:"onu_id"`
-	PersIntfID             uint32          `json:"intf_id"`
-	PersSerialNumber       string          `json:"serial_number"`
-	PersMacAddress         string          `json:"mac_address"`
-	PersVendorID           string          `json:"vendor_id"`
-	PersEquipmentID        string          `json:"equipment_id"`
-	PersActiveSwVersion    string          `json:"active_sw_version"`
-	PersAdminState         string          `json:"admin_state"`
-	PersOperState          string          `json:"oper_state"`
-	PersUniUnlockDone      bool            `json:"uni_unlock_done"`
-	PersUniDisableDone     bool            `json:"uni_disable_done"`
-	PersMibAuditInterval   time.Duration   `json:"mib_audit_interval"`
-	PersMibLastDbSync      uint32          `json:"mib_last_db_sync"`
-	PersMibDataSyncAdpt    uint8           `json:"mib_data_sync_adpt"`
-	PersUniConfig          []uniPersConfig `json:"uni_config"`
-	PersAlarmAuditInterval time.Duration   `json:"alarm_audit_interval"`
+	PersOnuID              uint32            `json:"onu_id"`
+	PersIntfID             uint32            `json:"intf_id"`
+	PersSerialNumber       string            `json:"serial_number"`
+	PersMacAddress         string            `json:"mac_address"`
+	PersVendorID           string            `json:"vendor_id"`
+	PersEquipmentID        string            `json:"equipment_id"`
+	PersActiveSwVersion    string            `json:"active_sw_version"`
+	PersAdminState         string            `json:"admin_state"`
+	PersOperState          string            `json:"oper_state"`
+	PersUniUnlockDone      bool              `json:"uni_unlock_done"`
+	PersUniDisableDone     bool              `json:"uni_disable_done"`
+	PersMibAuditInterval   time.Duration     `json:"mib_audit_interval"`
+	PersMibLastDbSync      uint32            `json:"mib_last_db_sync"`
+	PersMibDataSyncAdpt    uint8             `json:"mib_data_sync_adpt"`
+	PersUniConfig          []uniPersConfig   `json:"uni_config"`
+	PersAlarmAuditInterval time.Duration     `json:"alarm_audit_interval"`
+	PersTcontMap           map[uint16]uint16 `json:"tcont_map"` //alloc-id to me-instance-id map
 }
 
 // OnuDeviceEntry - ONU device info and FSM events.
@@ -305,6 +306,8 @@
 	//  within the FSM event procedures
 	omciMessageReceived              chan bool    //seperate channel needed by DownloadFsm
 	omciRebootMessageReceivedChannel chan Message // channel needed by Reboot request
+
+	mutexTcontMap sync.RWMutex
 }
 
 //newOnuDeviceEntry returns a new instance of a OnuDeviceEntry
@@ -319,6 +322,7 @@
 	onuDeviceEntry.adapterProxy = dh.AdapterProxy
 	onuDeviceEntry.devState = DeviceStatusInit
 	onuDeviceEntry.sOnuPersistentData.PersUniConfig = make([]uniPersConfig, 0)
+	onuDeviceEntry.sOnuPersistentData.PersTcontMap = make(map[uint16]uint16)
 	onuDeviceEntry.chOnuKvProcessingStep = make(chan uint8)
 	onuDeviceEntry.omciRebootMessageReceivedChannel = make(chan Message, 2048)
 	//openomciagent.lockDeviceHandlersMap = sync.RWMutex{}
@@ -612,7 +616,7 @@
 	oo.mutexPersOnuConfig.Lock()
 	defer oo.mutexPersOnuConfig.Unlock()
 	oo.sOnuPersistentData =
-		onuPersistentData{0, 0, "", "", "", "", "", "", "", false, false, oo.mibAuditInterval, 0, 0, make([]uniPersConfig, 0), oo.alarmAuditInterval}
+		onuPersistentData{0, 0, "", "", "", "", "", "", "", false, false, oo.mibAuditInterval, 0, 0, make([]uniPersConfig, 0), oo.alarmAuditInterval, make(map[uint16]uint16)}
 	oo.mutexOnuKVStore.RLock()
 	Value, err := oo.onuKVStore.Get(ctx, oo.onuKVStorePath)
 	oo.mutexOnuKVStore.RUnlock()
@@ -666,7 +670,7 @@
 
 	oo.sOnuPersistentData.PersUniConfig = nil //releasing all UniConfig entries to garbage collector default entry
 	oo.sOnuPersistentData =
-		onuPersistentData{0, 0, "", "", "", "", "", "", "", false, false, oo.mibAuditInterval, 0, 0, make([]uniPersConfig, 0), oo.alarmAuditInterval}
+		onuPersistentData{0, 0, "", "", "", "", "", "", "", false, false, oo.mibAuditInterval, 0, 0, make([]uniPersConfig, 0), oo.alarmAuditInterval, make(map[uint16]uint16)}
 	logger.Debugw(ctx, "delete ONU-data from KVStore", log.Fields{"device-id": oo.deviceID})
 	oo.mutexOnuKVStore.Lock()
 	err := oo.onuKVStore.Delete(ctx, oo.onuKVStorePath)
@@ -756,8 +760,9 @@
 
 	for k, v := range oo.sOnuPersistentData.PersUniConfig {
 		if v.PersUniID == aUniID {
-			logger.Debugw(ctx, "PersUniConfig-entry exists", log.Fields{"device-id": oo.deviceID, "uniID": aUniID})
 			existingPath, ok := oo.sOnuPersistentData.PersUniConfig[k].PersTpPathMap[aTpID]
+			logger.Debugw(ctx, "PersUniConfig-entry exists", log.Fields{"device-id": oo.deviceID, "uniID": aUniID,
+				"tpID": aTpID, "path": aPathString, "existingPath": existingPath, "ok": ok})
 			if !ok {
 				logger.Debugw(ctx, "tp-does-not-exist", log.Fields{"device-id": oo.deviceID, "uniID": aUniID, "tpID": aTpID, "path": aPathString})
 			}
@@ -892,3 +897,42 @@
 	defer oo.mutexPersOnuConfig.RUnlock()
 	return fmt.Sprintf(cSuffixMibTemplateKvStore, oo.sOnuPersistentData.PersVendorID, oo.sOnuPersistentData.PersEquipmentID, oo.sOnuPersistentData.PersActiveSwVersion)
 }
+
+func (oo *OnuDeviceEntry) allocateFreeTcont(ctx context.Context, allocID uint16) (uint16, bool, error) {
+	logger.Debugw(ctx, "allocate-free-tcont", log.Fields{"device-id": oo.deviceID, "allocID": allocID,
+		"allocated-instances": oo.sOnuPersistentData.PersTcontMap})
+
+	oo.mutexTcontMap.Lock()
+	defer oo.mutexTcontMap.Unlock()
+	if entityID, ok := oo.sOnuPersistentData.PersTcontMap[allocID]; ok {
+		//tcont already allocated before, return the used instance-id
+		return entityID, true, nil
+	}
+	//First allocation of tcont. Find a free instance
+	if tcontInstKeys := oo.pOnuDB.getSortedInstKeys(ctx, me.TContClassID); len(tcontInstKeys) > 0 {
+		logger.Debugw(ctx, "allocate-free-tcont-db-keys", log.Fields{"device-id": oo.deviceID, "keys": tcontInstKeys})
+		for _, instID := range tcontInstKeys {
+			instExist := false
+			//If this instance exist in map, it means it is not  empty. It is allocated before
+			for _, v := range oo.sOnuPersistentData.PersTcontMap {
+				if v == instID {
+					instExist = true
+					break
+				}
+			}
+			if !instExist {
+				oo.sOnuPersistentData.PersTcontMap[allocID] = instID
+				return instID, false, nil
+			}
+		}
+	}
+	return 0, false, fmt.Errorf(fmt.Sprintf("no-free-tcont-left-for-device-%s", oo.deviceID))
+
+}
+
+func (oo *OnuDeviceEntry) freeTcont(ctx context.Context, allocID uint16) {
+	logger.Debugw(ctx, "free-tcont", log.Fields{"device-id": oo.deviceID, "alloc": allocID})
+	oo.mutexTcontMap.Lock()
+	defer oo.mutexTcontMap.Unlock()
+	delete(oo.sOnuPersistentData.PersTcontMap, allocID)
+}
diff --git a/internal/pkg/onuadaptercore/platform.go b/internal/pkg/onuadaptercore/platform.go
index 1f9898f..d70d7f8 100644
--- a/internal/pkg/onuadaptercore/platform.go
+++ b/internal/pkg/onuadaptercore/platform.go
@@ -118,7 +118,7 @@
 //Mask to indicate which possibly active ONU UNI state  is really reported to the core
 // compare python code - at the moment restrict active state to the first ONU UNI port
 // check is limited to max 16 uni ports - cmp above UNI limit!!!
-var activeUniPortStateUpdateMask = 0x0001
+var activeUniPortStateUpdateMask = 0x00FF
 
 /*
 //MinUpstreamPortID value