Skip to contentSkip to conversation

ARM: etm: Don't require clock control

PATCH In review Arve Hjønnevåg 3 participants 19 messages Feb 3, 2011 – Feb 24, 2011 Arm (32-bit)

lore
Share & tools
lore
#0 lore raw

PATCH 1/5 ARM: etm: Don't require clock control

If clk_get fail, assume the etb does not need a separate clock.

---
 arch/arm/kernel/etm.c |   14 +++++++-------
 1 files changed, 7 insertions(+), 7 deletions(-)
arch/arm/kernel/etm.c +7 -7
index 11db628..308b733 100644
@@ -362,12 +362,10 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
 		goto out_unmap;
 
 	t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
-	if (IS_ERR(t->emu_clk)) {
+	if (IS_ERR(t->emu_clk))
 		dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
-		return -EFAULT;
-	}
-
-	clk_enable(t->emu_clk);
+	else
+		clk_enable(t->emu_clk);
 
 	etb_unlock(t);
 	t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
@@ -402,8 +400,10 @@ static int etb_remove(struct amba_device *dev)
 	iounmap(t->etb_regs);
 	t->etb_regs = NULL;
 
-	clk_disable(t->emu_clk);
-	clk_put(t->emu_clk);
+	if (!IS_ERR(t->emu_clk)) {
+		clk_disable(t->emu_clk);
+		clk_put(t->emu_clk);
+	}
 
 	amba_release_regions(dev);
 
Signature · 1 line
1.7.3.1
#1 lore raw

PATCH 2/5 ARM: etm: Don't limit tracing to only non-secure code.

On some systems kernel code is considered secure, and this code already limits tracing to the kernel text segment which results in no trace data.

---
 arch/arm/kernel/etm.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
arch/arm/kernel/etm.c +1 -1
index 308b733..2ea0b7c 100644
@@ -55,7 +55,7 @@ static inline bool trace_isrunning(struct tracectx *t)
 static int etm_setup_address_range(struct tracectx *t, int n,
 		unsigned long start, unsigned long end, int exclude, int data)
 {
-	u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \
+	u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
 		    ETMAAT_NOVALCMP;
 
 	if (n < 1 || n > t->ncmppairs)
Signature · 1 line
1.7.3.1
#2 lore raw

PATCH 3/5 ARM: etm: Don't trigger another overflow when trying to clear the RAM-full status

In reply to Arve Hjønnevåg

If the write address was at the end of the buffer, toggling the trace capture bit would set the RAM-full status instead of clearing it.

---
 arch/arm/kernel/etm.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
arch/arm/kernel/etm.c +2 -0
index 2ea0b7c..7652e9f 100644
@@ -235,6 +235,7 @@ static void etm_dump(void)
 	printk(KERN_INFO "\n--- ETB buffer end ---\n");
 
 	/* deassert the overflow bit */
+	etb_writel(t, 0, ETBR_WRITEADDR);
 	etb_writel(t, 1, ETBR_CTRL);
 	etb_writel(t, 0, ETBR_CTRL);
 
@@ -300,6 +301,7 @@ static ssize_t etb_read(struct file *file, char __user *data,
 		buf[i] = etb_readl(t, ETBR_READMEM);
 
 	/* the only way to deassert overflow bit in ETB status is this */
+	etb_writel(t, 0, ETBR_WRITEADDR);
 	etb_writel(t, 1, ETBR_CTRL);
 	etb_writel(t, 0, ETBR_CTRL);
 
Signature · 1 line
1.7.3.1
#3 lore raw

PATCH 4/5 ARM: etm: Allow range selection

In reply to Arve Hjønnevåg

Trace kernel text segment by default as before, allow tracing of other ranges by writing a range to /sys/devices/etm/trace_range, or to trace everything by writing 0 0.

---
 arch/arm/kernel/etm.c |   48 +++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 45 insertions(+), 3 deletions(-)
arch/arm/kernel/etm.c +45 -3
index 7652e9f..6ff1004 100644
@@ -40,12 +40,17 @@ struct tracectx {
 	unsigned long	flags;
 	int		ncmppairs;
 	int		etm_portsz;
+	unsigned long	range_start;
+	unsigned long	range_end;
 	struct device	*dev;
 	struct clk	*emu_clk;
 	struct mutex	mutex;
 };
 
-static struct tracectx tracer;
+static struct tracectx tracer = {
+	.range_start = (unsigned long)_stext,
+	.range_end = (unsigned long)_etext,
+};
 
 static inline bool trace_isrunning(struct tracectx *t)
 {
@@ -114,8 +119,12 @@ static int trace_start(struct tracectx *t)
 		return -EFAULT;
 	}
 
-	etm_setup_address_range(t, 1, (unsigned long)_stext,
-			(unsigned long)_etext, 0, 0);
+	if (t->range_start || t->range_end)
+		etm_setup_address_range(t, 1,
+					t->range_start, t->range_end, 0, 0);
+	else
+		etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+
 	etm_writel(t, 0, ETMR_TRACEENCTRL2);
 	etm_writel(t, 0, ETMR_TRACESSCTRL);
 	etm_writel(t, 0x6f, ETMR_TRACEENEVT);
@@ -532,6 +541,35 @@ static ssize_t trace_mode_store(struct kobject *kobj,
 static struct kobj_attribute trace_mode_attr =
 	__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
 
+static ssize_t trace_range_show(struct kobject *kobj,
+				  struct kobj_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%08lx %08lx\n",
+			tracer.range_start, tracer.range_end);
+}
+
+static ssize_t trace_range_store(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   const char *buf, size_t n)
+{
+	unsigned long range_start, range_end;
+
+	if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
+		return -EINVAL;
+
+	mutex_lock(&tracer.mutex);
+	tracer.range_start = range_start;
+	tracer.range_end = range_end;
+	mutex_unlock(&tracer.mutex);
+
+	return n;
+}
+
+
+static struct kobj_attribute trace_range_attr =
+	__ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+
 static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 {
 	struct tracectx *t = &tracer;
@@ -583,6 +621,10 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 	if (ret)
 		dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
 
+	ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
+	if (ret)
+		dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+
 	dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
 
 out:
Signature · 1 line
1.7.3.1
#4 lore raw

PATCH 5/5 ARM: etm: Configure data tracing

In reply to Arve Hjønnevåg

The old code enabled data tracing, but did not configure the range. We now configure it to trace all data addresses by default, and add a trace_data_range attribute to change the range or disable data tracing.

---
 arch/arm/include/asm/hardware/coresight.h |   10 +++-
 arch/arm/kernel/etm.c                     |   76 +++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 4 deletions(-)
arch/arm/include/asm/hardware/coresight.h +9 -1
index 7ecd793..b95a6f0 100644
@@ -17,9 +17,11 @@
 #define TRACER_ACCESSED_BIT	0
 #define TRACER_RUNNING_BIT	1
 #define TRACER_CYCLE_ACC_BIT	2
+#define TRACER_TRACE_DATA_BIT	3
 #define TRACER_ACCESSED		BIT(TRACER_ACCESSED_BIT)
 #define TRACER_RUNNING		BIT(TRACER_RUNNING_BIT)
 #define TRACER_CYCLE_ACC	BIT(TRACER_CYCLE_ACC_BIT)
+#define TRACER_TRACE_DATA	BIT(TRACER_TRACE_DATA_BIT)
 
 #define TRACER_TIMEOUT 10000
 
@@ -113,8 +115,14 @@
 #define ETMR_TRACEENCTRL	0x24
 #define ETMTE_INCLEXCL		BIT(24)
 #define ETMR_TRACEENEVT		0x20
+
+#define ETMR_VIEWDATAEVT	0x30
+#define ETMR_VIEWDATACTRL1	0x34
+#define ETMR_VIEWDATACTRL2	0x38
+#define ETMR_VIEWDATACTRL3	0x3c
+#define ETMVDC3_EXCLONLY	(1 << 16)
+
 #define ETMCTRL_OPTS		(ETMCTRL_DO_CPRT | \
-				ETMCTRL_DATA_DO_ADDR | \
 				ETMCTRL_BRANCH_OUTPUT | \
 				ETMCTRL_DO_CONTEXTID)
 
arch/arm/kernel/etm.c +73 -3

118 lines not on this page · raw message

Signature · 1 line
1.7.3.1
#5 lore raw
In reply to Arve Hjønnevåg
Quoted 3 lines · Arve Hj??nnev??g

If clk_get fail, assume the etb does not need a separate clock.

Would it not be cleaner for the affected platforms to ensure that clk_get() does the right thing here, for example by returning a dummy clock? Otherwise we'll just silently carry on if we can't get a clock we were supposed to which doesn't seem ideal.

#6 lore raw
Quoted 10 lines · Mark Brown
Quoted 3 lines · Arve Hj??nnev??g

If clk_get fail, assume the etb does not need a separate clock.

Would it not be cleaner for the affected platforms to ensure that clk_get() does the right thing here, for example by returning a dummy clock? ?Otherwise we'll just silently carry on if we can't get a clock we were supposed to which doesn't seem ideal.

This clock seem to be an omap specific virtual clock that switches the clock source of the etb. It is not used to enable the clock when the etb is in use, and it does not seem to have a failure case other than not existing. So, I don't know that requiring this clock would cause fewer problems than making it optional.

--
Arve Hj?nnev?g

#7 lore raw
Quoted 7 lines · Arve Hjønnevåg

On Thu, Feb 3, 2011 at 4:45 AM, Mark Brown

Quoted 4 lines

Would it not be cleaner for the affected platforms to ensure that clk_get() does the right thing here, for example by returning a dummy clock?  Otherwise we'll just silently carry on if we can't get a clock we were supposed to which doesn't seem ideal.

Quoted 5 lines

This clock seem to be an omap specific virtual clock that switches the clock source of the etb. It is not used to enable the clock when the etb is in use, and it does not seem to have a failure case other than not existing. So, I don't know that requiring this clock would cause fewer problems than making it optional.

That doesn't seem unreasonable, but in that case it'd be good to explain why things are this way in the code (and the changelog) so someone doesn't change the code back.

#8 lore raw

PATCH 1/8 ARM: etm: Don't require clock control

In reply to Arve Hjønnevåg

If clk_get fail, assume the etb does not need a separate clock.

---
 arch/arm/kernel/etm.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)
arch/arm/kernel/etm.c +8 -7
index 11db628..d4ec368 100644
@@ -361,13 +361,12 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
 	if (ret)
 		goto out_unmap;
 
+	/* Get optional clock. Currently used to select clock source on omap3 */
 	t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
-	if (IS_ERR(t->emu_clk)) {
+	if (IS_ERR(t->emu_clk))
 		dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
-		return -EFAULT;
-	}
-
-	clk_enable(t->emu_clk);
+	else
+		clk_enable(t->emu_clk);
 
 	etb_unlock(t);
 	t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
@@ -402,8 +401,10 @@ static int etb_remove(struct amba_device *dev)
 	iounmap(t->etb_regs);
 	t->etb_regs = NULL;
 
-	clk_disable(t->emu_clk);
-	clk_put(t->emu_clk);
+	if (!IS_ERR(t->emu_clk)) {
+		clk_disable(t->emu_clk);
+		clk_put(t->emu_clk);
+	}
 
 	amba_release_regions(dev);
 
Signature · 1 line
1.7.3.1
#9 lore raw

PATCH 2/8 ARM: etm: Don't limit tracing to only non-secure code.

In reply to Arve Hjønnevåg

On some systems kernel code is considered secure, and this code already limits tracing to the kernel text segment which results in no trace data.

---
 arch/arm/kernel/etm.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
arch/arm/kernel/etm.c +1 -1
index d4ec368..6e15c48 100644
@@ -55,7 +55,7 @@ static inline bool trace_isrunning(struct tracectx *t)
 static int etm_setup_address_range(struct tracectx *t, int n,
 		unsigned long start, unsigned long end, int exclude, int data)
 {
-	u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \
+	u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
 		    ETMAAT_NOVALCMP;
 
 	if (n < 1 || n > t->ncmppairs)
Signature · 1 line
1.7.3.1
#10 lore raw

PATCH 3/8 ARM: etm: Don't try to clear the buffer full status after reading the buffer

In reply to Arve Hjønnevåg

If the write address was at the end of the buffer, toggling the trace capture bit would set the RAM-full status instead of clearing it, and if any of the stop bits in the formatter is set toggling the trace capture bit may not do anything.

Instead use the read position to find out if the data has already been returned.

This also fixes the read function so it works when the trace buffer is larger than the buffer passed in from user space. The old version would reset the trace buffer pointers after every read, so the second call to read would always return 0.

---
 arch/arm/kernel/etm.c |   58 ++++++++++++++++++++++---------------------------
 1 files changed, 26 insertions(+), 32 deletions(-)
arch/arm/kernel/etm.c +26 -32
index 6e15c48..bc7d8f2 100644
@@ -91,6 +91,7 @@ static int trace_start(struct tracectx *t)
 
 	etb_unlock(t);
 
+	etb_writel(t, 0, ETBR_WRITEADDR);
 	etb_writel(t, 0, ETBR_FORMATTERCTRL);
 	etb_writel(t, 1, ETBR_CTRL);
 
@@ -184,24 +185,15 @@ static int trace_stop(struct tracectx *t)
 static int etb_getdatalen(struct tracectx *t)
 {
 	u32 v;
-	int rp, wp;
+	int wp;
 
 	v = etb_readl(t, ETBR_STATUS);
 
 	if (v & 1)
 		return t->etb_bufsz;
 
-	rp = etb_readl(t, ETBR_READADDR);
 	wp = etb_readl(t, ETBR_WRITEADDR);
-
-	if (rp > wp) {
-		etb_writel(t, 0, ETBR_READADDR);
-		etb_writel(t, 0, ETBR_WRITEADDR);
-
-		return 0;
-	}
-
-	return wp - rp;
+	return wp;
 }
 
 /* sysrq+v will always stop the running trace and leave it at that */
@@ -234,14 +226,6 @@ static void etm_dump(void)
 		printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM)));
 	printk(KERN_INFO "\n--- ETB buffer end ---\n");
 
