diff options
| author | Oleg Nesterov <oleg@redhat.com> | 2012-07-29 14:22:40 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2012-07-30 05:27:23 -0400 |
| commit | 891c39708144bbe401b4aa151bffd0fe41b1dafd (patch) | |
| tree | 4d49f2169a6f1e7051fd1b88a8a0fbe612a9224e /kernel/events | |
| parent | 89133786f9408d53361874a8c784fff150fc7f7c (diff) | |
uprobes: Teach build_probe_list() to consider the range
Currently build_probe_list() builds the list of all uprobes
attached to the given inode, and the caller should filter out
those who don't fall into the [start,end) range, this is
sub-optimal.
This patch turns find_least_offset_node() into
find_node_in_range() which returns the first node inside the
[min,max] range, and changes build_probe_list() to use this node
as a starting point for rb_prev() and rb_next() to find all
other nodes the caller needs. The resulting list is no longer
sorted but we do not care.
This can speed up both build_probe_list() and the callers, but
there is another reason to introduce find_node_in_range(). It
can be used to figure out whether the given vma has uprobes or
not, this will be needed soon.
While at it, shift INIT_LIST_HEAD(tmp_list) into
build_probe_list().
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Srikar Dronamraju <srikar.vnet.ibm.com>
Cc: Anton Arapov <anton@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/20120729182240.GA20352@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/events')
| -rw-r--r-- | kernel/events/uprobes.c | 103 |
1 files changed, 50 insertions, 53 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index fb961d5e205c..2ef123306183 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
| @@ -939,59 +939,66 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume | |||
| 939 | put_uprobe(uprobe); | 939 | put_uprobe(uprobe); |
| 940 | } | 940 | } |
| 941 | 941 | ||
| 942 | /* | 942 | static struct rb_node * |
| 943 | * Of all the nodes that correspond to the given inode, return the node | 943 | find_node_in_range(struct inode *inode, loff_t min, loff_t max) |
| 944 | * with the least offset. | ||
| 945 | */ | ||
| 946 | static struct rb_node *find_least_offset_node(struct inode *inode) | ||
| 947 | { | 944 | { |
| 948 | struct uprobe u = { .inode = inode, .offset = 0}; | ||
| 949 | struct rb_node *n = uprobes_tree.rb_node; | 945 | struct rb_node *n = uprobes_tree.rb_node; |
| 950 | struct rb_node *close_node = NULL; | ||
| 951 | struct uprobe *uprobe; | ||
| 952 | int match; | ||
| 953 | 946 | ||
| 954 | while (n) { | 947 | while (n) { |
| 955 | uprobe = rb_entry(n, struct uprobe, rb_node); | 948 | struct uprobe *u = rb_entry(n, struct uprobe, rb_node); |
| 956 | match = match_uprobe(&u, uprobe); | ||
| 957 | |||
| 958 | if (uprobe->inode == inode) | ||
| 959 | close_node = n; | ||
| 960 | 949 | ||
| 961 | if (!match) | 950 | if (inode < u->inode) { |
| 962 | return close_node; | ||
| 963 | |||
| 964 | if (match < 0) | ||
| 965 | n = n->rb_left; | 951 | n = n->rb_left; |
| 966 | else | 952 | } else if (inode > u->inode) { |
| 967 | n = n->rb_right; | 953 | n = n->rb_right; |
| 954 | } else { | ||
| 955 | if (max < u->offset) | ||
| 956 | n = n->rb_left; | ||
| 957 | else if (min > u->offset) | ||
| 958 | n = n->rb_right; | ||
| 959 | else | ||
| 960 | break; | ||
| 961 | } | ||
| 968 | } | 962 | } |
| 969 | 963 | ||
| 970 | return close_node; | 964 | return n; |
| 971 | } | 965 | } |
| 972 | 966 | ||
| 973 | /* | 967 | /* |
| 974 | * For a given inode, build a list of probes that need to be inserted. | 968 | * For a given range in vma, build a list of probes that need to be inserted. |
| 975 | */ | 969 | */ |
| 976 | static void build_probe_list(struct inode *inode, struct list_head *head) | 970 | static void build_probe_list(struct inode *inode, |
| 971 | struct vm_area_struct *vma, | ||
| 972 | unsigned long start, unsigned long end, | ||
| 973 | struct list_head *head) | ||
| 977 | { | 974 | { |
| 978 | struct uprobe *uprobe; | 975 | loff_t min, max; |
| 979 | unsigned long flags; | 976 | unsigned long flags; |
| 980 | struct rb_node *n; | 977 | struct rb_node *n, *t; |
| 981 | 978 | struct uprobe *u; | |
| 982 | spin_lock_irqsave(&uprobes_treelock, flags); | ||
| 983 | |||
| 984 | n = find_least_offset_node(inode); | ||
| 985 | 979 | ||
| 986 | for (; n; n = rb_next(n)) { | 980 | INIT_LIST_HEAD(head); |
| 987 | uprobe = rb_entry(n, struct uprobe, rb_node); | 981 | min = ((loff_t)vma->vm_pgoff << PAGE_SHIFT) + start - vma->vm_start; |
| 988 | if (uprobe->inode != inode) | 982 | max = min + (end - start) - 1; |
| 989 | break; | ||
| 990 | 983 | ||
| 991 | list_add(&uprobe->pending_list, head); | 984 | spin_lock_irqsave(&uprobes_treelock, flags); |
| 992 | atomic_inc(&uprobe->ref); | 985 | n = find_node_in_range(inode, min, max); |
| 986 | if (n) { | ||
| 987 | for (t = n; t; t = rb_prev(t)) { | ||
| 988 | u = rb_entry(t, struct uprobe, rb_node); | ||
| 989 | if (u->inode != inode || u->offset < min) | ||
| 990 | break; | ||
| 991 | list_add(&u->pending_list, head); | ||
| 992 | atomic_inc(&u->ref); | ||
| 993 | } | ||
| 994 | for (t = n; (t = rb_next(t)); ) { | ||
| 995 | u = rb_entry(t, struct uprobe, rb_node); | ||
| 996 | if (u->inode != inode || u->offset > max) | ||
| 997 | break; | ||
| 998 | list_add(&u->pending_list, head); | ||
| 999 | atomic_inc(&u->ref); | ||
| 1000 | } | ||
| 993 | } | 1001 | } |
| 994 | |||
| 995 | spin_unlock_irqrestore(&uprobes_treelock, flags); | 1002 | spin_unlock_irqrestore(&uprobes_treelock, flags); |
| 996 | } | 1003 | } |
| 997 | 1004 | ||
| @@ -1021,9 +1028,8 @@ int uprobe_mmap(struct vm_area_struct *vma) | |||
| 1021 | if (!inode) | 1028 | if (!inode) |
| 1022 | return 0; | 1029 | return 0; |
| 1023 | 1030 | ||
| 1024 | INIT_LIST_HEAD(&tmp_list); | ||
| 1025 | mutex_lock(uprobes_mmap_hash(inode)); | 1031 | mutex_lock(uprobes_mmap_hash(inode)); |
| 1026 | build_probe_list(inode, &tmp_list); | 1032 | build_probe_list(inode, vma, vma->vm_start, vma->vm_end, &tmp_list); |
| 1027 | 1033 | ||
| 1028 | ret = 0; | 1034 | ret = 0; |
| 1029 | count = 0; | 1035 | count = 0; |
| @@ -1032,11 +1038,6 @@ int uprobe_mmap(struct vm_area_struct *vma) | |||
| 1032 | if (!ret) { | 1038 | if (!ret) { |
| 1033 | loff_t vaddr = vma_address(vma, uprobe->offset); | 1039 | loff_t vaddr = vma_address(vma, uprobe->offset); |
| 1034 | 1040 | ||
| 1035 | if (vaddr < vma->vm_start || vaddr >= vma->vm_end) { | ||
| 1036 | put_uprobe(uprobe); | ||
| 1037 | continue; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); | 1041 | ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); |
| 1041 | /* | 1042 | /* |
| 1042 | * We can race against uprobe_register(), see the | 1043 | * We can race against uprobe_register(), see the |
| @@ -1092,21 +1093,17 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon | |||
| 1092 | if (!inode) | 1093 | if (!inode) |
| 1093 | return; | 1094 | return; |
| 1094 | 1095 | ||
| 1095 | INIT_LIST_HEAD(&tmp_list); | ||
| 1096 | mutex_lock(uprobes_mmap_hash(inode)); | 1096 | mutex_lock(uprobes_mmap_hash(inode)); |
| 1097 | build_probe_list(inode, &tmp_list); | 1097 | build_probe_list(inode, vma, start, end, &tmp_list); |
| 1098 | 1098 | ||
| 1099 | list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { | 1099 | list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { |
| 1100 | loff_t vaddr = vma_address(vma, uprobe->offset); | 1100 | loff_t vaddr = vma_address(vma, uprobe->offset); |
| 1101 | 1101 | /* | |
| 1102 | if (vaddr >= start && vaddr < end) { | 1102 | * An unregister could have removed the probe before |
| 1103 | /* | 1103 | * unmap. So check before we decrement the count. |
| 1104 | * An unregister could have removed the probe before | 1104 | */ |
| 1105 | * unmap. So check before we decrement the count. | 1105 | if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1) |
| 1106 | */ | 1106 | atomic_dec(&vma->vm_mm->uprobes_state.count); |
| 1107 | if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1) | ||
| 1108 | atomic_dec(&vma->vm_mm->uprobes_state.count); | ||
| 1109 | } | ||
| 1110 | put_uprobe(uprobe); | 1107 | put_uprobe(uprobe); |
| 1111 | } | 1108 | } |
| 1112 | mutex_unlock(uprobes_mmap_hash(inode)); | 1109 | mutex_unlock(uprobes_mmap_hash(inode)); |
