Skip to content

Commit 424f6a3

Browse files
olsajirianakryiko
authored andcommitted
bpf,x86: Use single ftrace_ops for direct calls
Using single ftrace_ops for direct calls update instead of allocating ftrace_ops object for each trampoline. With single ftrace_ops object we can use update_ftrace_direct_* api that allows multiple ip sites updates on single ftrace_ops object. Adding HAVE_SINGLE_FTRACE_DIRECT_OPS config option to be enabled on each arch that supports this. At the moment we can enable this only on x86 arch, because arm relies on ftrace_ops object representing just single trampoline image (stored in ftrace_ops::direct_call). Archs that do not support this will continue to use *_ftrace_direct api. Signed-off-by: Jiri Olsa <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Reviewed-by: Steven Rostedt (Google) <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 956747e commit 424f6a3

File tree

4 files changed

+200
-31
lines changed

4 files changed

+200
-31
lines changed

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ config X86
336336
select SCHED_SMT if SMP
337337
select ARCH_SUPPORTS_SCHED_CLUSTER if SMP
338338
select ARCH_SUPPORTS_SCHED_MC if SMP
339+
select HAVE_SINGLE_FTRACE_DIRECT_OPS if X86_64 && DYNAMIC_FTRACE_WITH_DIRECT_CALLS
339340

340341
config INSTRUCTION_DECODER
341342
def_bool y

kernel/bpf/trampoline.c

Lines changed: 190 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,40 @@ static DEFINE_MUTEX(trampoline_mutex);
3333
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
3434
static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex);
3535

