* [PATCH 0/2] uprobes: pre-filtering @ 2012-12-28 18:12 Oleg Nesterov 2012-12-28 18:13 ` [PATCH 1/2] uprobes: Rationalize the usage of filter_chain() Oleg Nesterov 2012-12-28 18:13 ` [PATCH 2/2] uprobes: Reintroduce uprobe_consumer->filter() Oleg Nesterov 0 siblings, 2 replies; 5+ messages in thread From: Oleg Nesterov @ 2012-12-28 18:12 UTC (permalink / raw) To: Ingo Molnar, Peter Zijlstra, Srikar Dronamraju Cc: Ananth N Mavinakayanahalli, Anton Arapov, Frank Eigler, Josh Stone, Suzuki K. Poulose, linux-kernel Hello. On top of preparations I sent a month ago. Srikar, you still didn't review the last 2 patches. I assume you do not care so I am going to add them without your ack. Initially I was going to send more patches in this series. We can add some optimizations to avoid the unnecessary register_for_each_vma() or even consumer->filter() if there is a consumer with ->filter == NULL. But lets do this later, lets discuss the functional change first. Next steps: - teach handler_chain() to remove the unwanted breakpoints - if the above is not enough, implement UPROBE_FILTER_FORK - implement uprobe_apply(consumer, task, is_register) ? Ole. ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] uprobes: Rationalize the usage of filter_chain() 2012-12-28 18:12 [PATCH 0/2] uprobes: pre-filtering Oleg Nesterov @ 2012-12-28 18:13 ` Oleg Nesterov 2013-01-03 11:56 ` Srikar Dronamraju 2012-12-28 18:13 ` [PATCH 2/2] uprobes: Reintroduce uprobe_consumer->filter() Oleg Nesterov 1 sibling, 1 reply; 5+ messages in thread From: Oleg Nesterov @ 2012-12-28 18:13 UTC (permalink / raw) To: Ingo Molnar, Peter Zijlstra, Srikar Dronamraju Cc: Ananth N Mavinakayanahalli, Anton Arapov, Frank Eigler, Josh Stone, Suzuki K. Poulose, linux-kernel filter_chain() was added into install_breakpoint/remove_breakpoint to simplify the initial changes but this is sub-optimal. This patch shifts the callsite to the callers, register_for_each_vma() and uprobe_mmap(). This way: - It will be easier to add the new arguments. This is the main reason, we can do more optimizations later. - register_for_each_vma(is_register => true) can be optimized, we only need to consult the new consumer. The previous consumers were already asked when they called uprobe_register(). This patch also moves the MMF_HAS_UPROBES check from remove_breakpoint(), this allows to avoid the potentionally costly filter_chain(). Note that register_for_each_vma(is_register => false) doesn't really need to take >consumer_rwsem, but I don't think it makes sense to optimize this and introduce filter_chain_lockless(). Signed-off-by: Oleg Nesterov <oleg@redhat.com> --- kernel/events/uprobes.c | 44 +++++++++++++++++++++----------------------- 1 files changed, 21 insertions(+), 23 deletions(-) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 105ac0d..60b0a90 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -579,6 +579,11 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, return ret; } +static inline bool consumer_filter(struct uprobe_consumer *uc) +{ + return true; /* TODO: !uc->filter || uc->filter(...) */ +} + static bool filter_chain(struct uprobe *uprobe) { struct uprobe_consumer *uc; @@ -586,8 +591,7 @@ static bool filter_chain(struct uprobe *uprobe) down_read(&uprobe->consumer_rwsem); for (uc = uprobe->consumers; uc; uc = uc->next) { - /* TODO: ret = uc->filter(...) */ - ret = true; + ret = consumer_filter(uc); if (ret) break; } @@ -603,15 +607,6 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, bool first_uprobe; int ret; - /* - * If probe is being deleted, unregister thread could be done with - * the vma-rmap-walk through. Adding a probe now can be fatal since - * nobody will be able to cleanup. But in this case filter_chain() - * must return false, all consumers have gone away. - */ - if (!filter_chain(uprobe)) - return 0; - ret = prepare_uprobe(uprobe, vma->vm_file, mm, vaddr); if (ret) return ret; @@ -636,12 +631,6 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, static int remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr) { - if (!test_bit(MMF_HAS_UPROBES, &mm->flags)) - return 0; - - if (filter_chain(uprobe)) - return 0; - set_bit(MMF_RECALC_UPROBES, &mm->flags); return set_orig_insn(&uprobe->arch, mm, vaddr); } @@ -781,10 +770,14 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) vaddr_to_offset(vma, info->vaddr) != uprobe->offset) goto unlock; - if (is_register) - err = install_breakpoint(uprobe, mm, vma, info->vaddr); - else - err |= remove_breakpoint(uprobe, mm, info->vaddr); + if (is_register) { + /* consult only the "caller", new consumer. */ + if (consumer_filter(uprobe->consumers)) + err = install_breakpoint(uprobe, mm, vma, info->vaddr); + } else if (test_bit(MMF_HAS_UPROBES, &mm->flags)) { + if (!filter_chain(uprobe)) + err |= remove_breakpoint(uprobe, mm, info->vaddr); + } unlock: up_write(&mm->mmap_sem); @@ -968,9 +961,14 @@ int uprobe_mmap(struct vm_area_struct *vma) mutex_lock(uprobes_mmap_hash(inode)); build_probe_list(inode, vma, vma->vm_start, vma->vm_end, &tmp_list); - + /* + * We can race with uprobe_unregister(), this uprobe can be already + * removed. But in this case filter_chain() must return false, all + * consumers have gone away. + */ list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { - if (!fatal_signal_pending(current)) { + if (!fatal_signal_pending(current) && + filter_chain(uprobe)) { unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset); install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); } -- 1.5.5.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] uprobes: Rationalize the usage of filter_chain() 2012-12-28 18:13 ` [PATCH 1/2] uprobes: Rationalize the usage of filter_chain() Oleg Nesterov @ 2013-01-03 11:56 ` Srikar Dronamraju 0 siblings, 0 replies; 5+ messages in thread From: Srikar Dronamraju @ 2013-01-03 11:56 UTC (permalink / raw) To: Oleg Nesterov Cc: Ingo Molnar, Peter Zijlstra, Ananth N Mavinakayanahalli, Anton Arapov, Frank Eigler, Josh Stone, Suzuki K. Poulose, linux-kernel * Oleg Nesterov <oleg@redhat.com> [2012-12-28 19:13:10]: > filter_chain() was added into install_breakpoint/remove_breakpoint to > simplify the initial changes but this is sub-optimal. > > This patch shifts the callsite to the callers, register_for_each_vma() > and uprobe_mmap(). This way: > > - It will be easier to add the new arguments. This is the main reason, > we can do more optimizations later. > > - register_for_each_vma(is_register => true) can be optimized, we only > need to consult the new consumer. The previous consumers were already > asked when they called uprobe_register(). > > This patch also moves the MMF_HAS_UPROBES check from remove_breakpoint(), > this allows to avoid the potentionally costly filter_chain(). Note that > register_for_each_vma(is_register => false) doesn't really need to take > >consumer_rwsem, but I don't think it makes sense to optimize this and > introduce filter_chain_lockless(). > > Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com> > --- > kernel/events/uprobes.c | 44 +++++++++++++++++++++----------------------- > 1 files changed, 21 insertions(+), 23 deletions(-) > > diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c > index 105ac0d..60b0a90 100644 > --- a/kernel/events/uprobes.c > +++ b/kernel/events/uprobes.c > @@ -579,6 +579,11 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, > return ret; > } > > +static inline bool consumer_filter(struct uprobe_consumer *uc) > +{ > + return true; /* TODO: !uc->filter || uc->filter(...) */ > +} > + > static bool filter_chain(struct uprobe *uprobe) > { > struct uprobe_consumer *uc; > @@ -586,8 +591,7 @@ static bool filter_chain(struct uprobe *uprobe) > > down_read(&uprobe->consumer_rwsem); > for (uc = uprobe->consumers; uc; uc = uc->next) { > - /* TODO: ret = uc->filter(...) */ > - ret = true; > + ret = consumer_filter(uc); > if (ret) > break; > } > @@ -603,15 +607,6 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, > bool first_uprobe; > int ret; > > - /* > - * If probe is being deleted, unregister thread could be done with > - * the vma-rmap-walk through. Adding a probe now can be fatal since > - * nobody will be able to cleanup. But in this case filter_chain() > - * must return false, all consumers have gone away. > - */ > - if (!filter_chain(uprobe)) > - return 0; > - > ret = prepare_uprobe(uprobe, vma->vm_file, mm, vaddr); > if (ret) > return ret; > @@ -636,12 +631,6 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, > static int > remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr) > { > - if (!test_bit(MMF_HAS_UPROBES, &mm->flags)) > - return 0; > - > - if (filter_chain(uprobe)) > - return 0; > - > set_bit(MMF_RECALC_UPROBES, &mm->flags); > return set_orig_insn(&uprobe->arch, mm, vaddr); > } > @@ -781,10 +770,14 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) > vaddr_to_offset(vma, info->vaddr) != uprobe->offset) > goto unlock; > > - if (is_register) > - err = install_breakpoint(uprobe, mm, vma, info->vaddr); > - else > - err |= remove_breakpoint(uprobe, mm, info->vaddr); > + if (is_register) { > + /* consult only the "caller", new consumer. */ > + if (consumer_filter(uprobe->consumers)) > + err = install_breakpoint(uprobe, mm, vma, info->vaddr); > + } else if (test_bit(MMF_HAS_UPROBES, &mm->flags)) { > + if (!filter_chain(uprobe)) > + err |= remove_breakpoint(uprobe, mm, info->vaddr); > + } > > unlock: > up_write(&mm->mmap_sem); > @@ -968,9 +961,14 @@ int uprobe_mmap(struct vm_area_struct *vma) > > mutex_lock(uprobes_mmap_hash(inode)); > build_probe_list(inode, vma, vma->vm_start, vma->vm_end, &tmp_list); > - > + /* > + * We can race with uprobe_unregister(), this uprobe can be already > + * removed. But in this case filter_chain() must return false, all > + * consumers have gone away. > + */ > list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { > - if (!fatal_signal_pending(current)) { > + if (!fatal_signal_pending(current) && > + filter_chain(uprobe)) { > unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset); > install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); > } > -- > 1.5.5.1 > ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2] uprobes: Reintroduce uprobe_consumer->filter() 2012-12-28 18:12 [PATCH 0/2] uprobes: pre-filtering Oleg Nesterov 2012-12-28 18:13 ` [PATCH 1/2] uprobes: Rationalize the usage of filter_chain() Oleg Nesterov @ 2012-12-28 18:13 ` Oleg Nesterov 2013-01-03 11:56 ` Srikar Dronamraju 1 sibling, 1 reply; 5+ messages in thread From: Oleg Nesterov @ 2012-12-28 18:13 UTC (permalink / raw) To: Ingo Molnar, Peter Zijlstra, Srikar Dronamraju Cc: Ananth N Mavinakayanahalli, Anton Arapov, Frank Eigler, Josh Stone, Suzuki K. Poulose, linux-kernel Finally add uprobe_consumer->filter() and change consumer_filter() to actually call this method. Note that ->filter() accepts mm_struct, not task_struct. Because: 1. We do not have for_each_mm_user(mm, task). 2. Even if we implement for_each_mm_user(), ->filter() can use it itself. 3. It is not clear who will actually need this interface to do the "nontrivial" filtering. Another argument is "enum uprobe_filter_ctx", consumer->filter() can use it to figure out why/where it was called. For example, perhaps we can add UPROBE_FILTER_PRE_REGISTER used by build_map_info() to quickly "nack" the unwanted mm's. In this case consumer should know that it is called under ->i_mmap_mutex. See the previous discussion at http://marc.info/?t=135214229700002 Perhaps we should pass more arguments, vma/vaddr? Note: this patch obviously can't help to filter out the child created by fork(), this will be addressed later. Signed-off-by: Oleg Nesterov <oleg@redhat.com> --- include/linux/uprobes.h | 9 +++++++++ kernel/events/uprobes.c | 18 +++++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 83742b9..c2df693 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -35,8 +35,17 @@ struct inode; # include <asm/uprobes.h> #endif +enum uprobe_filter_ctx { + UPROBE_FILTER_REGISTER, + UPROBE_FILTER_UNREGISTER, + UPROBE_FILTER_MMAP, +}; + struct uprobe_consumer { int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); + bool (*filter)(struct uprobe_consumer *self, + enum uprobe_filter_ctx ctx, + struct mm_struct *mm); struct uprobe_consumer *next; }; diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 60b0a90..e2ebb6f 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -579,19 +579,21 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, return ret; } -static inline bool consumer_filter(struct uprobe_consumer *uc) +static inline bool consumer_filter(struct uprobe_consumer *uc, + enum uprobe_filter_ctx ctx, struct mm_struct *mm) { - return true; /* TODO: !uc->filter || uc->filter(...) */ + return !uc->filter || uc->filter(uc, ctx, mm); } -static bool filter_chain(struct uprobe *uprobe) +static bool filter_chain(struct uprobe *uprobe, + enum uprobe_filter_ctx ctx, struct mm_struct *mm) { struct uprobe_consumer *uc; bool ret = false; down_read(&uprobe->consumer_rwsem); for (uc = uprobe->consumers; uc; uc = uc->next) { - ret = consumer_filter(uc); + ret = consumer_filter(uc, ctx, mm); if (ret) break; } @@ -772,10 +774,12 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) if (is_register) { /* consult only the "caller", new consumer. */ - if (consumer_filter(uprobe->consumers)) + if (consumer_filter(uprobe->consumers, + UPROBE_FILTER_REGISTER, mm)) err = install_breakpoint(uprobe, mm, vma, info->vaddr); } else if (test_bit(MMF_HAS_UPROBES, &mm->flags)) { - if (!filter_chain(uprobe)) + if (!filter_chain(uprobe, + UPROBE_FILTER_UNREGISTER, mm)) err |= remove_breakpoint(uprobe, mm, info->vaddr); } @@ -968,7 +972,7 @@ int uprobe_mmap(struct vm_area_struct *vma) */ list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { if (!fatal_signal_pending(current) && - filter_chain(uprobe)) { + filter_chain(uprobe, UPROBE_FILTER_MMAP, vma->vm_mm)) { unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset); install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); } -- 1.5.5.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] uprobes: Reintroduce uprobe_consumer->filter() 2012-12-28 18:13 ` [PATCH 2/2] uprobes: Reintroduce uprobe_consumer->filter() Oleg Nesterov @ 2013-01-03 11:56 ` Srikar Dronamraju 0 siblings, 0 replies; 5+ messages in thread From: Srikar Dronamraju @ 2013-01-03 11:56 UTC (permalink / raw) To: Oleg Nesterov Cc: Ingo Molnar, Peter Zijlstra, Ananth N Mavinakayanahalli, Anton Arapov, Frank Eigler, Josh Stone, Suzuki K. Poulose, linux-kernel * Oleg Nesterov <oleg@redhat.com> [2012-12-28 19:13:14]: > Finally add uprobe_consumer->filter() and change consumer_filter() > to actually call this method. > > Note that ->filter() accepts mm_struct, not task_struct. Because: > > 1. We do not have for_each_mm_user(mm, task). > > 2. Even if we implement for_each_mm_user(), ->filter() can > use it itself. > > 3. It is not clear who will actually need this interface to > do the "nontrivial" filtering. > > Another argument is "enum uprobe_filter_ctx", consumer->filter() can > use it to figure out why/where it was called. For example, perhaps > we can add UPROBE_FILTER_PRE_REGISTER used by build_map_info() to > quickly "nack" the unwanted mm's. In this case consumer should know > that it is called under ->i_mmap_mutex. > > See the previous discussion at http://marc.info/?t=135214229700002 > Perhaps we should pass more arguments, vma/vaddr? > > Note: this patch obviously can't help to filter out the child created > by fork(), this will be addressed later. > > Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com> > --- > include/linux/uprobes.h | 9 +++++++++ > kernel/events/uprobes.c | 18 +++++++++++------- > 2 files changed, 20 insertions(+), 7 deletions(-) > > diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h > index 83742b9..c2df693 100644 > --- a/include/linux/uprobes.h > +++ b/include/linux/uprobes.h > @@ -35,8 +35,17 @@ struct inode; > # include <asm/uprobes.h> > #endif > > +enum uprobe_filter_ctx { > + UPROBE_FILTER_REGISTER, > + UPROBE_FILTER_UNREGISTER, > + UPROBE_FILTER_MMAP, > +}; > + > struct uprobe_consumer { > int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); > + bool (*filter)(struct uprobe_consumer *self, > + enum uprobe_filter_ctx ctx, > + struct mm_struct *mm); > > struct uprobe_consumer *next; > }; > diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c > index 60b0a90..e2ebb6f 100644 > --- a/kernel/events/uprobes.c > +++ b/kernel/events/uprobes.c > @@ -579,19 +579,21 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, > return ret; > } > > -static inline bool consumer_filter(struct uprobe_consumer *uc) > +static inline bool consumer_filter(struct uprobe_consumer *uc, > + enum uprobe_filter_ctx ctx, struct mm_struct *mm) > { > - return true; /* TODO: !uc->filter || uc->filter(...) */ > + return !uc->filter || uc->filter(uc, ctx, mm); > } > > -static bool filter_chain(struct uprobe *uprobe) > +static bool filter_chain(struct uprobe *uprobe, > + enum uprobe_filter_ctx ctx, struct mm_struct *mm) > { > struct uprobe_consumer *uc; > bool ret = false; > > down_read(&uprobe->consumer_rwsem); > for (uc = uprobe->consumers; uc; uc = uc->next) { > - ret = consumer_filter(uc); > + ret = consumer_filter(uc, ctx, mm); > if (ret) > break; > } > @@ -772,10 +774,12 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) > > if (is_register) { > /* consult only the "caller", new consumer. */ > - if (consumer_filter(uprobe->consumers)) > + if (consumer_filter(uprobe->consumers, > + UPROBE_FILTER_REGISTER, mm)) > err = install_breakpoint(uprobe, mm, vma, info->vaddr); > } else if (test_bit(MMF_HAS_UPROBES, &mm->flags)) { > - if (!filter_chain(uprobe)) > + if (!filter_chain(uprobe, > + UPROBE_FILTER_UNREGISTER, mm)) > err |= remove_breakpoint(uprobe, mm, info->vaddr); > } > > @@ -968,7 +972,7 @@ int uprobe_mmap(struct vm_area_struct *vma) > */ > list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { > if (!fatal_signal_pending(current) && > - filter_chain(uprobe)) { > + filter_chain(uprobe, UPROBE_FILTER_MMAP, vma->vm_mm)) { > unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset); > install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); > } > -- > 1.5.5.1 > ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2013-01-03 11:56 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-12-28 18:12 [PATCH 0/2] uprobes: pre-filtering Oleg Nesterov 2012-12-28 18:13 ` [PATCH 1/2] uprobes: Rationalize the usage of filter_chain() Oleg Nesterov 2013-01-03 11:56 ` Srikar Dronamraju 2012-12-28 18:13 ` [PATCH 2/2] uprobes: Reintroduce uprobe_consumer->filter() Oleg Nesterov 2013-01-03 11:56 ` Srikar Dronamraju
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox