Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Alexander Shishkin and committed by
Arnaldo Carvalho de Melo
c60f83b8 18736eef

+62 -42
+5 -4
arch/x86/events/intel/pt.c
··· 1223 1223 static void pt_event_addr_filters_sync(struct perf_event *event) 1224 1224 { 1225 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 1228 struct pt_filters *filters = event->hw.addr_filters; 1228 1229 struct perf_addr_filter *filter; 1229 1230 int range = 0; ··· 1233 1232 return; 1234 1233 1235 1234 list_for_each_entry(filter, &head->list, entry) { 1236 - if (filter->path.dentry && !offs[range]) { 1235 + if (filter->path.dentry && !fr[range].start) { 1237 1236 msr_a = msr_b = 0; 1238 1237 } else { 1239 1238 /* apply the offset */ 1240 - msr_a = filter->offset + offs[range]; 1241 - msr_b = filter->size + msr_a - 1; 1239 + msr_a = fr[range].start; 1240 + msr_b = msr_a + fr[range].size - 1; 1242 1241 } 1243 1242 1244 1243 filters->filter[range].msr_a = msr_a;
+4 -3
drivers/hwtracing/coresight/coresight-etm-perf.c
··· 433 433 static void etm_addr_filters_sync(struct perf_event *event) 434 434 { 435 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 438 struct etm_filters *filters = event->hw.addr_filters; 438 439 struct etm_filter *etm_filter; 439 440 struct perf_addr_filter *filter; 440 441 int i = 0; 441 442 442 443 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; 445 446 etm_filter = &filters->etm_filter[i]; 446 447 447 448 switch (filter->action) {
+6 -1
include/linux/perf_event.h
··· 490 490 unsigned int nr_file_filters; 491 491 }; 492 492 493 + struct perf_addr_filter_range { 494 + unsigned long start; 495 + unsigned long size; 496 + }; 497 + 493 498 /** 494 499 * enum perf_event_state - the states of an event: 495 500 */ ··· 671 666 /* address range filters */ 672 667 struct perf_addr_filters_head addr_filters; 673 668 /* vma address array for file-based filders */ 674 - unsigned long *addr_filters_offs; 669 + struct perf_addr_filter_range *addr_filter_ranges; 675 670 unsigned long addr_filters_gen; 676 671 677 672 void (*destroy)(struct perf_event *);
+47 -34
kernel/events/core.c
··· 2799 2799 * 2800 2800 * (p1) when userspace mappings change as a result of (1) or (2) or (3) below, 2801 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 2803 * (p2) when an event is scheduled in (pmu::add), it calls 2804 2804 * perf_event_addr_filters_sync() which calls pmu::addr_filters_sync() 2805 2805 * if the generation has changed since the previous call. ··· 4446 4446 4447 4447 perf_event_free_bpf_prog(event); 4448 4448 perf_addr_filters_splice(event, NULL); 4449 - kfree(event->addr_filters_offs); 4449 + kfree(event->addr_filter_ranges); 4450 4450 4451 4451 if (event->destroy) 4452 4452 event->destroy(event); ··· 6687 6687 raw_spin_lock_irqsave(&ifh->lock, flags); 6688 6688 list_for_each_entry(filter, &ifh->list, entry) { 6689 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 6692 restart++; 6692 6693 } 6693 6694 ··· 7368 7367 return true; 7369 7368 } 7370 7369 7370 + static bool perf_addr_filter_vma_adjust(struct perf_addr_filter *filter, 7371 + struct vm_area_struct *vma, 7372 + struct perf_addr_filter_range *fr) 7373 + { 7374 + unsigned long vma_size = vma->vm_end - vma->vm_start; 7375 + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; 7376 + struct file *file = vma->vm_file; 7377 + 7378 + if (!perf_addr_filter_match(filter, file, off, vma_size)) 7379 + return false; 7380 + 7381 + if (filter->offset < off) { 7382 + fr->start = vma->vm_start; 7383 + fr->size = min(vma_size, filter->size - (off - filter->offset)); 7384 + } else { 7385 + fr->start = vma->vm_start + filter->offset - off; 7386 + fr->size = min(vma->vm_end - fr->start, filter->size); 7387 + } 7388 + 7389 + return true; 7390 + } 7391 + 7371 7392 static void __perf_addr_filters_adjust(struct perf_event *event, void *data) 7372 7393 { 7373 7394 struct perf_addr_filters_head *ifh = perf_event_addr_filters(event); 7374 7395 struct vm_area_struct *vma = data; 7375 - unsigned long off = vma->vm_pgoff << PAGE_SHIFT, flags; 7376 - struct file *file = vma->vm_file; 7377 7396 struct perf_addr_filter *filter; 7378 7397 unsigned int restart = 0, count = 0; 7398 + unsigned long flags; 7379 7399 7380 7400 if (!has_addr_filter(event)) 7381 7401 return; 7382 7402 7383 - if (!file) 7403 + if (!vma->vm_file) 7384 7404 return; 7385 7405 7386 7406 raw_spin_lock_irqsave(&ifh->lock, flags); 7387 7407 list_for_each_entry(filter, &ifh->list, entry) { 7388 - if (perf_addr_filter_match(filter, file, off, 7389 - vma->vm_end - vma->vm_start)) { 7390 - event->addr_filters_offs[count] = vma->vm_start; 7408 + if (perf_addr_filter_vma_adjust(filter, vma, 7409 + &event->addr_filter_ranges[count])) 7391 7410 restart++; 7392 - } 7393 7411 7394 7412 count++; 7395 7413 } ··· 8998 8978 * @filter; if so, adjust filter's address range. 8999 8979 * Called with mm::mmap_sem down for reading. 9000 8980 */ 9001 - static unsigned long perf_addr_filter_apply(struct perf_addr_filter *filter, 9002 - struct mm_struct *mm) 8981 + static void perf_addr_filter_apply(struct perf_addr_filter *filter, 8982 + struct mm_struct *mm, 8983 + struct perf_addr_filter_range *fr) 9003 8984 { 9004 8985 struct vm_area_struct *vma; 9005 8986 9006 8987 for (vma = mm->mmap; vma; vma = vma->vm_next) { 9007 - struct file *file = vma->vm_file; 9008 - unsigned long off = vma->vm_pgoff << PAGE_SHIFT; 9009 - unsigned long vma_size = vma->vm_end - vma->vm_start; 9010 - 9011 - if (!file) 8988 + if (!vma->vm_file) 9012 8989 continue; 9013 8990 9014 - if (!perf_addr_filter_match(filter, file, off, vma_size)) 9015 - continue; 9016 - 9017 - return vma->vm_start; 8991 + if (perf_addr_filter_vma_adjust(filter, vma, fr)) 8992 + return; 9018 8993 } 9019 - 9020 - return 0; 9021 8994 } 9022 8995 9023 8996 /* ··· 9044 9031 9045 9032 raw_spin_lock_irqsave(&ifh->lock, flags); 9046 9033 list_for_each_entry(filter, &ifh->list, entry) { 9047 - event->addr_filters_offs[count] = 0; 9034 + event->addr_filter_ranges[count].start = 0; 9035 + event->addr_filter_ranges[count].size = 0; 9048 9036 9049 9037 /* 9050 9038 * Adjust base offset if the filter is associated to a binary 9051 9039 * that needs to be mapped: 9052 9040 */ 9053 9041 if (filter->path.dentry) 9054 - event->addr_filters_offs[count] = 9055 - perf_addr_filter_apply(filter, mm); 9042 + perf_addr_filter_apply(filter, mm, &event->addr_filter_ranges[count]); 9056 9043 9057 9044 count++; 9058 9045 } ··· 10318 10305 goto err_pmu; 10319 10306 10320 10307 if (has_addr_filter(event)) { 10321 - event->addr_filters_offs = kcalloc(pmu->nr_addr_filters, 10322 - sizeof(unsigned long), 10323 - GFP_KERNEL); 10324 - if (!event->addr_filters_offs) { 10308 + event->addr_filter_ranges = kcalloc(pmu->nr_addr_filters, 10309 + sizeof(struct perf_addr_filter_range), 10310 + GFP_KERNEL); 10311 + if (!event->addr_filter_ranges) { 10325 10312 err = -ENOMEM; 10326 10313 goto err_per_task; 10327 10314 } ··· 10334 10321 struct perf_addr_filters_head *ifh = perf_event_addr_filters(event); 10335 10322 10336 10323 raw_spin_lock_irq(&ifh->lock); 10337 - memcpy(event->addr_filters_offs, 10338 - event->parent->addr_filters_offs, 10339 - pmu->nr_addr_filters * sizeof(unsigned long)); 10324 + memcpy(event->addr_filter_ranges, 10325 + event->parent->addr_filter_ranges, 10326 + pmu->nr_addr_filters * sizeof(struct perf_addr_filter_range)); 10340 10327 raw_spin_unlock_irq(&ifh->lock); 10341 10328 } 10342 10329 ··· 10358 10345 return event; 10359 10346 10360 10347 err_addr_filters: 10361 - kfree(event->addr_filters_offs); 10348 + kfree(event->addr_filter_ranges); 10362 10349 10363 10350 err_per_task: 10364 10351 exclusive_event_destroy(event);