Skip to content

Commit ba27f2b

Browse files
committed
ftrace: Remove use of control list and ops
Currently perf has its own list function within the ftrace infrastructure that seems to be used only to allow for it to have per-cpu disabling as well as a check to make sure that it's not called while RCU is not watching. It uses something called the "control_ops" which is used to iterate over ops under it with the control_list_func(). The problem is that this control_ops and control_list_func unnecessarily complicates the code. By replacing FTRACE_OPS_FL_CONTROL with two new flags (FTRACE_OPS_FL_RCU and FTRACE_OPS_FL_PER_CPU) we can remove all the code that is special with the control ops and add the needed checks within the generic ftrace_list_func(). Signed-off-by: Steven Rostedt <[email protected]>
1 parent 030f4e1 commit ba27f2b

File tree

4 files changed

+57
-108
lines changed

4 files changed

+57
-108
lines changed

include/linux/ftrace.h

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
7676
* ENABLED - set/unset when ftrace_ops is registered/unregistered
7777
* DYNAMIC - set when ftrace_ops is registered to denote dynamically
7878
* allocated ftrace_ops which need special care
79-
* CONTROL - set manualy by ftrace_ops user to denote the ftrace_ops
80-
* could be controled by following calls:
79+
* PER_CPU - set manualy by ftrace_ops user to denote the ftrace_ops
80+
* could be controlled by following calls:
8181
* ftrace_function_local_enable
8282
* ftrace_function_local_disable
8383
* SAVE_REGS - The ftrace_ops wants regs saved at each function called
@@ -121,7 +121,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
121121
enum {
122122
FTRACE_OPS_FL_ENABLED = 1 << 0,
123123
FTRACE_OPS_FL_DYNAMIC = 1 << 1,
124-
FTRACE_OPS_FL_CONTROL = 1 << 2,
124+
FTRACE_OPS_FL_PER_CPU = 1 << 2,
125125
FTRACE_OPS_FL_SAVE_REGS = 1 << 3,
126126
FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 4,
127127
FTRACE_OPS_FL_RECURSION_SAFE = 1 << 5,
@@ -134,6 +134,7 @@ enum {
134134
FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 12,
135135
FTRACE_OPS_FL_IPMODIFY = 1 << 13,
136136
FTRACE_OPS_FL_PID = 1 << 14,
137+
FTRACE_OPS_FL_RCU = 1 << 15,
137138
};
138139

139140
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -146,11 +147,11 @@ struct ftrace_ops_hash {
146147
#endif
147148

148149
/*
149-
* Note, ftrace_ops can be referenced outside of RCU protection.
150-
* (Although, for perf, the control ops prevent that). If ftrace_ops is
151-
* allocated and not part of kernel core data, the unregistering of it will
152-
* perform a scheduling on all CPUs to make sure that there are no more users.
153-
* Depending on the load of the system that may take a bit of time.
150+
* Note, ftrace_ops can be referenced outside of RCU protection, unless
151+
* the RCU flag is set. If ftrace_ops is allocated and not part of kernel
152+
* core data, the unregistering of it will perform a scheduling on all CPUs
153+
* to make sure that there are no more users. Depending on the load of the
154+
* system that may take a bit of time.
154155
*
155156
* Any private data added must also take care not to be freed and if private
156157
* data is added to a ftrace_ops that is in core code, the user of the
@@ -196,34 +197,34 @@ int unregister_ftrace_function(struct ftrace_ops *ops);
196197
void clear_ftrace_function(void);
197198

198199
/**
199-
* ftrace_function_local_enable - enable controlled ftrace_ops on current cpu
200+
* ftrace_function_local_enable - enable ftrace_ops on current cpu
200201
*
201202
* This function enables tracing on current cpu by decreasing
202203
* the per cpu control variable.
203204
* It must be called with preemption disabled and only on ftrace_ops
204-
* registered with FTRACE_OPS_FL_CONTROL. If called without preemption
205+
* registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
205206
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
206207
*/
207208
static inline void ftrace_function_local_enable(struct ftrace_ops *ops)
208209
{
209-
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)))
210+
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
210211
return;
211212

