Skip to content

Commit f233259

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 282feb1 commit f233259

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

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

555557
#else
@@ -576,6 +578,11 @@ static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned l
576578
return -ENODEV;
577579
}
578580

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

62546382
/**

0 commit comments

Comments
 (0)