Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b3d9264c authored by Harsh Shah's avatar Harsh Shah Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: camera: sync: resolve race condition between merge and signal" into dev/msm-4.14-camx

parents c3a44ea3 33f3a7f4
Loading
Loading
Loading
Loading
+26 −13
Original line number Diff line number Diff line
@@ -43,7 +43,8 @@ int cam_sync_create(int32_t *sync_obj, const char *name)
	} while (bit);

	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
	rc = cam_sync_init_object(sync_dev->sync_table, idx, name);
	rc = cam_sync_init_row(sync_dev->sync_table, idx, name,
		CAM_SYNC_TYPE_INDV);
	if (rc) {
		CAM_ERR(CAM_SYNC, "Error: Unable to init row at idx = %ld",
			idx);
@@ -183,6 +184,7 @@ int cam_sync_signal(int32_t sync_obj, uint32_t status)
	struct list_head sync_list;
	struct cam_signalable_info *list_info = NULL;
	struct cam_signalable_info *temp_list_info = NULL;
	struct list_head parents_list;

	/* Objects to be signaled will be added into this list */
	INIT_LIST_HEAD(&sync_list);
@@ -237,20 +239,36 @@ int cam_sync_signal(int32_t sync_obj, uint32_t status)
		return rc;
	}

	/* copy parent list to local and release child lock */
	INIT_LIST_HEAD(&parents_list);
	list_splice_init(&row->parents_list, &parents_list);
	spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]);

	if (list_empty(&parents_list))
		goto dispatch_cb;

	/*
	 * Now iterate over all parents of this object and if they too need to
	 * be signaled add them to the list
	 */
	list_for_each_entry(parent_info,
		&row->parents_list,
		&parents_list,
		list) {
		parent_row = sync_dev->sync_table + parent_info->sync_id;
		spin_lock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
		parent_row->remaining--;

		parent_row->state = cam_sync_util_get_state(
			parent_row->state,
		rc = cam_sync_util_update_parent_state(
			parent_row,
			status);
		if (rc) {
			CAM_ERR(CAM_SYNC, "Invalid parent state %d",
				parent_row->state);
			spin_unlock_bh(
				&sync_dev->row_spinlocks[parent_info->sync_id]);
			kfree(parent_info);
			continue;
		}

		if (!parent_row->remaining) {
			rc = cam_sync_util_add_to_signalable_list
@@ -261,15 +279,13 @@ int cam_sync_signal(int32_t sync_obj, uint32_t status)
				spin_unlock_bh(
					&sync_dev->row_spinlocks[
						parent_info->sync_id]);
				spin_unlock_bh(
					&sync_dev->row_spinlocks[sync_obj]);
				return rc;
				continue;
			}
		}
		spin_unlock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
	}

	spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]);
