diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-02-03 13:21:12 -0500 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2013-02-08 12:28:04 -0500 |
commit | bdf8647c44766590ed02f9a84a450a796558b753 (patch) | |
tree | fb3510335d4c1ce67e109df2f80eb26c67d8b589 /kernel/events | |
parent | f22c1bb6b4706be3502b378cb14564449b15f983 (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')
-rw-r--r-- | kernel/events/uprobes.c | 39 |
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 | ||
736 | static int register_for_each_vma(struct uprobe *uprobe, bool is_register) | 736 | static int |
737 | register_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) | |||
788 | static int __uprobe_register(struct uprobe *uprobe, struct uprobe_consumer *uc) | 790 | static 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 | ||
794 | static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc) | 796 | static 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 * | |||
855 | EXPORT_SYMBOL_GPL(uprobe_register); | 857 | EXPORT_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 | */ | ||
866 | int 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. |