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

Commit 5f548341 authored by Veerabhadrarao Badiganti's avatar Veerabhadrarao Badiganti Committed by Gerrit - the friendly Code Review server
Browse files

mmc: host: cmdq: Check if tag info extraced from CQTERRI is valid



In the case of data errors (data CRC/timeout), we are extracting the
tag associated with the on-going command (in-case command is active)
instead of the tag associated with ongoing data. Which is wrong.
Updated the logic to use tag associated with data for data errors.

In some cases, CQTERRI is information is not reliable. So check if the
tag info provided by CQTERRI is valid or not. If not valid, complete
the errord request with any valid tag, so that all requests gets
requeued.

Without these above two check, we may use wrong tag (sometimes
completed tag, eg if CMD13 is going on CMD line, we have task field
as zero, it can be treated as tag-0 ) which can result in NULL pointer
dereferance issues.

Change-Id: Ia590e36d411975f383683a449892c71b51c1b882
Signed-off-by: default avatarVeerabhadrarao Badiganti <vbadigan@codeaurora.org>
Signed-off-by: default avatarSahitya Tummala <stummala@codeaurora.org>
parent 4f5511f8
Loading
Loading
Loading
Loading
+65 −14
Original line number Diff line number Diff line
@@ -864,6 +864,33 @@ static int cmdq_request(struct mmc_host *mmc, struct mmc_request *mrq)
	return err;
}

static int cmdq_get_first_valid_tag(struct cmdq_host *cq_host)
{
	u32 dbr_set = 0, tag = 0;

	dbr_set = cmdq_readl(cq_host, CQTDBR);
	if (!dbr_set) {
		pr_err("%s: spurious/force error interrupt\n",
				mmc_hostname(cq_host->mmc));
		cmdq_halt_poll(cq_host->mmc, false);
		mmc_host_clr_halt(cq_host->mmc);
		return -EINVAL;
	}

	tag = ffs(dbr_set) - 1;
	pr_err("%s: error tag selected: tag = %d\n",
		mmc_hostname(cq_host->mmc), tag);
	return tag;
}

static bool cmdq_is_valid_tag(struct mmc_host *mmc, unsigned int tag)
{
	struct mmc_cmdq_context_info *ctx_info = &mmc->cmdq_ctx;

	return
	(!!(ctx_info->data_active_reqs & (1 << tag)) || tag == DCMD_SLOT);
}

static void cmdq_finish_data(struct mmc_host *mmc, unsigned int tag)
{
	struct mmc_request *mrq;
@@ -897,7 +924,7 @@ static void cmdq_finish_data(struct mmc_host *mmc, unsigned int tag)
	mrq->done(mrq);
}

irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
irqreturn_t cmdq_irq(struct mmc_host *mmc, int err, bool is_cmd_err)
{
	u32 status;
	unsigned long tag = 0, comp_status;
@@ -959,18 +986,10 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
			 *   have caused such error, so check for any first
			 *   bit set in doorbell and proceed with an error.
			 */
			dbr_set = cmdq_readl(cq_host, CQTDBR);
			if (!dbr_set) {
				pr_err("%s: spurious/force error interrupt\n",
						mmc_hostname(mmc));
				cmdq_halt_poll(mmc, false);
				mmc_host_clr_halt(mmc);
				return IRQ_HANDLED;
			}
			tag = cmdq_get_first_valid_tag(cq_host);
			if (tag == -EINVAL)
				goto hac;

			tag = ffs(dbr_set) - 1;
			pr_err("%s: error tag selected: tag = %lu\n",
					mmc_hostname(mmc), tag);
			mrq = get_req_by_tag(cq_host, tag);
			if (mrq->data)
				mrq->data->error = err;
@@ -985,10 +1004,24 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
			goto skip_cqterri;
		}

		if (err_info & CQ_RMEFV) {
		if (is_cmd_err && (err_info & CQ_RMEFV)) {
			tag = GET_CMD_ERR_TAG(err_info);
			pr_err("%s: CMD err tag: %lu\n", __func__, tag);

			/*
			 * In some cases CQTERRI is not providing reliable tag
			 * info. If the tag is not valid, complete the request
			 * with any valid tag so that all tags will get
			 * requeued.
			 */
			if (!cmdq_is_valid_tag(mmc, tag)) {
				pr_err("%s: CMD err tag is invalid: %lu\n",
						__func__, tag);
				tag = cmdq_get_first_valid_tag(cq_host);
				if (tag == -EINVAL)
					goto hac;
			}

			mrq = get_req_by_tag(cq_host, tag);
			/* CMD44/45/46/47 will not have a valid cmd */
			if (mrq->cmd)
@@ -998,8 +1031,26 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
		} else {
			tag = GET_DAT_ERR_TAG(err_info);
			pr_err("%s: Dat err  tag: %lu\n", __func__, tag);

			/*
			 * In some cases CQTERRI is not providing reliable tag
			 * info. If the tag is not valid, complete the request
			 * with any valid tag so that all tags will get
			 * requeued.
			 */
			if (!cmdq_is_valid_tag(mmc, tag)) {
				pr_err("%s: CMD err tag is invalid: %lu\n",
						__func__, tag);
				tag = cmdq_get_first_valid_tag(cq_host);
				if (tag == -EINVAL)
					goto hac;
			}
			mrq = get_req_by_tag(cq_host, tag);

			if (mrq->data)
				mrq->data->error = err;
			else
				mrq->cmd->error = err;
		}

skip_cqterri:
+2 −2
Original line number Diff line number Diff line
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2018, 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
@@ -244,7 +244,7 @@ static inline u32 cmdq_readl(struct cmdq_host *host, int reg)
		return readl_relaxed(host->mmio + reg);
}

extern irqreturn_t cmdq_irq(struct mmc_host *mmc, int err);
extern irqreturn_t cmdq_irq(struct mmc_host *mmc, int err, bool is_cmd_err);
extern int cmdq_init(struct cmdq_host *cq_host, struct mmc_host *mmc,
		     bool dma64);
extern struct cmdq_host *cmdq_pltfm_init(struct platform_device *pdev);
+8 −3
Original line number Diff line number Diff line
@@ -3300,13 +3300,18 @@ static irqreturn_t sdhci_cmdq_irq(struct sdhci_host *host, u32 intmask)
	int err = 0;
	u32 mask = 0;
	irqreturn_t ret;
	bool is_cmd_err = false;

	if (intmask & SDHCI_INT_CMD_MASK)
	if (intmask & SDHCI_INT_CMD_MASK) {
		err = sdhci_get_cmd_err(host, intmask);
	else if (intmask & SDHCI_INT_DATA_MASK)
		is_cmd_err = true;
	} else if (intmask & SDHCI_INT_DATA_MASK) {
		err = sdhci_get_data_err(host, intmask);
		if (intmask & SDHCI_INT_DATA_TIMEOUT)
			is_cmd_err = sdhci_card_busy(host->mmc);
	}

	ret = cmdq_irq(host->mmc, err);
	ret = cmdq_irq(host->mmc, err, is_cmd_err);
	if (err) {
		/* Clear the error interrupts */
		mask = intmask & SDHCI_INT_ERROR_MASK;