Skip to content

Commit 68c088e

Browse files
olsajiriKernel Patches Daemon
authored andcommitted
ftrace: Use direct hash interface in direct functions
Implement current *_ftrace_direct function with their *_hash function counterparts. Signed-off-by: Jiri Olsa <[email protected]>
1 parent 1f7ac1b commit 68c088e

File tree

4 files changed

+45
-219
lines changed

4 files changed

+45
-219
lines changed

include/linux/ftrace.h

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -536,11 +536,10 @@ struct ftrace_func_entry {
536536

537537
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
538538
unsigned long ftrace_find_rec_direct(unsigned long ip);
539-
int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr);
540-
int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
539+
int register_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr);
540+
int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr,
541541
bool free_filters);
542-
int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr);
543-
int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr);
542+
int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool lock_direct_mutex);
544543

545544
int register_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash);
546545
int unregister_ftrace_direct_hash(struct ftrace_ops *ops, struct ftrace_hash *hash);
@@ -554,20 +553,16 @@ static inline unsigned long ftrace_find_rec_direct(unsigned long ip)
554553
{
555554
return 0;
556555
}
557-
static inline int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
556+
static inline int register_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr)
558557
{
559558
return -ENODEV;
560559
}
561-
static inline int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
560+
static inline int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr,
562561
bool free_filters)
563562
{
564563
return -ENODEV;
565564
}
566-
static inline int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
567-
{
568-
return -ENODEV;
569-
}
570-
static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr)
565+
static inline int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool lock_direct_mutex)
571566
{
572567
return -ENODEV;
573568
}

kernel/bpf/trampoline.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr)
181181
int ret;
182182

183183
if (tr->func.ftrace_managed)
184-
ret = unregister_ftrace_direct(tr->fops, (long)old_addr, false);
184+
ret = unregister_ftrace_direct(tr->fops, (unsigned long) ip, (long)old_addr, false);
185185
else
186186
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL);
187187

@@ -195,10 +195,7 @@ static int modify_fentry(struct bpf_trampoline *tr, void *old_addr, void *new_ad
195195
int ret;
196196

197197
if (tr->func.ftrace_managed) {
198-
if (lock_direct_mutex)
199-
ret = modify_ftrace_direct(tr->fops, (long)new_addr);
200-
else
201-
ret = modify_ftrace_direct_nolock(tr->fops, (long)new_addr);
198+
ret = modify_ftrace_direct(tr->fops, (unsigned long) ip, (long)new_addr, lock_direct_mutex);
202199
} else {
203200
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, new_addr);
204201
}
@@ -220,8 +217,7 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
220217
}
221218

222219
if (tr->func.ftrace_managed) {
223-
ftrace_set_filter_ip(tr->fops, (unsigned long)ip, 0, 1);
224-
ret = register_ftrace_direct(tr->fops, (long)new_addr);
220+
ret = register_ftrace_direct(tr->fops, (unsigned long)ip, (long)new_addr);
225221
} else {
226222
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr);
227223
}

kernel/trace/ftrace.c

Lines changed: 34 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -5935,28 +5935,24 @@ static int check_direct_multi(struct ftrace_ops *ops)
59355935
return 0;
59365936
}
59375937

5938-
static void remove_direct_functions_hash(struct ftrace_hash *hash, unsigned long addr)
5938+
static void register_ftrace_direct_cb(struct rcu_head *rhp)
59395939
{
5940-
struct ftrace_func_entry *entry, *del;
5941-
int size, i;
5940+
struct ftrace_hash *fhp = container_of(rhp, struct ftrace_hash, rcu);
59425941

5943-
size = 1 << hash->size_bits;
5944-
for (i = 0; i < size; i++) {
5945-
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
5946-
del = __ftrace_lookup_ip(direct_functions, entry->ip);
5947-
if (del && del->direct == addr) {
5948-
remove_hash_entry(direct_functions, del);
5949-
kfree(del);
5950-
}
5951-
}
5952-
}
5942+
free_ftrace_hash(fhp);
59535943
}
59545944

5955-
static void register_ftrace_direct_cb(struct rcu_head *rhp)
5945+
static struct ftrace_hash *hash_from_ip(unsigned long ip, unsigned long addr)
59565946
{
5957-
struct ftrace_hash *fhp = container_of(rhp, struct ftrace_hash, rcu);
5947+
struct ftrace_hash *hash;
59585948

5959-
free_ftrace_hash(fhp);
5949+
ip = ftrace_location(ip);
5950+
if (!ip)
5951+
return NULL;
5952+
hash = alloc_ftrace_hash(FTRACE_HASH_DEFAULT_BITS);
5953+
if (!hash || !add_hash_entry_direct(hash, ip, addr))
5954+
return NULL;
5955+
return hash;
59605956
}
59615957

59625958
/**
@@ -5981,89 +5977,17 @@ static void register_ftrace_direct_cb(struct rcu_head *rhp)
59815977
* -ENODEV - @ip does not point to a ftrace nop location (or not supported)
59825978
* -ENOMEM - There was an allocation failure.
59835979
*/
5984-
int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
5980+
int register_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr)
59855981
{
5986-
struct ftrace_hash *hash, *new_hash = NULL, *free_hash = NULL;
5987-
struct ftrace_func_entry *entry, *new;
5988-
int err = -EBUSY, size, i;
5989-
5990-
if (ops->func || ops->trampoline)
5991-
return -EINVAL;
5992-
if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED))
5993-
return -EINVAL;
5994-
if (ops->flags & FTRACE_OPS_FL_ENABLED)
5995-
return -EINVAL;
5996-
5997-
hash = ops->func_hash->filter_hash;
5998-
if (ftrace_hash_empty(hash))
5999-
return -EINVAL;
6000-
6001-
mutex_lock(&direct_mutex);
6002-
6003-
/* Make sure requested entries are not already registered.. */
6004-
size = 1 << hash->size_bits;
6005-
for (i = 0; i < size; i++) {
6006-
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
6007-
if (ftrace_find_rec_direct(entry->ip))
6008-
goto out_unlock;
6009-
}
6010-
}
6011-
6012-
err = -ENOMEM;
6013-
6014-
/* Make a copy hash to place the new and the old entries in */
6015-
size = hash->count + direct_functions->count;
6016-
size = fls(size);
6017-
if (size > FTRACE_HASH_MAX_BITS)
6018-
size = FTRACE_HASH_MAX_BITS;
6019-
new_hash = alloc_ftrace_hash(size);
6020-
if (!new_hash)
6021-
goto out_unlock;
6022-
6023-
/* Now copy over the existing direct entries */
6024-
size = 1 << direct_functions->size_bits;
6025-
for (i = 0; i < size; i++) {
6026-
hlist_for_each_entry(entry, &direct_functions->buckets[i], hlist) {
6027-
new = add_hash_entry(new_hash, entry->ip);
6028-
if (!new)
6029-
goto out_unlock;
6030-
new->direct = entry->direct;
6031-
}
6032-
}
6033-
6034-
/* ... and add the new entries */
6035-
size = 1 << hash->size_bits;
6036-
for (i = 0; i < size; i++) {
6037-
hlist_for_each_entry(entry, &hash->buckets[i], hlist) {
6038-
new = add_hash_entry(new_hash, entry->ip);
6039-
if (!new)
6040-
goto out_unlock;
6041-
/* Update both the copy and the hash entry */
6042-
new->direct = addr;
6043-
entry->direct = addr;
6044-
}
6045-
}
6046-
6047-
free_hash = direct_functions;
6048-
rcu_assign_pointer(direct_functions, new_hash);
6049-
new_hash = NULL;
6050-
6051-
ops->func = call_direct_funcs;
6052-
ops->flags = MULTI_FLAGS;
6053-
ops->trampoline = FTRACE_REGS_ADDR;
6054-
ops->direct_call = addr;
6055-
6056-
err = register_ftrace_function_nolock(ops);
6057-
6058-
out_unlock:
6059-
mutex_unlock(&direct_mutex);
6060-
6061-
if (free_hash && free_hash != EMPTY_HASH)
6062-
call_rcu_tasks(&free_hash->rcu, register_ftrace_direct_cb);
5982+
struct ftrace_hash *hash;
5983+
int err;
60635984

6064-
if (new_hash)
6065-
free_ftrace_hash(new_hash);
5985+
hash = hash_from_ip(ip, addr);
5986+
if (!hash)
5987+
return -ENOMEM;
60665988

5989+
err = register_ftrace_direct_hash(ops, hash);
5990+
free_ftrace_hash(hash);
60675991
return err;
60685992
}
60695993
EXPORT_SYMBOL_GPL(register_ftrace_direct);
@@ -6083,111 +6007,24 @@ EXPORT_SYMBOL_GPL(register_ftrace_direct);
60836007
* 0 on success
60846008
* -EINVAL - The @ops object was not properly registered.
60856009
*/
6086-
int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
6010+
int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr,
60876011
bool free_filters)
60886012
{
6089-
struct ftrace_hash *hash = ops->func_hash->filter_hash;
6013+
struct ftrace_hash *hash;
60906014
int err;
60916015

6092-
if (check_direct_multi(ops))
6093-
return -EINVAL;
6094-
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
6095-
return -EINVAL;
6096-
6097-
mutex_lock(&direct_mutex);
6098-
err = unregister_ftrace_function(ops);
6099-
remove_direct_functions_hash(hash, addr);
6100-
mutex_unlock(&direct_mutex);
6101-
6102-
/* cleanup for possible another register call */
6103-
ops->func = NULL;
6104-
ops->trampoline = 0;
6016+
hash = hash_from_ip(ip, addr);
6017+
if (!hash)
6018+
return -ENOMEM;
61056019

6020+
err = unregister_ftrace_direct_hash(ops, hash);
6021+
free_ftrace_hash(hash);
61066022
if (free_filters)
61076023
ftrace_free_filter(ops);
61086024
return err;
61096025
}
61106026
EXPORT_SYMBOL_GPL(unregister_ftrace_direct);
61116027

6112-
static int
6113-
__modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
6114-
{
6115-
struct ftrace_hash *hash;
6116-
struct ftrace_func_entry *entry, *iter;
6117-
static struct ftrace_ops tmp_ops = {
6118-
.func = ftrace_stub,
6119-
.flags = FTRACE_OPS_FL_STUB,
6120-
};
6121-
int i, size;
6122-
int err;
6123-
6124-
lockdep_assert_held_once(&direct_mutex);
6125-
6126-
/* Enable the tmp_ops to have the same functions as the direct ops */
6127-
ftrace_ops_init(&tmp_ops);
6128-
tmp_ops.func_hash = ops->func_hash;
6129-
tmp_ops.direct_call = addr;
6130-
6131-
err = register_ftrace_function_nolock(&tmp_ops);
6132-
if (err)
6133-
return err;
6134-
6135-
/*
6136-
* Now the ftrace_ops_list_func() is called to do the direct callers.
6137-
* We can safely change the direct functions attached to each entry.
6138-
*/
6139-
mutex_lock(&ftrace_lock);
6140-
6141-
hash = ops->func_hash->filter_hash;
6142-
size = 1 << hash->size_bits;
6143-
for (i = 0; i < size; i++) {
6144-
hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
6145-
entry = __ftrace_lookup_ip(direct_functions, iter->ip);
6146-
if (!entry)
6147-
continue;
6148-
entry->direct = addr;
6149-
}
6150-
}
6151-
/* Prevent store tearing if a trampoline concurrently accesses the value */
6152-
WRITE_ONCE(ops->direct_call, addr);
6153-
6154-
mutex_unlock(&ftrace_lock);
6155-
6156-
/* Removing the tmp_ops will add the updated direct callers to the functions */
6157-
unregister_ftrace_function(&tmp_ops);
6158-
6159-
return err;
6160-
}
6161-
6162-
/**
6163-
* modify_ftrace_direct_nolock - Modify an existing direct 'multi' call
6164-
* to call something else
6165-
* @ops: The address of the struct ftrace_ops object
6166-
* @addr: The address of the new trampoline to call at @ops functions
6167-
*
6168-
* This is used to unregister currently registered direct caller and
6169-
* register new one @addr on functions registered in @ops object.
6170-
*
6171-
* Note there's window between ftrace_shutdown and ftrace_startup calls
6172-
* where there will be no callbacks called.
6173-
*
6174-
* Caller should already have direct_mutex locked, so we don't lock
6175-
* direct_mutex here.
6176-
*
6177-
* Returns: zero on success. Non zero on error, which includes:
6178-
* -EINVAL - The @ops object was not properly registered.
6179-
*/
6180-
int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr)
6181-
{
6182-
if (check_direct_multi(ops))
6183-
return -EINVAL;
6184-
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
6185-
return -EINVAL;
6186-
6187-
return __modify_ftrace_direct(ops, addr);
6188-
}
6189-
EXPORT_SYMBOL_GPL(modify_ftrace_direct_nolock);
6190-
61916028
/**
61926029
* modify_ftrace_direct - Modify an existing direct 'multi' call
61936030
* to call something else
@@ -6203,18 +6040,17 @@ EXPORT_SYMBOL_GPL(modify_ftrace_direct_nolock);
62036040
* Returns: zero on success. Non zero on error, which includes:
62046041
* -EINVAL - The @ops object was not properly registered.
62056042
*/
6206-
int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
6043+
int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long ip, unsigned long addr, bool lock_direct_mutex)
62076044
{
6045+
struct ftrace_hash *hash;
62086046
int err;
62096047

6210-
if (check_direct_multi(ops))
6211-
return -EINVAL;
6212-
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
6213-
return -EINVAL;
6048+
hash = hash_from_ip(ip, addr);
6049+
if (!hash)
6050+
return -ENOMEM;
62146051

6215-
mutex_lock(&direct_mutex);
6216-
err = __modify_ftrace_direct(ops, addr);
6217-
mutex_unlock(&direct_mutex);
6052+
err = modify_ftrace_direct_hash(ops, hash, lock_direct_mutex);
6053+
free_ftrace_hash(hash);
62186054
return err;
62196055
}
62206056
EXPORT_SYMBOL_GPL(modify_ftrace_direct);

kernel/trace/trace_selftest.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,8 +1135,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,
11351135
* Register direct function together with graph tracer
11361136
* and make sure we get graph trace.
11371137
*/
1138-
ftrace_set_filter_ip(&direct, (unsigned long)DYN_FTRACE_TEST_NAME, 0, 0);
1139-
ret = register_ftrace_direct(&direct,
1138+
ret = register_ftrace_direct(&direct, (unsigned long)DYN_FTRACE_TEST_NAME,
11401139
(unsigned long)ftrace_stub_direct_tramp);
11411140
if (ret)
11421141
goto out;
@@ -1159,7 +1158,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,
11591158

11601159
unregister_ftrace_graph(&fgraph_ops);
11611160

1162-
ret = unregister_ftrace_direct(&direct,
1161+
ret = unregister_ftrace_direct(&direct, (unsigned long)DYN_FTRACE_TEST_NAME,
11631162
(unsigned long)ftrace_stub_direct_tramp,
11641163
true);
11651164
if (ret)

0 commit comments

Comments
 (0)