aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwtracing
diff options
context:
space:
mode:
authorMathieu Poirier <mathieu.poirier@linaro.org>2016-08-25 17:19:17 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-08-31 07:05:44 -0400
commit2703d74c1313271ba78439b0796444add6a9328f (patch)
treee9f4d95bea08e6799389817454d111aeb0375561 /drivers/hwtracing
parentf0d30cc30e545d0b059948f5d6be1b62ee54a355 (diff)
coresight: etm4x: adding configurable address range filtering
This patch adds the capability to specify address ranges from the perf cmd line using the --filter option. If the IP falls within the range(s) program flow traces are generated. Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hwtracing')
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.c128
1 files changed, 119 insertions, 9 deletions
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 1044ed609d81..ebaefb45130f 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -45,7 +45,9 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
45/* The number of ETMv4 currently registered */ 45/* The number of ETMv4 currently registered */
46static int etm4_count; 46static int etm4_count;
47static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; 47static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
48static void etm4_set_default(struct etmv4_config *config); 48static void etm4_set_default_config(struct etmv4_config *config);
49static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
50 struct perf_event *event);
49 51
50static enum cpuhp_state hp_online; 52static enum cpuhp_state hp_online;
51 53
@@ -187,11 +189,14 @@ static void etm4_enable_hw(void *info)
187static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, 189static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
188 struct perf_event *event) 190 struct perf_event *event)
189{ 191{
192 int ret = 0;
190 struct etmv4_config *config = &drvdata->config; 193 struct etmv4_config *config = &drvdata->config;
191 struct perf_event_attr *attr = &event->attr; 194 struct perf_event_attr *attr = &event->attr;
192 195
193 if (!attr) 196 if (!attr) {
194 return -EINVAL; 197 ret = -EINVAL;
198 goto out;
199 }
195 200
196 /* Clear configuration from previous run */ 201 /* Clear configuration from previous run */
197 memset(config, 0, sizeof(struct etmv4_config)); 202 memset(config, 0, sizeof(struct etmv4_config));
@@ -203,7 +208,12 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
203 config->mode = ETM_MODE_EXCL_USER; 208 config->mode = ETM_MODE_EXCL_USER;
204 209
205 /* Always start from the default config */ 210 /* Always start from the default config */
206 etm4_set_default(config); 211 etm4_set_default_config(config);
212
213 /* Configure filters specified on the perf cmd line, if any. */
214 ret = etm4_set_event_filters(drvdata, event);
215 if (ret)
216 goto out;
207 217
208 /* Go from generic option to ETMv4 specifics */ 218 /* Go from generic option to ETMv4 specifics */
209 if (attr->config & BIT(ETM_OPT_CYCACC)) 219 if (attr->config & BIT(ETM_OPT_CYCACC))
@@ -211,23 +221,30 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
211 if (attr->config & BIT(ETM_OPT_TS)) 221 if (attr->config & BIT(ETM_OPT_TS))
212 config->cfg |= ETMv4_MODE_TIMESTAMP; 222 config->cfg |= ETMv4_MODE_TIMESTAMP;
213 223
214 return 0; 224out:
225 return ret;
215} 226}
216 227
217static int etm4_enable_perf(struct coresight_device *csdev, 228static int etm4_enable_perf(struct coresight_device *csdev,
218 struct perf_event *event) 229 struct perf_event *event)
219{ 230{
231 int ret = 0;
220 struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 232 struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
221 233
222 if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) 234 if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
223 return -EINVAL; 235 ret = -EINVAL;
236 goto out;
237 }
224 238
225 /* Configure the tracer based on the session's specifics */ 239 /* Configure the tracer based on the session's specifics */
226 etm4_parse_event_config(drvdata, event); 240 ret = etm4_parse_event_config(drvdata, event);
241 if (ret)
242 goto out;
227 /* And enable it */ 243 /* And enable it */
228 etm4_enable_hw(drvdata); 244 etm4_enable_hw(drvdata);
229 245
230 return 0; 246out:
247 return ret;
231} 248}
232 249
233static int etm4_enable_sysfs(struct coresight_device *csdev) 250static int etm4_enable_sysfs(struct coresight_device *csdev)
@@ -682,6 +699,99 @@ static void etm4_set_default(struct etmv4_config *config)
682 etm4_set_default_filter(config); 699 etm4_set_default_filter(config);
683} 700}
684 701
702static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type)
703{
704 int nr_comparator, index = 0;
705 struct etmv4_config *config = &drvdata->config;
706
707 /*
708 * nr_addr_cmp holds the number of comparator _pair_, so time 2
709 * for the total number of comparators.
710 */
711 nr_comparator = drvdata->nr_addr_cmp * 2;
712
713 /* Go through the tally of comparators looking for a free one. */
714 while (index < nr_comparator) {
715 switch (type) {
716 case ETM_ADDR_TYPE_RANGE:
717 if (config->addr_type[index] == ETM_ADDR_TYPE_NONE &&
718 config->addr_type[index + 1] == ETM_ADDR_TYPE_NONE)
719 return index;
720
721 /* Address range comparators go in pairs */
722 index += 2;
723 break;
724 default:
725 return -EINVAL;
726 }
727 }
728
729 /* If we are here all the comparators have been used. */
730 return -ENOSPC;
731}
732
733static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
734 struct perf_event *event)
735{
736 int i, comparator, ret = 0;
737 struct etmv4_config *config = &drvdata->config;
738 struct etm_filters *filters = event->hw.addr_filters;
739
740 if (!filters)
741 goto default_filter;
742
743 /* Sync events with what Perf got */
744 perf_event_addr_filters_sync(event);
745
746 /*
747 * If there are no filters to deal with simply go ahead with
748 * the default filter, i.e the entire address range.
749 */
750 if (!filters->nr_filters)
751 goto default_filter;
752
753 for (i = 0; i < filters->nr_filters; i++) {
754 struct etm_filter *filter = &filters->etm_filter[i];
755 enum etm_addr_type type = filter->type;
756
757 /* See if a comparator is free. */
758 comparator = etm4_get_next_comparator(drvdata, type);
759 if (comparator < 0) {
760 ret = comparator;
761 goto out;
762 }
763
764 switch (type) {
765 case ETM_ADDR_TYPE_RANGE:
766 etm4_set_comparator_filter(config,
767 filter->start_addr,
768 filter->stop_addr,
769 comparator);
770 /*
771 * TRCVICTLR::SSSTATUS == 1, the start-stop logic is
772 * in the started state
773 */
774 config->vinst_ctrl |= BIT(9);
775
776 /* No start-stop filtering for ViewInst */
777 config->vissctlr = 0x0;
778 break;
779 default:
780 ret = -EINVAL;
781 goto out;
782 }
783 }
784
785 goto out;
786
787
788default_filter:
789 etm4_set_default_filter(config);
790
791out:
792 return ret;
793}
794
685void etm4_config_trace_mode(struct etmv4_config *config) 795void etm4_config_trace_mode(struct etmv4_config *config)
686{ 796{
687 u32 addr_acc, mode; 797 u32 addr_acc, mode;