diff options
| author | Mathieu Poirier <mathieu.poirier@linaro.org> | 2016-08-25 17:19:17 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-08-31 07:05:44 -0400 |
| commit | 2703d74c1313271ba78439b0796444add6a9328f (patch) | |
| tree | e9f4d95bea08e6799389817454d111aeb0375561 /drivers/hwtracing | |
| parent | f0d30cc30e545d0b059948f5d6be1b62ee54a355 (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.c | 128 |
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 */ |
| 46 | static int etm4_count; | 46 | static int etm4_count; |
| 47 | static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; | 47 | static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; |
| 48 | static void etm4_set_default(struct etmv4_config *config); | 48 | static void etm4_set_default_config(struct etmv4_config *config); |
| 49 | static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, | ||
| 50 | struct perf_event *event); | ||
| 49 | 51 | ||
| 50 | static enum cpuhp_state hp_online; | 52 | static enum cpuhp_state hp_online; |
| 51 | 53 | ||
| @@ -187,11 +189,14 @@ static void etm4_enable_hw(void *info) | |||
| 187 | static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, | 189 | static 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; | 224 | out: |
| 225 | return ret; | ||
| 215 | } | 226 | } |
| 216 | 227 | ||
| 217 | static int etm4_enable_perf(struct coresight_device *csdev, | 228 | static 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; | 246 | out: |
| 247 | return ret; | ||
| 231 | } | 248 | } |
| 232 | 249 | ||
| 233 | static int etm4_enable_sysfs(struct coresight_device *csdev) | 250 | static 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 | ||
| 702 | static 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 | |||
| 733 | static 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 | |||
| 788 | default_filter: | ||
| 789 | etm4_set_default_filter(config); | ||
| 790 | |||
| 791 | out: | ||
| 792 | return ret; | ||
| 793 | } | ||
| 794 | |||
| 685 | void etm4_config_trace_mode(struct etmv4_config *config) | 795 | void etm4_config_trace_mode(struct etmv4_config *config) |
| 686 | { | 796 | { |
| 687 | u32 addr_acc, mode; | 797 | u32 addr_acc, mode; |
