Factor out ftrace_direct_update() from register_ftrace_direct(), which is used to add new entries to the direct_functions. This function will be used in the later patch.
Signed-off-by: Menglong Dong <dong...@chinatelecom.cn> --- kernel/trace/ftrace.c | 108 +++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 4203fad56b6c..f5f6d7bc26f0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5953,53 +5953,18 @@ static void register_ftrace_direct_cb(struct rcu_head *rhp) free_ftrace_hash(fhp); } -/** - * register_ftrace_direct - Call a custom trampoline directly - * for multiple functions registered in @ops - * @ops: The address of the struct ftrace_ops object - * @addr: The address of the trampoline to call at @ops functions - * - * This is used to connect a direct calls to @addr from the nop locations - * of the functions registered in @ops (with by ftrace_set_filter_ip - * function). - * - * The location that it calls (@addr) must be able to handle a direct call, - * and save the parameters of the function being traced, and restore them - * (or inject new ones if needed), before returning. - * - * Returns: - * 0 on success - * -EINVAL - The @ops object was already registered with this call or - * when there are no functions in @ops object. - * -EBUSY - Another direct function is already attached (there can be only one) - * -ENODEV - @ip does not point to a ftrace nop location (or not supported) - * -ENOMEM - There was an allocation failure. - */ -int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) +static int ftrace_direct_update(struct ftrace_hash *hash, unsigned long addr) { - struct ftrace_hash *hash, *new_hash = NULL, *free_hash = NULL; struct ftrace_func_entry *entry, *new; + struct ftrace_hash *new_hash = NULL; int err = -EBUSY, size, i; - if (ops->func || ops->trampoline) - return -EINVAL; - if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) - return -EINVAL; - if (ops->flags & FTRACE_OPS_FL_ENABLED) - return -EINVAL; - - hash = ops->func_hash->filter_hash; - if (ftrace_hash_empty(hash)) - return -EINVAL; - - mutex_lock(&direct_mutex); - /* Make sure requested entries are not already registered.. */ size = 1 << hash->size_bits; for (i = 0; i < size; i++) { hlist_for_each_entry(entry, &hash->buckets[i], hlist) { if (ftrace_find_rec_direct(entry->ip)) - goto out_unlock; + goto out; } } @@ -6012,7 +5977,7 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) size = FTRACE_HASH_MAX_BITS; new_hash = alloc_ftrace_hash(size); if (!new_hash) - goto out_unlock; + goto out; /* Now copy over the existing direct entries */ size = 1 << direct_functions->size_bits; @@ -6020,7 +5985,7 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) hlist_for_each_entry(entry, &direct_functions->buckets[i], hlist) { new = add_hash_entry(new_hash, entry->ip); if (!new) - goto out_unlock; + goto out; new->direct = entry->direct; } } @@ -6031,16 +5996,67 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) hlist_for_each_entry(entry, &hash->buckets[i], hlist) { new = add_hash_entry(new_hash, entry->ip); if (!new) - goto out_unlock; + goto out; /* Update both the copy and the hash entry */ new->direct = addr; entry->direct = addr; } } - free_hash = direct_functions; rcu_assign_pointer(direct_functions, new_hash); new_hash = NULL; + err = 0; +out: + if (new_hash) + free_ftrace_hash(new_hash); + + return err; +} + +/** + * register_ftrace_direct - Call a custom trampoline directly + * for multiple functions registered in @ops + * @ops: The address of the struct ftrace_ops object + * @addr: The address of the trampoline to call at @ops functions + * + * This is used to connect a direct calls to @addr from the nop locations + * of the functions registered in @ops (with by ftrace_set_filter_ip + * function). + * + * The location that it calls (@addr) must be able to handle a direct call, + * and save the parameters of the function being traced, and restore them + * (or inject new ones if needed), before returning. + * + * Returns: + * 0 on success + * -EINVAL - The @ops object was already registered with this call or + * when there are no functions in @ops object. + * -EBUSY - Another direct function is already attached (there can be only one) + * -ENODEV - @ip does not point to a ftrace nop location (or not supported) + * -ENOMEM - There was an allocation failure. + */ +int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) +{ + struct ftrace_hash *hash, *free_hash = NULL; + int err = -EBUSY; + + if (ops->func || ops->trampoline) + return -EINVAL; + if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) + return -EINVAL; + if (ops->flags & FTRACE_OPS_FL_ENABLED) + return -EINVAL; + + hash = ops->func_hash->filter_hash; + if (ftrace_hash_empty(hash)) + return -EINVAL; + + mutex_lock(&direct_mutex); + + free_hash = direct_functions; + err = ftrace_direct_update(hash, addr); + if (err) + goto out_unlock; ops->func = call_direct_funcs; ops->flags = MULTI_FLAGS; @@ -6048,15 +6064,11 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr) ops->direct_call = addr; err = register_ftrace_function_nolock(ops); - - out_unlock: - mutex_unlock(&direct_mutex); - if (free_hash && free_hash != EMPTY_HASH) call_rcu_tasks(&free_hash->rcu, register_ftrace_direct_cb); - if (new_hash) - free_ftrace_hash(new_hash); + out_unlock: + mutex_unlock(&direct_mutex); return err; } -- 2.39.5