Skip to content

Commit 0567d68

Browse files
committed
ftrace: Add modify_ftrace_direct()
Add a new function modify_ftrace_direct() that will allow a user to update an existing direct caller to a new trampoline, without missing hits due to unregistering one and then adding another. Link: https://lore.kernel.org/r/[email protected] Suggested-by: Alexei Starovoitov <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 36b3615 commit 0567d68

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

include/linux/ftrace.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ static inline void ftrace_free_mem(struct module *mod, void *start, void *end) {
250250
extern int ftrace_direct_func_count;
251251
int register_ftrace_direct(unsigned long ip, unsigned long addr);
252252
int unregister_ftrace_direct(unsigned long ip, unsigned long addr);
253+
int modify_ftrace_direct(unsigned long ip, unsigned long old_addr, unsigned long new_addr);
253254
struct ftrace_direct_func *ftrace_find_direct_func(unsigned long addr);
254255
#else
255256
# define ftrace_direct_func_count 0
@@ -261,6 +262,11 @@ static inline int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
261262
{
262263
return -ENODEV;
263264
}
265+
static inline int modify_ftrace_direct(unsigned long ip,
266+
unsigned long old_addr, unsigned long new_addr)
267+
{
268+
return -ENODEV;
269+
}
264270
static inline struct ftrace_direct_func *ftrace_find_direct_func(unsigned long addr)
265271
{
266272
return NULL;

kernel/trace/ftrace.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5160,6 +5160,84 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
51605160
return ret;
51615161
}
51625162
EXPORT_SYMBOL_GPL(unregister_ftrace_direct);
5163+
5164+
static struct ftrace_ops stub_ops = {
5165+
.func = ftrace_stub,
5166+
};
5167+
5168+
/**
5169+
* modify_ftrace_direct - Modify an existing direct call to call something else
5170+
* @ip: The instruction pointer to modify
5171+
* @old_addr: The address that the current @ip calls directly
5172+
* @new_addr: The address that the @ip should call
5173+
*
5174+
* This modifies a ftrace direct caller at an instruction pointer without
5175+
* having to disable it first. The direct call will switch over to the
5176+
* @new_addr without missing anything.
5177+
*
5178+
* Returns: zero on success. Non zero on error, which includes:
5179+
* -ENODEV : the @ip given has no direct caller attached
5180+
* -EINVAL : the @old_addr does not match the current direct caller
5181+
*/
5182+
int modify_ftrace_direct(unsigned long ip,
5183+
unsigned long old_addr, unsigned long new_addr)
5184+
{
5185+
struct ftrace_func_entry *entry;
5186+
struct dyn_ftrace *rec;
5187+
int ret = -ENODEV;
5188+
5189+
mutex_lock(&direct_mutex);
5190+
entry = __ftrace_lookup_ip(direct_functions, ip);
5191+
if (!entry) {
5192+
/* OK if it is off by a little */
5193+
rec = lookup_rec(ip, ip);
5194+
if (!rec || rec->ip == ip)
5195+
goto out_unlock;
5196+
5197+
entry = __ftrace_lookup_ip(direct_functions, rec->ip);
5198+
if (!entry)
5199+
goto out_unlock;
5200+
5201+
ip = rec->ip;
5202+
WARN_ON(!(rec->flags & FTRACE_FL_DIRECT));
5203+
}
5204+
5205+
ret = -EINVAL;
5206+
if (entry->direct != old_addr)
5207+
goto out_unlock;
5208+
5209+
/*
5210+
* By setting a stub function at the same address, we force
5211+
* the code to call the iterator and the direct_ops helper.
5212+
* This means that @ip does not call the direct call, and
5213+
* we can simply modify it.
5214+
*/
5215+
ret = ftrace_set_filter_ip(&stub_ops, ip, 0, 0);
5216+
if (ret)
5217+
goto out_unlock;
5218+
5219+
ret = register_ftrace_function(&stub_ops);
5220+
if (ret) {
5221+
ftrace_set_filter_ip(&stub_ops, ip, 1, 0);
5222+
goto out_unlock;
5223+
}
5224+
5225+
entry->direct = new_addr;
5226+
5227+
/*
5228+
* By removing the stub, we put back the direct call, calling
5229+
* the @new_addr.
5230+
*/
5231+
unregister_ftrace_function(&stub_ops);
5232+
ftrace_set_filter_ip(&stub_ops, ip, 1, 0);
5233+
5234+
ret = 0;
5235+
5236+
out_unlock:
5237+
mutex_unlock(&direct_mutex);
5238+
return ret;
5239+
}
5240+
EXPORT_SYMBOL_GPL(modify_ftrace_direct);
51635241
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
51645242

51655243
/**

0 commit comments

Comments
 (0)