Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
[linux-2.6-microblaze.git] / kernel / kprobes.c
index 1b66ccb..53534aa 100644 (file)
@@ -961,9 +961,16 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
 
 #ifdef CONFIG_KPROBES_ON_FTRACE
 static struct ftrace_ops kprobe_ftrace_ops __read_mostly = {
+       .func = kprobe_ftrace_handler,
+       .flags = FTRACE_OPS_FL_SAVE_REGS,
+};
+
+static struct ftrace_ops kprobe_ipmodify_ops __read_mostly = {
        .func = kprobe_ftrace_handler,
        .flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY,
 };
+
+static int kprobe_ipmodify_enabled;
 static int kprobe_ftrace_enabled;
 
 /* Must ensure p->addr is really on ftrace */
@@ -976,58 +983,75 @@ static int prepare_kprobe(struct kprobe *p)
 }
 
 /* Caller must lock kprobe_mutex */
-static int arm_kprobe_ftrace(struct kprobe *p)
+static int __arm_kprobe_ftrace(struct kprobe *p, struct ftrace_ops *ops,
+                              int *cnt)
 {
        int ret = 0;
 
-       ret = ftrace_set_filter_ip(&kprobe_ftrace_ops,
-                                  (unsigned long)p->addr, 0, 0);
+       ret = ftrace_set_filter_ip(ops, (unsigned long)p->addr, 0, 0);
        if (ret) {
                pr_debug("Failed to arm kprobe-ftrace at %pS (%d)\n",
                         p->addr, ret);
                return ret;
        }
 
-       if (kprobe_ftrace_enabled == 0) {
-               ret = register_ftrace_function(&kprobe_ftrace_ops);
+       if (*cnt == 0) {
+               ret = register_ftrace_function(ops);
                if (ret) {
                        pr_debug("Failed to init kprobe-ftrace (%d)\n", ret);
                        goto err_ftrace;
                }
        }
 
-       kprobe_ftrace_enabled++;
+       (*cnt)++;
        return ret;
 
 err_ftrace:
        /*
-        * Note: Since kprobe_ftrace_ops has IPMODIFY set, and ftrace requires a
-        * non-empty filter_hash for IPMODIFY ops, we're safe from an accidental
-        * empty filter_hash which would undesirably trace all functions.
+        * At this point, sinec ops is not registered, we should be sefe from
+        * registering empty filter.
         */
-       ftrace_set_filter_ip(&kprobe_ftrace_ops, (unsigned long)p->addr, 1, 0);
+       ftrace_set_filter_ip(ops, (unsigned long)p->addr, 1, 0);
        return ret;
 }
 
+static int arm_kprobe_ftrace(struct kprobe *p)
+{
+       bool ipmodify = (p->post_handler != NULL);
+
+       return __arm_kprobe_ftrace(p,
+               ipmodify ? &kprobe_ipmodify_ops : &kprobe_ftrace_ops,
+               ipmodify ? &kprobe_ipmodify_enabled : &kprobe_ftrace_enabled);
+}
+
 /* Caller must lock kprobe_mutex */
-static int disarm_kprobe_ftrace(struct kprobe *p)
+static int __disarm_kprobe_ftrace(struct kprobe *p, struct ftrace_ops *ops,
+                                 int *cnt)
 {
        int ret = 0;
 
-       if (kprobe_ftrace_enabled == 1) {
-               ret = unregister_ftrace_function(&kprobe_ftrace_ops);
+       if (*cnt == 1) {
+               ret = unregister_ftrace_function(ops);
                if (WARN(ret < 0, "Failed to unregister kprobe-ftrace (%d)\n", ret))
                        return ret;
        }
 
-       kprobe_ftrace_enabled--;
+       (*cnt)--;
 
-       ret = ftrace_set_filter_ip(&kprobe_ftrace_ops,
-                          (unsigned long)p->addr, 1, 0);
+       ret = ftrace_set_filter_ip(ops, (unsigned long)p->addr, 1, 0);
        WARN_ONCE(ret < 0, "Failed to disarm kprobe-ftrace at %pS (%d)\n",
                  p->addr, ret);
        return ret;
 }
+
+static int disarm_kprobe_ftrace(struct kprobe *p)
+{
+       bool ipmodify = (p->post_handler != NULL);
+
+       return __disarm_kprobe_ftrace(p,
+               ipmodify ? &kprobe_ipmodify_ops : &kprobe_ftrace_ops,
+               ipmodify ? &kprobe_ipmodify_enabled : &kprobe_ftrace_enabled);
+}
 #else  /* !CONFIG_KPROBES_ON_FTRACE */
 #define prepare_kprobe(p)      arch_prepare_kprobe(p)
 #define arm_kprobe_ftrace(p)   (-ENODEV)