public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
To: Steven Rostedt <rostedt@goodmis.org>
Cc: linux-kernel@vger.kernel.org, Ingo Molnar <mingo@elte.hu>,
	Andrew Morton <akpm@linux-foundation.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Peter Zijlstra <peterz@infradead.org>,
	Frederic Weisbecker <fweisbec@gmail.com>,
	Arnaldo Carvalho de Melo <acme@redhat.com>,
	Jason Baron <jbaron@redhat.com>,
	yrl.pp-manager.tt@hitachi.com
Subject: Re: [PATCH 5/5][RFC] kprobes: Use ftrace hooks when probing ftrace nops
Date: Thu, 11 Aug 2011 16:41:27 +0900	[thread overview]
Message-ID: <4E4387A7.7040200@hitachi.com> (raw)
In-Reply-To: <20110810163039.000615163@goodmis.org>

Hi Steven,

As I suggested in another reply, I'm now attracted by the idea
of using ftrace in kprobe-tracer, because of simplicity.
Anyway, here is my review.

(2011/08/11 1:22), Steven Rostedt wrote:
> From: Steven Rostedt <srostedt@redhat.com>
>
> Currently, if a probe is set at an ftrace nop, the probe will fail.
>
> When -mfentry is used by ftrace (in the near future), the nop will be
> at the beginning of the function. This will prevent kprobes from hooking
> to the most common place that kprobes are attached.
>
> Now that ftrace supports individual users as well as pt_regs passing,
> kprobes can use the ftrace infrastructure when a probe is placed on
> a ftrace nop.

My one general concern is the timing of enabling ftrace-based probe.
Breakpoint-based kprobe (including optimized kprobe) is enabled
right after registering. Users might expect that.
And AFAIK, dynamic ftraces handler will be enabled (activated)
after a while, because it has to wait for an NMI, doesn't it?

And theoretically, this ftrace-based probe can't support jprobe,
because it changes IP address. Nowadays, this may becomes minor
problem (because someone who wants to trace function args can
use kprobe-tracer), but still exist.


> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
> ---
>  include/linux/kprobes.h |    6 ++
>  kernel/kprobes.c        |  163 ++++++++++++++++++++++++++++++++++++++++++++---
>  2 files changed, 160 insertions(+), 9 deletions(-)
>
> diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
> index dd7c12e..5742071 100644
> --- a/include/linux/kprobes.h
> +++ b/include/linux/kprobes.h
> @@ -37,6 +37,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/rcupdate.h>
>  #include <linux/mutex.h>
> +#include <linux/ftrace.h>
>
>  #ifdef CONFIG_KPROBES
>  #include <asm/kprobes.h>
> @@ -112,6 +113,11 @@ struct kprobe {
>  	/* copy of the original instruction */
>  	struct arch_specific_insn ainsn;
>
> +#ifdef CONFIG_FUNCTION_TRACER
> +	/* If it is possible to use ftrace to probe */
> +	struct ftrace_ops fops;
> +#endif
> +
>  	/*
>  	 * Indicates various status flags.
>  	 * Protected by kprobe_mutex after this kprobe is registered.
> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> index e6c25eb..2160768 100644
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
> @@ -102,6 +102,31 @@ static struct kprobe_blackpoint kprobe_blacklist[] = {
>  	{NULL}    /* Terminator */
>  };
>
> +#ifdef CONFIG_FUNCTION_TRACER
> +/* kprobe stub function for use when probe is not connected to ftrace */
> +static void
> +kprobe_ftrace_stub(unsigned long ip, unsigned long pip,
> +		   struct ftrace_ops *op, struct pt_regs *pt_regs)
> +{
> +}
> +
> +static bool ftrace_kprobe(struct kprobe *p)
> +{
> +	return p->fops.func && p->fops.func != kprobe_ftrace_stub;
> +}
> +
> +static void init_non_ftrace_kprobe(struct kprobe *p)
> +{
> +	p->fops.func = kprobe_ftrace_stub;
> +}
> +
> +#else
> +static bool ftrace_kprobe(struct kprobe *p)
> +{
> +	return false;
> +}
> +#endif
> +
>  #ifdef __ARCH_WANT_KPROBES_INSN_SLOT
>  /*
>   * kprobe->ainsn.insn points to the copy of the instruction to be
> @@ -396,6 +421,9 @@ static __kprobes void free_aggr_kprobe(struct kprobe *p)
>  {
>  	struct optimized_kprobe *op;
>
> +	if (ftrace_kprobe(p))
> +		return;
> +
>  	op = container_of(p, struct optimized_kprobe, kp);
>  	arch_remove_optimized_kprobe(op);
>  	arch_remove_kprobe(p);
> @@ -759,6 +787,9 @@ static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
>  	struct kprobe *ap;
>  	struct optimized_kprobe *op;
>
> +	if (ftrace_kprobe(p))
> +		return;
> +
>  	ap = alloc_aggr_kprobe(p);
>  	if (!ap)
>  		return;
> @@ -849,6 +880,10 @@ static void __kprobes __arm_kprobe(struct kprobe *p)
>  {
>  	struct kprobe *_p;
>
> +	/* Only arm non-ftrace probes */
> +	if (ftrace_kprobe(p))
> +		return;
> +
>  	/* Check collision with other optimized kprobes */
>  	_p = get_optimized_kprobe((unsigned long)p->addr);
>  	if (unlikely(_p))
> @@ -864,6 +899,10 @@ static void __kprobes __disarm_kprobe(struct kprobe *p,
bool reopt)
>  {
>  	struct kprobe *_p;
>
> +	/* Only disarm non-ftrace probes */
> +	if (ftrace_kprobe(p))
> +		return;
> +
>  	unoptimize_kprobe(p, false);	/* Try to unoptimize */
>
>  	if (!kprobe_queued(p)) {
> @@ -878,13 +917,26 @@ static void __kprobes __disarm_kprobe(struct kprobe *p,
bool reopt)
>
>  #else /* !CONFIG_OPTPROBES */
>
> +static void __kprobes __arm_kprobe(struct kprobe *p)
> +{
> +	/* Only arm non-ftrace probes */
> +	if (!ftrace_kprobe(p))
> +		arch_arm_kprobe(p);
> +}
> +
> +/* Remove the breakpoint of a probe. Must be called with text_mutex locked */
> +static void __kprobes __disarm_kprobe(struct kprobe *p, bool reopt)
> +{
> +	/* Only disarm non-ftrace probes */
> +	if (!ftrace_kprobe(p))
> +		arch_disarm_kprobe(p);
> +}

If it ignores disabling/enabling, kprobe_ftrace_callback must
check kprobe_disabled(p) and skip it.

> +
>  #define optimize_kprobe(p)			do {} while (0)
>  #define unoptimize_kprobe(p, f)			do {} while (0)
>  #define kill_optimized_kprobe(p)		do {} while (0)
>  #define prepare_optimized_kprobe(p)		do {} while (0)
>  #define try_to_optimize_kprobe(p)		do {} while (0)
> -#define __arm_kprobe(p)				arch_arm_kprobe(p)
> -#define __disarm_kprobe(p, o)			arch_disarm_kprobe(p)
>  #define kprobe_disarmed(p)			kprobe_disabled(p)
>  #define wait_for_kprobe_optimizer()		do {} while (0)
>
> @@ -897,7 +949,9 @@ static void reuse_unused_kprobe(struct kprobe *ap)
>
>  static __kprobes void free_aggr_kprobe(struct kprobe *p)
>  {
> -	arch_remove_kprobe(p);
> +
> +	if (!ftrace_kprobe(p))
> +		arch_remove_kprobe(p);
>  	kfree(p);
>  }
>
> @@ -910,6 +964,12 @@ static __kprobes struct kprobe *alloc_aggr_kprobe(struct
kprobe *p)
>  /* Arm a kprobe with text_mutex */
>  static void __kprobes arm_kprobe(struct kprobe *kp)
>  {
> +	/* ftrace probes can skip arch calls */
> +	if (ftrace_kprobe(kp)) {
> +		register_ftrace_function(&kp->fops);
> +		return;
> +	}
> +
>  	/*
>  	 * Here, since __arm_kprobe() doesn't use stop_machine(),
>  	 * this doesn't cause deadlock on text_mutex. So, we don't
> @@ -924,6 +984,12 @@ static void __kprobes arm_kprobe(struct kprobe *kp)
>  static void __kprobes disarm_kprobe(struct kprobe *kp)
>  {
>  	/* Ditto */
> +
> +	if (ftrace_kprobe(kp)) {
> +		unregister_ftrace_function(&kp->fops);
> +		return;
> +	}
> +
>  	mutex_lock(&text_mutex);
>  	__disarm_kprobe(kp, true);
>  	mutex_unlock(&text_mutex);
> @@ -1313,6 +1379,56 @@ static inline int check_kprobe_rereg(struct kprobe *p)
>  	return ret;
>  }
>
> +#ifdef CONFIG_FUNCTION_TRACER
> +static notrace void
> +kprobe_ftrace_callback(unsigned long ip, unsigned long parent_ip,
> +		       struct ftrace_ops *op, struct pt_regs *pt_regs)
> +{
> +	struct kprobe *p = container_of(op, struct kprobe, fops);
> +

Here, we need to set up kprobe_ctlblk and some of pt_regs members,
ip, cs and orig_ax as optimized_callback()@arch/x86/kernel/kprobes.c
does.

> +	/* A nop has been trapped, just run both handlers back to back */
> +	if (p->pre_handler)
> +		p->pre_handler(p, pt_regs);

And increment regs->ip here for NOP.

> +	if (p->post_handler)
> +		p->post_handler(p, pt_regs, 0);
> +}

Anyway, above operations strongly depends on arch, so
kprobe_ftrace_callback should be moved to arch/*/kprobes.c.

And I think most of the code can be shared with optimized code.

> +
> +static int use_ftrace_hook(struct kprobe *p)
> +{
> +	char str[KSYM_SYMBOL_LEN];
> +
> +	/* see if this is an ftrace function */
> +	if (!ftrace_text_reserved(p->addr, p->addr)) {
> +		/* make sure fops->func is nop */
> +		init_non_ftrace_kprobe(p);
> +		return 0;
> +	}
> +
> +	/* If arch does not support pt_regs passing, bail */
> +	if (!ARCH_SUPPORTS_FTRACE_SAVE_REGS)
> +		return -EINVAL;

Hmm, I think this should be checked at build time...

> +
> +	/* Use ftrace hook instead */
> +
> +	memset(&p->fops, 0, sizeof(p->fops));
> +
> +	/* Find the function this is connected with this addr */
> +	kallsyms_lookup((unsigned long)p->addr, NULL, NULL, NULL, str);
> +
> +	p->fops.flags = FTRACE_OPS_FL_SAVE_REGS;
> +	p->fops.func = kprobe_ftrace_callback;
> +
> +	ftrace_set_filter(&p->fops, str, strlen(str), 1);

Hmm, IMHO, ftrace_set_filter should not be called here, because
there can be other kprobes are already registered on the same
address. In that case, it is natural that we use an aggr_kprobe
for handling several kprobes on same address. Or, kprobe hash table
will have several different probes on same address.

> +
> +	return 0;
> +}
> +#else
> +static int use_ftrace_hook(struct kprobe *p)
> +{
> +	return 0;
> +}
> +#endif
> +
>  int __kprobes register_kprobe(struct kprobe *p)
>  {
>  	int ret = 0;
> @@ -1329,11 +1445,14 @@ int __kprobes register_kprobe(struct kprobe *p)
>  	if (ret)
>  		return ret;
>
> +	ret = use_ftrace_hook(p);
> +	if (ret)
> +		return ret;
> +
>  	jump_label_lock();
>  	preempt_disable();
>  	if (!kernel_text_address((unsigned long) p->addr) ||
>  	    in_kprobes_functions((unsigned long) p->addr) ||
> -	    ftrace_text_reserved(p->addr, p->addr) ||
>  	    jump_label_text_reserved(p->addr, p->addr))
>  		goto fail_with_jump_label;
>
> @@ -1384,15 +1503,17 @@ int __kprobes register_kprobe(struct kprobe *p)
>  		goto out;
>  	}
>
> -	ret = arch_prepare_kprobe(p);
> -	if (ret)
> -		goto out;
> +	if (!ftrace_kprobe(p)) {
> +		ret = arch_prepare_kprobe(p);
> +		if (ret)
> +			goto out;
> +	}
>
>  	INIT_HLIST_NODE(&p->hlist);
>  	hlist_add_head_rcu(&p->hlist,
>  		       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
>
> -	if (!kprobes_all_disarmed && !kprobe_disabled(p))
> +	if (!ftrace_kprobe(p) && !kprobes_all_disarmed && !kprobe_disabled(p))
>  		__arm_kprobe(p);
>
>  	/* Try to optimize kprobe */
> @@ -1400,6 +1521,12 @@ int __kprobes register_kprobe(struct kprobe *p)
>
>  out:
>  	mutex_unlock(&text_mutex);
> +
> +	/* ftrace kprobes must be set outside of text_mutex */
> +	if (!ret && ftrace_kprobe(p) &&
> +	    !kprobes_all_disarmed && !kprobe_disabled(p))
> +		arm_kprobe(p);
> +
>  	put_online_cpus();
>  	jump_label_unlock();
>  	mutex_unlock(&kprobe_mutex);

After this, we must handle some fails which can happen when probing
on a module.


> @@ -2134,6 +2261,14 @@ static void __kprobes arm_all_kprobes(void)
>  	}
>  	mutex_unlock(&text_mutex);
>
> +	/* ftrace kprobes are enabled outside of text_mutex */
> +	for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
> +		head = &kprobe_table[i];
> +		hlist_for_each_entry_rcu(p, node, head, hlist)
> +			if (ftrace_kprobe(p) && !kprobe_disabled(p))
> +				arm_kprobe(p);
> +	}
> +
>  	kprobes_all_disarmed = false;
>  	printk(KERN_INFO "Kprobes globally enabled\n");
>
> @@ -2164,11 +2299,21 @@ static void __kprobes disarm_all_kprobes(void)
>  	for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
>  		head = &kprobe_table[i];
>  		hlist_for_each_entry_rcu(p, node, head, hlist) {
> -			if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p))
> +			if (!ftrace_kprobe(p) &&
> +			    !arch_trampoline_kprobe(p) && !kprobe_disabled(p))
>  				__disarm_kprobe(p, false);
>  		}
>  	}
>  	mutex_unlock(&text_mutex);
> +
> +	/* ftrace kprobes are disabled outside of text_mutex */
> +	for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
> +		head = &kprobe_table[i];
> +		hlist_for_each_entry_rcu(p, node, head, hlist) {
> +			if (ftrace_kprobe(p) && !kprobe_disabled(p))
> +				disarm_kprobe(p);
> +		}
> +	}
>  	mutex_unlock(&kprobe_mutex);
>
>  	/* Wait for disarming all kprobes by optimizer */

