aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/events/uprobes.c
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2013-02-03 13:21:12 -0500
committerOleg Nesterov <oleg@redhat.com>2013-02-08 12:28:04 -0500
commitbdf8647c44766590ed02f9a84a450a796558b753 (patch)
treefb3510335d4c1ce67e109df2f80eb26c67d8b589 /kernel/events/uprobes.c
parentf22c1bb6b4706be3502b378cb14564449b15f983 (diff)
uprobes: Introduce uprobe_apply()
Currently it is not possible to change the filtering constraints after uprobe_register(), so a consumer can not, say, start to trace a task/mm which was previously filtered out, or remove the no longer needed bp's. Introduce uprobe_apply() which simply does register_for_each_vma() again to consult uprobe_consumer->filter() and install/remove the breakpoints. The only complication is that register_for_each_vma() can no longer assume that uprobe->consumers should be consulter if is_register == T, so we change it to accept "struct uprobe_consumer *new" instead. Unlike uprobe_register(), uprobe_apply(true) doesn't do "unregister" if register_for_each_vma() fails, it is up to caller to handle the error. Note: we probably need to cleanup the current interface, it is strange that uprobe_apply/unregister need inode/offset. We should either change uprobe_register() to return "struct uprobe *", or add a private ->uprobe member in uprobe_consumer. And in the long term uprobe_apply() should take a single argument, uprobe or consumer, even "bool add" should go away. Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Diffstat (limited to 'kernel/events/uprobes.c')
-rw-r--r--kernel/events/uprobes.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 221fc58f59e3..a567c8c7ef31 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -733,8 +733,10 @@ build_map_info(struct address_space *mapping, loff_t offset, bool is_register)
733 return curr; 733 return curr;
734} 734}
735 735
736static int register_for_each_vma(struct uprobe *uprobe, bool is_register) 736static int
737register_for_each_vma(struct uprobe *uprobe, struct uprobe_consumer *new)
737{ 738{
739 bool is_register = !!new;
738 struct map_info *info; 740 struct map_info *info;
739 int err = 0; 741 int err = 0;
740 742
@@ -765,7 +767,7 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
765 767
766 if (is_register) { 768 if (is_register) {
767 /* consult only the "caller", new consumer. */ 769 /* consult only the "caller", new consumer. */
768 if (consumer_filter(uprobe->consumers, 770 if (consumer_filter(new,
769 UPROBE_FILTER_REGISTER, mm)) 771 UPROBE_FILTER_REGISTER, mm))
770 err = install_breakpoint(uprobe, mm, vma, info->vaddr); 772 err = install_breakpoint(uprobe, mm, vma, info->vaddr);
771 } else if (test_bit(MMF_HAS_UPROBES, &mm->flags)) { 773 } else if (test_bit(MMF_HAS_UPROBES, &mm->flags)) {
@@ -788,7 +790,7 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
788static int __uprobe_register(struct uprobe *uprobe, struct uprobe_consumer *uc) 790static int __uprobe_register(struct uprobe *uprobe, struct uprobe_consumer *uc)
789{ 791{
790 consumer_add(uprobe, uc); 792 consumer_add(uprobe, uc);
791 return register_for_each_vma(uprobe, true); 793 return register_for_each_vma(uprobe, uc);
792} 794}
793 795
794static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc) 796static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
@@ -798,7 +800,7 @@ static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *u
798 if (!consumer_del(uprobe, uc)) /* WARN? */ 800 if (!consumer_del(uprobe, uc)) /* WARN? */
799 return; 801 return;
800 802
801 err = register_for_each_vma(uprobe, false); 803 err = register_for_each_vma(uprobe, NULL);
802 /* TODO : cant unregister? schedule a worker thread */ 804 /* TODO : cant unregister? schedule a worker thread */
803 if (!uprobe->consumers && !err) 805 if (!uprobe->consumers && !err)
804 delete_uprobe(uprobe); 806 delete_uprobe(uprobe);
@@ -855,6 +857,35 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *
855EXPORT_SYMBOL_GPL(uprobe_register); 857EXPORT_SYMBOL_GPL(uprobe_register);
856 858
857/* 859/*
860 * uprobe_apply - unregister a already registered probe.
861 * @inode: the file in which the probe has to be removed.
862 * @offset: offset from the start of the file.
863 * @uc: consumer which wants to add more or remove some breakpoints
864 * @add: add or remove the breakpoints
865 */
866int uprobe_apply(struct inode *inode, loff_t offset,
867 struct uprobe_consumer *uc, bool add)
868{
869 struct uprobe *uprobe;
870 struct uprobe_consumer *con;
871 int ret = -ENOENT;
872
873 uprobe = find_uprobe(inode, offset);
874 if (!uprobe)
875 return ret;
876
877 down_write(&uprobe->register_rwsem);
878 for (con = uprobe->consumers; con && con != uc ; con = con->next)
879 ;
880 if (con)
881 ret = register_for_each_vma(uprobe, add ? uc : NULL);
882 up_write(&uprobe->register_rwsem);
883 put_uprobe(uprobe);
884
885 return ret;
886}
887
888/*
858 * uprobe_unregister - unregister a already registered probe. 889 * uprobe_unregister - unregister a already registered probe.
859 * @inode: the file in which the probe has to be removed. 890 * @inode: the file in which the probe has to be removed.
860 * @offset: offset from the start of the file. 891 * @offset: offset from the start of the file.