mmc: sdhci: fix dma memory leak in sdhci_pre_req()
authorHaibo Chen <haibo.chen@freescale.com>
Tue, 25 Aug 2015 02:02:11 +0000 (10:02 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Thu, 27 Aug 2015 12:50:49 +0000 (14:50 +0200)
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: 348487cb28e6 ("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>
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h

index 0f1a8876e3b10e9252cec7fdf8ccdbf6cdd2e01d..31678b55b5ec56bd9a10e9e54eb8b08a5335762e 100644 (file)
@@ -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.
index 67046ca0c1f05d8deac0ee476da60401ff88a0d9..7c02ff46c8ac3ecdaf37e792fd6bcb43c9bd029e 100644 (file)
@@ -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;
 };