msm: camera: sync:Cleanup sync object link properly

During the sync object deinit, it is only cleaning sync object
to parent object link and sync object to child object links.
Child object has link to parent point sync object.
Sync object parent also has child link point to sync object.
This links need to be clean up properly.

Change-Id: If8b2b303048d18d5bbf10179ea6bdfdda7151ff8
Signed-off-by: Ravikishore Pampana <rpampana@codeaurora.org>
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
index 2422016..e7dcbe7 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
@@ -230,6 +230,8 @@
 				spin_unlock_bh(
 					&sync_dev->row_spinlocks[
 						parent_info->sync_id]);
+				spin_unlock_bh(
+					&sync_dev->row_spinlocks[sync_obj]);
 				return rc;
 			}
 		}
@@ -344,24 +346,8 @@
 
 int cam_sync_destroy(int32_t sync_obj)
 {
-	struct sync_table_row *row = NULL;
-
-	if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0)
-		return -EINVAL;
-
-	spin_lock_bh(&sync_dev->row_spinlocks[sync_obj]);
-	row = sync_dev->sync_table + sync_obj;
-	if (row->state == CAM_SYNC_STATE_INVALID) {
-		CAM_ERR(CAM_SYNC,
-			"Error: accessing an uninitialized sync obj: idx = %d",
-			sync_obj);
-		spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]);
-		return -EINVAL;
-	}
 
 	cam_sync_deinit_object(sync_dev->sync_table, sync_obj);
-	spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]);
-
 	return 0;
 }
 
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h b/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h
index ba9bef4..e2a7fcb 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h
@@ -55,6 +55,18 @@
 };
 
 /**
+ * enum sync_list_clean_type - Enum to indicate the type of list clean action
+ * to be peformed, i.e. specific sync ID or all list sync ids.
+ *
+ * @SYNC_CLEAN_ID  : Specific object to be cleaned in the list
+ * @SYNC_CLEAN_ALL : Clean all objects in the list
+ */
+enum sync_list_clean_type {
+	SYNC_LIST_CLEAN_ID,
+	SYNC_LIST_CLEAN_ALL
+};
+
+/**
  * struct sync_parent_info - Single node of information about a parent
  * of a sync object, usually part of the parents linked list
  *
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c
index f66b882..6aa7c23 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c
@@ -145,7 +145,7 @@
 
 		if (!child_info) {
 			cam_sync_util_cleanup_children_list(
-				&row->children_list);
+				&row->children_list, SYNC_LIST_CLEAN_ALL, 0);
 			return -ENOMEM;
 		}
 
@@ -160,9 +160,10 @@
 		parent_info = kzalloc(sizeof(*parent_info), GFP_ATOMIC);
 		if (!parent_info) {
 			cam_sync_util_cleanup_parents_list(
-				&child_row->parents_list);
+				&child_row->parents_list,
+				SYNC_LIST_CLEAN_ALL, 0);
 			cam_sync_util_cleanup_children_list(
-				&row->children_list);
+				&row->children_list, SYNC_LIST_CLEAN_ALL, 0);
 			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
 			return -ENOMEM;
 		}
@@ -197,27 +198,131 @@
 int cam_sync_deinit_object(struct sync_table_row *table, uint32_t idx)
 {
 	struct sync_table_row *row = table + idx;
-	struct sync_child_info *child_info, *temp_child;
+	struct sync_child_info *child_info, *temp_child, *child_copy_info;
 	struct sync_callback_info *sync_cb, *temp_cb;
-	struct sync_parent_info *parent_info, *temp_parent;
+	struct sync_parent_info *parent_info, *temp_parent, *parent_copy_info;
 	struct sync_user_payload *upayload_info, *temp_upayload;
+	struct sync_table_row   *child_row = NULL, *parent_row = NULL;
+	struct list_head child_copy_list, parent_copy_list;
 
 	if (!table || idx <= 0 || idx >= CAM_SYNC_MAX_OBJS)
 		return -EINVAL;
 
-	clear_bit(idx, sync_dev->bitmap);
-	list_for_each_entry_safe(child_info, temp_child,
-				&row->children_list, list) {
+	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
+	if (row->state == CAM_SYNC_STATE_INVALID) {
+		CAM_ERR(CAM_SYNC,
+			"Error: accessing an uninitialized sync obj: idx = %d",
+			idx);
+		spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+		return -EINVAL;
+	}
+
+	/* Objects child and parent objects will be added into this list */
+	INIT_LIST_HEAD(&child_copy_list);
+	INIT_LIST_HEAD(&parent_copy_list);
+
+	list_for_each_entry_safe(child_info, temp_child, &row->children_list,
+		list) {
+		if (child_info->sync_id <= 0)
+			continue;
+
+		child_copy_info = kzalloc(sizeof(*child_copy_info), GFP_ATOMIC);
+		if (!child_copy_info) {
+			/* No free memory, clean up the child_copy_list */
+			while (!list_empty(&child_copy_list)) {
+				child_info = list_first_entry(&child_copy_list,
+					struct sync_child_info, list);
+				list_del_init(&child_info->list);
+				kfree(child_info);
+			}
+			spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+			goto deinit;
+		}
+		child_copy_info->sync_id = child_info->sync_id;
+		list_add_tail(&child_copy_info->list, &child_copy_list);
+	}
+
+	list_for_each_entry_safe(parent_info, temp_parent, &row->parents_list,
+		list) {
+		if (parent_info->sync_id <= 0)
+			continue;
+		parent_copy_info = kzalloc(sizeof(*parent_copy_info),
+			GFP_ATOMIC);
+		if (!parent_copy_info) {
+			/* No free memory, clean up the parent_copy_list */
+			while (!list_empty(&parent_copy_list)) {
+				parent_info = list_first_entry(
+					&parent_copy_list,
+					struct sync_parent_info, list);
+				list_del_init(&parent_info->list);
+				kfree(parent_info);
+			}
+			/* No free memory, clean up the child_copy_list */
+			while (!list_empty(&child_copy_list)) {
+				child_info = list_first_entry(&child_copy_list,
+					struct sync_child_info, list);
+				list_del_init(&child_info->list);
+				kfree(child_info);
+			}
+			spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+			goto deinit;
+		}
+		parent_copy_info->sync_id = parent_info->sync_id;
+		list_add_tail(&parent_copy_info->list, &parent_copy_list);
+	}
+
+	spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+	/* Cleanup the child to parent link from child list*/
+	while (!list_empty(&child_copy_list)) {
+		child_info = list_first_entry(&child_copy_list,
+			struct sync_child_info, list);
+		child_row = sync_dev->sync_table + child_info->sync_id;
+		spin_lock_bh(&sync_dev->row_spinlocks[child_info->sync_id]);
+		if (child_row->state == CAM_SYNC_STATE_INVALID) {
+			spin_unlock_bh(&sync_dev->row_spinlocks[
+				child_info->sync_id]);
+			list_del_init(&child_info->list);
+			kfree(child_info);
+			continue;
+		}
+
+		cam_sync_util_cleanup_parents_list(&child_row->parents_list,
+			SYNC_LIST_CLEAN_ID, idx);
+
+		spin_unlock_bh(&sync_dev->row_spinlocks[child_info->sync_id]);
 		list_del_init(&child_info->list);
 		kfree(child_info);
 	}
 