36+
#ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS
37+
static struct bpf_trampoline *direct_ops_ip_lookup(struct ftrace_ops *ops, unsigned long ip)
38+
{
39+
struct hlist_head *head_ip;
40+
struct bpf_trampoline *tr;
41+
42+
mutex_lock(&trampoline_mutex);
43+
head_ip = &trampoline_ip_table[hash_64(ip, TRAMPOLINE_HASH_BITS)];
44+
hlist_for_each_entry(tr, head_ip, hlist_ip) {
45+
if (tr->ip == ip)
46+
goto out;
47+
}
48+
tr = NULL;
49+
out:
50+
mutex_unlock(&trampoline_mutex);
51+
return tr;
52+
}
53+
#else
54+
static struct bpf_trampoline *direct_ops_ip_lookup(struct ftrace_ops *ops, unsigned long ip)
55+
{
56+
return ops->private;
57+
}
58+
#endif /* CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS */
59+
3660
static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, unsigned long ip,
3761
enum ftrace_ops_cmd cmd)
3862
{
39-
struct bpf_trampoline *tr = ops->private;
63+
struct bpf_trampoline *tr;
4064
int ret = 0;
4165

66+
tr = direct_ops_ip_lookup(ops, ip);
67+
if (!tr)
68+
return -EINVAL;
69+
4270
if (cmd == FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF) {
4371
/* This is called inside register_ftrace_direct_multi(), so
4472
* tr->mutex is already locked.
@@ -144,6 +172,162 @@ void bpf_image_ksym_del(struct bpf_ksym *ksym)
144172
PAGE_SIZE, true, ksym->name);
145173
}
146174

175+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
176+
#ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS
177+
/*
178+
* We have only single direct_ops which contains all the direct call
179+
* sites and is the only global ftrace_ops for all trampolines.
180+
*
181+
* We use 'update_ftrace_direct_*' api for attachment.
182+
*/
183+
struct ftrace_ops direct_ops = {
184+
.ops_func = bpf_tramp_ftrace_ops_func,
185+
};
186+
187+
static int direct_ops_alloc(struct bpf_trampoline *tr)
188+
{
189+
tr->fops = &direct_ops;
190+
return 0;
191+
}
192+
193+
static void direct_ops_free(struct bpf_trampoline *tr) { }
194+
195+
static struct ftrace_hash *hash_from_ip(struct bpf_trampoline *tr, void *ptr)
196+
{
197+
unsigned long ip, addr = (unsigned long) ptr;
198+
struct ftrace_hash *hash;
199+
200+
ip = ftrace_location(tr->ip);
201+
if (!ip)
202+
return NULL;
203+
hash = alloc_ftrace_hash(FTRACE_HASH_DEFAULT_BITS);
204+
if (!hash)
205+
return NULL;
206+
if (bpf_trampoline_use_jmp(tr->flags))
207+
addr = ftrace_jmp_set(addr);
208+
if (!add_ftrace_hash_entry_direct(hash, ip, addr)) {
209+
free_ftrace_hash(hash);
210+
return NULL;
211+
}
212+
return hash;
213+
}
214+
215+
static int direct_ops_add(struct bpf_trampoline *tr, void *addr)
216+
{
217+
struct ftrace_hash *hash = hash_from_ip(tr, addr);
218+
int err;
219+
220+
if (!hash)
221+
return -ENOMEM;
222+
err = update_ftrace_direct_add(tr->fops, hash);
223+
free_ftrace_hash(hash);
224+
return err;
225+
}
226+
227+
static int direct_ops_del(struct bpf_trampoline *tr, void *addr)
228+
{
229+
struct ftrace_hash *hash = hash_from_ip(tr, addr);
230+
int err;
231+
232+
if (!hash)
233+
return -ENOMEM;
234+
err = update_ftrace_direct_del(tr->fops, hash);
235+
free_ftrace_hash(hash);
236+
return err;
237+
}
238+
239+
static int direct_ops_mod(struct bpf_trampoline *tr, void *addr, bool lock_direct_mutex)
240+
{
241+
struct ftrace_hash *hash = hash_from_ip(tr, addr);
242+
int err;
243+
244+
if (!hash)
245+
return -ENOMEM;
246+
err = update_ftrace_direct_mod(tr->fops, hash, lock_direct_mutex);
247+
free_ftrace_hash(hash);
248+
return err;
249+
}
250+
#else
251+
/*
252+
* We allocate ftrace_ops object for each trampoline and it contains
253+
* call site specific for that trampoline.
254+
*
255+
* We use *_ftrace_direct api for attachment.
256+
*/
257+
static int direct_ops_alloc(struct bpf_trampoline *tr)
258+
{
259+
tr->fops = kzalloc(sizeof(struct ftrace_ops), GFP_KERNEL);
260+
if (!tr->fops)
261+
return -ENOMEM;
262+
tr->fops->private = tr;
263+
tr->fops->ops_func = bpf_tramp_ftrace_ops_func;
264+
return 0;
265+
}
266+
267+
static void direct_ops_free(struct bpf_trampoline *tr)
268+
{
269+
if (!tr->fops)
270+
return;
271+
ftrace_free_filter(tr->fops);
272+
kfree(tr->fops);
273+
}
274+
275+
static int direct_ops_add(struct bpf_trampoline *tr, void *ptr)
276+
{
277+
unsigned long addr = (unsigned long) ptr;
278+
struct ftrace_ops *ops = tr->fops;
279+
int ret;
280+
281+
if (bpf_trampoline_use_jmp(tr->flags))
282+
addr = ftrace_jmp_set(addr);
283+
284+
ret = ftrace_set_filter_ip(ops, tr->ip, 0, 1);
285+
if (ret)
286+
return ret;
287+
return register_ftrace_direct(ops, addr);
288+
}
289+
290+
static int direct_ops_del(struct bpf_trampoline *tr, void *addr)
291+
{
292+
return unregister_ftrace_direct(tr->fops, (long)addr, false);
293+
}
294+
295+
static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex)
296+
{
297+
unsigned long addr = (unsigned long) ptr;
298+
struct ftrace_ops *ops = tr->fops;
299+
300+
if (bpf_trampoline_use_jmp(tr->flags))
301+
addr = ftrace_jmp_set(addr);
302+
if (lock_direct_mutex)
303+
return modify_ftrace_direct(ops, addr);
304+
return modify_ftrace_direct_nolock(ops, addr);
305+
}
306+
#endif /* CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS */
307+
#else
308+
static void direct_ops_free(struct bpf_trampoline *tr) { }
309+
310+
static int direct_ops_alloc(struct bpf_trampoline *tr)
311+
{
312+
return 0;
313+
}
314+
315+
static int direct_ops_add(struct bpf_trampoline *tr, void *addr)
316+
{
317+
return -ENODEV;
318+
}
319+
320+
static int direct_ops_del(struct bpf_trampoline *tr, void *addr)
321+
{
322+
return -ENODEV;
323+
}
324+
325+
static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex)
326+
{
327+
return -ENODEV;
328+
}
329+
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
330+
147331
static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip)
148332
{
149333
struct bpf_trampoline *tr;
@@ -161,16 +345,11 @@ static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip)
161345
tr = kzalloc(sizeof(*tr), GFP_KERNEL);
162346
if (!tr)
163347
goto out;
164-
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
165-
tr->fops = kzalloc(sizeof(struct ftrace_ops), GFP_KERNEL);
166-
if (!tr->fops) {
348+
if (direct_ops_alloc(tr)) {
167349
kfree(tr);
168350
tr = NULL;
169351
goto out;
170352
}
171-
tr->fops->private = tr;
172-
tr->fops->ops_func = bpf_tramp_ftrace_ops_func;
173-
#endif
174353

175354
tr->key = key;
176355
tr->ip = ftrace_location(ip);
@@ -213,7 +392,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, u32 orig_flags,
213392
int ret;
214393

215394
if (tr->func.ftrace_managed)
216-
ret = unregister_ftrace_direct(tr->fops, (long)old_addr, false);
395+
ret = direct_ops_del(tr, old_addr);
217396
else
218397
ret = bpf_trampoline_update_fentry(tr, orig_flags, old_addr, NULL);
219398

@@ -227,15 +406,7 @@ static int modify_fentry(struct bpf_trampoline *tr, u32 orig_flags,
227406
int ret;
228407

229408
if (tr->func.ftrace_managed) {
230-
unsigned long addr = (unsigned long) new_addr;
231-
232-
if (bpf_trampoline_use_jmp(tr->flags))
233-
addr = ftrace_jmp_set(addr);
234-
235-
if (lock_direct_mutex)
236-
ret = modify_ftrace_direct(tr->fops, addr);
237-
else
238-
ret = modify_ftrace_direct_nolock(tr->fops, addr);
409+
ret = direct_ops_mod(tr, new_addr, lock_direct_mutex);
239410
} else {
240411
ret = bpf_trampoline_update_fentry(tr, orig_flags, old_addr,
241412
new_addr);
@@ -258,15 +429,7 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
258429
}
259430

260431
if (tr->func.ftrace_managed) {
261-
unsigned long addr = (unsigned long) new_addr;
262-
263-
if (bpf_trampoline_use_jmp(tr->flags))
264-
addr = ftrace_jmp_set(addr);
265-
266-
ret = ftrace_set_filter_ip(tr->fops, (unsigned long)ip, 0, 1);
267-
if (ret)
268-
return ret;
269-
ret = register_ftrace_direct(tr->fops, addr);
432+
ret = direct_ops_add(tr, new_addr);
270433
} else {
271434
ret = bpf_trampoline_update_fentry(tr, 0, NULL, new_addr);
272435
}
@@ -947,10 +1110,7 @@ void bpf_trampoline_put(struct bpf_trampoline *tr)
9471110
*/
9481111
hlist_del(&tr->hlist_key);
9491112
hlist_del(&tr->hlist_ip);
950-
if (tr->fops) {
951-
ftrace_free_filter(tr->fops);
952-
kfree(tr->fops);
953-
}
1113+
direct_ops_free(tr);
9541114
kfree(tr);
9551115
out:
9561116
mutex_unlock(&trampoline_mutex);

kernel/trace/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ config HAVE_DYNAMIC_FTRACE_WITH_REGS
5050
config HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
5151
bool
5252

53+
config HAVE_SINGLE_FTRACE_DIRECT_OPS
54+
bool
55+
5356
config HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
5457
bool
5558

kernel/trace/ftrace.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2631,8 +2631,13 @@ unsigned long ftrace_find_rec_direct(unsigned long ip)
26312631
static void call_direct_funcs(unsigned long ip, unsigned long pip,
26322632
struct ftrace_ops *ops, struct ftrace_regs *fregs)
26332633
{
2634-
unsigned long addr = READ_ONCE(ops->direct_call);
2634+
unsigned long addr;
26352635

2636+
#ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS
2637+
addr = ftrace_find_rec_direct(ip);
2638+
#else
2639+
addr = READ_ONCE(ops->direct_call);
2640+
#endif
26362641
if (!addr)
26372642
return;
26382643

0 commit comments

Comments
 (0)