dispatch_cb:

	/*
	 * Now dispatch the various sync objects collected so far, in our
@@ -354,10 +370,8 @@ int cam_sync_merge(int32_t *sync_obj, uint32_t num_objs, int32_t *merged_obj)
		return -EINVAL;
	}

	rc = cam_sync_util_validate_merge(sync_obj,
		num_objs);
	if (rc < 0) {
		CAM_ERR(CAM_SYNC, "Validation failed, Merge not allowed");
	if (num_objs <= 1) {
		CAM_ERR(CAM_SYNC, "Single object merge is not allowed");
		return -EINVAL;
	}

@@ -369,7 +383,6 @@ int cam_sync_merge(int32_t *sync_obj, uint32_t num_objs, int32_t *merged_obj)
	} while (bit);

	spin_lock_bh(&sync_dev->row_spinlocks[idx]);

	rc = cam_sync_init_group_object(sync_dev->sync_table,
		idx, sync_obj,
		num_objs);
+69 −153
Original line number Diff line number Diff line
@@ -31,20 +31,21 @@ int cam_sync_util_find_and_set_empty_row(struct sync_device *sync_dev,
	return rc;
}

int cam_sync_init_object(struct sync_table_row *table,
	uint32_t idx,
	const char *name)
int cam_sync_init_row(struct sync_table_row *table,
	uint32_t idx, const char *name, uint32_t type)
{
	struct sync_table_row *row = table + idx;

	if (!table || idx <= 0 || idx >= CAM_SYNC_MAX_OBJS)
		return -EINVAL;

	memset(row, 0, sizeof(*row));

	if (name)
		strlcpy(row->name, name, SYNC_DEBUG_NAME_LEN);
	INIT_LIST_HEAD(&row->parents_list);
	INIT_LIST_HEAD(&row->children_list);
	row->type = CAM_SYNC_TYPE_INDV;
	row->type = type;
	row->sync_id = idx;
	row->state = CAM_SYNC_STATE_ACTIVE;
	row->remaining = 0;
@@ -58,147 +59,95 @@ int cam_sync_init_object(struct sync_table_row *table,
	return 0;
}

uint32_t cam_sync_util_get_group_object_state(struct sync_table_row *table,
int cam_sync_init_group_object(struct sync_table_row *table,
	uint32_t idx,
	uint32_t *sync_objs,
	uint32_t num_objs)
{
	int i;
	int i, rc = 0;
	struct sync_child_info *child_info;
	struct sync_parent_info *parent_info;
	struct sync_table_row *row = table + idx;
	struct sync_table_row *child_row = NULL;
	int success_count = 0;
	int active_count = 0;

	if (!table || !sync_objs)
		return CAM_SYNC_STATE_SIGNALED_ERROR;
	cam_sync_init_row(table, idx, "merged_fence", CAM_SYNC_TYPE_GROUP);

	/*
	 * We need to arrive at the state of the merged object based on
	 * counts of error, active and success states of all children objects
	 * While traversing for children, parent's row list is updated with
	 * child info and each child's row is updated with parent info.
	 * If any child state is ERROR or SUCCESS, it will not be added to list.
	 */
	for (i = 0; i < num_objs; i++) {
		spin_lock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
		child_row = table + sync_objs[i];
		switch (child_row->state) {
		case CAM_SYNC_STATE_SIGNALED_ERROR:
		spin_lock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);

		/* validate child */
		if ((child_row->type == CAM_SYNC_TYPE_GROUP) ||
			(child_row->state == CAM_SYNC_STATE_INVALID)) {
			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
			return CAM_SYNC_STATE_SIGNALED_ERROR;
		case CAM_SYNC_STATE_SIGNALED_SUCCESS:
			success_count++;
			break;
		case CAM_SYNC_STATE_ACTIVE:
			active_count++;
			break;
		default:
			CAM_ERR(CAM_SYNC,
				"Invalid state of child object during merge");
			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
			return CAM_SYNC_STATE_SIGNALED_ERROR;
		}
		spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
				"Invalid child fence:%i state:%u type:%u",
				child_row->sync_id, child_row->state,
				child_row->type);
			rc = -EINVAL;
			goto clean_children_info;
		}

	if (active_count)
		return CAM_SYNC_STATE_ACTIVE;

	if (success_count == num_objs)
		return CAM_SYNC_STATE_SIGNALED_SUCCESS;

	return CAM_SYNC_STATE_SIGNALED_ERROR;
}

static int cam_sync_util_get_group_object_remaining_count(
	struct sync_table_row *table,
	uint32_t *sync_objs,
	uint32_t num_objs)
{
	int i;
	struct sync_table_row *child_row = NULL;
	int remaining_count = 0;

	if (!table || !sync_objs)
		return -EINVAL;

	for (i = 0; i < num_objs; i++) {
		child_row = table + sync_objs[i];
		if (child_row->state == CAM_SYNC_STATE_ACTIVE)
			remaining_count++;
		/* check for child's state */
		if (child_row->state == CAM_SYNC_STATE_SIGNALED_ERROR) {
			row->state = CAM_SYNC_STATE_SIGNALED_ERROR;
			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
			continue;
		}

	return remaining_count;
		if (child_row->state != CAM_SYNC_STATE_ACTIVE) {
			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
			continue;
		}

int cam_sync_init_group_object(struct sync_table_row *table,
	uint32_t idx,
	uint32_t *sync_objs,
	uint32_t num_objs)
{
	int i;
	int remaining;
	struct sync_child_info *child_info;
	struct sync_parent_info *parent_info;
	struct sync_table_row *row = table + idx;
	struct sync_table_row *child_row = NULL;

	INIT_LIST_HEAD(&row->parents_list);

	INIT_LIST_HEAD(&row->children_list);
		row->remaining++;

	/*
	 * While traversing parents and children, we allocate in a loop and in
	 * case allocation fails, we call the clean up function which frees up
	 * all memory allocation thus far
	 */
	for (i = 0; i < num_objs; i++) {
		/* Add child info */
		child_info = kzalloc(sizeof(*child_info), GFP_ATOMIC);

		if (!child_info) {
			cam_sync_util_cleanup_children_list(row,
				SYNC_LIST_CLEAN_ALL, 0);
			return -ENOMEM;
			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
			rc = -ENOMEM;
			goto clean_children_info;
		}

		child_info->sync_id = sync_objs[i];
		list_add_tail(&child_info->list, &row->children_list);
	}

	for (i = 0; i < num_objs; i++) {
		/* This gets us the row corresponding to the sync object */
		child_row = table + sync_objs[i];
		spin_lock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
		/* Add parent info */
		parent_info = kzalloc(sizeof(*parent_info), GFP_ATOMIC);
		if (!parent_info) {
			cam_sync_util_cleanup_parents_list(child_row,
				SYNC_LIST_CLEAN_ALL, 0);
			cam_sync_util_cleanup_children_list(row,
				SYNC_LIST_CLEAN_ALL, 0);
			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
			return -ENOMEM;
			rc = -ENOMEM;
			goto clean_children_info;
		}
		parent_info->sync_id = idx;
		list_add_tail(&parent_info->list, &child_row->parents_list);
		spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
	}

	row->type = CAM_SYNC_TYPE_GROUP;
	row->sync_id = idx;
	row->state = cam_sync_util_get_group_object_state(table,
		sync_objs, num_objs);
	remaining = cam_sync_util_get_group_object_remaining_count(table,
		sync_objs, num_objs);
	if (remaining < 0) {
		CAM_ERR(CAM_SYNC, "Failed getting remaining count");
		return -ENODEV;
	if (!row->remaining) {
		if (row->state != CAM_SYNC_STATE_SIGNALED_ERROR)
			row->state = CAM_SYNC_STATE_SIGNALED_SUCCESS;
		complete_all(&row->signaled);
	}

	row->remaining = remaining;

	init_completion(&row->signaled);
	INIT_LIST_HEAD(&row->callback_list);
	INIT_LIST_HEAD(&row->user_payload_list);
	return 0;

	if (row->state != CAM_SYNC_STATE_ACTIVE)
		complete_all(&row->signaled);
clean_children_info:
	row->state = CAM_SYNC_STATE_INVALID;
	for (i = i-1; i >= 0; i--) {
		spin_lock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
		child_row = table + sync_objs[i];
		cam_sync_util_cleanup_parents_list(child_row,
			SYNC_LIST_CLEAN_ONE, idx);
		spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
	}

	return 0;
	cam_sync_util_cleanup_children_list(row, SYNC_LIST_CLEAN_ALL, 0);
	return rc;
}

int cam_sync_deinit_object(struct sync_table_row *table, uint32_t idx)
@@ -363,32 +312,6 @@ void cam_sync_util_send_v4l2_event(uint32_t id,
		sync_obj);
}

int cam_sync_util_validate_merge(uint32_t *sync_obj, uint32_t num_objs)
{
	int i;
	struct sync_table_row *row = NULL;

	if (num_objs <= 1) {
		CAM_ERR(CAM_SYNC, "Single object merge is not allowed");
		return -EINVAL;
	}

	for (i = 0; i < num_objs; i++) {
		row = sync_dev->sync_table + sync_obj[i];
		spin_lock_bh(&sync_dev->row_spinlocks[sync_obj[i]]);
		if (row->type == CAM_SYNC_TYPE_GROUP ||
			row->state == CAM_SYNC_STATE_INVALID) {
			CAM_ERR(CAM_SYNC,
				"Group obj %d can't be merged or obj UNINIT",
				sync_obj[i]);
			spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj[i]]);
			return -EINVAL;
		}
		spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj[i]]);
	}
	return 0;
}

