Skip to content

Commit de54619

Browse files
mathieupoiriergregkh
authored andcommitted
coresight: tmc: allocating memory when needed
In it's current form the TMC probe() function allocates trace buffer memory at boot time, event if coresight isn't used. This is highly inefficient since trace buffers can occupy a lot of memory that could be used otherwised. This patch allocates trace buffers on the fly, when the coresight subsystem is solicited. Allocated buffers are released when traces are read using the device descriptors under /dev. Signed-off-by: Mathieu Poirier <[email protected]> Reviewed-by: Suzuki K Poulose <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 4525412 commit de54619

File tree

3 files changed

+170
-27
lines changed

3 files changed

+170
-27
lines changed

drivers/hwtracing/coresight/coresight-tmc-etf.c

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@
1616
*/
1717

1818
#include <linux/coresight.h>
19+
#include <linux/slab.h>
1920
#include "coresight-priv.h"
2021
#include "coresight-tmc.h"
2122

2223
void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
2324
{
24-
/* Zero out the memory to help with debug */
25-
memset(drvdata->buf, 0, drvdata->size);
26-
2725
CS_UNLOCK(drvdata->base);
2826

2927
/* Wait for TMCSReady bit to be set */
@@ -110,21 +108,67 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
110108

111109
static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
112110
{
111+
int ret = 0;
112+
bool used = false;
113+
char *buf = NULL;
113114
unsigned long flags;
114115
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
115116

117+
/* This shouldn't be happening */
118+
if (WARN_ON(mode != CS_MODE_SYSFS))
119+
return -EINVAL;
120+
121+
/*
122+
* If we don't have a buffer release the lock and allocate memory.
123+
* Otherwise keep the lock and move along.
124+
*/
116125
spin_lock_irqsave(&drvdata->spinlock, flags);
117-
if (drvdata->reading) {
126+
if (!drvdata->buf) {
118127
spin_unlock_irqrestore(&drvdata->spinlock, flags);
119-
return -EBUSY;
128+
129+
/* Allocating the memory here while outside of the spinlock */
130+
buf = kzalloc(drvdata->size, GFP_KERNEL);
131+
if (!buf)
132+
return -ENOMEM;
133+
134+
/* Let's try again */
135+
spin_lock_irqsave(&drvdata->spinlock, flags);
136+
}
137+
138+
if (drvdata->reading) {
139+
ret = -EBUSY;
140+
goto out;
141+
}
142+
143+
/*
144+
* If drvdata::buf isn't NULL, memory was allocated for a previous
145+
* trace run but wasn't read. If so simply zero-out the memory.
146+
* Otherwise use the memory allocated above.
147+
*
148+
* The memory is freed when users read the buffer using the
149+
* /dev/xyz.{etf|etb} interface. See tmc_read_unprepare_etf() for
150+
* details.
151+
*/
152+
if (drvdata->buf) {
153+
memset(drvdata->buf, 0, drvdata->size);
154+
} else {
155+
used = true;
156+
drvdata->buf = buf;
120157
}
121158

122159
tmc_etb_enable_hw(drvdata);
123160
drvdata->enable = true;
161+
out:
124162
spin_unlock_irqrestore(&drvdata->spinlock, flags);
125163

126-
dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
127-
return 0;
164+
/* Free memory outside the spinlock if need be */
165+
if (!used && buf)
166+
kfree(buf);
167+
168+
if (!ret)
169+
dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
170+
171+
return ret;
128172
}
129173

130174
static void tmc_disable_etf_sink(struct coresight_device *csdev)
@@ -223,6 +267,12 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
223267
goto out;
224268
}
225269

270+
/* If drvdata::buf is NULL the trace data has been read already */
271+
if (drvdata->buf == NULL) {
272+
ret = -EINVAL;
273+
goto out;
274+
}
275+
226276
/* Disable the TMC if need be */
227277
if (drvdata->enable)
228278
tmc_etb_disable_hw(drvdata);
@@ -236,6 +286,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
236286

237287
int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
238288
{
289+
char *buf = NULL;
239290
enum tmc_mode mode;
240291
unsigned long flags;
241292

@@ -254,11 +305,34 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
254305
}
255306

256307
/* Re-enable the TMC if need be */
257-
if (drvdata->enable)
308+
if (drvdata->enable) {
309+
/*
310+
* The trace run will continue with the same allocated trace
311+
* buffer. As such zero-out the buffer so that we don't end
312+
* up with stale data.
313+
*
314+
* Since the tracer is still enabled drvdata::buf
315+
* can't be NULL.
316+
*/
317+
memset(drvdata->buf, 0, drvdata->size);
258318
tmc_etb_enable_hw(drvdata);
319+
} else {
320+
/*
321+
* The ETB/ETF is not tracing and the buffer was just read.
322+
* As such prepare to free the trace buffer.
323+
*/
324+
buf = drvdata->buf;
325+
drvdata->buf = NULL;
326+
}
259327

260328
drvdata->reading = false;
261329
spin_unlock_irqrestore(&drvdata->spinlock, flags);
262330

331+
/*
332+
* Free allocated memory outside of the spinlock. There is no need
333+
* to assert the validity of 'buf' since calling kfree(NULL) is safe.
334+
*/
335+
kfree(buf);
336+
263337
return 0;
264338
}

drivers/hwtracing/coresight/coresight-tmc-etr.c

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
#include <linux/coresight.h>
19+
#include <linux/dma-mapping.h>
1920
#include "coresight-priv.h"
2021
#include "coresight-tmc.h"
2122

@@ -83,21 +84,71 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
8384

8485
static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
8586
{
87+
int ret = 0;
88+
bool used = false;
8689
unsigned long flags;
90+
void __iomem *vaddr = NULL;
91+
dma_addr_t paddr;
8792
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
8893

94+
/* This shouldn't be happening */
95+
if (WARN_ON(mode != CS_MODE_SYSFS))
96+
return -EINVAL;
97+
98+
/*
99+
* If we don't have a buffer release the lock and allocate memory.
100+
* Otherwise keep the lock and move along.
101+
*/
89102
spin_lock_irqsave(&drvdata->spinlock, flags);
90-
if (drvdata->reading) {
103+
if (!drvdata->vaddr) {
91104
spin_unlock_irqrestore(&drvdata->spinlock, flags);
92-
return -EBUSY;
105+
106+
/*
107+
* Contiguous memory can't be allocated while a spinlock is
108+
* held. As such allocate memory here and free it if a buffer
109+
* has already been allocated (from a previous session).
110+
*/
111+
vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
112+
&paddr, GFP_KERNEL);
113+
if (!vaddr)
114+
return -ENOMEM;
115+
116+
/* Let's try again */
117+
spin_lock_irqsave(&drvdata->spinlock, flags);
118+
}
119+
120+
if (drvdata->reading) {
121+
ret = -EBUSY;
122+
goto out;
123+
}
124+
125+
/*
126+
* If drvdata::buf == NULL, use the memory allocated above.
127+
* Otherwise a buffer still exists from a previous session, so
128+
* simply use that.
129+
*/
130+
if (drvdata->buf == NULL) {
131+
used = true;
132+
drvdata->vaddr = vaddr;
133+
drvdata->paddr = paddr;
134+
drvdata->buf = drvdata->vaddr;
93135
}
94136

137+
memset(drvdata->vaddr, 0, drvdata->size);
138+
95139
tmc_etr_enable_hw(drvdata);
96140
drvdata->enable = true;
141+
out:
97142
spin_unlock_irqrestore(&drvdata->spinlock, flags);
98143

99-
dev_info(drvdata->dev, "TMC-ETR enabled\n");
100-
return 0;
144+
/* Free memory outside the spinlock if need be */
145+
if (!used && vaddr)
146+
dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
147+
148+
if (!ret)
149+
dev_info(drvdata->dev, "TMC-ETR enabled\n");
150+
151+
return ret;
101152
}
102153

103154
static void tmc_disable_etr_sink(struct coresight_device *csdev)
@@ -129,6 +180,7 @@ const struct coresight_ops tmc_etr_cs_ops = {
129180

130181
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
131182
{
183+
int ret = 0;
132184
unsigned long flags;
133185

134186
/* config types are set a boot time and never change */
@@ -137,11 +189,18 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
137189

138190
spin_lock_irqsave(&drvdata->spinlock, flags);
139191

192+
/* If drvdata::buf is NULL the trace data has been read already */
193+
if (drvdata->buf == NULL) {
194+
ret = -EINVAL;
195+
goto out;
196+
}
197+
140198
/* Disable the TMC if need be */
141199
if (drvdata->enable)
142200
tmc_etr_disable_hw(drvdata);
143201

144202
drvdata->reading = true;
203+
out:
145204
spin_unlock_irqrestore(&drvdata->spinlock, flags);
146205

147206
return 0;
@@ -150,6 +209,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
150209
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
151210
{
152211
unsigned long flags;
212+
dma_addr_t paddr;
213+
void __iomem *vaddr = NULL;
153214

154215
/* config types are set a boot time and never change */
155216
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
@@ -158,11 +219,33 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
158219
spin_lock_irqsave(&drvdata->spinlock, flags);
159220

160221
/* RE-enable the TMC if need be */
161-
if (drvdata->enable)
222+
if (drvdata->enable) {
223+
/*
224+
* The trace run will continue with the same allocated trace
225+
* buffer. As such zero-out the buffer so that we don't end
226+
* up with stale data.
227+
*
228+
* Since the tracer is still enabled drvdata::buf
229+
* can't be NULL.
230+
*/
231+
memset(drvdata->buf, 0, drvdata->size);
162232
tmc_etr_enable_hw(drvdata);
233+
} else {
234+
/*
235+
* The ETR is not tracing and the buffer was just read.
236+
* As such prepare to free the trace buffer.
237+
*/
238+
vaddr = drvdata->vaddr;
239+
paddr = drvdata->paddr;
240+
drvdata->buf = NULL;
241+
}
163242

164243
drvdata->reading = false;
165244
spin_unlock_irqrestore(&drvdata->spinlock, flags);
166245

246+
/* Free allocated memory out side of the spinlock */
247+
if (vaddr)
248+
dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
249+
167250
return 0;
168251
}

drivers/hwtracing/coresight/coresight-tmc.c

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -319,20 +319,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
319319

320320
pm_runtime_put(&adev->dev);
321321

322-
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
323-
drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size,
324-
&drvdata->paddr, GFP_KERNEL);
325-
if (!drvdata->vaddr)
326-
return -ENOMEM;
327-
328-
memset(drvdata->vaddr, 0, drvdata->size);
329-
drvdata->buf = drvdata->vaddr;
330-
} else {
331-
drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
332-
if (!drvdata->buf)
333-
return -ENOMEM;
334-
}
335-
336322
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
337323
if (!desc) {
338324
ret = -ENOMEM;

0 commit comments

Comments
 (0)