212213
(*this_cpu_ptr(ops->disabled))--;
213214
}
214215

215216
/**
216-
* ftrace_function_local_disable - enable controlled ftrace_ops on current cpu
217+
* ftrace_function_local_disable - disable ftrace_ops on current cpu
217218
*
218-
* This function enables tracing on current cpu by decreasing
219+
* This function disables tracing on current cpu by increasing
219220
* the per cpu control variable.
220221
* It must be called with preemption disabled and only on ftrace_ops
221-
* registered with FTRACE_OPS_FL_CONTROL. If called without preemption
222+
* registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
222223
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
223224
*/
224225
static inline void ftrace_function_local_disable(struct ftrace_ops *ops)
225226
{
226-
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)))
227+
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
227228
return;
228229

229230
(*this_cpu_ptr(ops->disabled))++;
@@ -235,12 +236,12 @@ static inline void ftrace_function_local_disable(struct ftrace_ops *ops)
235236
*
236237
* This function returns value of ftrace_ops::disabled on current cpu.
237238
* It must be called with preemption disabled and only on ftrace_ops
238-
* registered with FTRACE_OPS_FL_CONTROL. If called without preemption
239+
* registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
239240
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
240241
*/
241242
static inline int ftrace_function_local_disabled(struct ftrace_ops *ops)
242243
{
243-
WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL));
244+
WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU));
244245
return *this_cpu_ptr(ops->disabled);
245246
}
246247

kernel/trace/ftrace.c

Lines changed: 38 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@
6262
#define FTRACE_HASH_DEFAULT_BITS 10
6363
#define FTRACE_HASH_MAX_BITS 12
6464

65-
#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_CONTROL)
66-
6765
#ifdef CONFIG_DYNAMIC_FTRACE
6866
#define INIT_OPS_HASH(opsname) \
6967
.func_hash = &opsname.local_hash, \
@@ -113,11 +111,9 @@ static int ftrace_disabled __read_mostly;
113111

114112
static DEFINE_MUTEX(ftrace_lock);
115113

116-
static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end;
117114
static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
118115
ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
119116
static struct ftrace_ops global_ops;
120-
static struct ftrace_ops control_ops;
121117

122118
static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip,
123119
struct ftrace_ops *op, struct pt_regs *regs);
@@ -203,24 +199,27 @@ void clear_ftrace_function(void)
203199
ftrace_trace_function = ftrace_stub;
204200
}
205201

206-
static void control_ops_disable_all(struct ftrace_ops *ops)
202+
static void per_cpu_ops_disable_all(struct ftrace_ops *ops)
207203
{
208204
int cpu;
209205

210206
for_each_possible_cpu(cpu)
211207
*per_cpu_ptr(ops->disabled, cpu) = 1;
212208
}
213209

214-
static int control_ops_alloc(struct ftrace_ops *ops)
210+
static int per_cpu_ops_alloc(struct ftrace_ops *ops)
215211
{
216212
int __percpu *disabled;
217213

214+
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
215+
return -EINVAL;
216+
218217
disabled = alloc_percpu(int);
219218
if (!disabled)
220219
return -ENOMEM;
221220

222221
ops->disabled = disabled;
223-
control_ops_disable_all(ops);
222+
per_cpu_ops_disable_all(ops);
224223
return 0;
225224
}
226225

@@ -256,10 +255,11 @@ static inline void update_function_graph_func(void) { }
256255
static ftrace_func_t ftrace_ops_get_list_func(struct ftrace_ops *ops)
257256
{
258257
/*
259-
* If this is a dynamic ops or we force list func,
258+
* If this is a dynamic, RCU, or per CPU ops, or we force list func,
260259
* then it needs to call the list anyway.
261260
*/
262-
if (ops->flags & FTRACE_OPS_FL_DYNAMIC || FTRACE_FORCE_LIST_FUNC)
261+
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU |
262+
FTRACE_OPS_FL_RCU) || FTRACE_FORCE_LIST_FUNC)
263263
return ftrace_ops_list_func;
264264

265265
return ftrace_ops_get_func(ops);
@@ -383,26 +383,6 @@ static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops)
383383
return 0;
384384
}
385385

