Commit 9861668f authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Linus Torvalds

kprobes: add (un)register_kprobes for batch registration

Introduce unregister_/register_kprobes() for kprobe batch registration.  This
can reduce waiting time for synchronized_sched() when a lot of probes have to
be unregistered at once.
Signed-off-by: default avatarMasami Hiramatsu <mhiramat@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Prasanna S Panchamukhi <prasanna@in.ibm.com>
Cc: Shaohua Li <shaohua.li@intel.com>
Cc: David Miller <davem@davemloft.net>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 99602572
...@@ -234,6 +234,8 @@ static inline struct kprobe_ctlblk *get_kprobe_ctlblk(void) ...@@ -234,6 +234,8 @@ static inline struct kprobe_ctlblk *get_kprobe_ctlblk(void)
int register_kprobe(struct kprobe *p); int register_kprobe(struct kprobe *p);
void unregister_kprobe(struct kprobe *p); void unregister_kprobe(struct kprobe *p);
int register_kprobes(struct kprobe **kps, int num);
void unregister_kprobes(struct kprobe **kps, int num);
int setjmp_pre_handler(struct kprobe *, struct pt_regs *); int setjmp_pre_handler(struct kprobe *, struct pt_regs *);
int longjmp_break_handler(struct kprobe *, struct pt_regs *); int longjmp_break_handler(struct kprobe *, struct pt_regs *);
int register_jprobe(struct jprobe *p); int register_jprobe(struct jprobe *p);
...@@ -261,9 +263,16 @@ static inline int register_kprobe(struct kprobe *p) ...@@ -261,9 +263,16 @@ static inline int register_kprobe(struct kprobe *p)
{ {
return -ENOSYS; return -ENOSYS;
} }
static inline int register_kprobes(struct kprobe **kps, int num)
{
return -ENOSYS;
}
static inline void unregister_kprobe(struct kprobe *p) static inline void unregister_kprobe(struct kprobe *p)
{ {
} }
static inline void unregister_kprobes(struct kprobe **kps, int num)
{
}
static inline int register_jprobe(struct jprobe *p) static inline int register_jprobe(struct jprobe *p)
{ {
return -ENOSYS; return -ENOSYS;
......
...@@ -580,6 +580,7 @@ static int __kprobes __register_kprobe(struct kprobe *p, ...@@ -580,6 +580,7 @@ static int __kprobes __register_kprobe(struct kprobe *p,
} }
p->nmissed = 0; p->nmissed = 0;
INIT_LIST_HEAD(&p->list);
mutex_lock(&kprobe_mutex); mutex_lock(&kprobe_mutex);
old_p = get_kprobe(p->addr); old_p = get_kprobe(p->addr);
if (old_p) { if (old_p) {
...@@ -606,35 +607,28 @@ static int __kprobes __register_kprobe(struct kprobe *p, ...@@ -606,35 +607,28 @@ static int __kprobes __register_kprobe(struct kprobe *p,
return ret; return ret;
} }
int __kprobes register_kprobe(struct kprobe *p) /*
{ * Unregister a kprobe without a scheduler synchronization.
return __register_kprobe(p, (unsigned long)__builtin_return_address(0)); */
} static int __kprobes __unregister_kprobe_top(struct kprobe *p)
void __kprobes unregister_kprobe(struct kprobe *p)
{ {
struct module *mod;
struct kprobe *old_p, *list_p; struct kprobe *old_p, *list_p;
int cleanup_p;
mutex_lock(&kprobe_mutex);
old_p = get_kprobe(p->addr); old_p = get_kprobe(p->addr);
if (unlikely(!old_p)) { if (unlikely(!old_p))
mutex_unlock(&kprobe_mutex); return -EINVAL;
return;
}
if (p != old_p) { if (p != old_p) {
list_for_each_entry_rcu(list_p, &old_p->list, list) list_for_each_entry_rcu(list_p, &old_p->list, list)
if (list_p == p) if (list_p == p)
/* kprobe p is a valid probe */ /* kprobe p is a valid probe */
goto valid_p; goto valid_p;
mutex_unlock(&kprobe_mutex); return -EINVAL;
return;
} }
valid_p: valid_p:
if (old_p == p || if (old_p == p ||
(old_p->pre_handler == aggr_pre_handler && (old_p->pre_handler == aggr_pre_handler &&
p->list.next == &old_p->list && p->list.prev == &old_p->list)) { list_is_singular(&old_p->list))) {
/* /*
* Only probe on the hash list. Disarm only if kprobes are * Only probe on the hash list. Disarm only if kprobes are
* enabled - otherwise, the breakpoint would already have * enabled - otherwise, the breakpoint would already have
...@@ -643,43 +637,97 @@ void __kprobes unregister_kprobe(struct kprobe *p) ...@@ -643,43 +637,97 @@ void __kprobes unregister_kprobe(struct kprobe *p)
if (kprobe_enabled) if (kprobe_enabled)
arch_disarm_kprobe(p); arch_disarm_kprobe(p);
hlist_del_rcu(&old_p->hlist); hlist_del_rcu(&old_p->hlist);
cleanup_p = 1;
} else { } else {
if (p->break_handler)
old_p->break_handler = NULL;
if (p->post_handler) {
list_for_each_entry_rcu(list_p, &old_p->list, list) {
if ((list_p != p) && (list_p->post_handler))
goto noclean;
}
old_p->post_handler = NULL;
}
noclean:
list_del_rcu(&p->list); list_del_rcu(&p->list);
cleanup_p = 0;
} }
return 0;
}
mutex_unlock(&kprobe_mutex); static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
{
struct module *mod;
struct kprobe *old_p;
synchronize_sched();
if (p->mod_refcounted) { if (p->mod_refcounted) {
mod = module_text_address((unsigned long)p->addr); mod = module_text_address((unsigned long)p->addr);
if (mod) if (mod)
module_put(mod); module_put(mod);
} }
if (cleanup_p) { if (list_empty(&p->list) || list_is_singular(&p->list)) {
if (p != old_p) { if (!list_empty(&p->list)) {
list_del_rcu(&p->list); /* "p" is the last child of an aggr_kprobe */
old_p = list_entry(p->list.next, struct kprobe, list);
list_del(&p->list);
kfree(old_p); kfree(old_p);
} }
arch_remove_kprobe(p); arch_remove_kprobe(p);
} else { }
mutex_lock(&kprobe_mutex); }
if (p->break_handler)
old_p->break_handler = NULL; static int __register_kprobes(struct kprobe **kps, int num,
if (p->post_handler){ unsigned long called_from)
list_for_each_entry_rcu(list_p, &old_p->list, list){ {
if (list_p->post_handler){ int i, ret = 0;
cleanup_p = 2;
break; if (num <= 0)
} return -EINVAL;
} for (i = 0; i < num; i++) {
if (cleanup_p == 0) ret = __register_kprobe(kps[i], called_from);
old_p->post_handler = NULL; if (ret < 0 && i > 0) {
unregister_kprobes(kps, i);
break;
} }
mutex_unlock(&kprobe_mutex);
} }
return ret;
}
/*
* Registration and unregistration functions for kprobe.
*/
int __kprobes register_kprobe(struct kprobe *p)
{
return __register_kprobes(&p, 1,
(unsigned long)__builtin_return_address(0));
}
void __kprobes unregister_kprobe(struct kprobe *p)
{
unregister_kprobes(&p, 1);
}
int __kprobes register_kprobes(struct kprobe **kps, int num)
{
return __register_kprobes(kps, num,
(unsigned long)__builtin_return_address(0));
}
void __kprobes unregister_kprobes(struct kprobe **kps, int num)
{
int i;
if (num <= 0)
return;
mutex_lock(&kprobe_mutex);
for (i = 0; i < num; i++)
if (__unregister_kprobe_top(kps[i]) < 0)
kps[i]->addr = NULL;
mutex_unlock(&kprobe_mutex);
synchronize_sched();
for (i = 0; i < num; i++)
if (kps[i]->addr)
__unregister_kprobe_bottom(kps[i]);
} }
static struct notifier_block kprobe_exceptions_nb = { static struct notifier_block kprobe_exceptions_nb = {
...@@ -1118,6 +1166,8 @@ module_init(init_kprobes); ...@@ -1118,6 +1166,8 @@ module_init(init_kprobes);
EXPORT_SYMBOL_GPL(register_kprobe); EXPORT_SYMBOL_GPL(register_kprobe);
EXPORT_SYMBOL_GPL(unregister_kprobe); EXPORT_SYMBOL_GPL(unregister_kprobe);
EXPORT_SYMBOL_GPL(register_kprobes);
EXPORT_SYMBOL_GPL(unregister_kprobes);
EXPORT_SYMBOL_GPL(register_jprobe); EXPORT_SYMBOL_GPL(register_jprobe);
EXPORT_SYMBOL_GPL(unregister_jprobe); EXPORT_SYMBOL_GPL(unregister_jprobe);
#ifdef CONFIG_KPROBES #ifdef CONFIG_KPROBES
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment