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.

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 <jolsa@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Link: https://lore.kernel.org/bpf/20251230145010.103439-6-jolsa@kernel.org

authored by

Jiri Olsa and committed by
Andrii Nakryiko
8d2c1233 05dc5e9c

+133
+6
include/linux/ftrace.h
··· 544 544 int modify_ftrace_direct_nolock(struct ftrace_ops *ops, unsigned long addr); 545 545 546 546 int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash); 547 + int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash); 547 548 548 549 void ftrace_stub_direct_tramp(void); 549 550 ··· 573 572 } 574 573 575 574 static inline int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash) 575 + { 576 + return -ENODEV; 577 + } 578 + 579 + static inline int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) 576 580 { 577 581 return -ENODEV; 578 582 }
+127
kernel/trace/ftrace.c
··· 6418 6418 return err; 6419 6419 } 6420 6420 6421 + /** 6422 + * hash_sub - substracts @b from @a and returns the result 6423 + * @a: struct ftrace_hash object 6424 + * @b: struct ftrace_hash object 6425 + * 6426 + * Returns struct ftrace_hash object on success, NULL on error. 6427 + */ 6428 + static struct ftrace_hash *hash_sub(struct ftrace_hash *a, struct ftrace_hash *b) 6429 + { 6430 + struct ftrace_func_entry *entry, *del; 6431 + struct ftrace_hash *sub; 6432 + int size; 6433 + 6434 + sub = alloc_and_copy_ftrace_hash(a->size_bits, a); 6435 + if (!sub) 6436 + return NULL; 6437 + 6438 + size = 1 << b->size_bits; 6439 + for (int i = 0; i < size; i++) { 6440 + hlist_for_each_entry(entry, &b->buckets[i], hlist) { 6441 + del = __ftrace_lookup_ip(sub, entry->ip); 6442 + if (WARN_ON_ONCE(!del)) { 6443 + free_ftrace_hash(sub); 6444 + return NULL; 6445 + } 6446 + remove_hash_entry(sub, del); 6447 + kfree(del); 6448 + } 6449 + } 6450 + return sub; 6451 + } 6452 + 6453 + /** 6454 + * update_ftrace_direct_del - Updates @ops by removing its direct 6455 + * callers provided in @hash 6456 + * @ops: The address of the struct ftrace_ops object 6457 + * @hash: The address of the struct ftrace_hash object 6458 + * 6459 + * This is used to delete custom direct callers (ip -> addr) in 6460 + * @ops specified via @hash. The @ops will be either unregistered 6461 + * updated. 6462 + * 6463 + * Returns: zero on success. Non zero on error, which includes: 6464 + * -EINVAL - The @hash is empty 6465 + * -EINVAL - The @ops is not registered 6466 + */ 6467 + int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) 6468 + { 6469 + struct ftrace_hash *old_direct_functions = NULL; 6470 + struct ftrace_hash *new_direct_functions; 6471 + struct ftrace_hash *new_filter_hash = NULL; 6472 + struct ftrace_hash *old_filter_hash; 6473 + struct ftrace_func_entry *entry; 6474 + struct ftrace_func_entry *del; 6475 + unsigned long size; 6476 + int err = -EINVAL; 6477 + 6478 + if (!hash_count(hash)) 6479 + return -EINVAL; 6480 + if (check_direct_multi(ops)) 6481 + return -EINVAL; 6482 + if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) 6483 + return -EINVAL; 6484 + if (direct_functions == EMPTY_HASH) 6485 + return -EINVAL; 6486 + 6487 + mutex_lock(&direct_mutex); 6488 + 6489 + old_filter_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL; 6490 + 6491 + if (!hash_count(old_filter_hash)) 6492 + goto out_unlock; 6493 + 6494 + /* Make sure requested entries are already registered. */ 6495 + size = 1 << hash->size_bits; 6496 + for (int i = 0; i < size; i++) { 6497 + hlist_for_each_entry(entry, &hash->buckets[i], hlist) { 6498 + del = __ftrace_lookup_ip(direct_functions, entry->ip); 6499 + if (!del || del->direct != entry->direct) 6500 + goto out_unlock; 6501 + } 6502 + } 6503 + 6504 + err = -ENOMEM; 6505 + new_filter_hash = hash_sub(old_filter_hash, hash); 6506 + if (!new_filter_hash) 6507 + goto out_unlock; 6508 + 6509 + new_direct_functions = hash_sub(direct_functions, hash); 6510 + if (!new_direct_functions) 6511 + goto out_unlock; 6512 + 6513 + /* If there's nothing left, we need to unregister the ops. */ 6514 + if (ftrace_hash_empty(new_filter_hash)) { 6515 + err = unregister_ftrace_function(ops); 6516 + if (!err) { 6517 + /* cleanup for possible another register call */ 6518 + ops->func = NULL; 6519 + ops->trampoline = 0; 6520 + ftrace_free_filter(ops); 6521 + ops->func_hash->filter_hash = NULL; 6522 + } 6523 + } else { 6524 + err = ftrace_update_ops(ops, new_filter_hash, EMPTY_HASH); 6525 + /* 6526 + * new_filter_hash is dup-ed, so we need to release it anyway, 6527 + * old_filter_hash either stays on error or is already released 6528 + */ 6529 + } 6530 + 6531 + if (err) { 6532 + /* free the new_direct_functions */ 6533 + old_direct_functions = new_direct_functions; 6534 + } else { 6535 + rcu_assign_pointer(direct_functions, new_direct_functions); 6536 + } 6537 + 6538 + out_unlock: 6539 + mutex_unlock(&direct_mutex); 6540 + 6541 + if (old_direct_functions && old_direct_functions != EMPTY_HASH) 6542 + call_rcu_tasks(&old_direct_functions->rcu, register_ftrace_direct_cb); 6543 + free_ftrace_hash(new_filter_hash); 6544 + 6545 + return err; 6546 + } 6547 + 6421 6548 #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ 6422 6549 6423 6550 /**