|
|
5962555 |
From 5b942146e110c49f52bbaa034273b3affd787dfb Mon Sep 17 00:00:00 2001
|
|
|
069ff12 |
From: Haibo Chen <haibo.chen@freescale.com>
|
|
|
069ff12 |
Date: Tue, 25 Aug 2015 10:02:11 +0800
|
|
|
069ff12 |
Subject: [PATCH] mmc: sdhci: fix dma memory leak in sdhci_pre_req()
|
|
|
069ff12 |
|
|
|
069ff12 |
Currently one mrq->data maybe execute dma_map_sg() twice
|
|
|
069ff12 |
when mmc subsystem prepare over one new request, and the
|
|
|
069ff12 |
following log show up:
|
|
|
069ff12 |
sdhci[sdhci_pre_dma_transfer] invalid cookie: 24, next-cookie 25
|
|
|
069ff12 |
|
|
|
069ff12 |
In this condition, mrq->date map a dma-memory(1) in sdhci_pre_req
|
|
|
069ff12 |
for the first time, and map another dma-memory(2) in sdhci_prepare_data
|
|
|
069ff12 |
for the second time. But driver only unmap the dma-memory(2), and
|
|
|
069ff12 |
dma-memory(1) never unmapped, which cause the dma memory leak issue.
|
|
|
069ff12 |
|
|
|
069ff12 |
This patch use another method to map the dma memory for the mrq->data
|
|
|
069ff12 |
which can fix this dma memory leak issue.
|
|
|
069ff12 |
|
|
|
069ff12 |
Fixes: commit 348487cb28e66b0 ("mmc: sdhci: use pipeline mmc requests to improve performance")
|
|
|
069ff12 |
Cc: stable@vger.kernel.org # 4.0+
|
|
|
069ff12 |
Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz>
|
|
|
069ff12 |
Signed-off-by: Haibo Chen <haibo.chen@freescale.com>
|
|
|
5962555 |
[labbott@redhat.com: Add extra mmc field for compilation]
|
|
|
5962555 |
Signed-off-by: Laura Abbott <labbott@redhat.com>
|
|
|
5962555 |
|
|
|
069ff12 |
---
|
|
|
069ff12 |
drivers/mmc/host/sdhci.c | 67 ++++++++++++++++++------------------------------
|
|
|
069ff12 |
drivers/mmc/host/sdhci.h | 8 +++---
|
|
|
5962555 |
include/linux/mmc/core.h | 1 +
|
|
|
5962555 |
3 files changed, 30 insertions(+), 46 deletions(-)
|
|
|
069ff12 |
|
|
|
069ff12 |
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
|
|
|
069ff12 |
index bec8a30..ef5b604 100644
|
|
|
069ff12 |
--- a/drivers/mmc/host/sdhci.c
|
|
|
069ff12 |
+++ b/drivers/mmc/host/sdhci.c
|
|
|
069ff12 |
@@ -55,8 +55,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
|
|
069ff12 |
static void sdhci_tuning_timer(unsigned long data);
|
|
|
069ff12 |
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
|
|
069ff12 |
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
|
|
069ff12 |
- struct mmc_data *data,
|
|
|
069ff12 |
- struct sdhci_host_next *next);
|
|
|
069ff12 |
+ struct mmc_data *data);
|
|
|
069ff12 |
static int sdhci_do_get_cd(struct sdhci_host *host);
|
|
|
069ff12 |
|
|
|
069ff12 |
#ifdef CONFIG_PM
|
|
|
069ff12 |
@@ -510,7 +509,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|
|
069ff12 |
goto fail;
|
|
|
069ff12 |
BUG_ON(host->align_addr & host->align_mask);
|
|
|
069ff12 |
|
|
|
069ff12 |
- host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
|
|
|
069ff12 |
+ host->sg_count = sdhci_pre_dma_transfer(host, data);
|
|
|
069ff12 |
if (host->sg_count < 0)
|
|
|
069ff12 |
goto unmap_align;
|
|
|
069ff12 |
|
|
|
069ff12 |
@@ -649,9 +648,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
|
|
069ff12 |
}
|
|
|
069ff12 |
}
|
|
|
069ff12 |
|
|
|
069ff12 |
- if (!data->host_cookie)
|
|
|
069ff12 |
+ if (data->host_cookie == COOKIE_MAPPED) {
|
|
|
069ff12 |
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
|
|
069ff12 |
data->sg_len, direction);
|
|
|
069ff12 |
+ data->host_cookie = COOKIE_UNMAPPED;
|
|
|
069ff12 |
+ }
|
|
|
069ff12 |
}
|
|
|
069ff12 |
|
|
|
069ff12 |
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
|
|
069ff12 |
@@ -847,7 +848,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|
|
069ff12 |
} else {
|
|
|
069ff12 |
int sg_cnt;
|
|
|
069ff12 |
|
|
|
069ff12 |
- sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
|
|
|
069ff12 |
+ sg_cnt = sdhci_pre_dma_transfer(host, data);
|
|
|
069ff12 |
if (sg_cnt <= 0) {
|
|
|
069ff12 |
/*
|
|
|
069ff12 |
* This only happens when someone fed
|
|
|
069ff12 |
@@ -963,11 +964,13 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|
|
069ff12 |
if (host->flags & SDHCI_USE_ADMA)
|
|
|
069ff12 |
sdhci_adma_table_post(host, data);
|
|
|
069ff12 |
else {
|
|
|
069ff12 |
- if (!data->host_cookie)
|
|
|
069ff12 |
+ if (data->host_cookie == COOKIE_MAPPED) {
|
|
|
069ff12 |
dma_unmap_sg(mmc_dev(host->mmc),
|
|
|
069ff12 |
data->sg, data->sg_len,
|
|
|
069ff12 |
(data->flags & MMC_DATA_READ) ?
|
|
|
069ff12 |
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
|
|
069ff12 |
+ data->host_cookie = COOKIE_UNMAPPED;
|
|
|
069ff12 |
+ }
|
|
|
069ff12 |
}
|
|
|
069ff12 |
}
|
|
|
069ff12 |
|
|
|
069ff12 |
@@ -2129,49 +2132,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
|
069ff12 |
struct mmc_data *data = mrq->data;
|
|
|
069ff12 |
|
|
|
069ff12 |
if (host->flags & SDHCI_REQ_USE_DMA) {
|
|
|
069ff12 |
- if (data->host_cookie)
|
|
|
069ff12 |
+ if (data->host_cookie == COOKIE_GIVEN ||
|
|
|
069ff12 |
+ data->host_cookie == COOKIE_MAPPED)
|
|
|
069ff12 |
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
069ff12 |
data->flags & MMC_DATA_WRITE ?
|
|
|
069ff12 |
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
|
|
069ff12 |
- mrq->data->host_cookie = 0;
|
|
|
069ff12 |
+ data->host_cookie = COOKIE_UNMAPPED;
|
|
|
069ff12 |
}
|
|
|
069ff12 |
}
|
|
|
069ff12 |
|
|
|
069ff12 |
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
|
|
069ff12 |
- struct mmc_data *data,
|
|
|
069ff12 |
- struct sdhci_host_next *next)
|
|
|
069ff12 |
+ struct mmc_data *data)
|
|
|
069ff12 |
{
|
|
|
069ff12 |
int sg_count;
|
|
|
069ff12 |
|
|
|
069ff12 |
- if (!next && data->host_cookie &&
|
|
|
069ff12 |
- data->host_cookie != host->next_data.cookie) {
|
|
|
069ff12 |
- pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n",
|
|
|
069ff12 |
- __func__, data->host_cookie, host->next_data.cookie);
|
|
|
069ff12 |
- data->host_cookie = 0;
|
|
|
069ff12 |
+ if (data->host_cookie == COOKIE_MAPPED) {
|
|
|
069ff12 |
+ data->host_cookie = COOKIE_GIVEN;
|
|
|
069ff12 |
+ return data->sg_count;
|
|
|
069ff12 |
}
|
|
|
069ff12 |
|
|
|
069ff12 |
- /* Check if next job is already prepared */
|
|
|
069ff12 |
- if (next ||
|
|
|
069ff12 |
- (!next && data->host_cookie != host->next_data.cookie)) {
|
|
|
069ff12 |
- sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
|
069ff12 |
- data->sg_len,
|
|
|
069ff12 |
- data->flags & MMC_DATA_WRITE ?
|
|
|
069ff12 |
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
|
|
069ff12 |
-
|
|
|
069ff12 |
- } else {
|
|
|
069ff12 |
- sg_count = host->next_data.sg_count;
|
|
|
069ff12 |
- host->next_data.sg_count = 0;
|
|
|
069ff12 |
- }
|
|
|
069ff12 |
+ WARN_ON(data->host_cookie == COOKIE_GIVEN);
|
|
|
069ff12 |
|
|
|
069ff12 |
+ sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
069ff12 |
+ data->flags & MMC_DATA_WRITE ?
|
|
|
069ff12 |
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
|
|
069ff12 |
|
|
|
069ff12 |
if (sg_count == 0)
|
|
|
069ff12 |
- return -EINVAL;
|
|
|
069ff12 |
+ return -ENOSPC;
|
|
|
069ff12 |
|
|
|
069ff12 |
- if (next) {
|
|
|
069ff12 |
- next->sg_count = sg_count;
|
|
|
069ff12 |
- data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
|
|
|
069ff12 |
- } else
|
|
|
069ff12 |
- host->sg_count = sg_count;
|
|
|
069ff12 |
+ data->sg_count = sg_count;
|
|
|
069ff12 |
+ data->host_cookie = COOKIE_MAPPED;
|
|
|
069ff12 |
|
|
|
069ff12 |
return sg_count;
|
|
|
069ff12 |
}
|
|
|
069ff12 |
@@ -2181,16 +2171,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
|
069ff12 |
{
|
|
|
069ff12 |
struct sdhci_host *host = mmc_priv(mmc);
|
|
|
069ff12 |
|
|
|
069ff12 |
- if (mrq->data->host_cookie) {
|
|
|
069ff12 |
- mrq->data->host_cookie = 0;
|
|
|
069ff12 |
- return;
|
|
|
069ff12 |
- }
|
|
|
069ff12 |
+ mrq->data->host_cookie = COOKIE_UNMAPPED;
|
|
|
069ff12 |
|
|
|
069ff12 |
if (host->flags & SDHCI_REQ_USE_DMA)
|
|
|
069ff12 |
- if (sdhci_pre_dma_transfer(host,
|
|
|
069ff12 |
- mrq->data,
|
|
|
069ff12 |
- &host->next_data) < 0)
|
|
|
069ff12 |
- mrq->data->host_cookie = 0;
|
|
|
069ff12 |
+ sdhci_pre_dma_transfer(host, mrq->data);
|
|
|
069ff12 |
}
|
|
|
069ff12 |
|
|
|
069ff12 |
static void sdhci_card_event(struct mmc_host *mmc)
|
|
|
069ff12 |
@@ -3088,7 +3072,6 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
|
069ff12 |
host->max_clk = host->ops->get_max_clock(host);
|
|
|
069ff12 |
}
|
|
|
069ff12 |
|
|
|
069ff12 |
- host->next_data.cookie = 1;
|
|
|
069ff12 |
/*
|
|
|
069ff12 |
* In case of Host Controller v3.00, find out whether clock
|
|
|
069ff12 |
* multiplier is supported.
|
|
|
069ff12 |
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
|
|
|
069ff12 |
index e639b7f..eea23f6 100644
|
|
|
069ff12 |
--- a/drivers/mmc/host/sdhci.h
|
|
|
069ff12 |
+++ b/drivers/mmc/host/sdhci.h
|
|
|
069ff12 |
@@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc {
|
|
|
069ff12 |
*/
|
|
|
069ff12 |
#define SDHCI_MAX_SEGS 128
|
|
|
069ff12 |
|
|
|
069ff12 |
-struct sdhci_host_next {
|
|
|
069ff12 |
- unsigned int sg_count;
|
|
|
069ff12 |
- s32 cookie;
|
|
|
069ff12 |
+enum sdhci_cookie {
|
|
|
069ff12 |
+ COOKIE_UNMAPPED,
|
|
|
069ff12 |
+ COOKIE_MAPPED,
|
|
|
069ff12 |
+ COOKIE_GIVEN,
|
|
|
069ff12 |
};
|
|
|
069ff12 |
|
|
|
069ff12 |
struct sdhci_host {
|
|
|
069ff12 |
@@ -506,7 +507,6 @@ struct sdhci_host {
|
|
|
069ff12 |
#define SDHCI_TUNING_MODE_1 0
|
|
|
069ff12 |
struct timer_list tuning_timer; /* Timer for tuning */
|
|
|
069ff12 |
|
|
|
069ff12 |
- struct sdhci_host_next next_data;
|
|
|
069ff12 |
unsigned long private[0] ____cacheline_aligned;
|
|
|
069ff12 |
};
|
|
|
069ff12 |
|
|
|
5962555 |
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
|
|
|
5962555 |
index de722d4e..258daf9 100644
|
|
|
5962555 |
--- a/include/linux/mmc/core.h
|
|
|
5962555 |
+++ b/include/linux/mmc/core.h
|
|
|
5962555 |
@@ -121,6 +121,7 @@ struct mmc_data {
|
|
|
5962555 |
struct mmc_request *mrq; /* associated request */
|
|
|
5962555 |
|
|
|
5962555 |
unsigned int sg_len; /* size of scatter list */
|
|
|
5962555 |
+ int sg_count; /* mapped sg entries */
|
|
|
5962555 |
struct scatterlist *sg; /* I/O scatter list */
|
|
|
5962555 |
s32 host_cookie; /* host private data */
|
|
|
5962555 |
};
|
|
|
069ff12 |
--
|
|
|
069ff12 |
2.4.3
|
|
|
069ff12 |
|