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

Commit 9ec3241e authored by Vinay Kalia's avatar Vinay Kalia
Browse files

msm: vidc: Fix a race condition in masking interrupts



Fix a race condition when interaction with firmware happens
in following sequence:

1. Firmware writes IDLE message into the message queue.
2. Driver writes a new command into command queue.
3. Firmware reads this command and starts processing it.
4. Driver checks if command queue is empty and finds it empty.
5. Firmware writes the response (of the command sent in 2) in
   message queue. Firmware also writes another IDLE message and
   sets the idle register.
6. Driver check if the IDLE register is set and finds it set.
7. Driver masks the interrupts. Since interrupts are masked,
   driver misses the response written by firmware in 5.

This race condition is fixed by checking if message queue is
empty or not between 6 and 7.

Change-Id: I4aa17cfc5fd9e33f6b41b6c1b12625789ff02283
Signed-off-by: default avatarVinay Kalia <vkalia@codeaurora.org>
parent d2056c0d
Loading
Loading
Loading
Loading
+19 −9
Original line number Diff line number Diff line
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -1739,16 +1739,24 @@ static int venus_hfi_core_release(void *device)
	return 0;
}

static int venus_hfi_is_cmd_pending(struct venus_hfi_device *dev)
static int venus_hfi_get_q_size(struct venus_hfi_device *dev,
	unsigned int q_index)
{
	struct hfi_queue_header *queue;
	struct vidc_iface_q_info *q_info;
	u32 write_ptr, read_ptr;
	u32 rc = 0;

	q_info = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
	if (!q_info)
	if (q_index >= VIDC_IFACEQ_NUMQ) {
		dprintk(VIDC_ERR, "Invalid q index: %d\n", q_index);
		return -ENOENT;
	}

	q_info = &dev->iface_queues[q_index];
	if (!q_info) {
		dprintk(VIDC_ERR, "cannot read shared Q's\n");
		return -ENOENT;
	}

	queue = (struct hfi_queue_header *)q_info->q_hdr;
	if (!queue) {
@@ -1774,10 +1782,12 @@ static inline void venus_hfi_clk_gating_on(struct venus_hfi_device *device)
	}
	/*SYS Idle should be last message so mask any further interrupts
	 * until clocks are enabled again.*/
	if (!venus_hfi_get_q_size(device, VIDC_IFACEQ_MSGQ_IDX)) {
		venus_hfi_write_register(device,
				VIDC_WRAPPER_INTR_MASK,
				VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK |
				VIDC_WRAPPER_INTR_MASK_A2HCPU_BMSK, 0);
	}
	venus_hfi_clk_disable(device);
	if (!queue_delayed_work(device->venus_pm_workq, &venus_hfi_pm_work,
			msecs_to_jiffies(msm_vidc_pwr_collapse_delay)))
@@ -2534,7 +2544,7 @@ static int venus_hfi_try_clk_gating(struct venus_hfi_device *device)
	}
	mutex_lock(&device->write_lock);
	mutex_lock(&device->clk_pwr_lock);
	rc = venus_hfi_is_cmd_pending(device);
	rc = venus_hfi_get_q_size(device, VIDC_IFACEQ_CMDQ_IDX);
	ctrl_status = venus_hfi_read_register(
		device,
		VIDC_CPU_CS_SCIACMDARG0);