unsigned long ip;
 };
 
+struct ftrace_func_probe {
+       struct ftrace_probe_ops *probe_ops;
+       struct ftrace_ops       ops;
+       struct trace_array      *tr;
+       struct list_head        list;
+       int                     ref;
+};
+
 /*
  * We make these constant because no one should touch them,
  * but they are used as the default "empty hash", to avoid allocating
        loff_t                          func_pos;
        struct ftrace_page              *pg;
        struct dyn_ftrace               *func;
-       struct ftrace_probe_ops         *probe;
+       struct ftrace_func_probe        *probe;
        struct ftrace_func_entry        *probe_entry;
        struct trace_parser             parser;
        struct ftrace_hash              *hash;
 
        if (!iter->probe) {
                next = func_probes->next;
-               iter->probe = list_entry(next, struct ftrace_probe_ops, list);
+               iter->probe = list_entry(next, struct ftrace_func_probe, list);
        }
 
        if (iter->probe_entry)
                if (iter->probe->list.next == func_probes)
                        return NULL;
                next = iter->probe->list.next;
-               iter->probe = list_entry(next, struct ftrace_probe_ops, list);
+               iter->probe = list_entry(next, struct ftrace_func_probe, list);
                hash = iter->probe->ops.func_hash->filter_hash;
                size = 1 << hash->size_bits;
                iter->pidx = 0;
 static int
 t_probe_show(struct seq_file *m, struct ftrace_iterator *iter)
 {
-       struct ftrace_probe_ops *probe;
        struct ftrace_func_entry *probe_entry;
+       struct ftrace_probe_ops *probe_ops;
+       struct ftrace_func_probe *probe;
 
        probe = iter->probe;
        probe_entry = iter->probe_entry;
        if (WARN_ON_ONCE(!probe || !probe_entry))
                return -EIO;
 
-       if (probe->print)
-               return probe->print(m, probe_entry->ip, probe, NULL);
+       probe_ops = probe->probe_ops;
+
+       if (probe_ops->print)
+               return probe_ops->print(m, probe_entry->ip, probe_ops, NULL);
 
-       seq_printf(m, "%ps:%ps\n", (void *)probe_entry->ip, (void *)probe->func);
+       seq_printf(m, "%ps:%ps\n", (void *)probe_entry->ip,
+                  (void *)probe_ops->func);
 
        return 0;
 }
                                      struct ftrace_ops *op, struct pt_regs *pt_regs)
 {
        struct ftrace_probe_ops *probe_ops;
-       struct trace_array *tr = op->private;
+       struct ftrace_func_probe *probe;
 
-       probe_ops = container_of(op, struct ftrace_probe_ops, ops);
+       probe = container_of(op, struct ftrace_func_probe, ops);
+       probe_ops = probe->probe_ops;
 
        /*
         * Disable preemption for these calls to prevent a RCU grace
         * on the hash. rcu_read_lock is too dangerous here.
         */
        preempt_disable_notrace();
-       probe_ops->func(ip, parent_ip, tr, probe_ops, NULL);
+       probe_ops->func(ip, parent_ip, probe->tr, probe_ops, NULL);
        preempt_enable_notrace();
 }
 
        free_ftrace_hash(&mapper->hash);
 }
 
