Skip to content

Commit cb27c5a

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 bfdccd7 commit cb27c5a

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

62536381
/**

0 commit comments

Comments
 (0)