aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Shishkin <alexander.shishkin@linux.intel.com>2019-02-15 06:56:55 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2019-02-22 14:52:07 -0500
commitc60f83b813e5b25ccd5de7e8c8925c31b3aebcc1 (patch)
treeb17b0a30fab156a840ab55518337db38f6e9fac7
parent18736eef12137c59f60cc9f56dc5bea05c92e0eb (diff)
perf, pt, coresight: Fix address filters for vmas with non-zero offset
Currently, the address range calculation for file-based filters works as long as the vma that maps the matching part of the object file starts from offset zero into the file (vm_pgoff==0). Otherwise, the resulting filter range would be off by vm_pgoff pages. Another related problem is that in case of a partially matching vma, that is, a vma that matches part of a filter region, the filter range size wouldn't be adjusted. Fix the arithmetics around address filter range calculations, taking into account vma offset, so that the entire calculation is done before the filter configuration is passed to the PMU drivers instead of having those drivers do the final bit of arithmetics. Based on the patch by Adrian Hunter <adrian.hunter.intel.com>. Reported-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Tested-by: Mathieu Poirier <mathieu.poirier@linaro.org> Acked-by: Peter Zijlstra <peterz@infradead.org> Cc: Jiri Olsa <jolsa@redhat.com> Fixes: 375637bc5249 ("perf/core: Introduce address range filtering") Link: http://lkml.kernel.org/r/20190215115655.63469-3-alexander.shishkin@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--arch/x86/events/intel/pt.c9
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c7
-rw-r--r--include/linux/perf_event.h7
-rw-r--r--kernel/events/core.c81
4 files changed, 62 insertions, 42 deletions
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index c0e86ff21f81..fb3a2f13fc70 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -1223,7 +1223,8 @@ static int pt_event_addr_filters_validate(struct list_head *filters)
1223static void pt_event_addr_filters_sync(struct perf_event *event) 1223static void pt_event_addr_filters_sync(struct perf_event *event)
1224{ 1224{
1225 struct perf_addr_filters_head *head = perf_event_addr_filters(event); 1225 struct perf_addr_filters_head *head = perf_event_addr_filters(event);
1226 unsigned long msr_a, msr_b, *offs = event->addr_filters_offs; 1226 unsigned long msr_a, msr_b;
1227 struct perf_addr_filter_range *fr = event->addr_filter_ranges;
1227 struct pt_filters *filters = event->hw.addr_filters; 1228 struct pt_filters *filters = event->hw.addr_filters;
1228 struct perf_addr_filter *filter; 1229 struct perf_addr_filter *filter;
1229 int range = 0; 1230 int range = 0;
@@ -1232,12 +1233,12 @@ static void pt_event_addr_filters_sync(struct perf_event *event)
1232 return; 1233 return;
1233 1234
1234 list_for_each_entry(filter, &head->list, entry) { 1235 list_for_each_entry(filter, &head->list, entry) {
1235 if (filter->path.dentry && !offs[range]) { 1236 if (filter->path.dentry && !fr[range].start) {
1236 msr_a = msr_b = 0; 1237 msr_a = msr_b = 0;
1237 } else { 1238 } else {
1238 /* apply the offset */ 1239 /* apply the offset */
1239 msr_a = filter->offset + offs[range]; 1240 msr_a = fr[range].start;
1240 msr_b = filter->size + msr_a - 1; 1241 msr_b = msr_a + fr[range].size - 1;
1241 } 1242 }
1242 1243
1243 filters->filter[range].msr_a = msr_a; 1244 filters->filter[range].msr_a = msr_a;
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 8c88bf0a1e5f..4d5a2b9f9d6a 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -433,15 +433,16 @@ static int etm_addr_filters_validate(struct list_head *filters)
433static void etm_addr_filters_sync(struct perf_event *event) 433static void etm_addr_filters_sync(struct perf_event *event)
434{ 434{
435 struct perf_addr_filters_head *head = perf_event_addr_filters(event); 435 struct perf_addr_filters_head *head = perf_event_addr_filters(event);
436 unsigned long start, stop, *offs = event->addr_filters_offs; 436 unsigned long start, stop;
437 struct perf_addr_filter_range *fr = event->addr_filter_ranges;
437 struct etm_filters *filters = event->hw.addr_filters; 438 struct etm_filters *filters = event->hw.addr_filters;
438 struct etm_filter *etm_filter; 439 struct etm_filter *etm_filter;
439 struct perf_addr_filter *filter; 440 struct perf_addr_filter *filter;
440 int i = 0; 441 int i = 0;
441 442
442 list_for_each_entry(filter, &head->list, entry) { 443 list_for_each_entry(filter, &head->list, entry) {
443 start = filter->offset + offs[i]; 444 start = fr[i].start;
444 stop = start + filter->size; 445 stop = start + fr[i].size;
445 etm_filter = &filters->etm_filter[i]; 446 etm_filter = &filters->etm_filter[i];
446 447
447 switch (filter->action) { 448 switch (filter->action) {
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index d9c3610e0e25..6ebc72f65017 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -490,6 +490,11 @@ struct perf_addr_filters_head {
490 unsigned int nr_file_filters; 490 unsigned int nr_file_filters;
491}; 491};
492 492
493struct perf_addr_filter_range {
494 unsigned long start;
495 unsigned long size;
496};
497
493/** 498/**
494 * enum perf_event_state - the states of an event: 499 * enum perf_event_state - the states of an event:
495 */ 500 */
@@ -666,7 +671,7 @@ struct perf_event {
666 /* address range filters */ 671 /* address range filters */
667 struct perf_addr_filters_head addr_filters; 672 struct perf_addr_filters_head addr_filters;
668 /* vma address array for file-based filders */ 673 /* vma address array for file-based filders */
669 unsigned long *addr_filters_offs; 674 struct perf_addr_filter_range *addr_filter_ranges;
670 unsigned long addr_filters_gen; 675 unsigned long addr_filters_gen;
671 676
672 void (*destroy)(struct perf_event *); 677 void (*destroy)(struct perf_event *);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 2d89efc0a3e0..16609f6737da 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2799,7 +2799,7 @@ static int perf_event_stop(struct perf_event *event, int restart)
2799 * 2799 *
2800 * (p1) when userspace mappings change as a result of (1) or (2) or (3) below, 2800 * (p1) when userspace mappings change as a result of (1) or (2) or (3) below,
2801 * we update the addresses of corresponding vmas in 2801 * we update the addresses of corresponding vmas in
2802 * event::addr_filters_offs array and bump the event::addr_filters_gen; 2802 * event::addr_filter_ranges array and bump the event::addr_filters_gen;
2803 * (p2) when an event is scheduled in (pmu::add), it calls 2803 * (p2) when an event is scheduled in (pmu::add), it calls
2804 * perf_event_addr_filters_sync() which calls pmu::addr_filters_sync() 2804 * perf_event_addr_filters_sync() which calls pmu::addr_filters_sync()
2805 * if the generation has changed since the previous call. 2805 * if the generation has changed since the previous call.
@@ -4446,7 +4446,7 @@ static void _free_event(struct perf_event *event)
4446 4446
4447 perf_event_free_bpf_prog(event); 4447 perf_event_free_bpf_prog(event);
4448 perf_addr_filters_splice(event, NULL); 4448 perf_addr_filters_splice(event, NULL);
4449 kfree(event->addr_filters_offs); 4449 kfree(event->addr_filter_ranges);
4450 4450
4451 if (event->destroy) 4451 if (event->destroy)
4452 event->destroy(event); 4452 event->destroy(event);
@@ -6687,7 +6687,8 @@ static void perf_event_addr_filters_exec(struct perf_event *event, void *data)
6687 raw_spin_lock_irqsave(&ifh->lock, flags); 6687 raw_spin_lock_irqsave(&ifh->lock, flags);
6688 list_for_each_entry(filter, &ifh->list, entry) { 6688 list_for_each_entry(filter, &ifh->list, entry) {
6689 if (filter->path.dentry) { 6689 if (filter->path.dentry) {
6690 event->addr_filters_offs[count] = 0; 6690 event->addr_filter_ranges[count].start = 0;
6691 event->addr_filter_ranges[count].size = 0;
6691 restart++; 6692 restart++;
6692 } 6693 }
6693 6694
@@ -7367,28 +7368,47 @@ static bool perf_addr_filter_match(struct perf_addr_filter *filter,
7367 return true; 7368 return true;
7368} 7369}
7369 7370
7371static bool perf_addr_filter_vma_adjust(struct perf_addr_filter *filter,
7372 struct vm_area_struct *vma,
7373 struct perf_addr_filter_range *fr)
7374{
7375 unsigned long vma_size = vma->vm_end - vma->vm_start;
7376 unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
7377 struct file *file = vma->vm_file;
7378
7379 if (!perf_addr_filter_match(filter, file, off, vma_size))
7380 return false;
7381
7382 if (filter->offset < off) {
7383 fr->start = vma->vm_start;
7384 fr->size = min(vma_size, filter->size - (off - filter->offset));
7385 } else {
7386 fr->start = vma->vm_start + filter->offset - off;
7387 fr->size = min(vma->vm_end - fr->start, filter->size);
7388 }
7389
7390 return true;
7391}
7392
7370static void __perf_addr_filters_adjust(struct perf_event *event, void *data) 7393static void __perf_addr_filters_adjust(struct perf_event *event, void *data)
7371{ 7394{
7372 struct perf_addr_filters_head *ifh = perf_event_addr_filters(event); 7395 struct perf_addr_filters_head *ifh = perf_event_addr_filters(event);
7373 struct vm_area_struct *vma = data; 7396 struct vm_area_struct *vma = data;
7374 unsigned long off = vma->vm_pgoff << PAGE_SHIFT, flags;
7375 struct file *file = vma->vm_file;
7376 struct perf_addr_filter *filter; 7397 struct perf_addr_filter *filter;
7377 unsigned int restart = 0, count = 0; 7398 unsigned int restart = 0, count = 0;
7399 unsigned long flags;
7378 7400
7379 if (!has_addr_filter(event)) 7401 if (!has_addr_filter(event))
7380 return; 7402 return;
7381 7403
7382 if (!file) 7404 if (!vma->vm_file)
7383 return; 7405 return;
7384 7406
7385 raw_spin_lock_irqsave(&ifh->lock, flags); 7407 raw_spin_lock_irqsave(&ifh->lock, flags);
7386 list_for_each_entry(filter, &ifh->list, entry) { 7408 list_for_each_entry(filter, &ifh->list, entry) {
7387 if (perf_addr_filter_match(filter, file, off, 7409 if (perf_addr_filter_vma_adjust(filter, vma,
7388 vma->vm_end - vma->vm_start)) { 7410 &event->addr_filter_ranges[count]))
7389 event->addr_filters_offs[count] = vma->vm_start;
7390 restart++; 7411 restart++;
7391 }
7392 7412
7393 count++; 7413 count++;
7394 } 7414 }
@@ -8978,26 +8998,19 @@ static void perf_addr_filters_splice(struct perf_event *event,
8978 * @filter; if so, adjust filter's address range. 8998 * @filter; if so, adjust filter's address range.
8979 * Called with mm::mmap_sem down for reading. 8999 * Called with mm::mmap_sem down for reading.
8980 */ 9000 */
8981static unsigned long perf_addr_filter_apply(struct perf_addr_filter *filter, 9001static void perf_addr_filter_apply(struct perf_addr_filter *filter,
8982 struct mm_struct *mm) 9002 struct mm_struct *mm,
9003 struct perf_addr_filter_range *fr)
8983{ 9004{
8984 struct vm_area_struct *vma; 9005 struct vm_area_struct *vma;
8985 9006
8986 for (vma = mm->mmap; vma; vma = vma->vm_next) { 9007 for (vma = mm->mmap; vma; vma = vma->vm_next) {
8987 struct file *file = vma->vm_file; 9008 if (!vma->vm_file)
8988 unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
8989 unsigned long vma_size = vma->vm_end - vma->vm_start;
8990
8991 if (!file)
8992 continue; 9009 continue;
8993 9010
8994 if (!perf_addr_filter_match(filter, file, off, vma_size)) 9011 if (perf_addr_filter_vma_adjust(filter, vma, fr))
8995 continue; 9012 return;
8996
8997 return vma->vm_start;
8998 } 9013 }
8999
9000 return 0;
9001} 9014}
9002 9015
9003/* 9016/*
@@ -9031,15 +9044,15 @@ static void perf_event_addr_filters_apply(struct perf_event *event)
9031 9044
9032 raw_spin_lock_irqsave(&ifh->lock, flags); 9045 raw_spin_lock_irqsave(&ifh->lock, flags);
9033 list_for_each_entry(filter, &ifh->list, entry) { 9046 list_for_each_entry(filter, &ifh->list, entry) {
9034 event->addr_filters_offs[count] = 0; 9047 event->addr_filter_ranges[count].start = 0;
9048 event->addr_filter_ranges[count].size = 0;
9035 9049
9036 /* 9050 /*
9037 * Adjust base offset if the filter is associated to a binary 9051 * Adjust base offset if the filter is associated to a binary
9038 * that needs to be mapped: 9052 * that needs to be mapped:
9039 */ 9053 */
9040 if (filter->path.dentry) 9054 if (filter->path.dentry)
9041 event->addr_filters_offs[count] = 9055 perf_addr_filter_apply(filter, mm, &event->addr_filter_ranges[count]);
9042 perf_addr_filter_apply(filter, mm);
9043 9056
9044 count++; 9057 count++;
9045 } 9058 }
@@ -10305,10 +10318,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
10305 goto err_pmu; 10318 goto err_pmu;
10306 10319
10307 if (has_addr_filter(event)) { 10320 if (has_addr_filter(event)) {
10308 event->addr_filters_offs = kcalloc(pmu->nr_addr_filters, 10321 event->addr_filter_ranges = kcalloc(pmu->nr_addr_filters,
10309 sizeof(unsigned long), 10322 sizeof(struct perf_addr_filter_range),
10310 GFP_KERNEL); 10323 GFP_KERNEL);
10311 if (!event->addr_filters_offs) { 10324 if (!event->addr_filter_ranges) {
10312 err = -ENOMEM; 10325 err = -ENOMEM;
10313 goto err_per_task; 10326 goto err_per_task;
10314 } 10327 }
@@ -10321,9 +10334,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
10321 struct perf_addr_filters_head *ifh = perf_event_addr_filters(event); 10334 struct perf_addr_filters_head *ifh = perf_event_addr_filters(event);
10322 10335
10323 raw_spin_lock_irq(&ifh->lock); 10336 raw_spin_lock_irq(&ifh->lock);
10324 memcpy(event->addr_filters_offs, 10337 memcpy(event->addr_filter_ranges,
10325 event->parent->addr_filters_offs, 10338 event->parent->addr_filter_ranges,
10326 pmu->nr_addr_filters * sizeof(unsigned long)); 10339 pmu->nr_addr_filters * sizeof(struct perf_addr_filter_range));
10327 raw_spin_unlock_irq(&ifh->lock); 10340 raw_spin_unlock_irq(&ifh->lock);
10328 } 10341 }
10329 10342
@@ -10345,7 +10358,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
10345 return event; 10358 return event;
10346 10359
10347err_addr_filters: 10360err_addr_filters:
10348 kfree(event->addr_filters_offs); 10361 kfree(event->addr_filter_ranges);
10349 10362
10350err_per_task: 10363err_per_task:
10351 exclusive_event_destroy(event); 10364 exclusive_event_destroy(event);