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: 348487cb28
("mmc: sdhci: use pipeline mmc requests to improve performance")
Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Haibo Chen <haibo.chen@freescale.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
Родитель
90614cd904
Коммит
d31911b937
|
@ -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
|
||||
|
@ -495,7 +494,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;
|
||||
|
||||
|
@ -634,9 +633,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)
|
||||
|
@ -832,7 +833,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
|
||||
|
@ -948,11 +949,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2116,49 +2119,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;
|
||||
}
|
||||
|
@ -2168,16 +2158,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)
|
||||
|
@ -3049,7 +3033,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.
|
||||
|
|
|
@ -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 {
|
||||
|
@ -505,7 +506,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;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче