kprobes: add (un)register_kretprobes for batch registration
authorMasami Hiramatsu <mhiramat@redhat.com>
Mon, 28 Apr 2008 09:14:29 +0000 (02:14 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 28 Apr 2008 15:58:32 +0000 (08:58 -0700)
Introduce unregister_/register_kretprobes() for kretprobe batch registration.

Signed-off-by: Masami 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/kprobes.h
kernel/kprobes.c

index 2ba7df645a84e7f0243534244ea4da8846372074..94c855a236ae66b8424a9bfe26727f5c17f57c24 100644 (file)
@@ -245,6 +245,8 @@ unsigned long arch_deref_entry_point(void *);
 
 int register_kretprobe(struct kretprobe *rp);
 void unregister_kretprobe(struct kretprobe *rp);
+int register_kretprobes(struct kretprobe **rps, int num);
+void unregister_kretprobes(struct kretprobe **rps, int num);
 
 void kprobe_flush_task(struct task_struct *tk);
 void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head);
@@ -287,9 +289,16 @@ static inline int register_kretprobe(struct kretprobe *rp)
 {
        return -ENOSYS;
 }
+static inline int register_kretprobes(struct kretprobe **rps, int num)
+{
+       return -ENOSYS;
+}
 static inline void unregister_kretprobe(struct kretprobe *rp)
 {
 }
+static inline void unregister_kretprobes(struct kretprobe **rps, int num)
+{
+}
 static inline void kprobe_flush_task(struct task_struct *tk)
 {
 }
index 76275fc025a59f405380781068b848d156479afb..5e3144ad9b64dc4ad7b25be5a3a9af6220f6dc08 100644 (file)
@@ -429,6 +429,21 @@ static inline void free_rp_inst(struct kretprobe *rp)
        }
 }
 
+static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
+{
+       unsigned long flags;
+       struct kretprobe_instance *ri;
+       struct hlist_node *pos, *next;
+       /* No race here */
+       spin_lock_irqsave(&kretprobe_lock, flags);
+       hlist_for_each_entry_safe(ri, pos, next, &rp->used_instances, uflist) {
+               ri->rp = NULL;
+               hlist_del(&ri->uflist);
+       }
+       spin_unlock_irqrestore(&kretprobe_lock, flags);
+       free_rp_inst(rp);
+}
+
 /*
  * Keep all fields in the kprobe consistent
  */
@@ -798,7 +813,8 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,
        return 0;
 }
 
-int __kprobes register_kretprobe(struct kretprobe *rp)
+static int __kprobes __register_kretprobe(struct kretprobe *rp,
+                                         unsigned long called_from)
 {
        int ret = 0;
        struct kretprobe_instance *inst;
@@ -844,43 +860,93 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
 
        rp->nmissed = 0;
        /* Establish function entry probe point */
-       if ((ret = __register_kprobe(&rp->kp,
-               (unsigned long)__builtin_return_address(0))) != 0)
+       ret = __register_kprobe(&rp->kp, called_from);
+       if (ret != 0)
                free_rp_inst(rp);
        return ret;
 }
 
+static int __register_kretprobes(struct kretprobe **rps, int num,
+       unsigned long called_from)
+{
+       int ret = 0, i;
+
+       if (num <= 0)
+               return -EINVAL;
+       for (i = 0; i < num; i++) {
+               ret = __register_kretprobe(rps[i], called_from);
+               if (ret < 0 && i > 0) {
+                       unregister_kretprobes(rps, i);
+                       break;
+               }
+       }
+       return ret;
+}
+
+int __kprobes register_kretprobe(struct kretprobe *rp)
+{
+       return __register_kretprobes(&rp, 1,
+                       (unsigned long)__builtin_return_address(0));
+}
+
+void __kprobes unregister_kretprobe(struct kretprobe *rp)
+{
+       unregister_kretprobes(&rp, 1);
+}
+
+int __kprobes register_kretprobes(struct kretprobe **rps, int num)
+{
+       return __register_kretprobes(rps, num,
+                       (unsigned long)__builtin_return_address(0));
+}
+
+void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+{
+       int i;
+
+       if (num <= 0)
+               return;
+       mutex_lock(&kprobe_mutex);
+       for (i = 0; i < num; i++)
+               if (__unregister_kprobe_top(&rps[i]->kp) < 0)
+                       rps[i]->kp.addr = NULL;
+       mutex_unlock(&kprobe_mutex);
+
+       synchronize_sched();
+       for (i = 0; i < num; i++) {
+               if (rps[i]->kp.addr) {
+                       __unregister_kprobe_bottom(&rps[i]->kp);
+                       cleanup_rp_inst(rps[i]);
+               }
+       }
+}
+
 #else /* CONFIG_KRETPROBES */
 int __kprobes register_kretprobe(struct kretprobe *rp)
 {
        return -ENOSYS;
 }
 
-static int __kprobes pre_handler_kretprobe(struct kprobe *p,
-                                          struct pt_regs *regs)
+int __kprobes register_kretprobes(struct kretprobe **rps, int num)
 {
-       return 0;
+       return -ENOSYS;
 }
-#endif /* CONFIG_KRETPROBES */
-
 void __kprobes unregister_kretprobe(struct kretprobe *rp)
 {
-       unsigned long flags;
-       struct kretprobe_instance *ri;
-       struct hlist_node *pos, *next;
+}
 
-       unregister_kprobe(&rp->kp);
+void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+{
+}
 
-       /* No race here */
-       spin_lock_irqsave(&kretprobe_lock, flags);
-       hlist_for_each_entry_safe(ri, pos, next, &rp->used_instances, uflist) {
-               ri->rp = NULL;
-               hlist_del(&ri->uflist);
-       }
-       spin_unlock_irqrestore(&kretprobe_lock, flags);
-       free_rp_inst(rp);
+static int __kprobes pre_handler_kretprobe(struct kprobe *p,
+                                          struct pt_regs *regs)
+{
+       return 0;
 }
 
+#endif /* CONFIG_KRETPROBES */
+
 static int __init init_kprobes(void)
 {
        int i, err = 0;
@@ -1177,4 +1243,6 @@ EXPORT_SYMBOL_GPL(jprobe_return);
 #ifdef CONFIG_KPROBES
 EXPORT_SYMBOL_GPL(register_kretprobe);
 EXPORT_SYMBOL_GPL(unregister_kretprobe);
+EXPORT_SYMBOL_GPL(register_kretprobes);
+EXPORT_SYMBOL_GPL(unregister_kretprobes);
 #endif