int cam_sync_util_add_to_signalable_list(int32_t sync_obj,
	uint32_t status,
	struct list_head *sync_list)
@@ -409,34 +332,27 @@ int cam_sync_util_add_to_signalable_list(int32_t sync_obj,
	return 0;
}

int cam_sync_util_get_state(int current_state,
int cam_sync_util_update_parent_state(struct sync_table_row *parent_row,
	int new_state)
{
	int result = CAM_SYNC_STATE_SIGNALED_ERROR;

	if (new_state != CAM_SYNC_STATE_SIGNALED_SUCCESS &&
		new_state != CAM_SYNC_STATE_SIGNALED_ERROR)
		return CAM_SYNC_STATE_SIGNALED_ERROR;

	switch (current_state) {
	case CAM_SYNC_STATE_INVALID:
		result =  CAM_SYNC_STATE_SIGNALED_ERROR;
		break;
	int rc = 0;

	switch (parent_row->state) {
	case CAM_SYNC_STATE_ACTIVE:
	case CAM_SYNC_STATE_SIGNALED_SUCCESS:
		if (new_state == CAM_SYNC_STATE_SIGNALED_ERROR)
			result = CAM_SYNC_STATE_SIGNALED_ERROR;
		else if (new_state == CAM_SYNC_STATE_SIGNALED_SUCCESS)
			result = CAM_SYNC_STATE_SIGNALED_SUCCESS;
		parent_row->state = new_state;
		break;

	case CAM_SYNC_STATE_SIGNALED_ERROR:
		result = CAM_SYNC_STATE_SIGNALED_ERROR;
		break;

	case CAM_SYNC_STATE_INVALID:
	default:
		rc = -EINVAL;
		break;
	}

	return result;
	return rc;
}

void cam_sync_util_cleanup_children_list(struct sync_table_row *row,
+4 −15
Original line number Diff line number Diff line
@@ -41,12 +41,11 @@ int cam_sync_util_find_and_set_empty_row(struct sync_device *sync_dev,
 * @param idx   : Index of row to initialize
 * @param name  : Optional string representation of the sync object. Should be
 *                63 characters or less
 *
 * @param type  : type of row to be initialized
 * @return Status of operation. Negative in case of error. Zero otherwise.
 */
int cam_sync_init_object(struct sync_table_row *table,
	uint32_t idx,
	const char *name);
int cam_sync_init_row(struct sync_table_row *table,
	uint32_t idx, const char *name, uint32_t type);

/**
 * @brief: Function to uninitialize a row in the sync table
@@ -103,16 +102,6 @@ void cam_sync_util_send_v4l2_event(uint32_t id,
	void *payload,
	int len);

/**
 * @brief: Function to validate sync merge arguments
 *
 * @param sync_obj : Array of sync objects to merge
 * @param num_objs : Number of sync objects in the array
 *
 * @return Status of operation. Negative in case of error. Zero otherwise.
 */
int cam_sync_util_validate_merge(uint32_t *sync_obj, uint32_t num_objs);

/**
 * @brief: Function which adds sync object information to the signalable list
 *
@@ -135,7 +124,7 @@ int cam_sync_util_add_to_signalable_list(int32_t sync_obj,
 *
 * @return Next state of the sync object
 */
int cam_sync_util_get_state(int current_state,
int cam_sync_util_update_parent_state(struct sync_table_row *parent_row,
	int new_state);

/**