+static void release_probe(struct ftrace_func_probe *probe)
+{
+       struct ftrace_probe_ops *probe_ops;
+
+       mutex_lock(&ftrace_lock);
+
+       WARN_ON(probe->ref <= 0);
+
+       /* Subtract the ref that was used to protect this instance */
+       probe->ref--;
+
+       if (!probe->ref) {
+               probe_ops = probe->probe_ops;
+               list_del(&probe->list);
+               kfree(probe);
+       }
+       mutex_unlock(&ftrace_lock);
+}
+
+static void acquire_probe_locked(struct ftrace_func_probe *probe)
+{
+       /*
+        * Add one ref to keep it from being freed when releasing the
+        * ftrace_lock mutex.
+        */
+       probe->ref++;
+}
+
 int
 register_ftrace_function_probe(char *glob, struct trace_array *tr,
-                              struct ftrace_probe_ops *ops, void *data)
+                              struct ftrace_probe_ops *probe_ops,
+                              void *data)
 {
        struct ftrace_func_entry *entry;
+       struct ftrace_func_probe *probe;
        struct ftrace_hash **orig_hash;
        struct ftrace_hash *old_hash;
        struct ftrace_hash *hash;
        if (WARN_ON(glob[0] == '!'))
                return -EINVAL;
 
-       if (!(ops->ops.flags & FTRACE_OPS_FL_INITIALIZED)) {
-               ops->ops.func = function_trace_probe_call;
-               ftrace_ops_init(&ops->ops);
-               INIT_LIST_HEAD(&ops->list);
-               ops->ops.private = tr;
+
+       mutex_lock(&ftrace_lock);
+       /* Check if the probe_ops is already registered */
+       list_for_each_entry(probe, &tr->func_probes, list) {
+               if (probe->probe_ops == probe_ops)
+                       break;
        }
+       if (&probe->list == &tr->func_probes) {
+               probe = kzalloc(sizeof(*probe), GFP_KERNEL);
+               if (!probe) {
+                       mutex_unlock(&ftrace_lock);
+                       return -ENOMEM;
+               }
+               probe->probe_ops = probe_ops;
+               probe->ops.func = function_trace_probe_call;
+               probe->tr = tr;
+               ftrace_ops_init(&probe->ops);
+               list_add(&probe->list, &tr->func_probes);
+       }
+
+       acquire_probe_locked(probe);
 
-       mutex_lock(&ops->ops.func_hash->regex_lock);
+       mutex_unlock(&ftrace_lock);
+
+       mutex_lock(&probe->ops.func_hash->regex_lock);
 
-       orig_hash = &ops->ops.func_hash->filter_hash;
+       orig_hash = &probe->ops.func_hash->filter_hash;
        old_hash = *orig_hash;
        hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, old_hash);
 
                         * for each function we find. We call the callback
                         * to give the caller an opportunity to do so.
                         */
-                       if (ops->init) {
-                               ret = ops->init(ops, tr, entry->ip, data);
+                       if (probe_ops->init) {
+                               ret = probe_ops->init(probe_ops, tr,
+                                                     entry->ip, data);
                                if (ret < 0)
                                        goto out;
                        }
 
        mutex_lock(&ftrace_lock);
 
-       ret = ftrace_hash_move_and_update_ops(&ops->ops, orig_hash,
-                                                     hash, 1);
+       if (!count) {
+               /* Nothing was added? */
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       ret = ftrace_hash_move_and_update_ops(&probe->ops, orig_hash,
+                                             hash, 1);
        if (ret < 0)
                goto err_unlock;
 
-       if (list_empty(&ops->list))
-               list_add(&ops->list, &tr->func_probes);
+       /* One ref for each new function traced */
+       probe->ref += count;
 
-       if (!(ops->ops.flags & FTRACE_OPS_FL_ENABLED))
-               ret = ftrace_startup(&ops->ops, 0);
+       if (!(probe->ops.flags & FTRACE_OPS_FL_ENABLED))
+               ret = ftrace_startup(&probe->ops, 0);
 
  out_unlock:
        mutex_unlock(&ftrace_lock);
        if (!ret)
                ret = count;
  out:
-       mutex_unlock(&ops->ops.func_hash->regex_lock);
+       mutex_unlock(&probe->ops.func_hash->regex_lock);
        free_ftrace_hash(hash);
 
+       release_probe(probe);
+
        return ret;
 
  err_unlock:
-       if (!ops->free)
+       if (!probe_ops->free || !count)
                goto out_unlock;
 
        /* Failed to do the move, need to call the free functions */
                hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
                        if (ftrace_lookup_ip(old_hash, entry->ip))
                                continue;
-                       ops->free(ops, tr, entry->ip, NULL);
+                       probe_ops->free(probe_ops, tr, entry->ip, NULL);
                }
        }
        goto out_unlock;
 }
 
 int
