Skip to content

Commit 3e903a7

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 b2ebe19 commit 3e903a7

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
@@ -551,6 +551,8 @@ int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
551551
int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr);
552552
int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr);
553553

554+
int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash);
555+
554556
void ftrace_stub_direct_tramp(void);
555557

556558
#else
@@ -577,6 +579,11 @@ static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned l
577579
return -ENODEV;
578580
}
579581

582+
static inline int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
583+
{
584+
return -ENODEV;
585+
}
586+
580587
/*
581588
* This must be implemented by the architecture.
582589
* 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
@@ -6263,6 +6263,134 @@ int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
62636263
return err;
62646264
}
62656265
EXPORT_SYMBOL_GPL(modify_ftrace_direct);
6266+
6267+
static unsigned long hash_count(struct ftrace_hash *hash)
6268+
{
6269+
return hash ? hash->count : 0;
6270+
}
6271+
6272+
/**
6273+
* hash_add - adds two struct ftrace_hash and returns the result
6274+
* @a: struct ftrace_hash object
6275+
* @b: struct ftrace_hash object
6276+
*
6277+
* Returns struct ftrace_hash object on success, NULL on error.
6278+
*/
6279+
static struct ftrace_hash *hash_add(struct ftrace_hash *a, struct ftrace_hash *b)
6280+
{
6281+
struct ftrace_func_entry *entry;
6282+
struct ftrace_hash *add;
6283+
int size, i;
6284+
6285+
size = hash_count(a) + hash_count(b);
6286+
if (size > 32)
6287+
size = 32;
6288+
6289+
add = alloc_and_copy_ftrace_hash(fls(size), a);
6290+
if (!add)
6291+
goto error;
6292+
6293+
size = 1 << b->size_bits;
6294+
for (i = 0; i < size; i++) {
6295+
hlist_for_each_entry(entry, &b->buckets[i], hlist) {
6296+
if (add_hash_entry_direct(add, entry->ip, entry->direct) == NULL)
6297+
goto error;
6298+
}
6299+
}
6300+
return add;
6301+
6302+
error:
6303+
free_ftrace_hash(add);
6304+
return NULL;
6305+
}
6306+
6307+
int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
6308+
{
6309+
struct ftrace_hash *old_direct_functions = NULL, *new_direct_functions = NULL;
6310+
struct ftrace_hash *old_filter_hash = NULL, *new_filter_hash = NULL;
6311+
struct ftrace_func_entry *entry;
6312+
int i, size, err = -EINVAL;
6313+
bool reg;
6314+
6315+
if (!hash_count(hash))
6316+
return -EINVAL;
6317+
6318+
mutex_lock(&direct_mutex);
6319+
6320+
/* Make sure requested entries are not already registered. */
6321+
size = 1 << hash->size_bits;
6322+
for (i = 0; i < size; i++) {
6323+
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
6324+
if (__ftrace_lookup_ip(direct_functions, entry->ip))
6325+
goto out_unlock;
6326+
}
6327+
}
6328+
6329+
old_filter_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL;
6330+
old_direct_functions = direct_functions;
6331+
6332+
/* If there's nothing in filter_hash we need to register the ops. */
6333+
reg = hash_count(old_filter_hash) == 0;
6334+
if (reg) {
6335+
if (ops->func || ops->trampoline)
6336+
goto out_unlock;
6337+
if (ops->flags & FTRACE_OPS_FL_ENABLED)
6338+
goto out_unlock;
6339+
}
6340+
6341+
err = -ENOMEM;
6342+
new_filter_hash = hash_add(old_filter_hash, hash);
6343+
if (!new_filter_hash)
6344+
goto out_unlock;
6345+
6346+
new_direct_functions = hash_add(old_direct_functions, hash);
6347+
if (!new_direct_functions)
6348+
goto out_unlock;
6349+
6350+
rcu_assign_pointer(direct_functions, new_direct_functions);
6351+
6352+
if (reg) {
6353+
ops->func = call_direct_funcs;
6354+
ops->flags |= MULTI_FLAGS;
6355+
ops->trampoline = FTRACE_REGS_ADDR;
6356+
ops->local_hash.filter_hash = new_filter_hash;
6357+
6358+
err = register_ftrace_function_nolock(ops);
6359+
if (err) {
6360+
/* restore old filter on error */
6361+
ops->local_hash.filter_hash = old_filter_hash;
6362+
old_filter_hash = new_filter_hash;
6363+
6364+
/* cleanup for possible another register call */
6365+
ops->func = NULL;
6366+
ops->trampoline = 0;
6367+
}
6368+
} else {
6369+
err = ftrace_update_ops(ops, new_filter_hash, EMPTY_HASH);
6370+
/*
6371+
* new_filter_hash is dup-ed, so we need to release it anyway,
6372+
* old_filter_hash either stays on error or is released already
6373+
*/
6374+
old_filter_hash = new_filter_hash;
6375+
}
6376+
6377+
if (err) {
6378+
/* reset direct_functions and free the new one */
6379+
rcu_assign_pointer(direct_functions, old_direct_functions);
6380+
old_direct_functions = new_direct_functions;
6381+
}
6382+
6383+
out_unlock:
6384+
mutex_unlock(&direct_mutex);
6385+
6386+
if (old_direct_functions && old_direct_functions != EMPTY_HASH)
6387+
call_rcu_tasks(&old_direct_functions->rcu, register_ftrace_direct_cb);
6388+
if (old_filter_hash)
6389+
free_ftrace_hash(old_filter_hash);
6390+
6391+
return err;
6392+
}
6393+
62666394
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
62676395

62686396
/**

0 commit comments

Comments
 (0)