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

Commit c5dfb29c authored by Harshdeep Dhatt's avatar Harshdeep Dhatt Committed by Gerrit - the friendly Code Review server
Browse files

msm: kgsl: Process HFI message queue inline



When we send out a command to GMU, we do an
unrealistic wait for five seconds for processing
the ack from GMU. This code path is invoked with
device mutex held, and therefore this wait can starve
other kgsl threads leading to fence timeouts and
performance hit. So reduce the timeout to a
reasonable scale. Also, so far this queue is used for
acknowledgements of commands sent to the gmu. Since
these acks are synchronous, we can process them inline
instead of using interrupts and scheduling a tasklet.
This way, We also get rid of unnecessary lists, their
locks and gmu interrupts.

Change-Id: Id6724d6e4ce489d776816d68933a2a8d30fd4ece
Signed-off-by: default avatarHarshdeep Dhatt <hdhatt@codeaurora.org>
parent 90591030
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -1270,9 +1270,6 @@ static int gmu_probe(struct kgsl_device *device, struct device_node *node)
	disable_irq(hfi->hfi_interrupt_num);

	tasklet_init(&hfi->tasklet, hfi_receiver, (unsigned long) gmu);
	INIT_LIST_HEAD(&hfi->msglist);
	spin_lock_init(&hfi->msglock);
	spin_lock_init(&hfi->read_queue_lock);
	hfi->kgsldev = device;

	/* Retrieves GMU/GPU power level configurations*/
+60 −60
Original line number Diff line number Diff line
@@ -39,13 +39,13 @@
	 (((minor) & 0x7FFFFF) << 5) | \
	 ((branch) & 0x1F))

static void hfi_process_queue(struct gmu_device *gmu, uint32_t queue_idx);
static void hfi_process_queue(struct gmu_device *gmu, uint32_t queue_idx,
	struct pending_cmd *ret_cmd);

/* Size in below functions are in unit of dwords */
static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx,
		unsigned int *output, unsigned int max_size)
{
	struct kgsl_hfi *hfi = &gmu->hfi;
	struct gmu_memdesc *mem_addr = gmu->hfi_mem;
	struct hfi_queue_table *tbl = mem_addr->hostptr;
	struct hfi_queue_header *hdr = &tbl->qhdr[queue_idx];
@@ -58,8 +58,6 @@ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx,
	if (hdr->status == HFI_QUEUE_STATUS_DISABLED)
		return -EINVAL;

	spin_lock_bh(&hfi->read_queue_lock);

	if (hdr->read_index == hdr->write_index) {
		hdr->rx_req = 1;
		result = -ENODATA;
@@ -103,7 +101,6 @@ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx,
	hdr->read_index = read;

done:
	spin_unlock_bh(&hfi->read_queue_lock);
	return result;
}

@@ -234,39 +231,27 @@ void hfi_init(struct kgsl_hfi *hfi, struct gmu_memdesc *mem_addr,
#define HDR_CMP_SEQNUM(out_hdr, in_hdr) \
	(MSG_HDR_GET_SEQNUM(out_hdr) == MSG_HDR_GET_SEQNUM(in_hdr))

static void receive_ack_cmd(struct gmu_device *gmu, void *rcvd)
static void receive_ack_cmd(struct gmu_device *gmu, void *rcvd,
	struct pending_cmd *ret_cmd)
{
	uint32_t *ack = rcvd;
	uint32_t hdr = ack[0];
	uint32_t req_hdr = ack[1];
	struct kgsl_hfi *hfi = &gmu->hfi;
	struct pending_cmd *cmd = NULL;
	uint32_t waiters[64], i = 0, j;

	trace_kgsl_hfi_receive(MSG_HDR_GET_ID(req_hdr),
		MSG_HDR_GET_SIZE(req_hdr),
		MSG_HDR_GET_SEQNUM(req_hdr));

	spin_lock_bh(&hfi->msglock);
	list_for_each_entry(cmd, &hfi->msglist, node) {
		if (HDR_CMP_SEQNUM(cmd->sent_hdr, req_hdr)) {
			memcpy(&cmd->results, ack, MSG_HDR_GET_SIZE(hdr) << 2);
			complete(&cmd->msg_complete);
			spin_unlock_bh(&hfi->msglock);
	if (HDR_CMP_SEQNUM(ret_cmd->sent_hdr, req_hdr)) {
		memcpy(&ret_cmd->results, ack, MSG_HDR_GET_SIZE(hdr) << 2);
		return;
	}
		if (i < 64)
			waiters[i++] = cmd->sent_hdr;
	}
	spin_unlock_bh(&hfi->msglock);

	/* Didn't find the sender, list the waiter */
	dev_err_ratelimited(&gmu->pdev->dev,
			"HFI ACK: Cannot find sender for 0x%8.8X\n", req_hdr);
	/* Didn't find the sender, list all the waiters */
	for (j = 0; j < i && j < 64; j++) {
		dev_err_ratelimited(&gmu->pdev->dev,
				"HFI ACK: Waiters: 0x%8.8X\n", waiters[j]);
	}
		"HFI ACK: Cannot find sender for 0x%8.8x Waiter: 0x%8.8x\n",
		req_hdr, ret_cmd->sent_hdr);

	adreno_set_gpu_fault(ADRENO_DEVICE(hfi->kgsldev), ADRENO_GMU_FAULT);
	adreno_dispatcher_schedule(hfi->kgsldev);
@@ -275,6 +260,28 @@ static void receive_ack_cmd(struct gmu_device *gmu, void *rcvd)
#define MSG_HDR_SET_SEQNUM(hdr, num) \
	(((hdr) & 0xFFFFF) | ((num) << 20))

static int poll_adreno_gmu_reg(struct adreno_device *adreno_dev,
	enum adreno_regs offset_name, unsigned int expected_val,
	unsigned int mask, unsigned int timeout_ms)
{
	unsigned int val;
	unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);

	while (time_is_after_jiffies(timeout)) {
		adreno_read_gmureg(adreno_dev, offset_name, &val);
		if ((val & mask) == expected_val)
			return 0;
		usleep_range(10, 100);
	}

	/* Check one last time */
	adreno_read_gmureg(adreno_dev, offset_name, &val);
	if ((val & mask) == expected_val)
		return 0;

	return -ETIMEDOUT;
}

static int hfi_send_cmd(struct gmu_device *gmu, uint32_t queue_idx,
		void *data, struct pending_cmd *ret_cmd)
{
@@ -282,42 +289,34 @@ static int hfi_send_cmd(struct gmu_device *gmu, uint32_t queue_idx,
	uint32_t *cmd = data;
	struct kgsl_hfi *hfi = &gmu->hfi;
	unsigned int seqnum = atomic_inc_return(&hfi->seqnum);
	struct adreno_device *adreno_dev = ADRENO_DEVICE(hfi->kgsldev);

	*cmd = MSG_HDR_SET_SEQNUM(*cmd, seqnum);
	if (ret_cmd == NULL)
		return hfi_queue_write(gmu, queue_idx, cmd);

	init_completion(&ret_cmd->msg_complete);
	ret_cmd->sent_hdr = cmd[0];

	spin_lock_bh(&hfi->msglock);
	list_add_tail(&ret_cmd->node, &hfi->msglist);
	spin_unlock_bh(&hfi->msglock);

	rc = hfi_queue_write(gmu, queue_idx, cmd);
	if (rc)
		goto done;
		return rc;

	rc = poll_adreno_gmu_reg(adreno_dev, ADRENO_REG_GMU_GMU2HOST_INTR_INFO,
		HFI_IRQ_MSGQ_MASK, HFI_IRQ_MSGQ_MASK, HFI_RSP_TIMEOUT);

	rc = wait_for_completion_timeout(
			&ret_cmd->msg_complete,
			msecs_to_jiffies(HFI_RSP_TIMEOUT));
	if (!rc) {
		/* Check one more time to make sure there is no response */
		hfi_process_queue(gmu, HFI_MSG_ID);
		if (!completion_done(&ret_cmd->msg_complete)) {
	if (rc) {
		dev_err(&gmu->pdev->dev,
		"Timed out waiting on ack for 0x%8.8x (id %d, sequence %d)\n",
				cmd[0],
				MSG_HDR_GET_ID(*cmd),
				MSG_HDR_GET_SEQNUM(*cmd));
			rc = -ETIMEDOUT;
		cmd[0], MSG_HDR_GET_ID(*cmd), MSG_HDR_GET_SEQNUM(*cmd));
		return rc;
	}
	} else
		rc = 0;
done:
	spin_lock_bh(&hfi->msglock);
	list_del(&ret_cmd->node);
	spin_unlock_bh(&hfi->msglock);

	/* Clear the interrupt */
	adreno_write_gmureg(adreno_dev, ADRENO_REG_GMU_GMU2HOST_INTR_CLR,
		HFI_IRQ_MSGQ_MASK);

	hfi_process_queue(gmu, HFI_MSG_ID, ret_cmd);

	return rc;
}

@@ -530,11 +529,12 @@ static void receive_debug_req(struct gmu_device *gmu, void *rcvd)
			cmd->type, cmd->timestamp, cmd->data);
}

static void hfi_v1_receiver(struct gmu_device *gmu, uint32_t *rcvd)
static void hfi_v1_receiver(struct gmu_device *gmu, uint32_t *rcvd,
	struct pending_cmd *ret_cmd)
{
	/* V1 ACK Handler */
	if (MSG_HDR_GET_TYPE(rcvd[0]) == HFI_V1_MSG_ACK) {
		receive_ack_cmd(gmu, rcvd);
		receive_ack_cmd(gmu, rcvd, ret_cmd);
		return;
	}

@@ -554,20 +554,21 @@ static void hfi_v1_receiver(struct gmu_device *gmu, uint32_t *rcvd)
	}
}

