Commit 736288ba5016e255869c26296014eeff649971c2
1 parent
bdf8647c44
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
uprobes/perf: Teach trace_uprobe/perf code to track the active perf_event's
Introduce "struct trace_uprobe_filter" which records the "active" perf_event's attached to ftrace_event_call. For the start we simply use list_head, we can optimize this later if needed. For example, we do not really need to record an event with ->parent != NULL, we can rely on parent->child_list. And we can certainly do some optimizations for the case when 2 events have the same ->tp_target or tp_target->mm. Change trace_uprobe_register() to process TRACE_REG_PERF_OPEN/CLOSE and add/del this perf_event to the list. We can probably avoid any locking, but lets start with the "obvioulsy correct" trace_uprobe_filter->rwlock which protects everything. Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Showing 1 changed file with 55 additions and 0 deletions Side-by-side Diff
kernel/trace/trace_uprobe.c
... | ... | @@ -28,6 +28,12 @@ |
28 | 28 | |
29 | 29 | #define UPROBE_EVENT_SYSTEM "uprobes" |
30 | 30 | |
31 | +struct trace_uprobe_filter { | |
32 | + rwlock_t rwlock; | |
33 | + int nr_systemwide; | |
34 | + struct list_head perf_events; | |
35 | +}; | |
36 | + | |
31 | 37 | /* |
32 | 38 | * uprobe event core functions |
33 | 39 | */ |
... | ... | @@ -35,6 +41,7 @@ |
35 | 41 | struct list_head list; |
36 | 42 | struct ftrace_event_class class; |
37 | 43 | struct ftrace_event_call call; |
44 | + struct trace_uprobe_filter filter; | |
38 | 45 | struct uprobe_consumer consumer; |
39 | 46 | struct inode *inode; |
40 | 47 | char *filename; |
... | ... | @@ -58,6 +65,18 @@ |
58 | 65 | |
59 | 66 | static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs); |
60 | 67 | |
68 | +static inline void init_trace_uprobe_filter(struct trace_uprobe_filter *filter) | |
69 | +{ | |
70 | + rwlock_init(&filter->rwlock); | |
71 | + filter->nr_systemwide = 0; | |
72 | + INIT_LIST_HEAD(&filter->perf_events); | |
73 | +} | |
74 | + | |
75 | +static inline bool uprobe_filter_is_empty(struct trace_uprobe_filter *filter) | |
76 | +{ | |
77 | + return !filter->nr_systemwide && list_empty(&filter->perf_events); | |
78 | +} | |
79 | + | |
61 | 80 | /* |
62 | 81 | * Allocate new trace_uprobe and initialize it (including uprobes). |
63 | 82 | */ |
... | ... | @@ -87,6 +106,7 @@ |
87 | 106 | |
88 | 107 | INIT_LIST_HEAD(&tu->list); |
89 | 108 | tu->consumer.handler = uprobe_dispatcher; |
109 | + init_trace_uprobe_filter(&tu->filter); | |
90 | 110 | return tu; |
91 | 111 | |
92 | 112 | error: |
... | ... | @@ -544,6 +564,8 @@ |
544 | 564 | if (is_trace_uprobe_enabled(tu)) |
545 | 565 | return -EINTR; |
546 | 566 | |
567 | + WARN_ON(!uprobe_filter_is_empty(&tu->filter)); | |
568 | + | |
547 | 569 | tu->flags |= flag; |
548 | 570 | ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); |
549 | 571 | if (ret) |
... | ... | @@ -557,6 +579,8 @@ |
557 | 579 | if (!is_trace_uprobe_enabled(tu)) |
558 | 580 | return; |
559 | 581 | |
582 | + WARN_ON(!uprobe_filter_is_empty(&tu->filter)); | |
583 | + | |
560 | 584 | uprobe_unregister(tu->inode, tu->offset, &tu->consumer); |
561 | 585 | tu->flags &= ~flag; |
562 | 586 | } |
... | ... | @@ -632,6 +656,30 @@ |
632 | 656 | } |
633 | 657 | |
634 | 658 | #ifdef CONFIG_PERF_EVENTS |
659 | +static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event) | |
660 | +{ | |
661 | + write_lock(&tu->filter.rwlock); | |
662 | + if (event->hw.tp_target) | |
663 | + list_add(&event->hw.tp_list, &tu->filter.perf_events); | |
664 | + else | |
665 | + tu->filter.nr_systemwide++; | |
666 | + write_unlock(&tu->filter.rwlock); | |
667 | + | |
668 | + return 0; | |
669 | +} | |
670 | + | |
671 | +static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event) | |
672 | +{ | |
673 | + write_lock(&tu->filter.rwlock); | |
674 | + if (event->hw.tp_target) | |
675 | + list_del(&event->hw.tp_list); | |
676 | + else | |
677 | + tu->filter.nr_systemwide--; | |
678 | + write_unlock(&tu->filter.rwlock); | |
679 | + | |
680 | + return 0; | |
681 | +} | |
682 | + | |
635 | 683 | /* uprobe profile handler */ |
636 | 684 | static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) |
637 | 685 | { |
... | ... | @@ -687,6 +735,13 @@ |
687 | 735 | case TRACE_REG_PERF_UNREGISTER: |
688 | 736 | probe_event_disable(tu, TP_FLAG_PROFILE); |
689 | 737 | return 0; |
738 | + | |
739 | + case TRACE_REG_PERF_OPEN: | |
740 | + return uprobe_perf_open(tu, data); | |
741 | + | |
742 | + case TRACE_REG_PERF_CLOSE: | |
743 | + return uprobe_perf_close(tu, data); | |
744 | + | |
690 | 745 | #endif |
691 | 746 | default: |
692 | 747 | return 0; |