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

Commit e8c46e61 authored by Harsh Shah's avatar Harsh Shah
Browse files

msm: camera: isp: Fix race condition in tasklet stop



This change sets the atomic variable tasklet_active to 0 in
stop before calling flush. This also adds call to tasklet_kill
before tasklet_disable in stop. This will fix a race condition
between tasklet stop/enqueue. And it will ensure the tasklet
is correctly halted and will not be raised repeatedly.

Change-Id: Ibf9f78b41e3201be50cdee8653dd4e64d2142fbd
Signed-off-by: default avatarHarsh Shah <harshs@codeaurora.org>
parent caf2454f
Loading
Loading
Loading
Loading
+28 −32
Original line number Diff line number Diff line
@@ -93,14 +93,14 @@ int cam_tasklet_get_cmd(
	}

	if (!atomic_read(&tasklet->tasklet_active)) {
		CAM_ERR_RATE_LIMIT(CAM_ISP, "Tasklet is not active!\n");
		CAM_ERR_RATE_LIMIT(CAM_ISP, "Tasklet is not active");
		rc = -EPIPE;
		return rc;
	}

	spin_lock_irqsave(&tasklet->tasklet_lock, flags);
	if (list_empty(&tasklet->free_cmd_list)) {
		CAM_ERR_RATE_LIMIT(CAM_ISP, "No more free tasklet cmd!\n");
		CAM_ERR_RATE_LIMIT(CAM_ISP, "No more free tasklet cmd");
		rc = -ENODEV;
		goto spin_unlock;
	} else {
@@ -163,12 +163,6 @@ static int cam_tasklet_dequeue_cmd(

	*tasklet_cmd = NULL;

	if (!atomic_read(&tasklet->tasklet_active)) {
		CAM_ERR(CAM_ISP, "Tasklet is not active!");
		rc = -EPIPE;
		return rc;
	}

	CAM_DBG(CAM_ISP, "Dequeue before lock.");
	spin_lock_irqsave(&tasklet->tasklet_lock, flags);
	if (list_empty(&tasklet->used_cmd_list)) {
@@ -199,16 +193,20 @@ void cam_tasklet_enqueue_cmd(
	struct cam_tasklet_info       *tasklet = bottom_half;

	if (!bottom_half) {
		CAM_ERR(CAM_ISP, "NULL bottom half");
		CAM_ERR_RATE_LIMIT(CAM_ISP, "NULL bottom half");
		return;
	}

	if (!bh_cmd) {
		CAM_ERR(CAM_ISP, "NULL bh cmd");
		CAM_ERR_RATE_LIMIT(CAM_ISP, "NULL bh cmd");
		return;
	}

	if (!atomic_read(&tasklet->tasklet_active)) {
		CAM_ERR_RATE_LIMIT(CAM_ISP, "Tasklet is not active\n");
		return;
	}

	if (tasklet_cmd) {
	CAM_DBG(CAM_ISP, "Enqueue tasklet cmd");
	tasklet_cmd->bottom_half_handler = bottom_half_handler;
	tasklet_cmd->payload = evt_payload_priv;
@@ -217,9 +215,6 @@ void cam_tasklet_enqueue_cmd(
		&tasklet->used_cmd_list);
	spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
	tasklet_schedule(&tasklet->tasklet);
	} else {
		CAM_ERR(CAM_ISP, "tasklet cmd is NULL!");
	}
}

int cam_tasklet_init(
@@ -251,7 +246,7 @@ int cam_tasklet_init(
	}
	tasklet_init(&tasklet->tasklet, cam_tasklet_action,
		(unsigned long)tasklet);
	cam_tasklet_stop(tasklet);
	tasklet_disable(&tasklet->tasklet);

	*tasklet_info = tasklet;

@@ -262,19 +257,18 @@ void cam_tasklet_deinit(void **tasklet_info)
{
	struct cam_tasklet_info *tasklet = *tasklet_info;

	if (atomic_read(&tasklet->tasklet_active)) {
		atomic_set(&tasklet->tasklet_active, 0);
		tasklet_kill(&tasklet->tasklet);
		tasklet_disable(&tasklet->tasklet);
	}
	kfree(tasklet);
	*tasklet_info = NULL;
}

static void cam_tasklet_flush(void  *tasklet_info)
static inline void cam_tasklet_flush(struct cam_tasklet_info *tasklet_info)
{
	unsigned long data;
	struct cam_tasklet_info *tasklet = tasklet_info;

	data = (unsigned long)tasklet;
	cam_tasklet_action(data);
	cam_tasklet_action((unsigned long) tasklet_info);
}

int cam_tasklet_start(void  *tasklet_info)
@@ -287,7 +281,6 @@ int cam_tasklet_start(void *tasklet_info)
			tasklet->index);
		return -EBUSY;
	}
	atomic_set(&tasklet->tasklet_active, 1);

	/* clean up the command queue first */
	for (i = 0; i < CAM_TASKLETQ_SIZE; i++) {
@@ -296,6 +289,8 @@ int cam_tasklet_start(void *tasklet_info)
			&tasklet->free_cmd_list);
	}

	atomic_set(&tasklet->tasklet_active, 1);

	tasklet_enable(&tasklet->tasklet);

	return 0;
@@ -305,9 +300,10 @@ void cam_tasklet_stop(void *tasklet_info)
{
	struct cam_tasklet_info  *tasklet = tasklet_info;

	cam_tasklet_flush(tasklet);
	atomic_set(&tasklet->tasklet_active, 0);
	tasklet_kill(&tasklet->tasklet);
	tasklet_disable(&tasklet->tasklet);
	cam_tasklet_flush(tasklet);
}

/*
+2 −1
Original line number Diff line number Diff line
@@ -639,10 +639,11 @@ int cam_vfe_stop(void *hw_priv, void *stop_args, uint32_t arg_size)
	if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) {
		cam_irq_controller_unsubscribe_irq(
			core_info->vfe_irq_controller, isp_res->irq_handle);
		isp_res->irq_handle = 0;

		rc = core_info->vfe_top->hw_ops.stop(
			core_info->vfe_top->top_priv, isp_res,
			sizeof(struct cam_isp_resource_node));

	} else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) {
		rc = core_info->vfe_bus->hw_ops.stop(isp_res, NULL, 0);
	} else {