static void hfi_process_queue(struct gmu_device *gmu, uint32_t queue_idx)
static void hfi_process_queue(struct gmu_device *gmu, uint32_t queue_idx,
	struct pending_cmd *ret_cmd)
{
	uint32_t rcvd[MAX_RCVD_SIZE];

	while (hfi_queue_read(gmu, queue_idx, rcvd, sizeof(rcvd)) > 0) {
		/* Special case if we're v1 */
		if (HFI_VER_MAJOR(&gmu->hfi) < 2) {
			hfi_v1_receiver(gmu, rcvd);
			hfi_v1_receiver(gmu, rcvd, ret_cmd);
			continue;
		}

		/* V2 ACK Handler */
		if (MSG_HDR_GET_TYPE(rcvd[0]) == HFI_MSG_ACK) {
			receive_ack_cmd(gmu, rcvd);
			receive_ack_cmd(gmu, rcvd, ret_cmd);
			continue;
		}

@@ -590,9 +591,8 @@ static void hfi_process_queue(struct gmu_device *gmu, uint32_t queue_idx)

void hfi_receiver(unsigned long data)
{
	/* Process all read (firmware to host) queues */
	hfi_process_queue((struct gmu_device *) data, HFI_MSG_ID);
	hfi_process_queue((struct gmu_device *) data, HFI_DBG_ID);
	/* Process all asynchronous read (firmware to host) queues */
	hfi_process_queue((struct gmu_device *) data, HFI_DBG_ID, NULL);
}

#define GMU_VER_MAJOR(ver) (((ver) >> 28) & 0xF)
@@ -818,7 +818,7 @@ irqreturn_t hfi_irq_handler(int irq, void *data)
	adreno_write_gmureg(ADRENO_DEVICE(device),
			ADRENO_REG_GMU_GMU2HOST_INTR_CLR, status);

	if (status & HFI_IRQ_MSGQ_MASK)
	if (status & HFI_IRQ_DBGQ_MASK)
		tasklet_hi_schedule(&hfi->tasklet);
	if (status & HFI_IRQ_CM3_FAULT_MASK) {
		dev_err_ratelimited(&gmu->pdev->dev,
+2 −14
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ struct hfi_queue_table;
#define HFI_DBG_PRI 40
#define HFI_DSP_PRI_0 20

#define HFI_RSP_TIMEOUT 5000 /* msec */
#define HFI_RSP_TIMEOUT 100 /* msec */
#define HFI_H2F_CMD_IRQ_MASK BIT(0)

#define HFI_IRQ_MSGQ_MASK		BIT(0)
@@ -51,8 +51,7 @@ struct hfi_queue_table;
#define HFI_IRQ_DBGQ_MASK		BIT(2)
#define HFI_IRQ_CM3_FAULT_MASK		BIT(15)
#define HFI_IRQ_OOB_MASK		GENMASK(31, 16)
#define HFI_IRQ_MASK			(HFI_IRQ_MSGQ_MASK |\
					HFI_IRQ_SIDEMSGQ_MASK |\
#define HFI_IRQ_MASK			(HFI_IRQ_SIDEMSGQ_MASK |\
					HFI_IRQ_DBGQ_MASK |\
					HFI_IRQ_CM3_FAULT_MASK)

@@ -574,14 +573,10 @@ struct hfi_context_bad_reply_cmd {
/**
 * struct pending_cmd - data structure to track outstanding HFI
 *	command messages
 * @msg_complete: a blocking mechanism for sender to wait for ACK
 * @node: a node in pending message queue
 * @sent_hdr: copy of outgoing header for response comparison
 * @results: the payload of received return message (ACK)
 */
struct pending_cmd {
	struct completion msg_complete;
	struct list_head node;
	uint32_t sent_hdr;
	uint32_t results[MAX_RCVD_SIZE];
};
@@ -590,11 +585,7 @@ struct pending_cmd {
 * struct kgsl_hfi - HFI control structure
 * @kgsldev: Point to the kgsl device
 * @hfi_interrupt_num: number of GMU asserted HFI interrupt
 * @msglock: spinlock to protect access to outstanding command message list
 * @read_queue_lock: spinlock to protect against concurrent reading of queues
 * @cmdq_mutex: mutex to protect command queue access from multiple senders
 * @msglist: outstanding command message list. Each message in the list
 *	is waiting for ACK from GMU
 * @tasklet: the thread handling received messages from GMU
 * @version: HFI version number provided
 * @seqnum: atomic counter that is incremented for each message sent. The
@@ -604,10 +595,7 @@ struct pending_cmd {
struct kgsl_hfi {
	struct kgsl_device *kgsldev;
	int hfi_interrupt_num;
	spinlock_t msglock;
	spinlock_t read_queue_lock;
	struct mutex cmdq_mutex;
	struct list_head msglist;
	struct tasklet_struct tasklet;
	uint32_t version;
	atomic_t seqnum;