Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Infrastructure to took into function calls and returns.
4 : : * Copyright (c) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com>
5 : : * Mostly borrowed from function tracer which
6 : : * is Copyright (c) Steven Rostedt <srostedt@redhat.com>
7 : : *
8 : : * Highly modified by Steven Rostedt (VMware).
9 : : */
10 : : #include <linux/suspend.h>
11 : : #include <linux/ftrace.h>
12 : : #include <linux/slab.h>
13 : :
14 : : #include <trace/events/sched.h>
15 : :
16 : : #include "ftrace_internal.h"
17 : :
18 : : #ifdef CONFIG_DYNAMIC_FTRACE
19 : : #define ASSIGN_OPS_HASH(opsname, val) \
20 : : .func_hash = val, \
21 : : .local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock),
22 : : #else
23 : : #define ASSIGN_OPS_HASH(opsname, val)
24 : : #endif
25 : :
26 : : static bool kill_ftrace_graph;
27 : : int ftrace_graph_active;
28 : :
29 : : /* Both enabled by default (can be cleared by function_graph tracer flags */
30 : : static bool fgraph_sleep_time = true;
31 : :
32 : : /**
33 : : * ftrace_graph_is_dead - returns true if ftrace_graph_stop() was called
34 : : *
35 : : * ftrace_graph_stop() is called when a severe error is detected in
36 : : * the function graph tracing. This function is called by the critical
37 : : * paths of function graph to keep those paths from doing any more harm.
38 : : */
39 : 0 : bool ftrace_graph_is_dead(void)
40 : : {
41 : 0 : return kill_ftrace_graph;
42 : : }
43 : :
44 : : /**
45 : : * ftrace_graph_stop - set to permanently disable function graph tracincg
46 : : *
47 : : * In case of an error int function graph tracing, this is called
48 : : * to try to keep function graph tracing from causing any more harm.
49 : : * Usually this is pretty severe and this is called to try to at least
50 : : * get a warning out to the user.
51 : : */
52 : 0 : void ftrace_graph_stop(void)
53 : : {
54 : 0 : kill_ftrace_graph = true;
55 : 0 : }
56 : :
57 : : /* Add a function return address to the trace stack on thread info.*/
58 : : static int
59 : 0 : ftrace_push_return_trace(unsigned long ret, unsigned long func,
60 : : unsigned long frame_pointer, unsigned long *retp)
61 : : {
62 : : unsigned long long calltime;
63 : : int index;
64 : :
65 [ # # ]: 0 : if (unlikely(ftrace_graph_is_dead()))
66 : : return -EBUSY;
67 : :
68 [ # # ]: 0 : if (!current->ret_stack)
69 : : return -EBUSY;
70 : :
71 : : /*
72 : : * We must make sure the ret_stack is tested before we read
73 : : * anything else.
74 : : */
75 : 0 : smp_rmb();
76 : :
77 : : /* The return trace stack is full */
78 [ # # ]: 0 : if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) {
79 : 0 : atomic_inc(¤t->trace_overrun);
80 : 0 : return -EBUSY;
81 : : }
82 : :
83 : 0 : calltime = trace_clock_local();
84 : :
85 : 0 : index = ++current->curr_ret_stack;
86 : 0 : barrier();
87 : 0 : current->ret_stack[index].ret = ret;
88 : 0 : current->ret_stack[index].func = func;
89 : 0 : current->ret_stack[index].calltime = calltime;
90 : : #ifdef HAVE_FUNCTION_GRAPH_FP_TEST
91 : : current->ret_stack[index].fp = frame_pointer;
92 : : #endif
93 : : #ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
94 : : current->ret_stack[index].retp = retp;
95 : : #endif
96 : 0 : return 0;
97 : : }
98 : :
99 : 0 : int function_graph_enter(unsigned long ret, unsigned long func,
100 : : unsigned long frame_pointer, unsigned long *retp)
101 : : {
102 : : struct ftrace_graph_ent trace;
103 : :
104 : 0 : trace.func = func;
105 : 0 : trace.depth = ++current->curr_ret_depth;
106 : :
107 [ # # ]: 0 : if (ftrace_push_return_trace(ret, func, frame_pointer, retp))
108 : : goto out;
109 : :
110 : : /* Only trace if the calling function expects to */
111 [ # # ]: 0 : if (!ftrace_graph_entry(&trace))
112 : : goto out_ret;
113 : :
114 : : return 0;
115 : : out_ret:
116 : 0 : current->curr_ret_stack--;
117 : : out:
118 : 0 : current->curr_ret_depth--;
119 : 0 : return -EBUSY;
120 : : }
121 : :
122 : : /* Retrieve a function return address to the trace stack on thread info.*/
123 : : static void
124 : 0 : ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,
125 : : unsigned long frame_pointer)
126 : : {
127 : : int index;
128 : :
129 : 0 : index = current->curr_ret_stack;
130 : :
131 [ # # ]: 0 : if (unlikely(index < 0 || index >= FTRACE_RETFUNC_DEPTH)) {
132 : : ftrace_graph_stop();
133 : 0 : WARN_ON(1);
134 : : /* Might as well panic, otherwise we have no where to go */
135 : 0 : *ret = (unsigned long)panic;
136 : 0 : return;
137 : : }
138 : :
139 : : #ifdef HAVE_FUNCTION_GRAPH_FP_TEST
140 : : /*
141 : : * The arch may choose to record the frame pointer used
142 : : * and check it here to make sure that it is what we expect it
143 : : * to be. If gcc does not set the place holder of the return
144 : : * address in the frame pointer, and does a copy instead, then
145 : : * the function graph trace will fail. This test detects this
146 : : * case.
147 : : *
148 : : * Currently, x86_32 with optimize for size (-Os) makes the latest
149 : : * gcc do the above.
150 : : *
151 : : * Note, -mfentry does not use frame pointers, and this test
152 : : * is not needed if CC_USING_FENTRY is set.
153 : : */
154 : : if (unlikely(current->ret_stack[index].fp != frame_pointer)) {
155 : : ftrace_graph_stop();
156 : : WARN(1, "Bad frame pointer: expected %lx, received %lx\n"
157 : : " from func %ps return to %lx\n",
158 : : current->ret_stack[index].fp,
159 : : frame_pointer,
160 : : (void *)current->ret_stack[index].func,
161 : : current->ret_stack[index].ret);
162 : : *ret = (unsigned long)panic;
163 : : return;
164 : : }
165 : : #endif
166 : :
167 : 0 : *ret = current->ret_stack[index].ret;
168 : 0 : trace->func = current->ret_stack[index].func;
169 : 0 : trace->calltime = current->ret_stack[index].calltime;
170 : 0 : trace->overrun = atomic_read(¤t->trace_overrun);
171 : 0 : trace->depth = current->curr_ret_depth--;
172 : : /*
173 : : * We still want to trace interrupts coming in if
174 : : * max_depth is set to 1. Make sure the decrement is
175 : : * seen before ftrace_graph_return.
176 : : */
177 : 0 : barrier();
178 : : }
179 : :
180 : : /*
181 : : * Hibernation protection.
182 : : * The state of the current task is too much unstable during
183 : : * suspend/restore to disk. We want to protect against that.
184 : : */
185 : : static int
186 : : ftrace_suspend_notifier_call(struct notifier_block *bl, unsigned long state,
187 : : void *unused)
188 : : {
189 : : switch (state) {
190 : : case PM_HIBERNATION_PREPARE:
191 : : pause_graph_tracing();
192 : : break;
193 : :
194 : : case PM_POST_HIBERNATION:
195 : : unpause_graph_tracing();
196 : : break;
197 : : }
198 : : return NOTIFY_DONE;
199 : : }
200 : :
201 : : static struct notifier_block ftrace_suspend_notifier = {
202 : : .notifier_call = ftrace_suspend_notifier_call,
203 : : };
204 : :
205 : : /*
206 : : * Send the trace to the ring-buffer.
207 : : * @return the original return address.
208 : : */
209 : 0 : unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
210 : : {
211 : : struct ftrace_graph_ret trace;
212 : : unsigned long ret;
213 : :
214 : 0 : ftrace_pop_return_trace(&trace, &ret, frame_pointer);
215 : 0 : trace.rettime = trace_clock_local();
216 : 0 : ftrace_graph_return(&trace);
217 : : /*
218 : : * The ftrace_graph_return() may still access the current
219 : : * ret_stack structure, we need to make sure the update of
220 : : * curr_ret_stack is after that.
221 : : */
222 : 0 : barrier();
223 : 0 : current->curr_ret_stack--;
224 : :
225 [ # # ]: 0 : if (unlikely(!ret)) {
226 : : ftrace_graph_stop();
227 : 0 : WARN_ON(1);
228 : : /* Might as well panic. What else to do? */
229 : 0 : ret = (unsigned long)panic;
230 : : }
231 : :
232 : 0 : return ret;
233 : : }
234 : :
235 : : /**
236 : : * ftrace_graph_get_ret_stack - return the entry of the shadow stack
237 : : * @task: The task to read the shadow stack from
238 : : * @idx: Index down the shadow stack
239 : : *
240 : : * Return the ret_struct on the shadow stack of the @task at the
241 : : * call graph at @idx starting with zero. If @idx is zero, it
242 : : * will return the last saved ret_stack entry. If it is greater than
243 : : * zero, it will return the corresponding ret_stack for the depth
244 : : * of saved return addresses.
245 : : */
246 : : struct ftrace_ret_stack *
247 : 0 : ftrace_graph_get_ret_stack(struct task_struct *task, int idx)
248 : : {
249 : 0 : idx = task->curr_ret_stack - idx;
250 : :
251 [ # # # # ]: 0 : if (idx >= 0 && idx <= task->curr_ret_stack)
252 : 0 : return &task->ret_stack[idx];
253 : :
254 : : return NULL;
255 : : }
256 : :
257 : : /**
258 : : * ftrace_graph_ret_addr - convert a potentially modified stack return address
259 : : * to its original value
260 : : *
261 : : * This function can be called by stack unwinding code to convert a found stack
262 : : * return address ('ret') to its original value, in case the function graph
263 : : * tracer has modified it to be 'return_to_handler'. If the address hasn't
264 : : * been modified, the unchanged value of 'ret' is returned.
265 : : *
266 : : * 'idx' is a state variable which should be initialized by the caller to zero
267 : : * before the first call.
268 : : *
269 : : * 'retp' is a pointer to the return address on the stack. It's ignored if
270 : : * the arch doesn't have HAVE_FUNCTION_GRAPH_RET_ADDR_PTR defined.
271 : : */
272 : : #ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
273 : : unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
274 : : unsigned long ret, unsigned long *retp)
275 : : {
276 : : int index = task->curr_ret_stack;
277 : : int i;
278 : :
279 : : if (ret != (unsigned long)dereference_kernel_function_descriptor(return_to_handler))
280 : : return ret;
281 : :
282 : : if (index < 0)
283 : : return ret;
284 : :
285 : : for (i = 0; i <= index; i++)
286 : : if (task->ret_stack[i].retp == retp)
287 : : return task->ret_stack[i].ret;
288 : :
289 : : return ret;
290 : : }
291 : : #else /* !HAVE_FUNCTION_GRAPH_RET_ADDR_PTR */
292 : 0 : unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
293 : : unsigned long ret, unsigned long *retp)
294 : : {
295 : : int task_idx;
296 : :
297 [ # # ]: 0 : if (ret != (unsigned long)dereference_kernel_function_descriptor(return_to_handler))
298 : : return ret;
299 : :
300 : 0 : task_idx = task->curr_ret_stack;
301 : :
302 [ # # # # ]: 0 : if (!task->ret_stack || task_idx < *idx)
303 : : return ret;
304 : :
305 : 0 : task_idx -= *idx;
306 : 0 : (*idx)++;
307 : :
308 : 0 : return task->ret_stack[task_idx].ret;
309 : : }
310 : : #endif /* HAVE_FUNCTION_GRAPH_RET_ADDR_PTR */
311 : :
312 : : static struct ftrace_ops graph_ops = {
313 : : .func = ftrace_stub,
314 : : .flags = FTRACE_OPS_FL_RECURSION_SAFE |
315 : : FTRACE_OPS_FL_INITIALIZED |
316 : : FTRACE_OPS_FL_PID |
317 : : FTRACE_OPS_FL_STUB,
318 : : #ifdef FTRACE_GRAPH_TRAMP_ADDR
319 : : .trampoline = FTRACE_GRAPH_TRAMP_ADDR,
320 : : /* trampoline_size is only needed for dynamically allocated tramps */
321 : : #endif
322 : : ASSIGN_OPS_HASH(graph_ops, &global_ops.local_hash)
323 : : };
324 : :
325 : 0 : void ftrace_graph_sleep_time_control(bool enable)
326 : : {
327 : 0 : fgraph_sleep_time = enable;
328 : 0 : }
329 : :
330 : 0 : int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
331 : : {
332 : 0 : return 0;
333 : : }
334 : :
335 : : /* The callbacks that hook a function */
336 : : trace_func_graph_ret_t ftrace_graph_return =
337 : : (trace_func_graph_ret_t)ftrace_stub;
338 : : trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub;
339 : : static trace_func_graph_ent_t __ftrace_graph_entry = ftrace_graph_entry_stub;
340 : :
341 : : /* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */
342 : 0 : static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list)
343 : : {
344 : : int i;
345 : : int ret = 0;
346 : : int start = 0, end = FTRACE_RETSTACK_ALLOC_SIZE;
347 : : struct task_struct *g, *t;
348 : :
349 [ # # ]: 0 : for (i = 0; i < FTRACE_RETSTACK_ALLOC_SIZE; i++) {
350 : 0 : ret_stack_list[i] =
351 : 0 : kmalloc_array(FTRACE_RETFUNC_DEPTH,
352 : : sizeof(struct ftrace_ret_stack),
353 : : GFP_KERNEL);
354 [ # # ]: 0 : if (!ret_stack_list[i]) {
355 : : start = 0;
356 : 0 : end = i;
357 : : ret = -ENOMEM;
358 : 0 : goto free;
359 : : }
360 : : }
361 : :
362 : 0 : read_lock(&tasklist_lock);
363 [ # # ]: 0 : do_each_thread(g, t) {
364 [ # # ]: 0 : if (start == end) {
365 : : ret = -EAGAIN;
366 : : goto unlock;
367 : : }
368 : :
369 [ # # ]: 0 : if (t->ret_stack == NULL) {
370 : : atomic_set(&t->tracing_graph_pause, 0);
371 : : atomic_set(&t->trace_overrun, 0);
372 : 0 : t->curr_ret_stack = -1;
373 : 0 : t->curr_ret_depth = -1;
374 : : /* Make sure the tasks see the -1 first: */
375 : 0 : smp_wmb();
376 : 0 : t->ret_stack = ret_stack_list[start++];
377 : : }
378 [ # # ]: 0 : } while_each_thread(g, t);
379 : :
380 : : unlock:
381 : : read_unlock(&tasklist_lock);
382 : : free:
383 [ # # ]: 0 : for (i = start; i < end; i++)
384 : 0 : kfree(ret_stack_list[i]);
385 : 0 : return ret;
386 : : }
387 : :
388 : : static void
389 : 0 : ftrace_graph_probe_sched_switch(void *ignore, bool preempt,
390 : : struct task_struct *prev, struct task_struct *next)
391 : : {
392 : : unsigned long long timestamp;
393 : : int index;
394 : :
395 : : /*
396 : : * Does the user want to count the time a function was asleep.
397 : : * If so, do not update the time stamps.
398 : : */
399 [ # # ]: 0 : if (fgraph_sleep_time)
400 : : return;
401 : :
402 : 0 : timestamp = trace_clock_local();
403 : :
404 : 0 : prev->ftrace_timestamp = timestamp;
405 : :
406 : : /* only process tasks that we timestamped */
407 [ # # ]: 0 : if (!next->ftrace_timestamp)
408 : : return;
409 : :
410 : : /*
411 : : * Update all the counters in next to make up for the
412 : : * time next was sleeping.
413 : : */
414 : 0 : timestamp -= next->ftrace_timestamp;
415 : :
416 [ # # ]: 0 : for (index = next->curr_ret_stack; index >= 0; index--)
417 : 0 : next->ret_stack[index].calltime += timestamp;
418 : : }
419 : :
420 : 0 : static int ftrace_graph_entry_test(struct ftrace_graph_ent *trace)
421 : : {
422 [ # # ]: 0 : if (!ftrace_ops_test(&global_ops, trace->func, NULL))
423 : : return 0;
424 : 0 : return __ftrace_graph_entry(trace);
425 : : }
426 : :
427 : : /*
428 : : * The function graph tracer should only trace the functions defined
429 : : * by set_ftrace_filter and set_ftrace_notrace. If another function
430 : : * tracer ops is registered, the graph tracer requires testing the
431 : : * function against the global ops, and not just trace any function
432 : : * that any ftrace_ops registered.
433 : : */
434 : 0 : void update_function_graph_func(void)
435 : : {
436 : : struct ftrace_ops *op;
437 : : bool do_test = false;
438 : :
439 : : /*
440 : : * The graph and global ops share the same set of functions
441 : : * to test. If any other ops is on the list, then
442 : : * the graph tracing needs to test if its the function
443 : : * it should call.
444 : : */
445 : 0 : do_for_each_ftrace_op(op, ftrace_ops_list) {
446 [ # # # # : 0 : if (op != &global_ops && op != &graph_ops &&
# # ]
447 : : op != &ftrace_list_end) {
448 : : do_test = true;
449 : : /* in double loop, break out with goto */
450 : : goto out;
451 : : }
452 [ # # # # ]: 0 : } while_for_each_ftrace_op(op);
453 : : out:
454 [ # # ]: 0 : if (do_test)
455 : 0 : ftrace_graph_entry = ftrace_graph_entry_test;
456 : : else
457 : 0 : ftrace_graph_entry = __ftrace_graph_entry;
458 : 0 : }
459 : :
460 : : static DEFINE_PER_CPU(struct ftrace_ret_stack *, idle_ret_stack);
461 : :
462 : : static void
463 : : graph_init_task(struct task_struct *t, struct ftrace_ret_stack *ret_stack)
464 : : {
465 : : atomic_set(&t->tracing_graph_pause, 0);
466 : : atomic_set(&t->trace_overrun, 0);
467 : 0 : t->ftrace_timestamp = 0;
468 : : /* make curr_ret_stack visible before we add the ret_stack */
469 : 0 : smp_wmb();
470 : 0 : t->ret_stack = ret_stack;
471 : : }
472 : :
473 : : /*
474 : : * Allocate a return stack for the idle task. May be the first
475 : : * time through, or it may be done by CPU hotplug online.
476 : : */
477 : 2070 : void ftrace_graph_init_idle_task(struct task_struct *t, int cpu)
478 : : {
479 : 2070 : t->curr_ret_stack = -1;
480 : 2070 : t->curr_ret_depth = -1;
481 : : /*
482 : : * The idle task has no parent, it either has its own
483 : : * stack or no stack at all.
484 : : */
485 [ - + ]: 2070 : if (t->ret_stack)
486 [ # # ]: 0 : WARN_ON(t->ret_stack != per_cpu(idle_ret_stack, cpu));
487 : :
488 [ - + ]: 2070 : if (ftrace_graph_active) {
489 : : struct ftrace_ret_stack *ret_stack;
490 : :
491 : 0 : ret_stack = per_cpu(idle_ret_stack, cpu);
492 [ # # ]: 0 : if (!ret_stack) {
493 : 0 : ret_stack =
494 : : kmalloc_array(FTRACE_RETFUNC_DEPTH,
495 : : sizeof(struct ftrace_ret_stack),
496 : : GFP_KERNEL);
497 [ # # ]: 0 : if (!ret_stack)
498 : 2070 : return;
499 : 0 : per_cpu(idle_ret_stack, cpu) = ret_stack;
500 : : }
501 : : graph_init_task(t, ret_stack);
502 : : }
503 : : }
504 : :
505 : : /* Allocate a return stack for newly created task */
506 : 278906 : void ftrace_graph_init_task(struct task_struct *t)
507 : : {
508 : : /* Make sure we do not use the parent ret_stack */
509 : 278906 : t->ret_stack = NULL;
510 : 278906 : t->curr_ret_stack = -1;
511 : 278906 : t->curr_ret_depth = -1;
512 : :
513 [ - + ]: 278906 : if (ftrace_graph_active) {
514 : : struct ftrace_ret_stack *ret_stack;
515 : :
516 : 0 : ret_stack = kmalloc_array(FTRACE_RETFUNC_DEPTH,
517 : : sizeof(struct ftrace_ret_stack),
518 : : GFP_KERNEL);
519 [ # # ]: 0 : if (!ret_stack)
520 : 278906 : return;
521 : : graph_init_task(t, ret_stack);
522 : : }
523 : : }
524 : :
525 : 239361 : void ftrace_graph_exit_task(struct task_struct *t)
526 : : {
527 : 239361 : struct ftrace_ret_stack *ret_stack = t->ret_stack;
528 : :
529 : 239361 : t->ret_stack = NULL;
530 : : /* NULL must become visible to IRQs before we free it: */
531 : 239361 : barrier();
532 : :
533 : 239369 : kfree(ret_stack);
534 : 239296 : }
535 : :
536 : : /* Allocate a return stack for each task */
537 : 0 : static int start_graph_tracing(void)
538 : : {
539 : : struct ftrace_ret_stack **ret_stack_list;
540 : : int ret, cpu;
541 : :
542 : 0 : ret_stack_list = kmalloc_array(FTRACE_RETSTACK_ALLOC_SIZE,
543 : : sizeof(struct ftrace_ret_stack *),
544 : : GFP_KERNEL);
545 : :
546 [ # # ]: 0 : if (!ret_stack_list)
547 : : return -ENOMEM;
548 : :
549 : : /* The cpu_boot init_task->ret_stack will never be freed */
550 [ # # ]: 0 : for_each_online_cpu(cpu) {
551 [ # # ]: 0 : if (!idle_task(cpu)->ret_stack)
552 : 0 : ftrace_graph_init_idle_task(idle_task(cpu), cpu);
553 : : }
554 : :
555 : : do {
556 : 0 : ret = alloc_retstack_tasklist(ret_stack_list);
557 [ # # ]: 0 : } while (ret == -EAGAIN);
558 : :
559 [ # # ]: 0 : if (!ret) {
560 : : ret = register_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
561 [ # # ]: 0 : if (ret)
562 : 0 : pr_info("ftrace_graph: Couldn't activate tracepoint"
563 : : " probe to kernel_sched_switch\n");
564 : : }
565 : :
566 : 0 : kfree(ret_stack_list);
567 : 0 : return ret;
568 : : }
569 : :
570 : 0 : int register_ftrace_graph(struct fgraph_ops *gops)
571 : : {
572 : : int ret = 0;
573 : :
574 : 0 : mutex_lock(&ftrace_lock);
575 : :
576 : : /* we currently allow only one tracer registered at a time */
577 [ # # ]: 0 : if (ftrace_graph_active) {
578 : : ret = -EBUSY;
579 : : goto out;
580 : : }
581 : :
582 : : register_pm_notifier(&ftrace_suspend_notifier);
583 : :
584 : 0 : ftrace_graph_active++;
585 : 0 : ret = start_graph_tracing();
586 [ # # ]: 0 : if (ret) {
587 : 0 : ftrace_graph_active--;
588 : 0 : goto out;
589 : : }
590 : :
591 : 0 : ftrace_graph_return = gops->retfunc;
592 : :
593 : : /*
594 : : * Update the indirect function to the entryfunc, and the
595 : : * function that gets called to the entry_test first. Then
596 : : * call the update fgraph entry function to determine if
597 : : * the entryfunc should be called directly or not.
598 : : */
599 : 0 : __ftrace_graph_entry = gops->entryfunc;
600 : 0 : ftrace_graph_entry = ftrace_graph_entry_test;
601 : 0 : update_function_graph_func();
602 : :
603 : 0 : ret = ftrace_startup(&graph_ops, FTRACE_START_FUNC_RET);
604 : : out:
605 : 0 : mutex_unlock(&ftrace_lock);
606 : 0 : return ret;
607 : : }
608 : :
609 : 0 : void unregister_ftrace_graph(struct fgraph_ops *gops)
610 : : {
611 : 0 : mutex_lock(&ftrace_lock);
612 : :
613 [ # # ]: 0 : if (unlikely(!ftrace_graph_active))
614 : : goto out;
615 : :
616 : 0 : ftrace_graph_active--;
617 : 0 : ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
618 : 0 : ftrace_graph_entry = ftrace_graph_entry_stub;
619 : 0 : __ftrace_graph_entry = ftrace_graph_entry_stub;
620 : 0 : ftrace_shutdown(&graph_ops, FTRACE_STOP_FUNC_RET);
621 : : unregister_pm_notifier(&ftrace_suspend_notifier);
622 : : unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
623 : :
624 : : out:
625 : 0 : mutex_unlock(&ftrace_lock);
626 : 0 : }
|