aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mpc85xx/patches-3.14/210-spi-fsl-espi-preallocate-local-buffer.patch
blob: c095510d44096e8f8f2696fb1b2286ddd09419e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
From: Gabor Juhos <juhosg@openwrt.org>
Subject: spi-fsl-espi: avoid frequent high order allocations

The driver allocates 64KiB of memory fro a local buffer before
each transfer and releases that afterwards. When the memory is
fragmented this allocation often fails and causes a warning like
this:

  kworker/u2:2: page allocation failure: order:4, mode:0x10c0d0
  CPU: 0 PID: 7011 Comm: kworker/u2:2 Not tainted 3.10.24 #1
  Workqueue: ffe07000.spi mpc8xxx_spi_work
  Call Trace:
  [c1c6dcf0] [c0006914] show_stack+0x50/0x170 (unreliable)
  [c1c6dd30] [c0259858] dump_stack+0x24/0x34
  [c1c6dd40] [c00672e8] warn_alloc_failed+0x120/0x13c
  [c1c6dd90] [c0069920] __alloc_pages_nodemask+0x574/0x5c8
  [c1c6de20] [c0069990] __get_free_pages+0x1c/0x4c
  [c1c6de30] [c0185174] fsl_espi_do_one_msg+0x128/0x2a0
  [c1c6de90] [c0184290] mpc8xxx_spi_work+0x50/0x7c
  [c1c6dea0] [c0037af8] process_one_work+0x208/0x30c
  [c1c6dec0] [c00387a0] worker_thread+0x20c/0x308
  [c1c6def0] [c003de60] kthread+0xa4/0xa8
  [c1c6df40] [c000c4bc] ret_from_kernel_thread+0x5c/0x64

  m25p80 spi0.0: error -12 reading SR
  end_request: I/O error, dev mtdblock3, sector 680
  SQUASHFS error: squashfs_read_data failed to read block 0x54a4a
  SQUASHFS error: Unable to read data cache entry [54a4a]

Preallocate the buffer from the probe routine to avoid
this.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
---
 drivers/spi/spi-fsl-espi.c | 34 ++++++++++++++++------------------
 drivers/spi/spi-fsl-lib.h  |  1 +
 2 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 428dc7a..5207176 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -334,17 +334,13 @@ static void fsl_espi_do_trans(struct spi_message *m,
 static void fsl_espi_cmd_trans(struct spi_message *m,
 				struct fsl_espi_transfer *trans, u8 *rx_buff)
 {
+	struct spi_device *spi = m->spi;
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
 	struct spi_transfer *t;
-	u8 *local_buf;
+	u8 *local_buf = mspi->local_buf;
 	int i = 0;
 	struct fsl_espi_transfer *espi_trans = trans;
 
-	local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
-	if (!local_buf) {
-		espi_trans->status = -ENOMEM;
-		return;
-	}
-
 	list_for_each_entry(t, &m->transfers, transfer_list) {
 		if (t->tx_buf) {
 			memcpy(local_buf + i, t->tx_buf, t->len);
@@ -357,28 +353,23 @@ static void fsl_espi_cmd_trans(struct spi_message *m,
 	fsl_espi_do_trans(m, espi_trans);
 
 	espi_trans->actual_length = espi_trans->len;
-	kfree(local_buf);
 }
 
 static void fsl_espi_rw_trans(struct spi_message *m,
 				struct fsl_espi_transfer *trans, u8 *rx_buff)
 {
+	struct spi_device *spi = m->spi;
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
 	struct fsl_espi_transfer *espi_trans = trans;
 	unsigned int n_tx = espi_trans->n_tx;
 	unsigned int n_rx = espi_trans->n_rx;
 	struct spi_transfer *t;
-	u8 *local_buf;
+	u8 *local_buf = mspi->local_buf;
 	u8 *rx_buf = rx_buff;
 	unsigned int trans_len;
 	unsigned int addr;
 	int i, pos, loop;
 
-	local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
-	if (!local_buf) {
-		espi_trans->status = -ENOMEM;
-		return;
-	}
-
 	for (pos = 0, loop = 0; pos < n_rx; pos += trans_len, loop++) {
 		trans_len = n_rx - pos;
 		if (trans_len > SPCOM_TRANLEN_MAX - n_tx)
@@ -412,8 +403,6 @@ static void fsl_espi_rw_trans(struct spi_message *m,
 		else
 			espi_trans->actual_length += espi_trans->len;
 	}
-
-	kfree(local_buf);
 }
 
 static void fsl_espi_do_one_msg(struct spi_message *m)
@@ -581,6 +570,7 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
 static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
 {
 	iounmap(mspi->reg_base);
+	kfree(mspi->local_buf);
 }
 
 static struct spi_master * fsl_espi_probe(struct device *dev,
@@ -612,10 +602,16 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
 	mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg;
 	mpc8xxx_spi->spi_remove = fsl_espi_remove;
 
+	mpc8xxx_spi->local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
+	if (!mpc8xxx_spi->local_buf) {
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+
 	mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
 	if (!mpc8xxx_spi->reg_base) {
 		ret = -ENOMEM;
-		goto err_probe;
+		goto free_buf;
 	}
 
 	reg_base = mpc8xxx_spi->reg_base;
@@ -658,6 +654,8 @@ unreg_master:
 	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
 free_irq:
 	iounmap(mpc8xxx_spi->reg_base);
+free_buf:
+	kfree(mpc8xxx_spi->local_buf);
 err_probe:
 	spi_master_put(master);
 err:
diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h
index 52db693..8dda68b 100644
--- a/drivers/spi/spi-fsl-lib.h
+++ b/drivers/spi/spi-fsl-lib.h
@@ -30,6 +30,7 @@ struct mpc8xxx_spi {
 	void *rx;
 #ifdef CONFIG_SPI_FSL_ESPI
 	int len;
+	u8 *local_buf;
 #endif
 
 	int subblock;
-- 
2.1.3