Skip to content

Commit cdbe61b

Browse files
Steven Rostedtrostedt
authored andcommitted
ftrace: Allow dynamically allocated function tracers
Now that functions may be selected individually, it only makes sense that we should allow dynamically allocated trace structures to be traced. This will allow perf to allocate a ftrace_ops structure at runtime and use it to pick and choose which functions that structure will trace. Note, a dynamically allocated ftrace_ops will always be called indirectly instead of being called directly from the mcount in entry.S. This is because there's no safe way to prevent mcount from being preempted before calling the function, unless we modify every entry.S to do so (not likely). Thus, dynamically allocated functions will now be called by the ftrace_ops_list_func() that loops through the ops that are allocated if there are more than one op allocated at a time. This loop is protected with a preempt_disable. To determine if an ftrace_ops structure is allocated or not, a new util function was added to the kernel/extable.c called core_kernel_data(), which returns 1 if the address is between _sdata and _edata. Cc: Paul E. McKenney <[email protected]> Signed-off-by: Steven Rostedt <[email protected]>
1 parent b848914 commit cdbe61b

File tree

4 files changed

+40
-7
lines changed

4 files changed

+40
-7
lines changed

include/linux/ftrace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct ftrace_hash;
3434
enum {
3535
FTRACE_OPS_FL_ENABLED = 1 << 0,
3636
FTRACE_OPS_FL_GLOBAL = 1 << 1,
37+
FTRACE_OPS_FL_DYNAMIC = 1 << 2,
3738
};
3839

3940
struct ftrace_ops {

include/linux/kernel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ extern char *get_options(const char *str, int nints, int *ints);
283283
extern unsigned long long memparse(const char *ptr, char **retptr);
284284

285285
extern int core_kernel_text(unsigned long addr);
286+
extern int core_kernel_data(unsigned long addr);
286287
extern int __kernel_text_address(unsigned long addr);
287288
extern int kernel_text_address(unsigned long addr);
288289
extern int func_ptr_is_kernel_text(void *ptr);

kernel/extable.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ int core_kernel_text(unsigned long addr)
7272
return 0;
7373
}
7474

75+
int core_kernel_data(unsigned long addr)
76+
{
77+
if (addr >= (unsigned long)_sdata &&
78+
addr < (unsigned long)_edata)
79+
return 1;
80+
return 0;
81+
}
82+
7583
int __kernel_text_address(unsigned long addr)
7684
{
7785
if (core_kernel_text(addr))

kernel/trace/ftrace.c

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,14 @@ static void update_ftrace_function(void)
189189

190190
update_global_ops();
191191

192+
/*
193+
* If we are at the end of the list and this ops is
194+
* not dynamic, then have the mcount trampoline call
195+
* the function directly
196+
*/
192197
if (ftrace_ops_list == &ftrace_list_end ||
193-
ftrace_ops_list->next == &ftrace_list_end)
198+
(ftrace_ops_list->next == &ftrace_list_end &&
199+
!(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC)))
194200
func = ftrace_ops_list->func;
195201
else
196202
func = ftrace_ops_list_func;
@@ -250,6 +256,9 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
250256
if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED))
251257
return -EBUSY;
252258

259+
if (!core_kernel_data((unsigned long)ops))
260+
ops->flags |= FTRACE_OPS_FL_DYNAMIC;
261+
253262
if (ops->flags & FTRACE_OPS_FL_GLOBAL) {
254263
int first = ftrace_global_list == &ftrace_list_end;
255264
add_ftrace_ops(&ftrace_global_list, ops);
@@ -293,6 +302,13 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
293302
if (ftrace_enabled)
294303
update_ftrace_function();
295304

305+
/*
306+
* Dynamic ops may be freed, we must make sure that all
307+
* callers are done before leaving this function.
308+
*/
309+
if (ops->flags & FTRACE_OPS_FL_DYNAMIC)
310+
synchronize_sched();
311+
296312
return 0;
297313
}
298314

@@ -1225,6 +1241,9 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
12251241
* the filter_hash does not exist or is empty,
12261242
* AND
12271243
* the ip is not in the ops->notrace_hash.
1244+
*
1245+
* This needs to be called with preemption disabled as
1246+
* the hashes are freed with call_rcu_sched().
12281247
*/
12291248
static int
12301249
ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip)
@@ -1233,9 +1252,6 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip)
12331252
struct ftrace_hash *notrace_hash;
12341253
int ret;
12351254

1236-
/* The hashes are freed with call_rcu_sched() */
1237-
preempt_disable_notrace();
1238-
12391255
filter_hash = rcu_dereference_raw(ops->filter_hash);
12401256
notrace_hash = rcu_dereference_raw(ops->notrace_hash);
12411257

@@ -1246,7 +1262,6 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip)
12461262
ret = 1;
12471263
else
12481264
ret = 0;
1249-
preempt_enable_notrace();
12501265

12511266
return ret;
12521267
}
@@ -3425,14 +3440,20 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip)
34253440
static void
34263441
ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip)
34273442
{
3428-
/* see comment above ftrace_global_list_func */
3429-
struct ftrace_ops *op = rcu_dereference_raw(ftrace_ops_list);
3443+
struct ftrace_ops *op;
34303444

3445+
/*
3446+
* Some of the ops may be dynamically allocated,
3447+
* they must be freed after a synchronize_sched().
3448+
*/
3449+
preempt_disable_notrace();
3450+
op = rcu_dereference_raw(ftrace_ops_list);
34313451
while (op != &ftrace_list_end) {
34323452
if (ftrace_ops_test(op, ip))
34333453
op->func(ip, parent_ip);
34343454
op = rcu_dereference_raw(op->next);
34353455
};
3456+
preempt_enable_notrace();
34363457
}
34373458

34383459
static void clear_ftrace_swapper(void)
@@ -3743,6 +3764,7 @@ int register_ftrace_function(struct ftrace_ops *ops)
37433764
mutex_unlock(&ftrace_lock);
37443765
return ret;
37453766
}
3767+
EXPORT_SYMBOL_GPL(register_ftrace_function);
37463768

37473769
/**
37483770
* unregister_ftrace_function - unregister a function for profiling.
@@ -3762,6 +3784,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
37623784

37633785
return ret;
37643786
}
3787+
EXPORT_SYMBOL_GPL(unregister_ftrace_function);
37653788

37663789
int
37673790
ftrace_enable_sysctl(struct ctl_table *table, int write,

0 commit comments

Comments
 (0)