386-
static void add_ftrace_list_ops(struct ftrace_ops **list,
387-
struct ftrace_ops *main_ops,
388-
struct ftrace_ops *ops)
389-
{
390-
int first = *list == &ftrace_list_end;
391-
add_ftrace_ops(list, ops);
392-
if (first)
393-
add_ftrace_ops(&ftrace_ops_list, main_ops);
394-
}
395-
396-
static int remove_ftrace_list_ops(struct ftrace_ops **list,
397-
struct ftrace_ops *main_ops,
398-
struct ftrace_ops *ops)
399-
{
400-
int ret = remove_ftrace_ops(list, ops);
401-
if (!ret && *list == &ftrace_list_end)
402-
ret = remove_ftrace_ops(&ftrace_ops_list, main_ops);
403-
return ret;
404-
}
405-
406386
static void ftrace_update_trampoline(struct ftrace_ops *ops);
407387

408388
static int __register_ftrace_function(struct ftrace_ops *ops)
@@ -430,14 +410,12 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
430410
if (!core_kernel_data((unsigned long)ops))
431411
ops->flags |= FTRACE_OPS_FL_DYNAMIC;
432412

433-
if (ops->flags & FTRACE_OPS_FL_CONTROL) {
434-
if (control_ops_alloc(ops))
413+
if (ops->flags & FTRACE_OPS_FL_PER_CPU) {
414+
if (per_cpu_ops_alloc(ops))
435415
return -ENOMEM;
436-
add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops);
437-
/* The control_ops needs the trampoline update */
438-
ops = &control_ops;
439-
} else
440-
add_ftrace_ops(&ftrace_ops_list, ops);
416+
}
417+
418+
add_ftrace_ops(&ftrace_ops_list, ops);
441419

442420
/* Always save the function, and reset at unregistering */
443421
ops->saved_func = ops->func;
@@ -460,11 +438,7 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
460438
if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED)))
461439
return -EBUSY;
462440

463-
if (ops->flags & FTRACE_OPS_FL_CONTROL) {
464-
ret = remove_ftrace_list_ops(&ftrace_control_list,
465-
&control_ops, ops);
466-
} else
467-
ret = remove_ftrace_ops(&ftrace_ops_list, ops);
441+
ret = remove_ftrace_ops(&ftrace_ops_list, ops);
468442

469443
if (ret < 0)
470444
return ret;
@@ -2630,7 +2604,7 @@ void __weak arch_ftrace_trampoline_free(struct ftrace_ops *ops)
26302604
{
26312605
}
26322606

2633-
static void control_ops_free(struct ftrace_ops *ops)
2607+
static void per_cpu_ops_free(struct ftrace_ops *ops)
26342608
{
26352609
free_percpu(ops->disabled);
26362610
}
@@ -2731,13 +2705,13 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
27312705

27322706
if (!command || !ftrace_enabled) {
27332707
/*
2734-
* If these are control ops, they still need their
2708+
* If these are per_cpu ops, they still need their
27352709
* per_cpu field freed. Since, function tracing is
27362710
* not currently active, we can just free them
27372711
* without synchronizing all CPUs.
27382712
*/
2739-
if (ops->flags & FTRACE_OPS_FL_CONTROL)
2740-
control_ops_free(ops);
2713+
if (ops->flags & FTRACE_OPS_FL_PER_CPU)
2714+
per_cpu_ops_free(ops);
27412715
return 0;
27422716
}
27432717

@@ -2778,7 +2752,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
27782752
/*
27792753
* Dynamic ops may be freed, we must make sure that all
27802754
* callers are done before leaving this function.
2781-
* The same goes for freeing the per_cpu data of the control
2755+
* The same goes for freeing the per_cpu data of the per_cpu
27822756
* ops.
27832757
*
27842758
* Again, normal synchronize_sched() is not good enough.
@@ -2789,13 +2763,13 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
27892763
* infrastructure to do the synchronization, thus we must do it
27902764
* ourselves.
27912765
*/
2792-
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_CONTROL)) {
2766+
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) {
27932767
schedule_on_each_cpu(ftrace_sync);
27942768

27952769
arch_ftrace_trampoline_free(ops);
27962770

2797-
if (ops->flags & FTRACE_OPS_FL_CONTROL)
2798-
control_ops_free(ops);
2771+
if (ops->flags & FTRACE_OPS_FL_PER_CPU)
2772+
per_cpu_ops_free(ops);
27992773
}
28002774

