summaryrefslogtreecommitdiffstats
path: root/mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch
diff options
context:
space:
mode:
Diffstat (limited to 'mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch')
-rw-r--r--mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch203
1 files changed, 203 insertions, 0 deletions
diff --git a/mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch b/mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch
new file mode 100644
index 000000000..c755534a0
--- /dev/null
+++ b/mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch
@@ -0,0 +1,203 @@
+From: Haibo Chen <haibo.chen@freescale.com>
+Date: Tue, 25 Aug 2015 10:02:11 +0800
+Subject: [PATCH] mmc: sdhci: fix dma memory leak in sdhci_pre_req()
+
+Currently one mrq->data maybe execute dma_map_sg() twice
+when mmc subsystem prepare over one new request, and the
+following log show up:
+ sdhci[sdhci_pre_dma_transfer] invalid cookie: 24, next-cookie 25
+
+In this condition, mrq->date map a dma-memory(1) in sdhci_pre_req
+for the first time, and map another dma-memory(2) in sdhci_prepare_data
+for the second time. But driver only unmap the dma-memory(2), and
+dma-memory(1) never unmapped, which cause the dma memory leak issue.
+
+This patch use another method to map the dma memory for the mrq->data
+which can fix this dma memory leak issue.
+
+Fixes: commit 348487cb28e66b0 ("mmc: sdhci: use pipeline mmc requests to improve performance")
+Cc: stable@vger.kernel.org # 4.0+
+Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz>
+Signed-off-by: Haibo Chen <haibo.chen@freescale.com>
+---
+ drivers/mmc/host/sdhci.c | 67 ++++++++++++++++++------------------------------
+ drivers/mmc/host/sdhci.h | 8 +++---
+ 2 files changed, 29 insertions(+), 46 deletions(-)
+
+diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
+index 1dbe932..ea10ebc 100644
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -54,8 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *);
+ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
+ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
+ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
+- struct mmc_data *data,
+- struct sdhci_host_next *next);
++ struct mmc_data *data);
+ static int sdhci_do_get_cd(struct sdhci_host *host);
+
+ #ifdef CONFIG_PM
+@@ -496,7 +495,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
+ goto fail;
+ BUG_ON(host->align_addr & host->align_mask);
+
+- host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
++ host->sg_count = sdhci_pre_dma_transfer(host, data);
+ if (host->sg_count < 0)
+ goto unmap_align;
+
+@@ -635,9 +634,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
+ }
+ }
+
+- if (!data->host_cookie)
++ if (data->host_cookie == COOKIE_MAPPED) {
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
++ data->host_cookie = COOKIE_UNMAPPED;
++ }
+ }
+
+ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
+@@ -833,7 +834,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
+ } else {
+ int sg_cnt;
+
+- sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
++ sg_cnt = sdhci_pre_dma_transfer(host, data);
+ if (sg_cnt <= 0) {
+ /*
+ * This only happens when someone fed
+@@ -949,11 +950,13 @@ static void sdhci_finish_data(struct sdhci_host *host)
+ if (host->flags & SDHCI_USE_ADMA)
+ sdhci_adma_table_post(host, data);
+ else {
+- if (!data->host_cookie)
++ if (data->host_cookie == COOKIE_MAPPED) {
+ dma_unmap_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len,
+ (data->flags & MMC_DATA_READ) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
++ data->host_cookie = COOKIE_UNMAPPED;
++ }
+ }
+ }
+
+@@ -2097,49 +2100,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ struct mmc_data *data = mrq->data;
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+- if (data->host_cookie)
++ if (data->host_cookie == COOKIE_GIVEN ||
++ data->host_cookie == COOKIE_MAPPED)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ data->flags & MMC_DATA_WRITE ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+- mrq->data->host_cookie = 0;
++ data->host_cookie = COOKIE_UNMAPPED;
+ }
+ }
+
+ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
+- struct mmc_data *data,
+- struct sdhci_host_next *next)
++ struct mmc_data *data)
+ {
+ int sg_count;
+
+- if (!next && data->host_cookie &&
+- data->host_cookie != host->next_data.cookie) {
+- pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n",
+- __func__, data->host_cookie, host->next_data.cookie);
+- data->host_cookie = 0;
++ if (data->host_cookie == COOKIE_MAPPED) {
++ data->host_cookie = COOKIE_GIVEN;
++ return data->sg_count;
+ }
+
+- /* Check if next job is already prepared */
+- if (next ||
+- (!next && data->host_cookie != host->next_data.cookie)) {
+- sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
+- data->sg_len,
+- data->flags & MMC_DATA_WRITE ?
+- DMA_TO_DEVICE : DMA_FROM_DEVICE);
+-
+- } else {
+- sg_count = host->next_data.sg_count;
+- host->next_data.sg_count = 0;
+- }
++ WARN_ON(data->host_cookie == COOKIE_GIVEN);
+
++ sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
++ data->flags & MMC_DATA_WRITE ?
++ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ if (sg_count == 0)
+- return -EINVAL;
++ return -ENOSPC;
+
+- if (next) {
+- next->sg_count = sg_count;
+- data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
+- } else
+- host->sg_count = sg_count;
++ data->sg_count = sg_count;
++ data->host_cookie = COOKIE_MAPPED;
+
+ return sg_count;
+ }
+@@ -2149,16 +2139,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ {
+ struct sdhci_host *host = mmc_priv(mmc);
+
+- if (mrq->data->host_cookie) {
+- mrq->data->host_cookie = 0;
+- return;
+- }
++ mrq->data->host_cookie = COOKIE_UNMAPPED;
+
+ if (host->flags & SDHCI_REQ_USE_DMA)
+- if (sdhci_pre_dma_transfer(host,
+- mrq->data,
+- &host->next_data) < 0)
+- mrq->data->host_cookie = 0;
++ sdhci_pre_dma_transfer(host, mrq->data);
+ }
+
+ static void sdhci_card_event(struct mmc_host *mmc)
+@@ -3030,7 +3014,6 @@ int sdhci_add_host(struct sdhci_host *host)
+ host->max_clk = host->ops->get_max_clock(host);
+ }
+
+- host->next_data.cookie = 1;
+ /*
+ * In case of Host Controller v3.00, find out whether clock
+ * multiplier is supported.
+diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
+index 5521d29..a9512a4 100644
+--- a/drivers/mmc/host/sdhci.h
++++ b/drivers/mmc/host/sdhci.h
+@@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc {
+ */
+ #define SDHCI_MAX_SEGS 128
+
+-struct sdhci_host_next {
+- unsigned int sg_count;
+- s32 cookie;
++enum sdhci_cookie {
++ COOKIE_UNMAPPED,
++ COOKIE_MAPPED,
++ COOKIE_GIVEN,
+ };
+
+ struct sdhci_host {
+@@ -503,7 +504,6 @@ struct sdhci_host {
+ unsigned int tuning_mode; /* Re-tuning mode supported by host */
+ #define SDHCI_TUNING_MODE_1 0
+
+- struct sdhci_host_next next_data;
+ unsigned long private[0] ____cacheline_aligned;
+ };
+