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