-unregister_ftrace_function_probe_func(char *glob, struct ftrace_probe_ops *ops)
+unregister_ftrace_function_probe_func(char *glob, struct trace_array *tr,
+                                     struct ftrace_probe_ops *probe_ops)
 {
        struct ftrace_ops_hash old_hash_ops;
        struct ftrace_func_entry *entry;
+       struct ftrace_func_probe *probe;
        struct ftrace_glob func_g;
        struct ftrace_hash **orig_hash;
        struct ftrace_hash *old_hash;
        struct ftrace_hash *hash = NULL;
        struct hlist_node *tmp;
        struct hlist_head hhd;
-       struct trace_array *tr;
        char str[KSYM_SYMBOL_LEN];
-       int i, ret;
+       int count = 0;
+       int i, ret = -ENODEV;
        int size;
 
-       if (!(ops->ops.flags & FTRACE_OPS_FL_INITIALIZED))
-               return -EINVAL;
-
-       tr = ops->ops.private;
-
        if (glob && (strcmp(glob, "*") == 0 || !strlen(glob)))
                func_g.search = NULL;
        else if (glob) {
                        return -EINVAL;
        }
 
-       mutex_lock(&ops->ops.func_hash->regex_lock);
+       mutex_lock(&ftrace_lock);
+       /* Check if the probe_ops is already registered */
+       list_for_each_entry(probe, &tr->func_probes, list) {
+               if (probe->probe_ops == probe_ops)
+                       break;
+       }
+       if (&probe->list == &tr->func_probes)
+               goto err_unlock_ftrace;
+
+       ret = -EINVAL;
+       if (!(probe->ops.flags & FTRACE_OPS_FL_INITIALIZED))
+               goto err_unlock_ftrace;
+
+       acquire_probe_locked(probe);
 
-       orig_hash = &ops->ops.func_hash->filter_hash;
+       mutex_unlock(&ftrace_lock);
+
+       mutex_lock(&probe->ops.func_hash->regex_lock);
+
+       orig_hash = &probe->ops.func_hash->filter_hash;
        old_hash = *orig_hash;
 
-       ret = -EINVAL;
        if (ftrace_hash_empty(old_hash))
                goto out_unlock;
 
                                if (!ftrace_match(str, &func_g))
                                        continue;
                        }
-
+                       count++;
                        remove_hash_entry(hash, entry);
                        hlist_add_head(&entry->hlist, &hhd);
                }
        }
 
        /* Nothing found? */
-       if (hlist_empty(&hhd)) {
+       if (!count) {
                ret = -EINVAL;
                goto out_unlock;
        }
 
        mutex_lock(&ftrace_lock);
 
-       if (ftrace_hash_empty(hash)) {
-               ftrace_shutdown(&ops->ops, 0);
-               list_del_init(&ops->list);
-       }
+       WARN_ON(probe->ref < count);
 
+       probe->ref -= count;
 
-       ret = ftrace_hash_move_and_update_ops(&ops->ops, orig_hash,
+       if (ftrace_hash_empty(hash))
+               ftrace_shutdown(&probe->ops, 0);
+
+       ret = ftrace_hash_move_and_update_ops(&probe->ops, orig_hash,
                                              hash, 1);
 
        /* still need to update the function call sites */
        if (ftrace_enabled && !ftrace_hash_empty(hash))
-               ftrace_run_modify_code(&ops->ops, FTRACE_UPDATE_CALLS,
+               ftrace_run_modify_code(&probe->ops, FTRACE_UPDATE_CALLS,
                                       &old_hash_ops);
        synchronize_sched();
 
        hlist_for_each_entry_safe(entry, tmp, &hhd, hlist) {
                hlist_del(&entry->hlist);
-               if (ops->free)
-                       ops->free(ops, tr, entry->ip, NULL);
+               if (probe_ops->free)
+                       probe_ops->free(probe_ops, tr, entry->ip, NULL);
                kfree(entry);
        }
        mutex_unlock(&ftrace_lock);
 
  out_unlock:
-       mutex_unlock(&ops->ops.func_hash->regex_lock);
+       mutex_unlock(&probe->ops.func_hash->regex_lock);
        free_ftrace_hash(hash);
+
+       release_probe(probe);
+
+       return ret;
+
+ err_unlock_ftrace:
+       mutex_unlock(&ftrace_lock);
        return ret;
 }