-	/* deassert the overflow bit */
-	etb_writel(t, 1, ETBR_CTRL);
-	etb_writel(t, 0, ETBR_CTRL);
-
-	etb_writel(t, 0, ETBR_TRIGGERCOUNT);
-	etb_writel(t, 0, ETBR_READADDR);
-	etb_writel(t, 0, ETBR_WRITEADDR);
-
 	etb_lock(t);
 }
 
@@ -275,6 +259,10 @@ static ssize_t etb_read(struct file *file, char __user *data,
 	struct tracectx *t = file->private_data;
 	u32 first = 0;
 	u32 *buf;
+	int wpos;
+	int skip;
+	long wlength;
+	loff_t pos = *ppos;
 
 	mutex_lock(&t->mutex);
 
@@ -289,28 +277,34 @@ static ssize_t etb_read(struct file *file, char __user *data,
 	if (total == t->etb_bufsz)
 		first = etb_readl(t, ETBR_WRITEADDR);
 
+	if (pos > total * 4) {
+		skip = 0;
+		wpos = total;
+	} else {
+		skip = (int)pos % 4;
+		wpos = (int)pos / 4;
+	}
+	total -= wpos;
+	first = (first + wpos) % t->etb_bufsz;
+
 	etb_writel(t, first, ETBR_READADDR);
 
-	length = min(total * 4, (int)len);
-	buf = vmalloc(length);
+	wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4));
+	length = min(total * 4 - skip, (int)len);
+	buf = vmalloc(wlength * 4);
 
-	dev_dbg(t->dev, "ETB buffer length: %d\n", total);
+	dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n",
+		length, pos, wlength, first);
+	dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos);
 	dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS));
-	for (i = 0; i < length / 4; i++)
+	for (i = 0; i < wlength; i++)
 		buf[i] = etb_readl(t, ETBR_READMEM);
 
-	/* the only way to deassert overflow bit in ETB status is this */
-	etb_writel(t, 1, ETBR_CTRL);
-	etb_writel(t, 0, ETBR_CTRL);
-
-	etb_writel(t, 0, ETBR_WRITEADDR);
-	etb_writel(t, 0, ETBR_READADDR);
-	etb_writel(t, 0, ETBR_TRIGGERCOUNT);
-
 	etb_lock(t);
 
-	length -= copy_to_user(data, buf, length);
+	length -= copy_to_user(data, (u8 *)buf + skip, length);
 	vfree(buf);
+	*ppos = pos + length;
 
 out:
 	mutex_unlock(&t->mutex);
Signature · 1 line
1.7.3.1
#11 lore raw

PATCH 4/8 ARM: etm: Allow range selection

In reply to Arve Hjønnevåg

Trace kernel text segment by default as before, allow tracing of other ranges by writing a range to /sys/devices/etm/trace_range, or to trace everything by writing 0 0.

---
 arch/arm/kernel/etm.c |   49 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 46 insertions(+), 3 deletions(-)
arch/arm/kernel/etm.c +46 -3
index bc7d8f2..8a1c422 100644
@@ -40,12 +40,17 @@ struct tracectx {
 	unsigned long	flags;
 	int		ncmppairs;
 	int		etm_portsz;
+	unsigned long	range_start;
+	unsigned long	range_end;
 	struct device	*dev;
 	struct clk	*emu_clk;
 	struct mutex	mutex;
 };
 
-static struct tracectx tracer;
+static struct tracectx tracer = {
+	.range_start = (unsigned long)_stext,
+	.range_end = (unsigned long)_etext,
+};
 
 static inline bool trace_isrunning(struct tracectx *t)
 {
@@ -115,8 +120,12 @@ static int trace_start(struct tracectx *t)
 		return -EFAULT;
 	}
 
-	etm_setup_address_range(t, 1, (unsigned long)_stext,
-			(unsigned long)_etext, 0, 0);
+	if (t->range_start || t->range_end)
+		etm_setup_address_range(t, 1,
+					t->range_start, t->range_end, 0, 0);
+	else
+		etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+
 	etm_writel(t, 0, ETMR_TRACEENCTRL2);
 	etm_writel(t, 0, ETMR_TRACESSCTRL);
 	etm_writel(t, 0x6f, ETMR_TRACEENEVT);
@@ -525,6 +534,35 @@ static ssize_t trace_mode_store(struct kobject *kobj,
 static struct kobj_attribute trace_mode_attr =
 	__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
 
+static ssize_t trace_range_show(struct kobject *kobj,
+				  struct kobj_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%08lx %08lx\n",
+			tracer.range_start, tracer.range_end);
+}
+
+static ssize_t trace_range_store(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   const char *buf, size_t n)
+{
+	unsigned long range_start, range_end;
+
+	if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
+		return -EINVAL;
+
+	mutex_lock(&tracer.mutex);
+	tracer.range_start = range_start;
+	tracer.range_end = range_end;
+	mutex_unlock(&tracer.mutex);
+
+	return n;
+}
+
+
+static struct kobj_attribute trace_range_attr =
+	__ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+
 static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 {
 	struct tracectx *t = &tracer;
@@ -576,6 +614,10 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 	if (ret)
 		dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
 
+	ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
+	if (ret)
+		dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+
 	dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
 
 out:
@@ -605,6 +647,7 @@ static int etm_remove(struct amba_device *dev)
 	sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
 	sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
 	sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
+	sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
 
 	return 0;
 }
Signature · 1 line
1.7.3.1
#12 lore raw

Re: [PATCH 4/8] ARM: etm: Allow range selection

Quoted 3 lines · Arve Hj?nnev?g

Trace kernel text segment by default as before, allow tracing of other ranges by writing a range to /sys/devices/etm/trace_range, or to trace everything by writing 0 0.

Since you're adding this, I think it might make sense to add a 3rd optional ctx id field, so that userspace tracing is also possible, especially when on-chip ETB sizes increase to something more sensible than what they are these days.

Thanks!

Quoted 102 lines
---
 arch/arm/kernel/etm.c |   49 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 46 insertions(+), 3 deletions(-)
arch/arm/kernel/etm.c +46 -3
index bc7d8f2..8a1c422 100644
@@ -40,12 +40,17 @@ struct tracectx {
 	unsigned long	flags;
 	int		ncmppairs;
 	int		etm_portsz;
+	unsigned long	range_start;
+	unsigned long	range_end;
 	struct device	*dev;
 	struct clk	*emu_clk;
 	struct mutex	mutex;
 };
 
-static struct tracectx tracer;
+static struct tracectx tracer = {
+	.range_start = (unsigned long)_stext,
+	.range_end = (unsigned long)_etext,
+};
 
 static inline bool trace_isrunning(struct tracectx *t)
 {
@@ -115,8 +120,12 @@ static int trace_start(struct tracectx *t)
 		return -EFAULT;
 	}
 
-	etm_setup_address_range(t, 1, (unsigned long)_stext,
-			(unsigned long)_etext, 0, 0);
+	if (t->range_start || t->range_end)
+		etm_setup_address_range(t, 1,
+					t->range_start, t->range_end, 0, 0);
+	else
+		etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+
 	etm_writel(t, 0, ETMR_TRACEENCTRL2);
 	etm_writel(t, 0, ETMR_TRACESSCTRL);
 	etm_writel(t, 0x6f, ETMR_TRACEENEVT);
@@ -525,6 +534,35 @@ static ssize_t trace_mode_store(struct kobject *kobj,
 static struct kobj_attribute trace_mode_attr =
 	__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
 
+static ssize_t trace_range_show(struct kobject *kobj,
+				  struct kobj_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%08lx %08lx\n",
+			tracer.range_start, tracer.range_end);
+}
+
+static ssize_t trace_range_store(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   const char *buf, size_t n)
+{
+	unsigned long range_start, range_end;
+
+	if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
+		return -EINVAL;
+
+	mutex_lock(&tracer.mutex);
+	tracer.range_start = range_start;
+	tracer.range_end = range_end;
+	mutex_unlock(&tracer.mutex);
+
+	return n;
+}
+
+
+static struct kobj_attribute trace_range_attr =
+	__ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+
 static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 {
 	struct tracectx *t = &tracer;
@@ -576,6 +614,10 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 	if (ret)
 		dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
 
+	ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
+	if (ret)
+		dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+
 	dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
 
 out:
@@ -605,6 +647,7 @@ static int etm_remove(struct amba_device *dev)
 	sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
 	sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
 	sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
+	sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
 
 	return 0;
 }
Signature · 1 line
1.7.3.1
#13 lore raw

Re: [PATCH 4/8] ARM: etm: Allow range selection

Quoted 10 lines · Alexander Shishkin
Quoted 3 lines · Arve Hj?nnev?g

Trace kernel text segment by default as before, allow tracing of other ranges by writing a range to /sys/devices/etm/trace_range, or to trace everything by writing 0 0.

Since you're adding this, I think it might make sense to add a 3rd optional ctx id field, so that userspace tracing is also possible, especially when on-chip ETB sizes increase to something more sensible than what they are these days.

That might be useful, but I think it can be added later. My main motivation for adding this is so we can turn off the filtering. We want to use tracing in combination with a hardware watchdog to debug hard lockups and we don't want any filtering in this case.

Signature · 1 line
Arve Hj?nnev?g
#14 lore raw

PATCH 5/8 ARM: etm: Configure data tracing

In reply to Arve Hjønnevåg

The old code enabled data tracing, but did not configure the range. We now configure it to trace all data addresses by default, and add a trace_data_range attribute to change the range or disable data tracing.

---
 arch/arm/include/asm/hardware/coresight.h |   10 +++-
 arch/arm/kernel/etm.c                     |   77 +++++++++++++++++++++++++++-
 2 files changed, 83 insertions(+), 4 deletions(-)
arch/arm/include/asm/hardware/coresight.h +9 -1
index 7ecd793..6ea507f 100644
@@ -17,9 +17,11 @@
 #define TRACER_ACCESSED_BIT	0
 #define TRACER_RUNNING_BIT	1
 #define TRACER_CYCLE_ACC_BIT	2
+#define TRACER_TRACE_DATA_BIT	3
 #define TRACER_ACCESSED		BIT(TRACER_ACCESSED_BIT)
 #define TRACER_RUNNING		BIT(TRACER_RUNNING_BIT)
 #define TRACER_CYCLE_ACC	BIT(TRACER_CYCLE_ACC_BIT)
+#define TRACER_TRACE_DATA	BIT(TRACER_TRACE_DATA_BIT)
 
 #define TRACER_TIMEOUT 10000
 
@@ -113,8 +115,14 @@
 #define ETMR_TRACEENCTRL	0x24
 #define ETMTE_INCLEXCL		BIT(24)
 #define ETMR_TRACEENEVT		0x20
+
+#define ETMR_VIEWDATAEVT	0x30
+#define ETMR_VIEWDATACTRL1	0x34
+#define ETMR_VIEWDATACTRL2	0x38
+#define ETMR_VIEWDATACTRL3	0x3c
+#define ETMVDC3_EXCLONLY	BIT(16)
+
 #define ETMCTRL_OPTS		(ETMCTRL_DO_CPRT | \
-				ETMCTRL_DATA_DO_ADDR | \
 				ETMCTRL_BRANCH_OUTPUT | \
 				ETMCTRL_DO_CONTEXTID)
 
arch/arm/kernel/etm.c +74 -3

125 lines not on this page · raw message

Signature · 1 line
1.7.3.1
#15 lore raw

PATCH 6/8 ARM: etm: Add some missing locks and error checks

In reply to Arve Hjønnevåg

It is not safe to call etm_lock or etb_lock without holding the mutex since another thread may also have unlocked the registers.

Also add some missing checks for valid etb_regs in the etm sysfs entries.

---
 arch/arm/kernel/etm.c |   57 +++++++++++++++++++++++++++++++++---------------
 1 files changed, 39 insertions(+), 18 deletions(-)
arch/arm/kernel/etm.c +39 -18
index 96b1abb..8126beb 100644
@@ -263,8 +263,13 @@ static void etm_dump(void)
 
 static void sysrq_etm_dump(int key)
 {
+	if (!mutex_trylock(&tracer.mutex)) {
+		printk(KERN_INFO "Tracing hardware busy\n");
+		return;
+	}
 	dev_dbg(tracer.dev, "Dumping ETB buffer\n");
 	etm_dump();
+	mutex_unlock(&tracer.mutex);
 }
 
 static struct sysrq_key_op sysrq_etm_op = {
@@ -373,6 +378,7 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
 	if (ret)
 		goto out;
 
+	mutex_lock(&t->mutex);
 	t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
 	if (!t->etb_regs) {
 		ret = -ENOMEM;
@@ -381,6 +387,16 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
 
 	amba_set_drvdata(dev, t);
 
+	etb_unlock(t);
+	t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
+	dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz);
+
+	/* make sure trace capture is disabled */
+	etb_writel(t, 0, ETBR_CTRL);
+	etb_writel(t, 0x1000, ETBR_FORMATTERCTRL);
+	etb_lock(t);
+	mutex_unlock(&t->mutex);
+
 	etb_miscdev.parent = &dev->dev;
 
 	ret = misc_register(&etb_miscdev);
@@ -394,25 +410,19 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
 	else
 		clk_enable(t->emu_clk);
 
-	etb_unlock(t);
-	t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
-	dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz);
-
-	/* make sure trace capture is disabled */
-	etb_writel(t, 0, ETBR_CTRL);
-	etb_writel(t, 0x1000, ETBR_FORMATTERCTRL);
-	etb_lock(t);
-
 	dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n");
 
 out:
 	return ret;
 
 out_unmap:
+	mutex_lock(&t->mutex);
 	amba_set_drvdata(dev, NULL);
 	iounmap(t->etb_regs);
+	t->etb_regs = NULL;
 
 out_release:
+	mutex_unlock(&t->mutex);
 	amba_release_regions(dev);
 
 	return ret;
@@ -474,7 +484,10 @@ static ssize_t trace_running_store(struct kobject *kobj,
 		return -EINVAL;
 
 	mutex_lock(&tracer.mutex);
-	ret = value ? trace_start(&tracer) : trace_stop(&tracer);
+	if (!tracer.etb_regs)
+		ret = -ENODEV;
+	else
+		ret = value ? trace_start(&tracer) : trace_stop(&tracer);
 	mutex_unlock(&tracer.mutex);
 
 	return ret ? : n;
@@ -490,18 +503,25 @@ static ssize_t trace_info_show(struct kobject *kobj,
 	u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st;
 	int datalen;
 
-	etb_unlock(&tracer);
-	datalen = etb_getdatalen(&tracer);
-	etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
-	etb_ra = etb_readl(&tracer, ETBR_READADDR);
-	etb_st = etb_readl(&tracer, ETBR_STATUS);
-	etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
-	etb_lock(&tracer);
+	mutex_lock(&tracer.mutex);
+	if (tracer.etb_regs) {
+		etb_unlock(&tracer);
+		datalen = etb_getdatalen(&tracer);
+		etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
+		etb_ra = etb_readl(&tracer, ETBR_READADDR);
+		etb_st = etb_readl(&tracer, ETBR_STATUS);
+		etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
+		etb_lock(&tracer);
+	} else {
+		etb_wa = etb_ra = etb_st = etb_fc = ~0;
+		datalen = -1;
+	}
 
 	etm_unlock(&tracer);
 	etm_ctrl = etm_readl(&tracer, ETMR_CTRL);
 	etm_st = etm_readl(&tracer, ETMR_STATUS);
 	etm_lock(&tracer);
+	mutex_unlock(&tracer.mutex);
 
 	return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
 			"ETBR_WRITEADDR:\t%08x\n"
@@ -651,7 +671,6 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 
 	amba_set_drvdata(dev, t);
 
-	mutex_init(&t->mutex);
 	t->dev = &dev->dev;
 	t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA;
 	t->etm_portsz = 1;
@@ -745,6 +764,8 @@ static int __init etm_init(void)
 {
 	int retval;
 
+	mutex_init(&tracer.mutex);
+
 	retval = amba_driver_register(&etb_driver);
 	if (retval) {
 		printk(KERN_ERR "Failed to register etb\n");
Signature · 1 line
1.7.3.1
#16 lore raw

PATCH 7/8 ARM: etm: Return the entire trace buffer if it is empty after reset

In reply to Arve Hjønnevåg

On some SOCs the read and write pointer are reset when the chip resets, but the trace buffer content is preserved. If the status bits indicates that the buffer is empty and we have never started tracing, assume the buffer is full instead. This can be useful if the system rebooted from a watchdog reset.

---
 arch/arm/kernel/etm.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)
arch/arm/kernel/etm.c +5 -0
index 8126beb..4ad1257 100644
@@ -44,6 +44,7 @@ struct tracectx {
 	unsigned long	range_end;
 	unsigned long	data_range_start;
 	unsigned long	data_range_end;
+	bool		dump_initial_etb;
 	struct device	*dev;
 	struct clk	*emu_clk;
 	struct mutex	mutex;
@@ -105,6 +106,7 @@ static int trace_start(struct tracectx *t)
 
 	etb_unlock(t);
 
+	t->dump_initial_etb = false;
 	etb_writel(t, 0, ETBR_WRITEADDR);
 	etb_writel(t, 0, ETBR_FORMATTERCTRL);
 	etb_writel(t, 1, ETBR_CTRL);
@@ -311,6 +313,8 @@ static ssize_t etb_read(struct file *file, char __user *data,
 	etb_unlock(t);
 
 	total = etb_getdatalen(t);
+	if (total == 0 && t->dump_initial_etb)
+		total = t->etb_bufsz;
 	if (total == t->etb_bufsz)
 		first = etb_readl(t, ETBR_WRITEADDR);
 
@@ -385,6 +389,7 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
 		goto out_release;
 	}
 
+	t->dump_initial_etb = true;
 	amba_set_drvdata(dev, t);
 
 	etb_unlock(t);
Signature · 1 line
1.7.3.1
#17 lore raw

PATCH 8/8 ARM: etm: Support multiple ETMs/PTMs.

In reply to Arve Hjønnevåg

If more than one ETM or PTM are present, configure all of them and enable the formatter in the ETB. This allows tracing on dual core systems (e.g. omap4).

---
 arch/arm/include/asm/hardware/coresight.h |   16 ++-
 arch/arm/kernel/etm.c                     |  234 +++++++++++++++++++----------
 2 files changed, 166 insertions(+), 84 deletions(-)
arch/arm/include/asm/hardware/coresight.h +10 -6
index 6ea507f..6643d6c 100644
@@ -25,9 +25,9 @@
 
 #define TRACER_TIMEOUT 10000
 
-#define etm_writel(t, v, x) \
-	(__raw_writel((v), (t)->etm_regs + (x)))
-#define etm_readl(t, x) (__raw_readl((t)->etm_regs + (x)))
+#define etm_writel(t, id, v, x) \
+	(__raw_writel((v), (t)->etm_regs[(id)] + (x)))
+#define etm_readl(t, id, x) (__raw_readl((t)->etm_regs[(id)] + (x)))
 
 /* CoreSight Management Registers */
 #define CSMR_LOCKACCESS 0xfb0
@@ -126,6 +126,8 @@
 				ETMCTRL_BRANCH_OUTPUT | \
 				ETMCTRL_DO_CONTEXTID)
 
+#define ETMR_TRACEIDR		0x200
+
 /* ETM management registers, "ETM Architecture", 3.5.24 */
 #define ETMMR_OSLAR	0x300
 #define ETMMR_OSLSR	0x304
@@ -148,14 +150,16 @@
 #define ETBFF_TRIGIN		BIT(8)
 #define ETBFF_TRIGEVT		BIT(9)
 #define ETBFF_TRIGFL		BIT(10)
+#define ETBFF_STOPFL		BIT(12)
 
 #define etb_writel(t, v, x) \
 	(__raw_writel((v), (t)->etb_regs + (x)))
 #define etb_readl(t, x) (__raw_readl((t)->etb_regs + (x)))
 
-#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0)
-#define etm_unlock(t) \
-	do { etm_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0)
+#define etm_lock(t, id) \
+	do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0)
+#define etm_unlock(t, id) \
+	do { etm_writel((t), (id), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0)
 
 #define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
 #define etb_unlock(t) \
arch/arm/kernel/etm.c +156 -78

419 lines not on this page · raw message

Signature · 1 line
1.7.3.1
#18 lore raw

PATCH 9/9 ARM: etm: Power down etm(s) when tracing is not enabled

In reply to Arve Hjønnevåg

Without this change a saw an 18% increase in idle power consumption on one deivce when trace support is compiled into the kernel. Now I see the same increase only when tracing.

---
 arch/arm/kernel/etm.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
arch/arm/kernel/etm.c +2 -2
index 48de5a4..f2bc95e 100644
@@ -199,7 +199,7 @@ static int trace_stop_etm(struct tracectx *t, int id)
 
 	etm_unlock(t, id);
 
-	etm_writel(t, id, 0x440, ETMR_CTRL);
+	etm_writel(t, id, 0x441, ETMR_CTRL);
 	while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
 		;
 	if (!timeout) {
@@ -738,7 +738,7 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
 	(void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR);
 
 	t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf;
-	etm_writel(t, t->etm_regs_count, 0x440, ETMR_CTRL);
+	etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL);
 	etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR);
 	etm_lock(t, t->etm_regs_count);
 
Signature · 1 line
1.7.3.1