lkundrak / rpms / kernel

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