Skip to content

Commit f727dc5

Browse files
olsajiriKernel Patches Daemon
authored andcommitted
ftrace: Add update_ftrace_direct_add function
Adding update_ftrace_direct_add function that adds all entries (ip -> addr) provided in hash argument to direct ftrace ops and updates its attachments. The difference to current register_ftrace_direct is - hash argument that allows to register multiple ip -> direct entries at once - we can call update_ftrace_direct_add multiple times on the same ftrace_ops object, becase after first registration with register_ftrace_function_nolock, it uses ftrace_update_ops to update the ftrace_ops object This change will allow us to have simple ftrace_ops for all bpf direct interface users in following changes. Signed-off-by: Jiri Olsa <[email protected]>
1 parent b329558 commit f727dc5

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

include/linux/ftrace.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@ int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
542542
int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr);
543543
int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr);
544544

545+
int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash);
546+
545547
void ftrace_stub_direct_tramp(void);
546548

547549
#else
@@ -568,6 +570,11 @@ static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned l
568570
return -ENODEV;
569571
}
570572

573+
int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
574+
{
575+
return -ENODEV;
576+
}
577+
571578
/*
572579
* This must be implemented by the architecture.
573580
* It is the way the ftrace direct_ops helper, when called

kernel/trace/ftrace.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6218,6 +6218,134 @@ int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
62186218
return err;
62196219
}
62206220
EXPORT_SYMBOL_GPL(modify_ftrace_direct);
6221+
6222+
static unsigned long hash_count(struct ftrace_hash *hash)
6223+
{
6224+
return hash ? hash->count : 0;
6225+
}
6226+
6227+
/**
6228+
* hash_add - adds two struct ftrace_hash and returns the result
6229+
* @a: struct ftrace_hash object
6230+
* @b: struct ftrace_hash object
6231+
*
6232+
* Returns struct ftrace_hash object on success, NULL on error.
6233+
*/
6234+
static struct ftrace_hash *hash_add(struct ftrace_hash *a, struct ftrace_hash *b)
6235+
{
6236+
struct ftrace_func_entry *entry;
6237+
struct ftrace_hash *add;
6238+
int size, i;
6239+
6240+
size = hash_count(a) + hash_count(b);
6241+
if (size > 32)
6242+
size = 32;
6243+
6244+
add = alloc_and_copy_ftrace_hash(fls(size), a);
6245+
if (!add)
6246+
goto error;
6247+
6248+
size = 1 << b->size_bits;
6249+
for (i = 0; i < size; i++) {
6250+
hlist_for_each_entry(entry, &b->buckets[i], hlist) {
6251+
if (add_hash_entry_direct(add, entry->ip, entry->direct) == NULL)
6252+
goto error;
6253+
}
6254+
}
6255+
return add;
6256+
6257+
error:
6258+
free_ftrace_hash(add);
6259+
return NULL;
6260+
}
6261+
6262+
int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
6263+
{
6264+
struct ftrace_hash *old_direct_functions = NULL, *new_direct_functions = NULL;
6265+
struct ftrace_hash *old_filter_hash = NULL, *new_filter_hash = NULL;
6266+
struct ftrace_func_entry *entry;
6267+
int i, size, err = -EINVAL;
6268+
bool reg;
6269+
6270+
if (!hash_count(hash))
6271+
return 0;
6272+
6273+
mutex_lock(&direct_mutex);
6274+
6275+
/* Make sure requested entries are not already registered. */
6276+
size = 1 << hash->size_bits;
6277+
for (i = 0; i < size; i++) {
6278+
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
6279+
if (__ftrace_lookup_ip(direct_functions, entry->ip))
6280+
goto out_unlock;
6281+
}
6282+
}
6283+
6284+
old_filter_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL;
6285+
old_direct_functions = direct_functions;
6286+
6287+
/* If there's nothing in filter_hash we need to register the ops. */
6288+
reg = hash_count(old_filter_hash) == 0;
6289+
if (reg) {
6290+
if (ops->func || ops->trampoline)
6291+
goto out_unlock;
6292+
if (ops->flags & FTRACE_OPS_FL_ENABLED)
6293+
goto out_unlock;
6294+
}
6295+
6296+
err = -ENOMEM;
6297+
new_filter_hash = hash_add(old_filter_hash, hash);
6298+
if (!new_filter_hash)
6299+
goto out_unlock;
6300+
6301+
new_direct_functions = hash_add(old_direct_functions, hash);
6302+
if (!new_direct_functions)
6303+
goto out_unlock;
6304+
6305+
rcu_assign_pointer(direct_functions, new_direct_functions);
6306+
6307+
if (reg) {
6308+
ops->func = call_direct_funcs;
6309+
ops->flags |= MULTI_FLAGS;
6310+
ops->trampoline = FTRACE_REGS_ADDR;
6311+
ops->local_hash.filter_hash = new_filter_hash;
6312+
6313+
err = register_ftrace_function_nolock(ops);
6314+
if (err) {
6315+
/* restore old filter on error */
6316+
ops->local_hash.filter_hash = old_filter_hash;
6317+
old_filter_hash = new_filter_hash;
6318+
6319+
/* cleanup for possible another register call */
6320+
ops->func = NULL;
6321+
ops->trampoline = 0;
6322+
}
6323+
} else {
6324+
err = ftrace_update_ops(ops, new_filter_hash, EMPTY_HASH);
6325+
/*
6326+
* new_filter_hash is dup-ed, so we need to release it anyway,
6327+
* old_filter_hash either stays on error or is released already
6328+
*/
6329+
old_filter_hash = new_filter_hash;
6330+
}
6331+
6332+
if (err) {
6333+
/* reset direct_functions and free the new one */
6334+
rcu_assign_pointer(direct_functions, old_direct_functions);
6335+
old_direct_functions = new_direct_functions;
6336+
}
6337+
6338+
out_unlock:
6339+
mutex_unlock(&direct_mutex);
6340+
6341+
if (old_direct_functions && old_direct_functions != EMPTY_HASH)
6342+
call_rcu_tasks(&old_direct_functions->rcu, register_ftrace_direct_cb);
6343+
if (old_filter_hash)
6344+
free_ftrace_hash(old_filter_hash);
6345+
6346+
return err;
6347+
}
6348+
62216349
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
62226350

62236351
/**

0 commit comments

Comments
 (0)