* [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
* [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 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
* 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