Avoid redundant entity association PDRs after merge

currently bmc creates merged entity association PDRs for
host starting from the top level of bmc's entity tree
due to that bmc creates redundant entity association PDRs
which host has not sent.
This commit creates merged(remote) entity association PDRs
starting from the level Host has sent it's entity
association PDRs.

if bmc has:
system->chassis
chassis->motherboard
motherboard->memory and drive backplane
drive backplane -> slot

and host sends:
motherboard->processor

previously we would create following merged entity PDRs:
system->chassis
chassis->motherboard
motherboard->memory and processor and drive back plane
drive back plane -> slot

after the change bmc will create:
motherboard->memory and processor and drive back plane

since the other two are redundant

Change-Id: I900997e23a80b4874eef23bb203b369e0b8d757e
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/host-bmc/host_pdr_handler.cpp b/host-bmc/host_pdr_handler.cpp
index f9d30b5..a2d4d17 100644
--- a/host-bmc/host_pdr_handler.cpp
+++ b/host-bmc/host_pdr_handler.cpp
@@ -207,13 +207,24 @@
             }
         }
     }
-    free(entities);
 
     if (merged)
     {
         // Update our PDR repo with the merged entity association PDRs
-        pldm_entity_association_pdr_add(entityTree, repo, true);
+        pldm_entity_node* node = nullptr;
+        pldm_find_entity_ref_in_tree(entityTree, entities[0], &node);
+        if (node == nullptr)
+        {
+            std::cerr
+                << "\ncould not find referrence of the entity in the tree \n";
+        }
+        else
+        {
+            pldm_entity_association_pdr_add_from_node(node, repo, &entities,
+                                                      numEntities, true);
+        }
     }
+    free(entities);
 }
 
 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
diff --git a/libpldm/pdr.c b/libpldm/pdr.c
index d887917..ec9ffee 100644
--- a/libpldm/pdr.c
+++ b/libpldm/pdr.c
@@ -631,15 +631,37 @@
 	}
 }
 
+bool is_present(pldm_entity entity, pldm_entity **entities, size_t num_entities)
+{
+	if (entities == NULL || num_entities == 0) {
+		return true;
+	}
+	size_t i = 0;
+	while (i < num_entities) {
+		if ((*entities + i)->entity_type == entity.entity_type) {
+			return true;
+		}
+		i++;
+	}
+	return false;
+}
+
 static void entity_association_pdr_add(pldm_entity_node *curr, pldm_pdr *repo,
-				       bool is_remote)
+				       pldm_entity **entities,
+				       size_t num_entities, bool is_remote)
 {
 	if (curr == NULL) {
 		return;
 	}
-	entity_association_pdr_add_entry(curr, repo, is_remote);
-	entity_association_pdr_add(curr->next_sibling, repo, is_remote);
-	entity_association_pdr_add(curr->first_child, repo, is_remote);
+	bool to_add = true;
+	to_add = is_present(curr->entity, entities, num_entities);
+	if (to_add) {
+		entity_association_pdr_add_entry(curr, repo, is_remote);
+	}
+	entity_association_pdr_add(curr->next_sibling, repo, entities,
+				   num_entities, is_remote);
+	entity_association_pdr_add(curr->first_child, repo, entities,
+				   num_entities, is_remote);
 }
 
 void pldm_entity_association_pdr_add(pldm_entity_association_tree *tree,
@@ -648,7 +670,44 @@
 	assert(tree != NULL);
 	assert(repo != NULL);
 
-	entity_association_pdr_add(tree->root, repo, is_remote);
+	entity_association_pdr_add(tree->root, repo, NULL, 0, is_remote);
+}
+
+void pldm_entity_association_pdr_add_from_node(pldm_entity_node *node,
+					       pldm_pdr *repo,
+					       pldm_entity **entities,
+					       size_t num_entities,
+					       bool is_remote)
+{
+	assert(repo != NULL);
+
+	entity_association_pdr_add(node, repo, entities, num_entities,
+				   is_remote);
+}
+
+void find_entity_ref_in_tree(pldm_entity_node *tree_node, pldm_entity entity,
+			     pldm_entity_node **node)
+{
+	if (tree_node == NULL) {
+		return;
+	}
+
+	if (tree_node->entity.entity_type == entity.entity_type &&
+	    tree_node->entity.entity_instance_num ==
+		entity.entity_instance_num) {
+		*node = tree_node;
+		return;
+	}
+
+	find_entity_ref_in_tree(tree_node->first_child, entity, node);
+	find_entity_ref_in_tree(tree_node->next_sibling, entity, node);
+}
+
+void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree,
+				  pldm_entity entity, pldm_entity_node **node)
+{
+	assert(tree != NULL);
+	find_entity_ref_in_tree(tree->root, entity, node);
 }
 
 void pldm_pdr_remove_remote_pdrs(pldm_pdr *repo)
diff --git a/libpldm/pdr.h b/libpldm/pdr.h
index 021a6d1..8f4d893 100644
--- a/libpldm/pdr.h
+++ b/libpldm/pdr.h
@@ -285,6 +285,26 @@
  */
 void pldm_entity_association_pdr_add(pldm_entity_association_tree *tree,
 				     pldm_pdr *repo, bool is_remote);
+/** @brief Add entity association pdr from node
+ *
+ *  @param[in] node - opaque pointer acting as a handle to an entity node
+ *  @param[in] repo - PDR repo where entity association records should be added
+ *  @param[in] is_remote  - if true, then the PDR is not from this terminus
+ */
+void pldm_entity_association_pdr_add_from_node(pldm_entity_node *node,
+					       pldm_pdr *repo,
+					       pldm_entity **entities,
+					       size_t num_entities,
+					       bool is_remote);
+
+/** @brief Find entity reference in tree
+ *
+ *  @param[in] tree - opaque pointer to entity association tree
+ *  @param[in] entity - PLDM entity
+ *  @param[in] node - node to the entity
+ */
+void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree,
+				  pldm_entity entity, pldm_entity_node **node);
 
 /** @brief Get number of children of entity
  *