Skip to content

Commit

Permalink
ftrace: Use manual free after synchronize_sched() not call_rcu_sched()
Browse files Browse the repository at this point in the history
The entries to the probe hash must be freed after a synchronize_sched()
after the entry has been removed from the hash.

As the entries are registered with ops that may have their own callbacks,
and these callbacks may sleep, we can not use call_rcu_sched() because
the rcu callbacks registered with that are called from a softirq context.

Instead of using call_rcu_sched(), manually save the entries on a free_list
and at the end of the loop that removes the entries, do a synchronize_sched()
and then go through the free_list, freeing the entries.

Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
  • Loading branch information
Steven Rostedt (Red Hat) committed Mar 15, 2013
1 parent e67efb9 commit 7818b38
Showing 1 changed file with 13 additions and 6 deletions.
19 changes: 13 additions & 6 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ struct ftrace_func_probe {
unsigned long flags;
unsigned long ip;
void *data;
struct rcu_head rcu;
struct list_head free_list;
};

struct ftrace_func_entry {
Expand Down Expand Up @@ -2978,11 +2978,8 @@ static void __disable_ftrace_function_probe(void)
}


static void ftrace_free_entry_rcu(struct rcu_head *rhp)
static void ftrace_free_entry(struct ftrace_func_probe *entry)
{
struct ftrace_func_probe *entry =
container_of(rhp, struct ftrace_func_probe, rcu);

if (entry->ops->free)
entry->ops->free(entry->ops, entry->ip, &entry->data);
kfree(entry);
Expand Down Expand Up @@ -3092,7 +3089,9 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
{
struct ftrace_func_entry *rec_entry;
struct ftrace_func_probe *entry;
struct ftrace_func_probe *p;
struct ftrace_hash **orig_hash = &trace_probe_ops.filter_hash;
struct list_head free_list;
struct ftrace_hash *hash;
struct hlist_node *n, *tmp;
char str[KSYM_SYMBOL_LEN];
Expand Down Expand Up @@ -3120,6 +3119,8 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
/* Hmm, should report this somehow */
goto out_unlock;

INIT_LIST_HEAD(&free_list);

for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
struct hlist_head *hhd = &ftrace_func_hash[i];

Expand All @@ -3146,7 +3147,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
free_hash_entry(hash, rec_entry);

hlist_del_rcu(&entry->node);
call_rcu_sched(&entry->rcu, ftrace_free_entry_rcu);
list_add(&entry->free_list, &free_list);
}
}
__disable_ftrace_function_probe();
Expand All @@ -3155,6 +3156,12 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
* probe is removed, a null hash means *all enabled*.
*/
ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash);
synchronize_sched();
list_for_each_entry_safe(entry, p, &free_list, free_list) {
list_del(&entry->free_list);
ftrace_free_entry(entry);
}

out_unlock:
mutex_unlock(&ftrace_lock);
free_ftrace_hash(hash);
Expand Down

0 comments on commit 7818b38

Please sign in to comment.