Skip to content

Commit e5e953c

Browse files
olsajiriKernel Patches Daemon
authored andcommitted
ftrace: Add update_ftrace_direct_del function
Adding update_ftrace_direct_del function that removes all entries (ip -> addr) provided in hash argument to direct ftrace ops and updates its attachments. The difference to current unregister_ftrace_direct is - hash argument that allows to unregister multiple ip -> direct entries at once - we can call update_ftrace_direct_del multiple times on the same ftrace_ops object, becase we do not need to unregister all entries at once, we can do it gradualy with the help of ftrace_update_ops function 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 f727dc5 commit e5e953c

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

include/linux/ftrace.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ 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

545545
int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash);
546+
int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash);
546547

547548
void ftrace_stub_direct_tramp(void);
548549

@@ -575,6 +576,11 @@ int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
575576
return -ENODEV;
576577
}
577578

579+
int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash)
580+
{
581+
return -ENODEV;
582+
}
583+
578584
/*
579585
* This must be implemented by the architecture.
580586
* It is the way the ftrace direct_ops helper, when called

kernel/trace/ftrace.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6346,6 +6346,105 @@ int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)
63466346
return err;
63476347
}
63486348

6349+
/**
6350+
* hash_sub - substracts @b from @a and returns the result
6351+
* @a: struct ftrace_hash object
6352+
* @b: struct ftrace_hash object
6353+
*
6354+
* Returns struct ftrace_hash object on success, NULL on error.
6355+
*/
6356+
static struct ftrace_hash *hash_sub(struct ftrace_hash *a, struct ftrace_hash *b)
6357+
{
6358+
struct ftrace_func_entry *entry, *del;
6359+
struct ftrace_hash *sub;
6360+
int size, i;
6361+
6362+
sub = alloc_and_copy_ftrace_hash(a->size_bits, a);
6363+
if (!sub)
6364+
goto error;
6365+
6366+
size = 1 << b->size_bits;
6367+
for (i = 0; i < size; i++) {
6368+
hlist_for_each_entry(entry, &b->buckets[i], hlist) {
6369+
del = __ftrace_lookup_ip(sub, entry->ip);
6370+
if (WARN_ON_ONCE(!del))
6371+
goto error;
6372+
remove_hash_entry(sub, del);
6373+
kfree(del);
6374+
}
6375+
}
6376+
return sub;
6377+
6378+
error:
6379+
free_ftrace_hash(sub);
6380+
return NULL;
6381+
}
6382+
6383+
int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash)
6384+
{
6385+
struct ftrace_hash *new_hash = NULL, *filter_hash = NULL, *free_hash = NULL;
6386+
struct ftrace_func_entry *del, *entry;
6387+
unsigned long size, i;
6388+
int err = -EINVAL;
6389+
6390+
if (!hash_count(hash))
6391+
return 0;
6392+
if (check_direct_multi(ops))
6393+
return -EINVAL;
6394+
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
6395+
return -EINVAL;
6396+
if (direct_functions == EMPTY_HASH)
6397+
return -EINVAL;
6398+
6399+
mutex_lock(&direct_mutex);
6400+
6401+
/* Make sure requested entries are already registered. */
6402+
size = 1 << hash->size_bits;
6403+
for (i = 0; i < size; i++) {
6404+
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
6405+
del = __ftrace_lookup_ip(direct_functions, entry->ip);
6406+
if (!del || del->direct != entry->direct)
6407+
goto out_unlock;
6408+
}
6409+
}
6410+
6411+
err = -ENOMEM;
6412+
filter_hash = hash_sub(ops->func_hash->filter_hash, hash);
6413+
if (!filter_hash)
6414+
goto out_unlock;
6415+
6416+
new_hash = hash_sub(direct_functions, hash);
6417+
if (!new_hash)
6418+
goto out_unlock;
6419+
6420+
/* If there's nothing left, we need to unregister the ops. */
6421+
if (ftrace_hash_empty(filter_hash)) {
6422+
err = unregister_ftrace_function(ops);
6423+
/* cleanup for possible another register call */
6424+
ops->func = NULL;
6425+
ops->trampoline = 0;
6426+
ftrace_free_filter(ops);
6427+
ops->func_hash->filter_hash = NULL;
6428+
} else {
6429+
err = ftrace_update_ops(ops, filter_hash, EMPTY_HASH);
6430+
}
6431+
6432+
if (!err) {
6433+
free_hash = direct_functions;
6434+
rcu_assign_pointer(direct_functions, new_hash);
6435+
}
6436+
6437+
out_unlock:
6438+
mutex_unlock(&direct_mutex);
6439+
6440+
if (free_hash && free_hash != EMPTY_HASH)
6441+
call_rcu_tasks(&free_hash->rcu, register_ftrace_direct_cb);
6442+
if (filter_hash)
6443+
free_ftrace_hash(filter_hash);
6444+
6445+
return err;
6446+
}
6447+
63496448
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
63506449

63516450
/**

0 commit comments

Comments
 (0)