Thank you,

-- 
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: masami.hiramatsu.pt@hitachi.com

  reply	other threads:[~2011-08-11  7:41 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-08-10 16:22 [PATCH 0/5][RFC] kprobes/ftrace: Have kprobes use ftrace on ftrace nops Steven Rostedt
2011-08-10 16:22 ` [PATCH 1/5][RFC] tracing: Clean up tb_fmt to not give faulty compile warning Steven Rostedt
2011-08-10 16:22 ` [PATCH 2/5][RFC] ftrace: Pass ftrace_ops as third parameter to function trace Steven Rostedt
2011-08-10 16:22 ` [PATCH 3/5][RFC] ftrace: Return pt_regs to function trace callback (x86_64 only so Steven Rostedt
2011-08-11  5:55   ` Masami Hiramatsu
2011-08-11 12:59     ` Steven Rostedt
2011-08-12  0:55       ` Masami Hiramatsu
2011-08-12 13:05         ` Steven Rostedt
2011-08-10 16:22 ` [PATCH 4/5][RFC] kprobes: Inverse taking of module_mutex with kprobe_mutex Steven Rostedt
2011-08-10 16:22 ` [PATCH 5/5][RFC] kprobes: Use ftrace hooks when probing ftrace nops Steven Rostedt
2011-08-11  7:41   ` Masami Hiramatsu [this message]
2011-08-11 13:22     ` Steven Rostedt
2011-08-12  2:41       ` Masami Hiramatsu
2011-08-12  5:46       ` Ananth N Mavinakayanahalli
2011-08-12 13:14         ` Steven Rostedt
2011-08-11  0:21 ` [PATCH 0/5][RFC] kprobes/ftrace: Have kprobes use ftrace on " Masami Hiramatsu
2011-08-11  0:34   ` Steven Rostedt
2011-08-11  6:28     ` Masami Hiramatsu
2011-08-11 13:01       ` Steven Rostedt
2011-08-12  2:57         ` Masami Hiramatsu
2011-08-12 13:08           ` Steven Rostedt
2011-08-13 10:09             ` Masami Hiramatsu
2011-08-14  2:58               ` Steven Rostedt
2011-08-14 10:28                 ` Masami Hiramatsu
2011-08-15 13:06                   ` Steven Rostedt
2011-08-17 12:12                     ` Masami Hiramatsu
2011-08-18 20:06                       ` Steven Rostedt
2011-08-19  2:41                         ` Masami Hiramatsu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E4387A7.7040200@hitachi.com \
    --to=masami.hiramatsu.pt@hitachi.com \
    --cc=acme@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=fweisbec@gmail.com \
    --cc=jbaron@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@elte.hu \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    --cc=tglx@linutronix.de \
    --cc=yrl.pp-manager.tt@hitachi.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox