Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ftrace-bpf-use-single-direct-ops-for-bpf-trampolines'

Jiri Olsa says:

====================
ftrace,bpf: Use single direct ops for bpf trampolines

hi,
while poking the multi-tracing interface I ended up with just one ftrace_ops
object to attach all trampolines.

This change allows to use less direct API calls during the attachment changes
in the future code, so in effect speeding up the attachment.

In current code we get a speed up from using just a single ftrace_ops object.

- with current code:

Performance counter stats for 'bpftrace -e fentry:vmlinux:ksys_* {} -c true':

6,364,157,902 cycles:k
828,728,902 cycles:u
1,064,803,824 instructions:u # 1.28 insn per cycle
23,797,500,067 instructions:k # 3.74 insn per cycle

4.416004987 seconds time elapsed

0.164121000 seconds user
1.289550000 seconds sys

- with the fix:

Performance counter stats for 'bpftrace -e fentry:vmlinux:ksys_* {} -c true':

6,535,857,905 cycles:k
810,809,429 cycles:u
1,064,594,027 instructions:u # 1.31 insn per cycle
23,962,552,894 instructions:k # 3.67 insn per cycle

1.666961239 seconds time elapsed

0.157412000 seconds user
1.283396000 seconds sys

The speedup seems to be related to the fact that with single ftrace_ops object
we don't call ftrace_shutdown anymore (we use ftrace_update_ops instead) and
we skip the synchronize rcu calls (each ~100ms) at the end of that function.

rfc: https://lore.kernel.org/bpf/20250729102813.1531457-1-jolsa@kernel.org/
v1: https://lore.kernel.org/bpf/20250923215147.1571952-1-jolsa@kernel.org/
v2: https://lore.kernel.org/bpf/20251113123750.2507435-1-jolsa@kernel.org/
v3: https://lore.kernel.org/bpf/20251120212402.466524-1-jolsa@kernel.org/
v4: https://lore.kernel.org/bpf/20251203082402.78816-1-jolsa@kernel.org/
v5: https://lore.kernel.org/bpf/20251215211402.353056-10-jolsa@kernel.org/

v6 changes:
- rename add_hash_entry_direct to add_ftrace_hash_entry_direct [Steven]
- factor hash_add/hash_sub [Steven]
- add kerneldoc header for update_ftrace_direct_* functions [Steven]
- few assorted smaller fixes [Steven]
- added missing direct_ops wrappers for !CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
case [Steven]

v5 changes:
- do not export ftrace_hash object [Steven]
- fix update_ftrace_direct_add new_filter_hash leak [ci]

v4 changes:
- rebased on top of bpf-next/master (with jmp attach changes)
added patch 1 to deal with that
- added extra checks for update_ftrace_direct_del/mod to address
the ci bot review

v3 changes:
- rebased on top of bpf-next/master
- fixed update_ftrace_direct_del cleanup path
- added missing inline to update_ftrace_direct_* stubs

v2 changes:
- rebased on top fo bpf-next/master plus Song's livepatch fixes [1]
- renamed the API functions [2] [Steven]
- do not export the new api [Steven]
- kept the original direct interface:

I'm not sure if we want to melt both *_ftrace_direct and the new interface
into single one. It's bit different in semantic (hence the name change as
Steven suggested [2]) and I don't think the changes are not that big so
we could easily keep both APIs.

v1 changes:
- make the change x86 specific, after discussing with Mark options for
arm64 [Mark]

thanks,
jirka

[1] https://lore.kernel.org/bpf/20251027175023.1521602-1-song@kernel.org/
[2] https://lore.kernel.org/bpf/20250924050415.4aefcb91@batman.local.home/
---
====================

Link: https://patch.msgid.link/20251230145010.103439-1-jolsa@kernel.org
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+632 -75
+1
arch/x86/Kconfig
··· 336 336 select SCHED_SMT if SMP 337 337 select ARCH_SUPPORTS_SCHED_CLUSTER if SMP 338 338 select ARCH_SUPPORTS_SCHED_MC if SMP 339 + select HAVE_SINGLE_FTRACE_DIRECT_OPS if X86_64 && DYNAMIC_FTRACE_WITH_DIRECT_CALLS 339 340 340 341 config INSTRUCTION_DECODER 341 342 def_bool y
+5 -2
include/linux/bpf.h
··· 1329 1329 }; 1330 1330 1331 1331 struct bpf_trampoline { 1332 - /* hlist for trampoline_table */ 1333 - struct hlist_node hlist; 1332 + /* hlist for trampoline_key_table */ 1333 + struct hlist_node hlist_key; 1334 + /* hlist for trampoline_ip_table */ 1335 + struct hlist_node hlist_ip; 1334 1336 struct ftrace_ops *fops; 1335 1337 /* serializes access to fields of this trampoline */ 1336 1338 struct mutex mutex; 1337 1339 refcount_t refcnt; 1338 1340 u32 flags; 1339 1341 u64 key; 1342 + unsigned long ip; 1340 1343 struct { 1341 1344 struct btf_func_model model; 1342 1345 void *addr;
+29 -2
include/linux/ftrace.h
··· 82 82 83 83 struct module; 84 84 struct ftrace_hash; 85 + struct ftrace_func_entry; 85 86 86 87 #if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_MODULES) && \ 87 88 defined(CONFIG_DYNAMIC_FTRACE) ··· 360 359 FTRACE_OPS_FL_DIRECT = BIT(17), 361 360 FTRACE_OPS_FL_SUBOP = BIT(18), 362 361 FTRACE_OPS_FL_GRAPH = BIT(19), 363 - FTRACE_OPS_FL_JMP = BIT(20), 364 362 }; 365 363 366 364 #ifndef CONFIG_DYNAMIC_FTRACE_WITH_ARGS ··· 403 403 * Negative on failure. The return value is dependent on the 404 404 * callback. 405 405 */ 406 - typedef int (*ftrace_ops_func_t)(struct ftrace_ops *op, enum ftrace_ops_cmd cmd); 406 + typedef int (*ftrace_ops_func_t)(struct ftrace_ops *op, unsigned long ip, enum ftrace_ops_cmd cmd); 407 407 408 408 #ifdef CONFIG_DYNAMIC_FTRACE 409 + 410 + #define FTRACE_HASH_DEFAULT_BITS 10 411 + 412 + struct ftrace_hash *alloc_ftrace_hash(int size_bits); 413 + void free_ftrace_hash(struct ftrace_hash *hash); 414 + struct ftrace_func_entry *add_ftrace_hash_entry_direct(struct ftrace_hash *hash, 415 + unsigned long ip, unsigned long direct); 416 + 409 417 /* The hash used to know what functions callbacks trace */ 410 418 struct ftrace_ops_hash { 411 419 struct ftrace_hash __rcu *notrace_hash; ··· 543 535 int modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr); 544 536 int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr); 545 537 538 + int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash); 539 + int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash); 540 + int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock); 541 + 546 542 void ftrace_stub_direct_tramp(void); 547 543 548 544 #else ··· 569 557 return -ENODEV; 570 558 } 571 559 static inline int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr) 560 + { 561 + return -ENODEV; 562 + } 563 + 564 + static inline int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash) 565 + { 566 + return -ENODEV; 567 + } 568 + 569 + static inline int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) 570 + { 571 + return -ENODEV; 572 + } 573 + 574 + static inline int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock) 572 575 { 573 576 return -ENODEV; 574 577 }
+212 -47
kernel/bpf/trampoline.c
··· 24 24 #define TRAMPOLINE_HASH_BITS 10 25 25 #define TRAMPOLINE_TABLE_SIZE (1 << TRAMPOLINE_HASH_BITS) 26 26 27 - static struct hlist_head trampoline_table[TRAMPOLINE_TABLE_SIZE]; 27 + static struct hlist_head trampoline_key_table[TRAMPOLINE_TABLE_SIZE]; 28 + static struct hlist_head trampoline_ip_table[TRAMPOLINE_TABLE_SIZE]; 28 29 29 - /* serializes access to trampoline_table */ 30 + /* serializes access to trampoline tables */ 30 31 static DEFINE_MUTEX(trampoline_mutex); 31 32 32 33 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS 33 34 static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex); 34 35 35 - static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, enum ftrace_ops_cmd cmd) 36 + #ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS 37 + static struct bpf_trampoline *direct_ops_ip_lookup(struct ftrace_ops *ops, unsigned long ip) 36 38 { 37 - struct bpf_trampoline *tr = ops->private; 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 + 60 + static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, unsigned long ip, 61 + enum ftrace_ops_cmd cmd) 62 + { 63 + struct bpf_trampoline *tr; 38 64 int ret = 0; 65 + 66 + tr = direct_ops_ip_lookup(ops, ip); 67 + if (!tr) 68 + return -EINVAL; 39 69 40 70 if (cmd == FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF) { 41 71 /* This is called inside register_ftrace_direct_multi(), so ··· 172 142 PAGE_SIZE, true, ksym->name); 173 143 } 174 144 175 - static struct bpf_trampoline *bpf_trampoline_lookup(u64 key) 145 + #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS 146 + #ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS 147 + /* 148 + * We have only single direct_ops which contains all the direct call 149 + * sites and is the only global ftrace_ops for all trampolines. 150 + * 151 + * We use 'update_ftrace_direct_*' api for attachment. 152 + */ 153 + struct ftrace_ops direct_ops = { 154 + .ops_func = bpf_tramp_ftrace_ops_func, 155 + }; 156 + 157 + static int direct_ops_alloc(struct bpf_trampoline *tr) 158 + { 159 + tr->fops = &direct_ops; 160 + return 0; 161 + } 162 + 163 + static void direct_ops_free(struct bpf_trampoline *tr) { } 164 + 165 + static struct ftrace_hash *hash_from_ip(struct bpf_trampoline *tr, void *ptr) 166 + { 167 + unsigned long ip, addr = (unsigned long) ptr; 168 + struct ftrace_hash *hash; 169 + 170 + ip = ftrace_location(tr->ip); 171 + if (!ip) 172 + return NULL; 173 + hash = alloc_ftrace_hash(FTRACE_HASH_DEFAULT_BITS); 174 + if (!hash) 175 + return NULL; 176 + if (bpf_trampoline_use_jmp(tr->flags)) 177 + addr = ftrace_jmp_set(addr); 178 + if (!add_ftrace_hash_entry_direct(hash, ip, addr)) { 179 + free_ftrace_hash(hash); 180 + return NULL; 181 + } 182 + return hash; 183 + } 184 + 185 + static int direct_ops_add(struct bpf_trampoline *tr, void *addr) 186 + { 187 + struct ftrace_hash *hash = hash_from_ip(tr, addr); 188 + int err; 189 + 190 + if (!hash) 191 + return -ENOMEM; 192 + err = update_ftrace_direct_add(tr->fops, hash); 193 + free_ftrace_hash(hash); 194 + return err; 195 + } 196 + 197 + static int direct_ops_del(struct bpf_trampoline *tr, void *addr) 198 + { 199 + struct ftrace_hash *hash = hash_from_ip(tr, addr); 200 + int err; 201 + 202 + if (!hash) 203 + return -ENOMEM; 204 + err = update_ftrace_direct_del(tr->fops, hash); 205 + free_ftrace_hash(hash); 206 + return err; 207 + } 208 + 209 + static int direct_ops_mod(struct bpf_trampoline *tr, void *addr, bool lock_direct_mutex) 210 + { 211 + struct ftrace_hash *hash = hash_from_ip(tr, addr); 212 + int err; 213 + 214 + if (!hash) 215 + return -ENOMEM; 216 + err = update_ftrace_direct_mod(tr->fops, hash, lock_direct_mutex); 217 + free_ftrace_hash(hash); 218 + return err; 219 + } 220 + #else 221 + /* 222 + * We allocate ftrace_ops object for each trampoline and it contains 223 + * call site specific for that trampoline. 224 + * 225 + * We use *_ftrace_direct api for attachment. 226 + */ 227 + static int direct_ops_alloc(struct bpf_trampoline *tr) 228 + { 229 + tr->fops = kzalloc(sizeof(struct ftrace_ops), GFP_KERNEL); 230 + if (!tr->fops) 231 + return -ENOMEM; 232 + tr->fops->private = tr; 233 + tr->fops->ops_func = bpf_tramp_ftrace_ops_func; 234 + return 0; 235 + } 236 + 237 + static void direct_ops_free(struct bpf_trampoline *tr) 238 + { 239 + if (!tr->fops) 240 + return; 241 + ftrace_free_filter(tr->fops); 242 + kfree(tr->fops); 243 + } 244 + 245 + static int direct_ops_add(struct bpf_trampoline *tr, void *ptr) 246 + { 247 + unsigned long addr = (unsigned long) ptr; 248 + struct ftrace_ops *ops = tr->fops; 249 + int ret; 250 + 251 + if (bpf_trampoline_use_jmp(tr->flags)) 252 + addr = ftrace_jmp_set(addr); 253 + 254 + ret = ftrace_set_filter_ip(ops, tr->ip, 0, 1); 255 + if (ret) 256 + return ret; 257 + return register_ftrace_direct(ops, addr); 258 + } 259 + 260 + static int direct_ops_del(struct bpf_trampoline *tr, void *addr) 261 + { 262 + return unregister_ftrace_direct(tr->fops, (long)addr, false); 263 + } 264 + 265 + static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex) 266 + { 267 + unsigned long addr = (unsigned long) ptr; 268 + struct ftrace_ops *ops = tr->fops; 269 + 270 + if (bpf_trampoline_use_jmp(tr->flags)) 271 + addr = ftrace_jmp_set(addr); 272 + if (lock_direct_mutex) 273 + return modify_ftrace_direct(ops, addr); 274 + return modify_ftrace_direct_nolock(ops, addr); 275 + } 276 + #endif /* CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS */ 277 + #else 278 + static void direct_ops_free(struct bpf_trampoline *tr) { } 279 + 280 + static int direct_ops_alloc(struct bpf_trampoline *tr) 281 + { 282 + return 0; 283 + } 284 + 285 + static int direct_ops_add(struct bpf_trampoline *tr, void *addr) 286 + { 287 + return -ENODEV; 288 + } 289 + 290 + static int direct_ops_del(struct bpf_trampoline *tr, void *addr) 291 + { 292 + return -ENODEV; 293 + } 294 + 295 + static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex) 296 + { 297 + return -ENODEV; 298 + } 299 + #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ 300 + 301 + static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip) 176 302 { 177 303 struct bpf_trampoline *tr; 178 304 struct hlist_head *head; 179 305 int i; 180 306 181 307 mutex_lock(&trampoline_mutex); 182 - head = &trampoline_table[hash_64(key, TRAMPOLINE_HASH_BITS)]; 183 - hlist_for_each_entry(tr, head, hlist) { 308 + head = &trampoline_key_table[hash_64(key, TRAMPOLINE_HASH_BITS)]; 309 + hlist_for_each_entry(tr, head, hlist_key) { 184 310 if (tr->key == key) { 185 311 refcount_inc(&tr->refcnt); 186 312 goto out; ··· 345 159 tr = kzalloc(sizeof(*tr), GFP_KERNEL); 346 160 if (!tr) 347 161 goto out; 348 - #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS 349 - tr->fops = kzalloc(sizeof(struct ftrace_ops), GFP_KERNEL); 350 - if (!tr->fops) { 162 + if (direct_ops_alloc(tr)) { 351 163 kfree(tr); 352 164 tr = NULL; 353 165 goto out; 354 166 } 355 - tr->fops->private = tr; 356 - tr->fops->ops_func = bpf_tramp_ftrace_ops_func; 357 - #endif 358 167 359 168 tr->key = key; 360 - INIT_HLIST_NODE(&tr->hlist); 361 - hlist_add_head(&tr->hlist, head); 169 + tr->ip = ftrace_location(ip); 170 + INIT_HLIST_NODE(&tr->hlist_key); 171 + INIT_HLIST_NODE(&tr->hlist_ip); 172 + hlist_add_head(&tr->hlist_key, head); 173 + head = &trampoline_ip_table[hash_64(tr->ip, TRAMPOLINE_HASH_BITS)]; 174 + hlist_add_head(&tr->hlist_ip, head); 362 175 refcount_set(&tr->refcnt, 1); 363 176 mutex_init(&tr->mutex); 364 177 for (i = 0; i < BPF_TRAMP_MAX; i++) ··· 392 207 int ret; 393 208 394 209 if (tr->func.ftrace_managed) 395 - ret = unregister_ftrace_direct(tr->fops, (long)old_addr, false); 210 + ret = direct_ops_del(tr, old_addr); 396 211 else 397 212 ret = bpf_trampoline_update_fentry(tr, orig_flags, old_addr, NULL); 398 213 ··· 406 221 int ret; 407 222 408 223 if (tr->func.ftrace_managed) { 409 - if (lock_direct_mutex) 410 - ret = modify_ftrace_direct(tr->fops, (long)new_addr); 411 - else 412 - ret = modify_ftrace_direct_nolock(tr->fops, (long)new_addr); 224 + ret = direct_ops_mod(tr, new_addr, lock_direct_mutex); 413 225 } else { 414 226 ret = bpf_trampoline_update_fentry(tr, orig_flags, old_addr, 415 227 new_addr); ··· 429 247 } 430 248 431 249 if (tr->func.ftrace_managed) { 432 - ret = ftrace_set_filter_ip(tr->fops, (unsigned long)ip, 0, 1); 433 - if (ret) 434 - return ret; 435 - ret = register_ftrace_direct(tr->fops, (long)new_addr); 250 + ret = direct_ops_add(tr, new_addr); 436 251 } else { 437 252 ret = bpf_trampoline_update_fentry(tr, 0, NULL, new_addr); 438 253 } ··· 685 506 if (err) 686 507 goto out_free; 687 508 688 - #ifdef CONFIG_DYNAMIC_FTRACE_WITH_JMP 689 - if (bpf_trampoline_use_jmp(tr->flags)) 690 - tr->fops->flags |= FTRACE_OPS_FL_JMP; 691 - else 692 - tr->fops->flags &= ~FTRACE_OPS_FL_JMP; 693 - #endif 694 - 695 509 WARN_ON(tr->cur_image && total == 0); 696 510 if (tr->cur_image) 697 511 /* progs already running at this address */ ··· 712 540 tr->cur_image = im; 713 541 out: 714 542 /* If any error happens, restore previous flags */ 715 - if (err) { 543 + if (err) 716 544 tr->flags = orig_flags; 717 - #ifdef CONFIG_DYNAMIC_FTRACE_WITH_JMP 718 - if (bpf_trampoline_use_jmp(tr->flags)) 719 - tr->fops->flags |= FTRACE_OPS_FL_JMP; 720 - else 721 - tr->fops->flags &= ~FTRACE_OPS_FL_JMP; 722 - #endif 723 - } 724 545 kfree(tlinks); 725 546 return err; 726 547 ··· 1052 887 prog->aux->attach_btf_id); 1053 888 1054 889 bpf_lsm_find_cgroup_shim(prog, &bpf_func); 1055 - tr = bpf_trampoline_lookup(key); 890 + tr = bpf_trampoline_lookup(key, 0); 1056 891 if (WARN_ON_ONCE(!tr)) 1057 892 return; 1058 893 ··· 1072 907 { 1073 908 struct bpf_trampoline *tr; 1074 909 1075 - tr = bpf_trampoline_lookup(key); 910 + tr = bpf_trampoline_lookup(key, tgt_info->tgt_addr); 1076 911 if (!tr) 1077 912 return NULL; 1078 913 ··· 1108 943 * fexit progs. The fentry-only trampoline will be freed via 1109 944 * multiple rcu callbacks. 1110 945 */ 1111 - hlist_del(&tr->hlist); 1112 - if (tr->fops) { 1113 - ftrace_free_filter(tr->fops); 1114 - kfree(tr->fops); 1115 - } 946 + hlist_del(&tr->hlist_key); 947 + hlist_del(&tr->hlist_ip); 948 + direct_ops_free(tr); 1116 949 kfree(tr); 1117 950 out: 1118 951 mutex_unlock(&trampoline_mutex); ··· 1379 1216 int i; 1380 1217 1381 1218 for (i = 0; i < TRAMPOLINE_TABLE_SIZE; i++) 1382 - INIT_HLIST_HEAD(&trampoline_table[i]); 1219 + INIT_HLIST_HEAD(&trampoline_key_table[i]); 1220 + for (i = 0; i < TRAMPOLINE_TABLE_SIZE; i++) 1221 + INIT_HLIST_HEAD(&trampoline_ip_table[i]); 1383 1222 return 0; 1384 1223 } 1385 1224 late_initcall(init_trampolines);
+3
kernel/trace/Kconfig
··· 50 50 config HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS 51 51 bool 52 52 53 + config HAVE_SINGLE_FTRACE_DIRECT_OPS 54 + bool 55 + 53 56 config HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS 54 57 bool 55 58
+382 -24
kernel/trace/ftrace.c
··· 68 68 }) 69 69 70 70 /* hash bits for specific function selection */ 71 - #define FTRACE_HASH_DEFAULT_BITS 10 72 71 #define FTRACE_HASH_MAX_BITS 12 73 72 74 73 #ifdef CONFIG_DYNAMIC_FTRACE ··· 1210 1211 hash->count++; 1211 1212 } 1212 1213 1213 - static struct ftrace_func_entry * 1214 - add_hash_entry(struct ftrace_hash *hash, unsigned long ip) 1214 + struct ftrace_func_entry * 1215 + add_ftrace_hash_entry_direct(struct ftrace_hash *hash, unsigned long ip, unsigned long direct) 1215 1216 { 1216 1217 struct ftrace_func_entry *entry; 1217 1218 ··· 1220 1221 return NULL; 1221 1222 1222 1223 entry->ip = ip; 1224 + entry->direct = direct; 1223 1225 __add_hash_entry(hash, entry); 1224 1226 1225 1227 return entry; 1228 + } 1229 + 1230 + static struct ftrace_func_entry * 1231 + add_hash_entry(struct ftrace_hash *hash, unsigned long ip) 1232 + { 1233 + return add_ftrace_hash_entry_direct(hash, ip, 0); 1226 1234 } 1227 1235 1228 1236 static void ··· 1290 1284 mutex_unlock(&ftrace_lock); 1291 1285 } 1292 1286 1293 - static void free_ftrace_hash(struct ftrace_hash *hash) 1287 + void free_ftrace_hash(struct ftrace_hash *hash) 1294 1288 { 1295 1289 if (!hash || hash == EMPTY_HASH) 1296 1290 return; ··· 1330 1324 } 1331 1325 EXPORT_SYMBOL_GPL(ftrace_free_filter); 1332 1326 1333 - static struct ftrace_hash *alloc_ftrace_hash(int size_bits) 1327 + struct ftrace_hash *alloc_ftrace_hash(int size_bits) 1334 1328 { 1335 1329 struct ftrace_hash *hash; 1336 1330 int size; ··· 1404 1398 size = 1 << hash->size_bits; 1405 1399 for (i = 0; i < size; i++) { 1406 1400 hlist_for_each_entry(entry, &hash->buckets[i], hlist) { 1407 - if (add_hash_entry(new_hash, entry->ip) == NULL) 1401 + if (add_ftrace_hash_entry_direct(new_hash, entry->ip, entry->direct) == NULL) 1408 1402 goto free_hash; 1409 1403 } 1410 1404 } ··· 2075 2069 */ 2076 2070 if (!ops->ops_func) 2077 2071 return -EBUSY; 2078 - ret = ops->ops_func(ops, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF); 2072 + ret = ops->ops_func(ops, rec->ip, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF); 2079 2073 if (ret) 2080 2074 return ret; 2081 2075 } else if (is_ipmodify) { ··· 2631 2625 static void call_direct_funcs(unsigned long ip, unsigned long pip, 2632 2626 struct ftrace_ops *ops, struct ftrace_regs *fregs) 2633 2627 { 2634 - unsigned long addr = READ_ONCE(ops->direct_call); 2628 + unsigned long addr; 2635 2629 2630 + #ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS 2631 + addr = ftrace_find_rec_direct(ip); 2632 + #else 2633 + addr = READ_ONCE(ops->direct_call); 2634 + #endif 2636 2635 if (!addr) 2637 2636 return; 2638 2637 ··· 6057 6046 if (ftrace_hash_empty(hash)) 6058 6047 return -EINVAL; 6059 6048 6060 - /* This is a "raw" address, and this should never happen. */ 6061 - if (WARN_ON_ONCE(ftrace_is_jmp(addr))) 6062 - return -EINVAL; 6063 - 6064 6049 mutex_lock(&direct_mutex); 6065 - 6066 - if (ops->flags & FTRACE_OPS_FL_JMP) 6067 - addr = ftrace_jmp_set(addr); 6068 6050 6069 6051 /* Make sure requested entries are not already registered.. */ 6070 6052 size = 1 << hash->size_bits; ··· 6179 6175 6180 6176 lockdep_assert_held_once(&direct_mutex); 6181 6177 6182 - /* This is a "raw" address, and this should never happen. */ 6183 - if (WARN_ON_ONCE(ftrace_is_jmp(addr))) 6184 - return -EINVAL; 6185 - 6186 - if (ops->flags & FTRACE_OPS_FL_JMP) 6187 - addr = ftrace_jmp_set(addr); 6188 - 6189 6178 /* Enable the tmp_ops to have the same functions as the direct ops */ 6190 6179 ftrace_ops_init(&tmp_ops); 6191 6180 tmp_ops.func_hash = ops->func_hash; ··· 6283 6286 return err; 6284 6287 } 6285 6288 EXPORT_SYMBOL_GPL(modify_ftrace_direct); 6289 + 6290 + static unsigned long hash_count(struct ftrace_hash *hash) 6291 + { 6292 + return hash ? hash->count : 0; 6293 + } 6294 + 6295 + /** 6296 + * hash_add - adds two struct ftrace_hash and returns the result 6297 + * @a: struct ftrace_hash object 6298 + * @b: struct ftrace_hash object 6299 + * 6300 + * Returns struct ftrace_hash object on success, NULL on error. 6301 + */ 6302 + static struct ftrace_hash *hash_add(struct ftrace_hash *a, struct ftrace_hash *b) 6303 + { 6304 + struct ftrace_func_entry *entry; 6305 + struct ftrace_hash *add; 6306 + int size; 6307 + 6308 + size = hash_count(a) + hash_count(b); 6309 + if (size > 32) 6310 + size = 32; 6311 + 6312 + add = alloc_and_copy_ftrace_hash(fls(size), a); 6313 + if (!add) 6314 + return NULL; 6315 + 6316 + size = 1 << b->size_bits; 6317 + for (int i = 0; i < size; i++) { 6318 + hlist_for_each_entry(entry, &b->buckets[i], hlist) { 6319 + if (add_ftrace_hash_entry_direct(add, entry->ip, entry->direct) == NULL) { 6320 + free_ftrace_hash(add); 6321 + return NULL; 6322 + } 6323 + } 6324 + } 6325 + return add; 6326 + } 6327 + 6328 + /** 6329 + * update_ftrace_direct_add - Updates @ops by adding direct 6330 + * callers provided in @hash 6331 + * @ops: The address of the struct ftrace_ops object 6332 + * @hash: The address of the struct ftrace_hash object 6333 + * 6334 + * This is used to add custom direct callers (ip -> addr) to @ops, 6335 + * specified in @hash. The @ops will be either registered or updated. 6336 + * 6337 + * Returns: zero on success. Non zero on error, which includes: 6338 + * -EINVAL - The @hash is empty 6339 + */ 6340 + int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash) 6341 + { 6342 + struct ftrace_hash *old_direct_functions = NULL; 6343 + struct ftrace_hash *new_direct_functions; 6344 + struct ftrace_hash *old_filter_hash; 6345 + struct ftrace_hash *new_filter_hash = NULL; 6346 + struct ftrace_func_entry *entry; 6347 + int err = -EINVAL; 6348 + int size; 6349 + bool reg; 6350 + 6351 + if (!hash_count(hash)) 6352 + return -EINVAL; 6353 + 6354 + mutex_lock(&direct_mutex); 6355 + 6356 + /* Make sure requested entries are not already registered. */ 6357 + size = 1 << hash->size_bits; 6358 + for (int i = 0; i < size; i++) { 6359 + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { 6360 + if (__ftrace_lookup_ip(direct_functions, entry->ip)) 6361 + goto out_unlock; 6362 + } 6363 + } 6364 + 6365 + old_filter_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL; 6366 + 6367 + /* If there's nothing in filter_hash we need to register the ops. */ 6368 + reg = hash_count(old_filter_hash) == 0; 6369 + if (reg) { 6370 + if (ops->func || ops->trampoline) 6371 + goto out_unlock; 6372 + if (ops->flags & FTRACE_OPS_FL_ENABLED) 6373 + goto out_unlock; 6374 + } 6375 + 6376 + err = -ENOMEM; 6377 + new_filter_hash = hash_add(old_filter_hash, hash); 6378 + if (!new_filter_hash) 6379 + goto out_unlock; 6380 + 6381 + new_direct_functions = hash_add(direct_functions, hash); 6382 + if (!new_direct_functions) 6383 + goto out_unlock; 6384 + 6385 + old_direct_functions = direct_functions; 6386 + rcu_assign_pointer(direct_functions, new_direct_functions); 6387 + 6388 + if (reg) { 6389 + ops->func = call_direct_funcs; 6390 + ops->flags |= MULTI_FLAGS; 6391 + ops->trampoline = FTRACE_REGS_ADDR; 6392 + ops->local_hash.filter_hash = new_filter_hash; 6393 + 6394 + err = register_ftrace_function_nolock(ops); 6395 + if (err) { 6396 + /* restore old filter on error */ 6397 + ops->local_hash.filter_hash = old_filter_hash; 6398 + 6399 + /* cleanup for possible another register call */ 6400 + ops->func = NULL; 6401 + ops->trampoline = 0; 6402 + } else { 6403 + new_filter_hash = old_filter_hash; 6404 + } 6405 + } else { 6406 + err = ftrace_update_ops(ops, new_filter_hash, EMPTY_HASH); 6407 + /* 6408 + * new_filter_hash is dup-ed, so we need to release it anyway, 6409 + * old_filter_hash either stays on error or is already released 6410 + */ 6411 + } 6412 + 6413 + if (err) { 6414 + /* reset direct_functions and free the new one */ 6415 + rcu_assign_pointer(direct_functions, old_direct_functions); 6416 + old_direct_functions = new_direct_functions; 6417 + } 6418 + 6419 + out_unlock: 6420 + mutex_unlock(&direct_mutex); 6421 + 6422 + if (old_direct_functions && old_direct_functions != EMPTY_HASH) 6423 + call_rcu_tasks(&old_direct_functions->rcu, register_ftrace_direct_cb); 6424 + free_ftrace_hash(new_filter_hash); 6425 + 6426 + return err; 6427 + } 6428 + 6429 + /** 6430 + * hash_sub - substracts @b from @a and returns the result 6431 + * @a: struct ftrace_hash object 6432 + * @b: struct ftrace_hash object 6433 + * 6434 + * Returns struct ftrace_hash object on success, NULL on error. 6435 + */ 6436 + static struct ftrace_hash *hash_sub(struct ftrace_hash *a, struct ftrace_hash *b) 6437 + { 6438 + struct ftrace_func_entry *entry, *del; 6439 + struct ftrace_hash *sub; 6440 + int size; 6441 + 6442 + sub = alloc_and_copy_ftrace_hash(a->size_bits, a); 6443 + if (!sub) 6444 + return NULL; 6445 + 6446 + size = 1 << b->size_bits; 6447 + for (int i = 0; i < size; i++) { 6448 + hlist_for_each_entry(entry, &b->buckets[i], hlist) { 6449 + del = __ftrace_lookup_ip(sub, entry->ip); 6450 + if (WARN_ON_ONCE(!del)) { 6451 + free_ftrace_hash(sub); 6452 + return NULL; 6453 + } 6454 + remove_hash_entry(sub, del); 6455 + kfree(del); 6456 + } 6457 + } 6458 + return sub; 6459 + } 6460 + 6461 + /** 6462 + * update_ftrace_direct_del - Updates @ops by removing its direct 6463 + * callers provided in @hash 6464 + * @ops: The address of the struct ftrace_ops object 6465 + * @hash: The address of the struct ftrace_hash object 6466 + * 6467 + * This is used to delete custom direct callers (ip -> addr) in 6468 + * @ops specified via @hash. The @ops will be either unregistered 6469 + * updated. 6470 + * 6471 + * Returns: zero on success. Non zero on error, which includes: 6472 + * -EINVAL - The @hash is empty 6473 + * -EINVAL - The @ops is not registered 6474 + */ 6475 + int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) 6476 + { 6477 + struct ftrace_hash *old_direct_functions = NULL; 6478 + struct ftrace_hash *new_direct_functions; 6479 + struct ftrace_hash *new_filter_hash = NULL; 6480 + struct ftrace_hash *old_filter_hash; 6481 + struct ftrace_func_entry *entry; 6482 + struct ftrace_func_entry *del; 6483 + unsigned long size; 6484 + int err = -EINVAL; 6485 + 6486 + if (!hash_count(hash)) 6487 + return -EINVAL; 6488 + if (check_direct_multi(ops)) 6489 + return -EINVAL; 6490 + if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) 6491 + return -EINVAL; 6492 + if (direct_functions == EMPTY_HASH) 6493 + return -EINVAL; 6494 + 6495 + mutex_lock(&direct_mutex); 6496 + 6497 + old_filter_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL; 6498 + 6499 + if (!hash_count(old_filter_hash)) 6500 + goto out_unlock; 6501 + 6502 + /* Make sure requested entries are already registered. */ 6503 + size = 1 << hash->size_bits; 6504 + for (int i = 0; i < size; i++) { 6505 + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { 6506 + del = __ftrace_lookup_ip(direct_functions, entry->ip); 6507 + if (!del || del->direct != entry->direct) 6508 + goto out_unlock; 6509 + } 6510 + } 6511 + 6512 + err = -ENOMEM; 6513 + new_filter_hash = hash_sub(old_filter_hash, hash); 6514 + if (!new_filter_hash) 6515 + goto out_unlock; 6516 + 6517 + new_direct_functions = hash_sub(direct_functions, hash); 6518 + if (!new_direct_functions) 6519 + goto out_unlock; 6520 + 6521 + /* If there's nothing left, we need to unregister the ops. */ 6522 + if (ftrace_hash_empty(new_filter_hash)) { 6523 + err = unregister_ftrace_function(ops); 6524 + if (!err) { 6525 + /* cleanup for possible another register call */ 6526 + ops->func = NULL; 6527 + ops->trampoline = 0; 6528 + ftrace_free_filter(ops); 6529 + ops->func_hash->filter_hash = NULL; 6530 + } 6531 + } else { 6532 + err = ftrace_update_ops(ops, new_filter_hash, EMPTY_HASH); 6533 + /* 6534 + * new_filter_hash is dup-ed, so we need to release it anyway, 6535 + * old_filter_hash either stays on error or is already released 6536 + */ 6537 + } 6538 + 6539 + if (err) { 6540 + /* free the new_direct_functions */ 6541 + old_direct_functions = new_direct_functions; 6542 + } else { 6543 + rcu_assign_pointer(direct_functions, new_direct_functions); 6544 + } 6545 + 6546 + out_unlock: 6547 + mutex_unlock(&direct_mutex); 6548 + 6549 + if (old_direct_functions && old_direct_functions != EMPTY_HASH) 6550 + call_rcu_tasks(&old_direct_functions->rcu, register_ftrace_direct_cb); 6551 + free_ftrace_hash(new_filter_hash); 6552 + 6553 + return err; 6554 + } 6555 + 6556 + /** 6557 + * update_ftrace_direct_mod - Updates @ops by modifing its direct 6558 + * callers provided in @hash 6559 + * @ops: The address of the struct ftrace_ops object 6560 + * @hash: The address of the struct ftrace_hash object 6561 + * @do_direct_lock: If true lock the direct_mutex 6562 + * 6563 + * This is used to modify custom direct callers (ip -> addr) in 6564 + * @ops specified via @hash. 6565 + * 6566 + * This can be called from within ftrace ops_func callback with 6567 + * direct_mutex already locked, in which case @do_direct_lock 6568 + * needs to be false. 6569 + * 6570 + * Returns: zero on success. Non zero on error, which includes: 6571 + * -EINVAL - The @hash is empty 6572 + * -EINVAL - The @ops is not registered 6573 + */ 6574 + int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock) 6575 + { 6576 + struct ftrace_func_entry *entry, *tmp; 6577 + static struct ftrace_ops tmp_ops = { 6578 + .func = ftrace_stub, 6579 + .flags = FTRACE_OPS_FL_STUB, 6580 + }; 6581 + struct ftrace_hash *orig_hash; 6582 + unsigned long size, i; 6583 + int err = -EINVAL; 6584 + 6585 + if (!hash_count(hash)) 6586 + return -EINVAL; 6587 + if (check_direct_multi(ops)) 6588 + return -EINVAL; 6589 + if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) 6590 + return -EINVAL; 6591 + if (direct_functions == EMPTY_HASH) 6592 + return -EINVAL; 6593 + 6594 + /* 6595 + * We can be called from within ops_func callback with direct_mutex 6596 + * already taken. 6597 + */ 6598 + if (do_direct_lock) 6599 + mutex_lock(&direct_mutex); 6600 + 6601 + orig_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL; 6602 + if (!orig_hash) 6603 + goto unlock; 6604 + 6605 + /* Enable the tmp_ops to have the same functions as the direct ops */ 6606 + ftrace_ops_init(&tmp_ops); 6607 + tmp_ops.func_hash = ops->func_hash; 6608 + 6609 + err = register_ftrace_function_nolock(&tmp_ops); 6610 + if (err) 6611 + goto unlock; 6612 + 6613 + /* 6614 + * Call __ftrace_hash_update_ipmodify() here, so that we can call 6615 + * ops->ops_func for the ops. This is needed because the above 6616 + * register_ftrace_function_nolock() worked on tmp_ops. 6617 + */ 6618 + err = __ftrace_hash_update_ipmodify(ops, orig_hash, orig_hash, true); 6619 + if (err) 6620 + goto out; 6621 + 6622 + /* 6623 + * Now the ftrace_ops_list_func() is called to do the direct callers. 6624 + * We can safely change the direct functions attached to each entry. 6625 + */ 6626 + mutex_lock(&ftrace_lock); 6627 + 6628 + size = 1 << hash->size_bits; 6629 + for (i = 0; i < size; i++) { 6630 + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { 6631 + tmp = __ftrace_lookup_ip(direct_functions, entry->ip); 6632 + if (!tmp) 6633 + continue; 6634 + tmp->direct = entry->direct; 6635 + } 6636 + } 6637 + 6638 + mutex_unlock(&ftrace_lock); 6639 + 6640 + out: 6641 + /* Removing the tmp_ops will add the updated direct callers to the functions */ 6642 + unregister_ftrace_function(&tmp_ops); 6643 + 6644 + unlock: 6645 + if (do_direct_lock) 6646 + mutex_unlock(&direct_mutex); 6647 + return err; 6648 + } 6649 + 6286 6650 #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ 6287 6651 6288 6652 /** ··· 9066 8708 if (!op->ops_func) 9067 8709 return -EBUSY; 9068 8710 9069 - ret = op->ops_func(op, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_PEER); 8711 + ret = op->ops_func(op, ip, FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_PEER); 9070 8712 if (ret) 9071 8713 return ret; 9072 8714 } ··· 9113 8755 9114 8756 /* The cleanup is optional, ignore any errors */ 9115 8757 if (found_op && op->ops_func) 9116 - op->ops_func(op, FTRACE_OPS_CMD_DISABLE_SHARE_IPMODIFY_PEER); 8758 + op->ops_func(op, ip, FTRACE_OPS_CMD_DISABLE_SHARE_IPMODIFY_PEER); 9117 8759 } 9118 8760 } 9119 8761 mutex_unlock(&direct_mutex);