-	list_for_each_entry_safe(parent_info, temp_parent,
-				&row->parents_list, list) {
+	/* Cleanup the parent to child link */
+	while (!list_empty(&parent_copy_list)) {
+		parent_info = list_first_entry(&parent_copy_list,
+			struct sync_parent_info, list);
+		parent_row = sync_dev->sync_table + parent_info->sync_id;
+		spin_lock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
+		if (parent_row->state == CAM_SYNC_STATE_INVALID) {
+			spin_unlock_bh(&sync_dev->row_spinlocks[
+				parent_info->sync_id]);
+			list_del_init(&parent_info->list);
+			kfree(parent_info);
+			continue;
+		}
+
+		cam_sync_util_cleanup_children_list(&parent_row->children_list,
+			SYNC_LIST_CLEAN_ID, idx);
+
+		spin_unlock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
 		list_del_init(&parent_info->list);
 		kfree(parent_info);
 	}
 
+deinit:
+	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
+	cam_sync_util_cleanup_children_list(&row->children_list,
+		SYNC_LIST_CLEAN_ALL, 0);
+	cam_sync_util_cleanup_parents_list(&row->parents_list,
+		SYNC_LIST_CLEAN_ALL, 0);
+
 	list_for_each_entry_safe(upayload_info, temp_upayload,
 				&row->user_payload_list, list) {
 		list_del_init(&upayload_info->list);
@@ -232,6 +337,8 @@
 
 	row->state = CAM_SYNC_STATE_INVALID;
 	memset(row, 0, sizeof(*row));
+	clear_bit(idx, sync_dev->bitmap);
+	spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 
 	return 0;
 }
@@ -350,26 +457,48 @@
 	return result;
 }
 
-void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean)
+void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj)
 {
 	struct sync_child_info *child_info = NULL;
 	struct sync_child_info *temp_child_info = NULL;
+	uint32_t                curr_sync_obj;
 
 	list_for_each_entry_safe(child_info,
 			temp_child_info, list_to_clean, list) {
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(child_info->sync_id != sync_obj))
+			continue;
+
+		curr_sync_obj = child_info->sync_id;
 		list_del_init(&child_info->list);
 		kfree(child_info);
+
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(curr_sync_obj == sync_obj))
+			break;
 	}
 }
 
-void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean)
+void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj)
 {
 	struct sync_parent_info *parent_info = NULL;
 	struct sync_parent_info *temp_parent_info = NULL;
+	uint32_t                 curr_sync_obj;
 
 	list_for_each_entry_safe(parent_info,
 			temp_parent_info, list_to_clean, list) {
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(parent_info->sync_id != sync_obj))
+			continue;
+
+		curr_sync_obj = parent_info->sync_id;
 		list_del_init(&parent_info->list);
 		kfree(parent_info);
+
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(curr_sync_obj == sync_obj))
+			break;
 	}
 }
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h
index 8b60ce1..1c5c4bf 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h
@@ -141,17 +141,25 @@
 /**
  * @brief: Function to clean up the children of a sync object
  * @param list_to_clean : List to clean up
+ * @list_clean_type     : Clean specific object or clean all objects
+ * @sync_obj            : Sync object to be clean if list clean type is
+ *                         SYNC_LIST_CLEAN_ID
  *
  * @return None
  */
-void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean);
+void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj);
 
 /**
  * @brief: Function to clean up the parents of a sync object
  * @param list_to_clean : List to clean up
+ * @list_clean_type     : Clean specific object or clean all objects
+ * @sync_obj            : Sync object to be clean if list clean type is
+ *                         SYNC_LIST_CLEAN_ID
  *
  * @return None
  */
-void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean);
+void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj);
 
 #endif /* __CAM_SYNC_UTIL_H__ */