Commit d31911b9 authored by Haibo Chen's avatar Haibo Chen Committed by Ulf Hansson

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: 348487cb ("mmc: sdhci: use pipeline mmc requests to improve performance")
Reported-and-tested-by: default avatarJiri Slaby <jslaby@suse.cz>
Signed-off-by: default avatarHaibo Chen <haibo.chen@freescale.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 90614cd9
...@@ -54,8 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *); ...@@ -54,8 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
static int sdhci_pre_dma_transfer(struct sdhci_host *host, static int sdhci_pre_dma_transfer(struct sdhci_host *host,
struct mmc_data *data, struct mmc_data *data);
struct sdhci_host_next *next);
static int sdhci_do_get_cd(struct sdhci_host *host); static int sdhci_do_get_cd(struct sdhci_host *host);
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -495,7 +494,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, ...@@ -495,7 +494,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
goto fail; goto fail;
BUG_ON(host->align_addr & host->align_mask); 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) if (host->sg_count < 0)
goto unmap_align; goto unmap_align;
...@@ -634,9 +633,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host, ...@@ -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, dma_unmap_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, direction); data->sg_len, direction);
data->host_cookie = COOKIE_UNMAPPED;
}
} }
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) 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) ...@@ -832,7 +833,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
} else { } else {
int sg_cnt; int sg_cnt;
sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); sg_cnt = sdhci_pre_dma_transfer(host, data);
if (sg_cnt <= 0) { if (sg_cnt <= 0) {
/* /*
* This only happens when someone fed * This only happens when someone fed
...@@ -948,11 +949,13 @@ static void sdhci_finish_data(struct sdhci_host *host) ...@@ -948,11 +949,13 @@ static void sdhci_finish_data(struct sdhci_host *host)
if (host->flags & SDHCI_USE_ADMA) if (host->flags & SDHCI_USE_ADMA)
sdhci_adma_table_post(host, data); sdhci_adma_table_post(host, data);
else { else {
if (!data->host_cookie) if (data->host_cookie == COOKIE_MAPPED) {
dma_unmap_sg(mmc_dev(host->mmc), dma_unmap_sg(mmc_dev(host->mmc),
data->sg, data->sg_len, data->sg, data->sg_len,
(data->flags & MMC_DATA_READ) ? (data->flags & MMC_DATA_READ) ?
DMA_FROM_DEVICE : DMA_TO_DEVICE); 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, ...@@ -2116,49 +2119,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
struct mmc_data *data = mrq->data; struct mmc_data *data = mrq->data;
if (host->flags & SDHCI_REQ_USE_DMA) { 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, dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
data->flags & MMC_DATA_WRITE ? data->flags & MMC_DATA_WRITE ?
DMA_TO_DEVICE : DMA_FROM_DEVICE); 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, static int sdhci_pre_dma_transfer(struct sdhci_host *host,
struct mmc_data *data, struct mmc_data *data)
struct sdhci_host_next *next)
{ {
int sg_count; int sg_count;
if (!next && data->host_cookie && if (data->host_cookie == COOKIE_MAPPED) {
data->host_cookie != host->next_data.cookie) { data->host_cookie = COOKIE_GIVEN;
pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n", return data->sg_count;
__func__, data->host_cookie, host->next_data.cookie);
data->host_cookie = 0;
} }
/* Check if next job is already prepared */ WARN_ON(data->host_cookie == COOKIE_GIVEN);
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;
}
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) if (sg_count == 0)
return -EINVAL; return -ENOSPC;
if (next) { data->sg_count = sg_count;
next->sg_count = sg_count; data->host_cookie = COOKIE_MAPPED;
data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
} else
host->sg_count = sg_count;
return sg_count; return sg_count;
} }
...@@ -2168,16 +2158,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, ...@@ -2168,16 +2158,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
{ {
struct sdhci_host *host = mmc_priv(mmc); struct sdhci_host *host = mmc_priv(mmc);
if (mrq->data->host_cookie) { mrq->data->host_cookie = COOKIE_UNMAPPED;
mrq->data->host_cookie = 0;
return;
}
if (host->flags & SDHCI_REQ_USE_DMA) if (host->flags & SDHCI_REQ_USE_DMA)
if (sdhci_pre_dma_transfer(host, sdhci_pre_dma_transfer(host, mrq->data);
mrq->data,
&host->next_data) < 0)
mrq->data->host_cookie = 0;
} }
static void sdhci_card_event(struct mmc_host *mmc) static void sdhci_card_event(struct mmc_host *mmc)
...@@ -3049,7 +3033,6 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -3049,7 +3033,6 @@ int sdhci_add_host(struct sdhci_host *host)
host->max_clk = host->ops->get_max_clock(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 * In case of Host Controller v3.00, find out whether clock
* multiplier is supported. * multiplier is supported.
......
...@@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc { ...@@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc {
*/ */
#define SDHCI_MAX_SEGS 128 #define SDHCI_MAX_SEGS 128
struct sdhci_host_next { enum sdhci_cookie {
unsigned int sg_count; COOKIE_UNMAPPED,
s32 cookie; COOKIE_MAPPED,
COOKIE_GIVEN,
}; };
struct sdhci_host { struct sdhci_host {
...@@ -505,7 +506,6 @@ struct sdhci_host { ...@@ -505,7 +506,6 @@ struct sdhci_host {
unsigned int tuning_mode; /* Re-tuning mode supported by host */ unsigned int tuning_mode; /* Re-tuning mode supported by host */
#define SDHCI_TUNING_MODE_1 0 #define SDHCI_TUNING_MODE_1 0
struct sdhci_host_next next_data;
unsigned long private[0] ____cacheline_aligned; unsigned long private[0] ____cacheline_aligned;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment