diff options
author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2017-09-07 15:36:05 +0300 |
---|---|---|
committer | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2020-03-11 13:32:58 +0200 |
commit | a0d1c0494b14ed3d5ea85e8ca2e76200441823d4 (patch) | |
tree | a3b1d161d3f3771a6d545d9e4adeeb418c85120a | |
parent | 944bff21a0a90a61d2ec6c177672f12fb591cd82 (diff) | |
download | linux-rtit.tar.gz |
perf/x86/intel/rtit: Support address range filteringrtit
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
-rw-r--r-- | arch/x86/events/intel/rtit.c | 231 |
1 files changed, 217 insertions, 14 deletions
diff --git a/arch/x86/events/intel/rtit.c b/arch/x86/events/intel/rtit.c index 78a1d15fe185c..97841d08b169f 100644 --- a/arch/x86/events/intel/rtit.c +++ b/arch/x86/events/intel/rtit.c @@ -44,11 +44,42 @@ #define MSR_RTIT_EVENTS 0x0000076c #define RTIT_EVENTS_FILTER_OFFSET 0 #define RTIT_EVENTS_TRACESTOP_OFFSET 3 -#define RTIT_EVENTS_ALWAYS_OFF 6 -#define RTIT_EVENTS_ALWAYS_ON 7 #define MSR_RTIT_BASE_ADDR 0x00000770 #define MSR_RTIT_LIMIT_MASK 0x00000771 #define MSR_RTIT_OFFSET 0x00000772 +#define MSR_RTIT_LIP0 0x00000760 +#define MSR_RTIT_LIP1 0x00000761 +#define MSR_RTIT_LIP2 0x00000762 +#define MSR_RTIT_LIP3 0x00000763 + +enum { + RTIT_EVENTS_RANGE0 = 0, + RTIT_EVENTS_RANGE1, + RTIT_EVENTS_RANGE_BOTH, + RTIT_EVENTS_ALWAYS_OFF = 6, + RTIT_EVENTS_ALWAYS_ON, +}; + +enum { + EVT_FILTER = 0, + EVT_STOP, + EVT_MAX, +}; + +static const unsigned int evt_config[EVT_MAX][4] = { + { /* Filter */ + RTIT_EVENTS_ALWAYS_ON << RTIT_EVENTS_FILTER_OFFSET, + RTIT_EVENTS_RANGE0 << RTIT_EVENTS_FILTER_OFFSET, + RTIT_EVENTS_RANGE1 << RTIT_EVENTS_FILTER_OFFSET, + RTIT_EVENTS_RANGE_BOTH << RTIT_EVENTS_FILTER_OFFSET, + }, + { /* TraceStop */ + RTIT_EVENTS_ALWAYS_OFF << RTIT_EVENTS_TRACESTOP_OFFSET, + RTIT_EVENTS_RANGE0 << RTIT_EVENTS_TRACESTOP_OFFSET, + RTIT_EVENTS_RANGE1 << RTIT_EVENTS_TRACESTOP_OFFSET, + RTIT_EVENTS_RANGE_BOTH << RTIT_EVENTS_TRACESTOP_OFFSET, + }, +}; struct rtit_buffer { void *buf; @@ -58,12 +89,28 @@ struct rtit_buffer { void **data_pages; }; +#define RTIT_FILTERS_NUM 2 + +struct rtit_filter { + unsigned long msr_a; + unsigned long msr_b; + unsigned what; +}; + +struct rtit_filters { + struct rtit_filter filter[RTIT_FILTERS_NUM]; + u64 rtit_event; + unsigned int nr_filters; +}; + /** * struct rtit - per-cpu rtit context * @handle: perf output handle + * @filters: address range filtering configuration */ struct rtit { struct perf_output_handle handle; + struct rtit_filters filters; }; static DEFINE_PER_CPU(struct rtit, rtit_ctx); @@ -136,6 +183,69 @@ static void rtit_buffer_free_aux(void *data) kfree(buf); } +static const struct rtit_address_range { + unsigned long msr_a; + unsigned long msr_b; +} rtit_address_ranges[] = { + { + .msr_a = MSR_RTIT_LIP0, + .msr_b = MSR_RTIT_LIP2, + }, + { + .msr_a = MSR_RTIT_LIP1, + .msr_b = MSR_RTIT_LIP3, + }, +}; + +static void rtit_config_filters(struct perf_event *event) +{ + struct rtit_filters *filters = event->hw.addr_filters; + struct rtit *rtit = this_cpu_ptr(&rtit_ctx); + int actions[EVT_MAX] = { 0, 0 }; + unsigned int range, act; + u64 rtit_event = 0; + + if (!filters) + goto done; + + perf_event_addr_filters_sync(event); + + for (range = 0; range < filters->nr_filters; range++) { + struct rtit_filter *filter = &filters->filter[range]; + + /* + * Note, if the range has zero start/end addresses due + * to its dynamic object not being loaded yet, we just + * go ahead and program zeroed range, which will simply + * produce no data. Note^2: if executable code at 0x0 + * is a concern, we can set up an "invalid" configuration + * such as msr_b < msr_a. + */ + + /* avoid redundant msr writes */ + if (rtit->filters.filter[range].msr_a != filter->msr_a) { + wrmsrl(rtit_address_ranges[range].msr_a, filter->msr_a); + rtit->filters.filter[range].msr_a = filter->msr_a; + } + + if (rtit->filters.filter[range].msr_b != filter->msr_b) { + wrmsrl(rtit_address_ranges[range].msr_b, filter->msr_b); + rtit->filters.filter[range].msr_b = filter->msr_b; + } + + actions[rtit->filters.filter[range].what] |= BIT(range); + } + +done: + for (act = 0; act < EVT_MAX; act++) + rtit_event |= evt_config[act][actions[act]]; + + if (rtit->filters.rtit_event != rtit_event) { + wrmsrl(MSR_RTIT_EVENTS, rtit_event); + rtit->filters.rtit_event = rtit_event; + } +} + static void rtit_config_stop(struct perf_event *event) { u64 ctl = READ_ONCE(event->hw.config); @@ -166,30 +276,114 @@ static void rtit_config_stop(struct perf_event *event) static void rtit_config(struct perf_event *event, struct rtit_buffer *buf) { - u64 reg; + u64 rtit_ctl; - /* set up filtering and TraceStop */ - reg = - RTIT_EVENTS_ALWAYS_ON << RTIT_EVENTS_FILTER_OFFSET | - RTIT_EVENTS_ALWAYS_OFF << RTIT_EVENTS_TRACESTOP_OFFSET; - wrmsrl(MSR_RTIT_EVENTS, reg); + rtit_config_filters(event); /* configure buffer address and size and write cursor*/ wrmsrl(MSR_RTIT_BASE_ADDR, virt_to_phys(buf->data_pages[0])); wrmsrl(MSR_RTIT_LIMIT_MASK, (buf->nr_pages << PAGE_SHIFT) - 1); wrmsrl(MSR_RTIT_OFFSET, local_read(&buf->head)); - reg = RTIT_CTL_DRAM | RTIT_CTL_TRACEEN | RTIT_CTL_TRACEACTIVE; + rtit_ctl = RTIT_CTL_DRAM | RTIT_CTL_TRACEEN | RTIT_CTL_TRACEACTIVE; if (!event->attr.exclude_kernel) - reg |= RTIT_CTL_OS; + rtit_ctl |= RTIT_CTL_OS; if (!event->attr.exclude_user) - reg |= RTIT_CTL_USR; + rtit_ctl |= RTIT_CTL_USR; - reg |= (event->attr.config & RTIT_CONFIG_MASK); - WRITE_ONCE(event->hw.config, reg); + rtit_ctl |= (event->attr.config & RTIT_CONFIG_MASK); + WRITE_ONCE(event->hw.config, rtit_ctl); - wrmsrl(MSR_RTIT_CTL, reg); + wrmsrl(MSR_RTIT_CTL, rtit_ctl); +} + +static int rtit_addr_filters_init(struct perf_event *event) +{ + struct rtit_filters *filters; + int node = event->cpu == -1 ? -1 : cpu_to_node(event->cpu); + + filters = kzalloc_node(sizeof(struct rtit_filters), GFP_KERNEL, node); + if (!filters) + return -ENOMEM; + + if (event->parent) + memcpy(filters, event->parent->hw.addr_filters, + sizeof(*filters)); + + event->hw.addr_filters = filters; + + return 0; +} + +static void rtit_addr_filters_fini(struct perf_event *event) +{ + kfree(event->hw.addr_filters); + event->hw.addr_filters = NULL; +} + +static inline bool valid_kernel_ip(unsigned long ip) +{ + return virt_addr_valid(ip) && kernel_ip(ip); +} + +static int rtit_event_addr_filters_validate(struct list_head *filters) +{ + struct perf_addr_filter *filter; + int range = 0; + + list_for_each_entry(filter, filters, entry) { + /* RTIT doesn't support single address triggers */ + if (!filter->size || + filter->action == PERF_ADDR_FILTER_ACTION_START) + return -EOPNOTSUPP; + + if (!filter->path.dentry) { + if (!valid_kernel_ip(filter->offset)) + return -EINVAL; + + if (!valid_kernel_ip(filter->offset + filter->size)) + return -EINVAL; + } + + if (++range > RTIT_FILTERS_NUM) + return -EOPNOTSUPP; + } + + return 0; +} + +static void rtit_event_addr_filters_sync(struct perf_event *event) +{ + struct perf_addr_filters_head *head = perf_event_addr_filters(event); + struct perf_addr_filter_range *fr = event->addr_filter_ranges; + struct rtit_filters *filters = event->hw.addr_filters; + struct perf_addr_filter *filter; + unsigned long msr_a, msr_b; + int range = 0; + + if (!filters) + return; + + list_for_each_entry(filter, &head->list, entry) { + if (filter->path.dentry && !fr[range].start) { + msr_a = msr_b = 0; + } else { + /* apply the offset */ + msr_a = fr[range].start; + msr_b = fr[range].size + msr_a - 1; + } + + filters->filter[range].msr_a = msr_a; + filters->filter[range].msr_b = msr_b; + if (filter->action == PERF_ADDR_FILTER_ACTION_FILTER) + filters->filter[range].what = EVT_FILTER; + else + filters->filter[range].what = EVT_STOP; + range++; + } + + filters->nr_filters = range; } /* @@ -296,6 +490,7 @@ static void rtit_event_read(struct perf_event *event) static void rtit_event_destroy(struct perf_event *event) { + rtit_addr_filters_fini(event); x86_del_exclusive(x86_lbr_exclusive_pt); } @@ -307,6 +502,11 @@ static int rtit_event_init(struct perf_event *event) if (x86_add_exclusive(x86_lbr_exclusive_pt)) return -EBUSY; + if (rtit_addr_filters_init(event)) { + x86_del_exclusive(x86_lbr_exclusive_pt); + return -ENOMEM; + } + event->destroy = rtit_event_destroy; return 0; @@ -350,6 +550,9 @@ static __init int rtit_init(void) rtit_pmu.read = rtit_event_read; rtit_pmu.setup_aux = rtit_buffer_setup_aux; rtit_pmu.free_aux = rtit_buffer_free_aux; + rtit_pmu.addr_filters_sync = rtit_event_addr_filters_sync; + rtit_pmu.addr_filters_validate = rtit_event_addr_filters_validate; + rtit_pmu.nr_addr_filters = RTIT_FILTERS_NUM; ret = perf_pmu_register(&rtit_pmu, "intel_pt", -1); return ret; |