Skip to content

Commit c60f83b

Browse files
virtuosoacmel
authored andcommitted
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 <[email protected]> Signed-off-by: Alexander Shishkin <[email protected]> Tested-by: Mathieu Poirier <[email protected]> Acked-by: Peter Zijlstra <[email protected]> Cc: Jiri Olsa <[email protected]> Fixes: 375637b ("perf/core: Introduce address range filtering") Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 18736ee commit c60f83b

File tree

4 files changed

+62
-42
lines changed

4 files changed

+62
-42
lines changed

arch/x86/events/intel/pt.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,8 @@ static int pt_event_addr_filters_validate(struct list_head *filters)
12231223
static void pt_event_addr_filters_sync(struct perf_event *event)
12241224
{
12251225
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;
12271228
struct pt_filters *filters = event->hw.addr_filters;
12281229
struct perf_addr_filter *filter;
12291230
int range = 0;
@@ -1232,12 +1233,12 @@ static void pt_event_addr_filters_sync(struct perf_event *event)
12321233
return;
12331234

12341235
list_for_each_entry(filter, &head->list, entry) {
1235-
if (filter->path.dentry && !offs[range]) {
1236+
if (filter->path.dentry && !fr[range].start) {
12361237
msr_a = msr_b = 0;
12371238
} else {
12381239
/* apply the offset */
1239-
msr_a = filter->offset + offs[range];
1240-
msr_b = filter->size + msr_a - 1;
1240+
msr_a = fr[range].start;
1241+
msr_b = msr_a + fr[range].size - 1;
12411242
}
12421243

12431244
filters->filter[range].msr_a = msr_a;

drivers/hwtracing/coresight/coresight-etm-perf.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,15 +433,16 @@ static int etm_addr_filters_validate(struct list_head *filters)
433433
static void etm_addr_filters_sync(struct perf_event *event)
434434
{
435435
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;
437438
struct etm_filters *filters = event->hw.addr_filters;
438439
struct etm_filter *etm_filter;
439440
struct perf_addr_filter *filter;
440441
int i = 0;
441442

442443
list_for_each_entry(filter, &head->list, entry) {
443-
start = filter->offset + offs[i];
444-
stop = start + filter->size;
444+
start = fr[i].start;
445+
stop = start + fr[i].size;
445446
etm_filter = &filters->etm_filter[i];
446447

447448
switch (filter->action) {

include/linux/perf_event.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,11 @@ struct perf_addr_filters_head {
490490
unsigned int nr_file_filters;
491491
};
492492

493+
struct perf_addr_filter_range {
494+
unsigned long start;
495+
unsigned long size;
496+
};
497+
493498
/**
494499
* enum perf_event_state - the states of an event:
495500
*/
@@ -666,7 +671,7 @@ struct perf_event {
666671
/* address range filters */
667672
struct perf_addr_filters_head addr_filters;
668673
/* vma address array for file-based filders */
669-
unsigned long *addr_filters_offs;
674+
struct perf_addr_filter_range *addr_filter_ranges;
670675
unsigned long addr_filters_gen;
671676

672677
void (*destroy)(struct perf_event *);

kernel/events/core.c

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,7 +2799,7 @@ static int perf_event_stop(struct perf_event *event, int restart)
27992799
*
28002800
* (p1) when userspace mappings change as a result of (1) or (2) or (3) below,
28012801
* 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;
28032803
* (p2) when an event is scheduled in (pmu::add), it calls
28042804
* perf_event_addr_filters_sync() which calls pmu::addr_filters_sync()
28052805
* if the generation has changed since the previous call.
@@ -4446,7 +4446,7 @@ static void _free_event(struct perf_event *event)
44464446

44474447
perf_event_free_bpf_prog(event);
44484448
perf_addr_filters_splice(event, NULL);
4449-
kfree(event->addr_filters_offs);
4449+
kfree(event->addr_filter_ranges);
44504450

44514451
if (event->destroy)
44524452
event->destroy(event);
@@ -6687,7 +6687,8 @@ static void perf_event_addr_filters_exec(struct perf_event *event, void *data)
66876687
raw_spin_lock_irqsave(&ifh->lock, flags);
66886688
list_for_each_entry(filter, &ifh->list, entry) {
66896689
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;
66916692
restart++;
66926693
}
66936694

@@ -7367,28 +7368,47 @@ static bool perf_addr_filter_match(struct perf_addr_filter *filter,
73677368
return true;
73687369
}
73697370

7371+
static 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+
73707393
static void __perf_addr_filters_adjust(struct perf_event *event, void *data)
73717394
{
73727395
struct perf_addr_filters_head *ifh = perf_event_addr_filters(event);
73737396
struct vm_area_struct *vma = data;
7374-
unsigned long off = vma->vm_pgoff << PAGE_SHIFT, flags;
7375-
struct file *file = vma->vm_file;
73767397
struct perf_addr_filter *filter;
73777398
unsigned int restart = 0, count = 0;
7399+
unsigned long flags;
73787400

73797401
if (!has_addr_filter(event))
73807402
return;
73817403

7382-
if (!file)
7404+
if (!vma->vm_file)
73837405
return;
73847406

73857407
raw_spin_lock_irqsave(&ifh->lock, flags);
73867408
list_for_each_entry(filter, &ifh->list, entry) {
7387-
if (perf_addr_filter_match(filter, file, off,
7388-
vma->vm_end - vma->vm_start)) {
7389-
event->addr_filters_offs[count] = vma->vm_start;
7409+
if (perf_addr_filter_vma_adjust(filter, vma,
7410+
&event->addr_filter_ranges[count]))
73907411
restart++;
7391-
}
73927412

73937413
count++;
73947414
}
@@ -8978,26 +8998,19 @@ static void perf_addr_filters_splice(struct perf_event *event,
89788998
* @filter; if so, adjust filter's address range.
89798999
* Called with mm::mmap_sem down for reading.
89809000
*/
8981-
static unsigned long perf_addr_filter_apply(struct perf_addr_filter *filter,
8982-
struct mm_struct *mm)
9001+
static void perf_addr_filter_apply(struct perf_addr_filter *filter,
9002+
struct mm_struct *mm,
9003+
struct perf_addr_filter_range *fr)
89839004
{
89849005
struct vm_area_struct *vma;
89859006

89869007
for (vma = mm->mmap; vma; vma = vma->vm_next) {
8987-
struct file *file = 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)
9008+
if (!vma->vm_file)
89929009
continue;
89939010

8994-
if (!perf_addr_filter_match(filter, file, off, vma_size))
8995-
continue;
8996-
8997-
return vma->vm_start;
9011+
if (perf_addr_filter_vma_adjust(filter, vma, fr))
9012+
return;
89989013
}
8999-
9000-
return 0;
90019014
}
90029015

90039016
/*
@@ -9031,15 +9044,15 @@ static void perf_event_addr_filters_apply(struct perf_event *event)
90319044

90329045
raw_spin_lock_irqsave(&ifh->lock, flags);
90339046
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;
90359049

90369050
/*
90379051
* Adjust base offset if the filter is associated to a binary
90389052
* that needs to be mapped:
90399053
*/
90409054
if (filter->path.dentry)
9041-
event->addr_filters_offs[count] =
9042-
perf_addr_filter_apply(filter, mm);
9055+
perf_addr_filter_apply(filter, mm, &event->addr_filter_ranges[count]);
90439056

90449057
count++;
90459058
}
@@ -10305,10 +10318,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
1030510318
goto err_pmu;
1030610319

1030710320
if (has_addr_filter(event)) {
10308-
event->addr_filters_offs = kcalloc(pmu->nr_addr_filters,
10309-
sizeof(unsigned long),
10310-
GFP_KERNEL);
10311-
if (!event->addr_filters_offs) {
10321+
event->addr_filter_ranges = kcalloc(pmu->nr_addr_filters,
10322+
sizeof(struct perf_addr_filter_range),
10323+
GFP_KERNEL);
10324+
if (!event->addr_filter_ranges) {
1031210325
err = -ENOMEM;
1031310326
goto err_per_task;
1031410327
}
@@ -10321,9 +10334,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
1032110334
struct perf_addr_filters_head *ifh = perf_event_addr_filters(event);
1032210335

1032310336
raw_spin_lock_irq(&ifh->lock);
10324-
memcpy(event->addr_filters_offs,
10325-
event->parent->addr_filters_offs,
10326-
pmu->nr_addr_filters * sizeof(unsigned long));
10337+
memcpy(event->addr_filter_ranges,
10338+
event->parent->addr_filter_ranges,
10339+
pmu->nr_addr_filters * sizeof(struct perf_addr_filter_range));
1032710340
raw_spin_unlock_irq(&ifh->lock);
1032810341
}
1032910342

@@ -10345,7 +10358,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
1034510358
return event;
1034610359

1034710360
err_addr_filters:
10348-
kfree(event->addr_filters_offs);
10361+
kfree(event->addr_filter_ranges);
1034910362

1035010363
err_per_task:
1035110364
exclusive_event_destroy(event);

0 commit comments

Comments
 (0)