28012775
return 0;
@@ -5185,44 +5159,6 @@ void ftrace_reset_array_ops(struct trace_array *tr)
51855159
tr->ops->func = ftrace_stub;
51865160
}
51875161

5188-
static void
5189-
ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,
5190-
struct ftrace_ops *op, struct pt_regs *regs)
5191-
{
5192-
if (unlikely(trace_recursion_test(TRACE_CONTROL_BIT)))
5193-
return;
5194-
5195-
/*
5196-
* Some of the ops may be dynamically allocated,
5197-
* they must be freed after a synchronize_sched().
5198-
*/
5199-
preempt_disable_notrace();
5200-
trace_recursion_set(TRACE_CONTROL_BIT);
5201-
5202-
/*
5203-
* Control funcs (perf) uses RCU. Only trace if
5204-
* RCU is currently active.
5205-
*/
5206-
if (!rcu_is_watching())
5207-
goto out;
5208-
5209-
do_for_each_ftrace_op(op, ftrace_control_list) {
5210-
if (!(op->flags & FTRACE_OPS_FL_STUB) &&
5211-
!ftrace_function_local_disabled(op) &&
5212-
ftrace_ops_test(op, ip, regs))
5213-
op->func(ip, parent_ip, op, regs);
5214-
} while_for_each_ftrace_op(op);
5215-
out:
5216-
trace_recursion_clear(TRACE_CONTROL_BIT);
5217-
preempt_enable_notrace();
5218-
}
5219-
5220-
static struct ftrace_ops control_ops = {
5221-
.func = ftrace_ops_control_func,
5222-
.flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED,
5223-
INIT_OPS_HASH(control_ops)
5224-
};
5225-
52265162
static inline void
52275163
__ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
52285164
struct ftrace_ops *ignored, struct pt_regs *regs)
@@ -5239,8 +5175,22 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
52395175
* they must be freed after a synchronize_sched().
52405176
*/
52415177
preempt_disable_notrace();
5178+
52425179
do_for_each_ftrace_op(op, ftrace_ops_list) {
5243-
if (ftrace_ops_test(op, ip, regs)) {
5180+
/*
5181+
* Check the following for each ops before calling their func:
5182+
* if RCU flag is set, then rcu_is_watching() must be true
5183+
* if PER_CPU is set, then ftrace_function_local_disable()
5184+
* must be false
5185+
* Otherwise test if the ip matches the ops filter
5186+
*
5187+
* If any of the above fails then the op->func() is not executed.
5188+
*/
5189+
if ((!(op->flags & FTRACE_OPS_FL_RCU) || rcu_is_watching()) &&
5190+
(!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
5191+
!ftrace_function_local_disabled(op)) &&
5192+
ftrace_ops_test(op, ip, regs)) {
5193+
52445194
if (FTRACE_WARN_ON(!op->func)) {
52455195
pr_warn("op=%p %pS\n", op, op);
52465196
goto out;

kernel/trace/trace.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,6 @@ enum {
467467
TRACE_INTERNAL_IRQ_BIT,
468468
TRACE_INTERNAL_SIRQ_BIT,
469469

470-
TRACE_CONTROL_BIT,
471-
472470
TRACE_BRANCH_BIT,
473471
/*
474472
* Abuse of the trace_recursion.

kernel/trace/trace_event_perf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ static int perf_ftrace_function_register(struct perf_event *event)
334334
{
335335
struct ftrace_ops *ops = &event->ftrace_ops;
336336

337-
ops->flags |= FTRACE_OPS_FL_CONTROL;
337+
ops->flags |= FTRACE_OPS_FL_PER_CPU | FTRACE_OPS_FL_RCU;
338338
ops->func = perf_ftrace_function_call;
339339
return register_ftrace_function(ops);
340340
}

0 commit comments

Comments
 (0)