diff options
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/ftrace.c | 580 | ||||
-rw-r--r-- | kernel/trace/trace.c | 119 | ||||
-rw-r--r-- | kernel/trace/trace.h | 4 | ||||
-rw-r--r-- | kernel/trace/trace_functions.c | 4 | ||||
-rw-r--r-- | kernel/trace/trace_irqsoff.c | 16 | ||||
-rw-r--r-- | kernel/trace/trace_sched_switch.c | 49 | ||||
-rw-r--r-- | kernel/trace/trace_sched_wakeup.c | 67 | ||||
-rw-r--r-- | kernel/trace/trace_selftest.c | 1 |
8 files changed, 628 insertions, 212 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 89bd9a6f52ec..0f271c45cd02 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -21,12 +21,15 @@ | |||
21 | #include <linux/hardirq.h> | 21 | #include <linux/hardirq.h> |
22 | #include <linux/kthread.h> | 22 | #include <linux/kthread.h> |
23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
24 | #include <linux/kprobes.h> | ||
24 | #include <linux/ftrace.h> | 25 | #include <linux/ftrace.h> |
25 | #include <linux/sysctl.h> | 26 | #include <linux/sysctl.h> |
26 | #include <linux/ctype.h> | 27 | #include <linux/ctype.h> |
27 | #include <linux/hash.h> | 28 | #include <linux/hash.h> |
28 | #include <linux/list.h> | 29 | #include <linux/list.h> |
29 | 30 | ||
31 | #include <asm/ftrace.h> | ||
32 | |||
30 | #include "trace.h" | 33 | #include "trace.h" |
31 | 34 | ||
32 | /* ftrace_enabled is a method to turn ftrace on or off */ | 35 | /* ftrace_enabled is a method to turn ftrace on or off */ |
@@ -50,7 +53,7 @@ static struct ftrace_ops ftrace_list_end __read_mostly = | |||
50 | static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end; | 53 | static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end; |
51 | ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; | 54 | ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; |
52 | 55 | ||
53 | void ftrace_list_func(unsigned long ip, unsigned long parent_ip) | 56 | static void ftrace_list_func(unsigned long ip, unsigned long parent_ip) |
54 | { | 57 | { |
55 | struct ftrace_ops *op = ftrace_list; | 58 | struct ftrace_ops *op = ftrace_list; |
56 | 59 | ||
@@ -151,8 +154,6 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
151 | #ifdef CONFIG_DYNAMIC_FTRACE | 154 | #ifdef CONFIG_DYNAMIC_FTRACE |
152 | 155 | ||
153 | static struct task_struct *ftraced_task; | 156 | static struct task_struct *ftraced_task; |
154 | static DECLARE_WAIT_QUEUE_HEAD(ftraced_waiters); | ||
155 | static unsigned long ftraced_iteration_counter; | ||
156 | 157 | ||
157 | enum { | 158 | enum { |
158 | FTRACE_ENABLE_CALLS = (1 << 0), | 159 | FTRACE_ENABLE_CALLS = (1 << 0), |
@@ -163,6 +164,8 @@ enum { | |||
163 | }; | 164 | }; |
164 | 165 | ||
165 | static int ftrace_filtered; | 166 | static int ftrace_filtered; |
167 | static int tracing_on; | ||
168 | static int frozen_record_count; | ||
166 | 169 | ||
167 | static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; | 170 | static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; |
168 | 171 | ||
@@ -170,7 +173,7 @@ static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu); | |||
170 | 173 | ||
171 | static DEFINE_SPINLOCK(ftrace_shutdown_lock); | 174 | static DEFINE_SPINLOCK(ftrace_shutdown_lock); |
172 | static DEFINE_MUTEX(ftraced_lock); | 175 | static DEFINE_MUTEX(ftraced_lock); |
173 | static DEFINE_MUTEX(ftrace_filter_lock); | 176 | static DEFINE_MUTEX(ftrace_regex_lock); |
174 | 177 | ||
175 | struct ftrace_page { | 178 | struct ftrace_page { |
176 | struct ftrace_page *next; | 179 | struct ftrace_page *next; |
@@ -189,11 +192,77 @@ static struct ftrace_page *ftrace_pages; | |||
189 | 192 | ||
190 | static int ftraced_trigger; | 193 | static int ftraced_trigger; |
191 | static int ftraced_suspend; | 194 | static int ftraced_suspend; |
195 | static int ftraced_stop; | ||
192 | 196 | ||
193 | static int ftrace_record_suspend; | 197 | static int ftrace_record_suspend; |
194 | 198 | ||
195 | static struct dyn_ftrace *ftrace_free_records; | 199 | static struct dyn_ftrace *ftrace_free_records; |
196 | 200 | ||
201 | |||
202 | #ifdef CONFIG_KPROBES | ||
203 | static inline void freeze_record(struct dyn_ftrace *rec) | ||
204 | { | ||
205 | if (!(rec->flags & FTRACE_FL_FROZEN)) { | ||
206 | rec->flags |= FTRACE_FL_FROZEN; | ||
207 | frozen_record_count++; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | static inline void unfreeze_record(struct dyn_ftrace *rec) | ||
212 | { | ||
213 | if (rec->flags & FTRACE_FL_FROZEN) { | ||
214 | rec->flags &= ~FTRACE_FL_FROZEN; | ||
215 | frozen_record_count--; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static inline int record_frozen(struct dyn_ftrace *rec) | ||
220 | { | ||
221 | return rec->flags & FTRACE_FL_FROZEN; | ||
222 | } | ||
223 | #else | ||
224 | # define freeze_record(rec) ({ 0; }) | ||
225 | # define unfreeze_record(rec) ({ 0; }) | ||
226 | # define record_frozen(rec) ({ 0; }) | ||
227 | #endif /* CONFIG_KPROBES */ | ||
228 | |||
229 | int skip_trace(unsigned long ip) | ||
230 | { | ||
231 | unsigned long fl; | ||
232 | struct dyn_ftrace *rec; | ||
233 | struct hlist_node *t; | ||
234 | struct hlist_head *head; | ||
235 | |||
236 | if (frozen_record_count == 0) | ||
237 | return 0; | ||
238 | |||
239 | head = &ftrace_hash[hash_long(ip, FTRACE_HASHBITS)]; | ||
240 | hlist_for_each_entry_rcu(rec, t, head, node) { | ||
241 | if (rec->ip == ip) { | ||
242 | if (record_frozen(rec)) { | ||
243 | if (rec->flags & FTRACE_FL_FAILED) | ||
244 | return 1; | ||
245 | |||
246 | if (!(rec->flags & FTRACE_FL_CONVERTED)) | ||
247 | return 1; | ||
248 | |||
249 | if (!tracing_on || !ftrace_enabled) | ||
250 | return 1; | ||
251 | |||
252 | if (ftrace_filtered) { | ||
253 | fl = rec->flags & (FTRACE_FL_FILTER | | ||
254 | FTRACE_FL_NOTRACE); | ||
255 | if (!fl || (fl & FTRACE_FL_NOTRACE)) | ||
256 | return 1; | ||
257 | } | ||
258 | } | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
197 | static inline int | 266 | static inline int |
198 | ftrace_ip_in_hash(unsigned long ip, unsigned long key) | 267 | ftrace_ip_in_hash(unsigned long ip, unsigned long key) |
199 | { | 268 | { |
@@ -201,7 +270,7 @@ ftrace_ip_in_hash(unsigned long ip, unsigned long key) | |||
201 | struct hlist_node *t; | 270 | struct hlist_node *t; |
202 | int found = 0; | 271 | int found = 0; |
203 | 272 | ||
204 | hlist_for_each_entry(p, t, &ftrace_hash[key], node) { | 273 | hlist_for_each_entry_rcu(p, t, &ftrace_hash[key], node) { |
205 | if (p->ip == ip) { | 274 | if (p->ip == ip) { |
206 | found = 1; | 275 | found = 1; |
207 | break; | 276 | break; |
@@ -214,7 +283,13 @@ ftrace_ip_in_hash(unsigned long ip, unsigned long key) | |||
214 | static inline void | 283 | static inline void |
215 | ftrace_add_hash(struct dyn_ftrace *node, unsigned long key) | 284 | ftrace_add_hash(struct dyn_ftrace *node, unsigned long key) |
216 | { | 285 | { |
217 | hlist_add_head(&node->node, &ftrace_hash[key]); | 286 | hlist_add_head_rcu(&node->node, &ftrace_hash[key]); |
287 | } | ||
288 | |||
289 | /* called from kstop_machine */ | ||
290 | static inline void ftrace_del_hash(struct dyn_ftrace *node) | ||
291 | { | ||
292 | hlist_del(&node->node); | ||
218 | } | 293 | } |
219 | 294 | ||
220 | static void ftrace_free_rec(struct dyn_ftrace *rec) | 295 | static void ftrace_free_rec(struct dyn_ftrace *rec) |
@@ -301,13 +376,6 @@ ftrace_record_ip(unsigned long ip) | |||
301 | if (ftrace_ip_in_hash(ip, key)) | 376 | if (ftrace_ip_in_hash(ip, key)) |
302 | goto out_unlock; | 377 | goto out_unlock; |
303 | 378 | ||
304 | /* | ||
305 | * There's a slight race that the ftraced will update the | ||
306 | * hash and reset here. If it is already converted, skip it. | ||
307 | */ | ||
308 | if (ftrace_ip_converted(ip)) | ||
309 | goto out_unlock; | ||
310 | |||
311 | node = ftrace_alloc_dyn_node(ip); | 379 | node = ftrace_alloc_dyn_node(ip); |
312 | if (!node) | 380 | if (!node) |
313 | goto out_unlock; | 381 | goto out_unlock; |
@@ -331,19 +399,16 @@ ftrace_record_ip(unsigned long ip) | |||
331 | } | 399 | } |
332 | 400 | ||
333 | #define FTRACE_ADDR ((long)(ftrace_caller)) | 401 | #define FTRACE_ADDR ((long)(ftrace_caller)) |
334 | #define MCOUNT_ADDR ((long)(mcount)) | ||
335 | 402 | ||
336 | static void | 403 | static int |
337 | __ftrace_replace_code(struct dyn_ftrace *rec, | 404 | __ftrace_replace_code(struct dyn_ftrace *rec, |
338 | unsigned char *old, unsigned char *new, int enable) | 405 | unsigned char *old, unsigned char *new, int enable) |
339 | { | 406 | { |
340 | unsigned long ip; | 407 | unsigned long ip, fl; |
341 | int failed; | ||
342 | 408 | ||
343 | ip = rec->ip; | 409 | ip = rec->ip; |
344 | 410 | ||
345 | if (ftrace_filtered && enable) { | 411 | if (ftrace_filtered && enable) { |
346 | unsigned long fl; | ||
347 | /* | 412 | /* |
348 | * If filtering is on: | 413 | * If filtering is on: |
349 | * | 414 | * |
@@ -356,20 +421,29 @@ __ftrace_replace_code(struct dyn_ftrace *rec, | |||
356 | * If this record is not set to be filtered | 421 | * If this record is not set to be filtered |
357 | * and it is not enabled do nothing. | 422 | * and it is not enabled do nothing. |
358 | * | 423 | * |
424 | * If this record is set not to trace then | ||
425 | * do nothing. | ||
426 | * | ||
427 | * If this record is set not to trace and | ||
428 | * it is enabled then disable it. | ||
429 | * | ||
359 | * If this record is not set to be filtered and | 430 | * If this record is not set to be filtered and |
360 | * it is enabled, disable it. | 431 | * it is enabled, disable it. |
361 | */ | 432 | */ |
362 | fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED); | 433 | |
434 | fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_NOTRACE | | ||
435 | FTRACE_FL_ENABLED); | ||
363 | 436 | ||
364 | if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || | 437 | if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || |
365 | (fl == 0)) | 438 | (fl == (FTRACE_FL_FILTER | FTRACE_FL_NOTRACE)) || |
366 | return; | 439 | !fl || (fl == FTRACE_FL_NOTRACE)) |
440 | return 0; | ||
367 | 441 | ||
368 | /* | 442 | /* |
369 | * If it is enabled disable it, | 443 | * If it is enabled disable it, |
370 | * otherwise enable it! | 444 | * otherwise enable it! |
371 | */ | 445 | */ |
372 | if (fl == FTRACE_FL_ENABLED) { | 446 | if (fl & FTRACE_FL_ENABLED) { |
373 | /* swap new and old */ | 447 | /* swap new and old */ |
374 | new = old; | 448 | new = old; |
375 | old = ftrace_call_replace(ip, FTRACE_ADDR); | 449 | old = ftrace_call_replace(ip, FTRACE_ADDR); |
@@ -380,41 +454,39 @@ __ftrace_replace_code(struct dyn_ftrace *rec, | |||
380 | } | 454 | } |
381 | } else { | 455 | } else { |
382 | 456 | ||
383 | if (enable) | 457 | if (enable) { |
458 | /* | ||
459 | * If this record is set not to trace and is | ||
460 | * not enabled, do nothing. | ||
461 | */ | ||
462 | fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); | ||
463 | if (fl == FTRACE_FL_NOTRACE) | ||
464 | return 0; | ||
465 | |||
384 | new = ftrace_call_replace(ip, FTRACE_ADDR); | 466 | new = ftrace_call_replace(ip, FTRACE_ADDR); |
385 | else | 467 | } else |
386 | old = ftrace_call_replace(ip, FTRACE_ADDR); | 468 | old = ftrace_call_replace(ip, FTRACE_ADDR); |
387 | 469 | ||
388 | if (enable) { | 470 | if (enable) { |
389 | if (rec->flags & FTRACE_FL_ENABLED) | 471 | if (rec->flags & FTRACE_FL_ENABLED) |
390 | return; | 472 | return 0; |
391 | rec->flags |= FTRACE_FL_ENABLED; | 473 | rec->flags |= FTRACE_FL_ENABLED; |
392 | } else { | 474 | } else { |
393 | if (!(rec->flags & FTRACE_FL_ENABLED)) | 475 | if (!(rec->flags & FTRACE_FL_ENABLED)) |
394 | return; | 476 | return 0; |
395 | rec->flags &= ~FTRACE_FL_ENABLED; | 477 | rec->flags &= ~FTRACE_FL_ENABLED; |
396 | } | 478 | } |
397 | } | 479 | } |
398 | 480 | ||
399 | failed = ftrace_modify_code(ip, old, new); | 481 | return ftrace_modify_code(ip, old, new); |
400 | if (failed) { | ||
401 | unsigned long key; | ||
402 | /* It is possible that the function hasn't been converted yet */ | ||
403 | key = hash_long(ip, FTRACE_HASHBITS); | ||
404 | if (!ftrace_ip_in_hash(ip, key)) { | ||
405 | rec->flags |= FTRACE_FL_FAILED; | ||
406 | ftrace_free_rec(rec); | ||
407 | } | ||
408 | |||
409 | } | ||
410 | } | 482 | } |
411 | 483 | ||
412 | static void ftrace_replace_code(int enable) | 484 | static void ftrace_replace_code(int enable) |
413 | { | 485 | { |
486 | int i, failed; | ||
414 | unsigned char *new = NULL, *old = NULL; | 487 | unsigned char *new = NULL, *old = NULL; |
415 | struct dyn_ftrace *rec; | 488 | struct dyn_ftrace *rec; |
416 | struct ftrace_page *pg; | 489 | struct ftrace_page *pg; |
417 | int i; | ||
418 | 490 | ||
419 | if (enable) | 491 | if (enable) |
420 | old = ftrace_nop_replace(); | 492 | old = ftrace_nop_replace(); |
@@ -429,7 +501,23 @@ static void ftrace_replace_code(int enable) | |||
429 | if (rec->flags & FTRACE_FL_FAILED) | 501 | if (rec->flags & FTRACE_FL_FAILED) |
430 | continue; | 502 | continue; |
431 | 503 | ||
432 | __ftrace_replace_code(rec, old, new, enable); | 504 | /* ignore updates to this record's mcount site */ |
505 | if (get_kprobe((void *)rec->ip)) { | ||
506 | freeze_record(rec); | ||
507 | continue; | ||
508 | } else { | ||
509 | unfreeze_record(rec); | ||
510 | } | ||
511 | |||
512 | failed = __ftrace_replace_code(rec, old, new, enable); | ||
513 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { | ||
514 | rec->flags |= FTRACE_FL_FAILED; | ||
515 | if ((system_state == SYSTEM_BOOTING) || | ||
516 | !core_kernel_text(rec->ip)) { | ||
517 | ftrace_del_hash(rec); | ||
518 | ftrace_free_rec(rec); | ||
519 | } | ||
520 | } | ||
433 | } | 521 | } |
434 | } | 522 | } |
435 | } | 523 | } |
@@ -443,7 +531,7 @@ static void ftrace_shutdown_replenish(void) | |||
443 | ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL); | 531 | ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL); |
444 | } | 532 | } |
445 | 533 | ||
446 | static void | 534 | static int |
447 | ftrace_code_disable(struct dyn_ftrace *rec) | 535 | ftrace_code_disable(struct dyn_ftrace *rec) |
448 | { | 536 | { |
449 | unsigned long ip; | 537 | unsigned long ip; |
@@ -458,19 +546,30 @@ ftrace_code_disable(struct dyn_ftrace *rec) | |||
458 | failed = ftrace_modify_code(ip, call, nop); | 546 | failed = ftrace_modify_code(ip, call, nop); |
459 | if (failed) { | 547 | if (failed) { |
460 | rec->flags |= FTRACE_FL_FAILED; | 548 | rec->flags |= FTRACE_FL_FAILED; |
461 | ftrace_free_rec(rec); | 549 | return 0; |
462 | } | 550 | } |
551 | return 1; | ||
463 | } | 552 | } |
464 | 553 | ||
554 | static int __ftrace_update_code(void *ignore); | ||
555 | |||
465 | static int __ftrace_modify_code(void *data) | 556 | static int __ftrace_modify_code(void *data) |
466 | { | 557 | { |
467 | unsigned long addr; | 558 | unsigned long addr; |
468 | int *command = data; | 559 | int *command = data; |
469 | 560 | ||
470 | if (*command & FTRACE_ENABLE_CALLS) | 561 | if (*command & FTRACE_ENABLE_CALLS) { |
562 | /* | ||
563 | * Update any recorded ips now that we have the | ||
564 | * machine stopped | ||
565 | */ | ||
566 | __ftrace_update_code(NULL); | ||
471 | ftrace_replace_code(1); | 567 | ftrace_replace_code(1); |
472 | else if (*command & FTRACE_DISABLE_CALLS) | 568 | tracing_on = 1; |
569 | } else if (*command & FTRACE_DISABLE_CALLS) { | ||
473 | ftrace_replace_code(0); | 570 | ftrace_replace_code(0); |
571 | tracing_on = 0; | ||
572 | } | ||
474 | 573 | ||
475 | if (*command & FTRACE_UPDATE_TRACE_FUNC) | 574 | if (*command & FTRACE_UPDATE_TRACE_FUNC) |
476 | ftrace_update_ftrace_func(ftrace_trace_function); | 575 | ftrace_update_ftrace_func(ftrace_trace_function); |
@@ -491,6 +590,25 @@ static void ftrace_run_update_code(int command) | |||
491 | stop_machine_run(__ftrace_modify_code, &command, NR_CPUS); | 590 | stop_machine_run(__ftrace_modify_code, &command, NR_CPUS); |
492 | } | 591 | } |
493 | 592 | ||
593 | void ftrace_disable_daemon(void) | ||
594 | { | ||
595 | /* Stop the daemon from calling kstop_machine */ | ||
596 | mutex_lock(&ftraced_lock); | ||
597 | ftraced_stop = 1; | ||
598 | mutex_unlock(&ftraced_lock); | ||
599 | |||
600 | ftrace_force_update(); | ||
601 | } | ||
602 | |||
603 | void ftrace_enable_daemon(void) | ||
604 | { | ||
605 | mutex_lock(&ftraced_lock); | ||
606 | ftraced_stop = 0; | ||
607 | mutex_unlock(&ftraced_lock); | ||
608 | |||
609 | ftrace_force_update(); | ||
610 | } | ||
611 | |||
494 | static ftrace_func_t saved_ftrace_func; | 612 | static ftrace_func_t saved_ftrace_func; |
495 | 613 | ||
496 | static void ftrace_startup(void) | 614 | static void ftrace_startup(void) |
@@ -583,14 +701,14 @@ unsigned long ftrace_update_tot_cnt; | |||
583 | 701 | ||
584 | static int __ftrace_update_code(void *ignore) | 702 | static int __ftrace_update_code(void *ignore) |
585 | { | 703 | { |
586 | struct dyn_ftrace *p; | 704 | int i, save_ftrace_enabled; |
587 | struct hlist_head head; | ||
588 | struct hlist_node *t; | ||
589 | int save_ftrace_enabled; | ||
590 | cycle_t start, stop; | 705 | cycle_t start, stop; |
591 | int i; | 706 | struct dyn_ftrace *p; |
707 | struct hlist_node *t, *n; | ||
708 | struct hlist_head *head, temp_list; | ||
592 | 709 | ||
593 | /* Don't be recording funcs now */ | 710 | /* Don't be recording funcs now */ |
711 | ftrace_record_suspend++; | ||
594 | save_ftrace_enabled = ftrace_enabled; | 712 | save_ftrace_enabled = ftrace_enabled; |
595 | ftrace_enabled = 0; | 713 | ftrace_enabled = 0; |
596 | 714 | ||
@@ -599,35 +717,79 @@ static int __ftrace_update_code(void *ignore) | |||
599 | 717 | ||
600 | /* No locks needed, the machine is stopped! */ | 718 | /* No locks needed, the machine is stopped! */ |
601 | for (i = 0; i < FTRACE_HASHSIZE; i++) { | 719 | for (i = 0; i < FTRACE_HASHSIZE; i++) { |
602 | if (hlist_empty(&ftrace_hash[i])) | 720 | INIT_HLIST_HEAD(&temp_list); |
603 | continue; | 721 | head = &ftrace_hash[i]; |
604 | |||
605 | head = ftrace_hash[i]; | ||
606 | INIT_HLIST_HEAD(&ftrace_hash[i]); | ||
607 | 722 | ||
608 | /* all CPUS are stopped, we are safe to modify code */ | 723 | /* all CPUS are stopped, we are safe to modify code */ |
609 | hlist_for_each_entry(p, t, &head, node) { | 724 | hlist_for_each_entry_safe(p, t, n, head, node) { |
610 | ftrace_code_disable(p); | 725 | /* Skip over failed records which have not been |
611 | ftrace_update_cnt++; | 726 | * freed. */ |
727 | if (p->flags & FTRACE_FL_FAILED) | ||
728 | continue; | ||
729 | |||
730 | /* Unconverted records are always at the head of the | ||
731 | * hash bucket. Once we encounter a converted record, | ||
732 | * simply skip over to the next bucket. Saves ftraced | ||
733 | * some processor cycles (ftrace does its bid for | ||
734 | * global warming :-p ). */ | ||
735 | if (p->flags & (FTRACE_FL_CONVERTED)) | ||
736 | break; | ||
737 | |||
738 | /* Ignore updates to this record's mcount site. | ||
739 | * Reintroduce this record at the head of this | ||
740 | * bucket to attempt to "convert" it again if | ||
741 | * the kprobe on it is unregistered before the | ||
742 | * next run. */ | ||
743 | if (get_kprobe((void *)p->ip)) { | ||
744 | ftrace_del_hash(p); | ||
745 | INIT_HLIST_NODE(&p->node); | ||
746 | hlist_add_head(&p->node, &temp_list); | ||
747 | freeze_record(p); | ||
748 | continue; | ||
749 | } else { | ||
750 | unfreeze_record(p); | ||
751 | } | ||
752 | |||
753 | /* convert record (i.e, patch mcount-call with NOP) */ | ||
754 | if (ftrace_code_disable(p)) { | ||
755 | p->flags |= FTRACE_FL_CONVERTED; | ||
756 | ftrace_update_cnt++; | ||
757 | } else { | ||
758 | if ((system_state == SYSTEM_BOOTING) || | ||
759 | !core_kernel_text(p->ip)) { | ||
760 | ftrace_del_hash(p); | ||
761 | ftrace_free_rec(p); | ||
762 | } | ||
763 | } | ||
612 | } | 764 | } |
613 | 765 | ||
766 | hlist_for_each_entry_safe(p, t, n, &temp_list, node) { | ||
767 | hlist_del(&p->node); | ||
768 | INIT_HLIST_NODE(&p->node); | ||
769 | hlist_add_head(&p->node, head); | ||
770 | } | ||
614 | } | 771 | } |
615 | 772 | ||
616 | stop = ftrace_now(raw_smp_processor_id()); | 773 | stop = ftrace_now(raw_smp_processor_id()); |
617 | ftrace_update_time = stop - start; | 774 | ftrace_update_time = stop - start; |
618 | ftrace_update_tot_cnt += ftrace_update_cnt; | 775 | ftrace_update_tot_cnt += ftrace_update_cnt; |
776 | ftraced_trigger = 0; | ||
619 | 777 | ||
620 | ftrace_enabled = save_ftrace_enabled; | 778 | ftrace_enabled = save_ftrace_enabled; |
779 | ftrace_record_suspend--; | ||
621 | 780 | ||
622 | return 0; | 781 | return 0; |
623 | } | 782 | } |
624 | 783 | ||
625 | static void ftrace_update_code(void) | 784 | static int ftrace_update_code(void) |
626 | { | 785 | { |
627 | if (unlikely(ftrace_disabled)) | 786 | if (unlikely(ftrace_disabled) || |
628 | return; | 787 | !ftrace_enabled || !ftraced_trigger) |
788 | return 0; | ||
629 | 789 | ||
630 | stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); | 790 | stop_machine_run(__ftrace_update_code, NULL, NR_CPUS); |
791 | |||
792 | return 1; | ||
631 | } | 793 | } |
632 | 794 | ||
633 | static int ftraced(void *ignore) | 795 | static int ftraced(void *ignore) |
@@ -646,14 +808,13 @@ static int ftraced(void *ignore) | |||
646 | 808 | ||
647 | mutex_lock(&ftrace_sysctl_lock); | 809 | mutex_lock(&ftrace_sysctl_lock); |
648 | mutex_lock(&ftraced_lock); | 810 | mutex_lock(&ftraced_lock); |
649 | if (ftrace_enabled && ftraced_trigger && !ftraced_suspend) { | 811 | if (!ftraced_suspend && !ftraced_stop && |
650 | ftrace_record_suspend++; | 812 | ftrace_update_code()) { |
651 | ftrace_update_code(); | ||
652 | usecs = nsecs_to_usecs(ftrace_update_time); | 813 | usecs = nsecs_to_usecs(ftrace_update_time); |
653 | if (ftrace_update_tot_cnt > 100000) { | 814 | if (ftrace_update_tot_cnt > 100000) { |
654 | ftrace_update_tot_cnt = 0; | 815 | ftrace_update_tot_cnt = 0; |
655 | pr_info("hm, dftrace overflow: %lu change%s" | 816 | pr_info("hm, dftrace overflow: %lu change%s" |
656 | " (%lu total) in %lu usec%s\n", | 817 | " (%lu total) in %lu usec%s\n", |
657 | ftrace_update_cnt, | 818 | ftrace_update_cnt, |
658 | ftrace_update_cnt != 1 ? "s" : "", | 819 | ftrace_update_cnt != 1 ? "s" : "", |
659 | ftrace_update_tot_cnt, | 820 | ftrace_update_tot_cnt, |
@@ -661,15 +822,10 @@ static int ftraced(void *ignore) | |||
661 | ftrace_disabled = 1; | 822 | ftrace_disabled = 1; |
662 | WARN_ON_ONCE(1); | 823 | WARN_ON_ONCE(1); |
663 | } | 824 | } |
664 | ftraced_trigger = 0; | ||
665 | ftrace_record_suspend--; | ||
666 | } | 825 | } |
667 | ftraced_iteration_counter++; | ||
668 | mutex_unlock(&ftraced_lock); | 826 | mutex_unlock(&ftraced_lock); |
669 | mutex_unlock(&ftrace_sysctl_lock); | 827 | mutex_unlock(&ftrace_sysctl_lock); |
670 | 828 | ||
671 | wake_up_interruptible(&ftraced_waiters); | ||
672 | |||
673 | ftrace_shutdown_replenish(); | 829 | ftrace_shutdown_replenish(); |
674 | } | 830 | } |
675 | __set_current_state(TASK_RUNNING); | 831 | __set_current_state(TASK_RUNNING); |
@@ -721,6 +877,8 @@ static int __init ftrace_dyn_table_alloc(void) | |||
721 | enum { | 877 | enum { |
722 | FTRACE_ITER_FILTER = (1 << 0), | 878 | FTRACE_ITER_FILTER = (1 << 0), |
723 | FTRACE_ITER_CONT = (1 << 1), | 879 | FTRACE_ITER_CONT = (1 << 1), |
880 | FTRACE_ITER_NOTRACE = (1 << 2), | ||
881 | FTRACE_ITER_FAILURES = (1 << 3), | ||
724 | }; | 882 | }; |
725 | 883 | ||
726 | #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ | 884 | #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ |
@@ -752,9 +910,18 @@ t_next(struct seq_file *m, void *v, loff_t *pos) | |||
752 | } | 910 | } |
753 | } else { | 911 | } else { |
754 | rec = &iter->pg->records[iter->idx++]; | 912 | rec = &iter->pg->records[iter->idx++]; |
755 | if ((rec->flags & FTRACE_FL_FAILED) || | 913 | if ((!(iter->flags & FTRACE_ITER_FAILURES) && |
914 | (rec->flags & FTRACE_FL_FAILED)) || | ||
915 | |||
916 | ((iter->flags & FTRACE_ITER_FAILURES) && | ||
917 | (!(rec->flags & FTRACE_FL_FAILED) || | ||
918 | (rec->flags & FTRACE_FL_FREE))) || | ||
919 | |||
756 | ((iter->flags & FTRACE_ITER_FILTER) && | 920 | ((iter->flags & FTRACE_ITER_FILTER) && |
757 | !(rec->flags & FTRACE_FL_FILTER))) { | 921 | !(rec->flags & FTRACE_FL_FILTER)) || |
922 | |||
923 | ((iter->flags & FTRACE_ITER_NOTRACE) && | ||
924 | !(rec->flags & FTRACE_FL_NOTRACE))) { | ||
758 | rec = NULL; | 925 | rec = NULL; |
759 | goto retry; | 926 | goto retry; |
760 | } | 927 | } |
@@ -847,22 +1014,42 @@ int ftrace_avail_release(struct inode *inode, struct file *file) | |||
847 | return 0; | 1014 | return 0; |
848 | } | 1015 | } |
849 | 1016 | ||
850 | static void ftrace_filter_reset(void) | 1017 | static int |
1018 | ftrace_failures_open(struct inode *inode, struct file *file) | ||
1019 | { | ||
1020 | int ret; | ||
1021 | struct seq_file *m; | ||
1022 | struct ftrace_iterator *iter; | ||
1023 | |||
1024 | ret = ftrace_avail_open(inode, file); | ||
1025 | if (!ret) { | ||
1026 | m = (struct seq_file *)file->private_data; | ||
1027 | iter = (struct ftrace_iterator *)m->private; | ||
1028 | iter->flags = FTRACE_ITER_FAILURES; | ||
1029 | } | ||
1030 | |||
1031 | return ret; | ||
1032 | } | ||
1033 | |||
1034 | |||
1035 | static void ftrace_filter_reset(int enable) | ||
851 | { | 1036 | { |
852 | struct ftrace_page *pg; | 1037 | struct ftrace_page *pg; |
853 | struct dyn_ftrace *rec; | 1038 | struct dyn_ftrace *rec; |
1039 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
854 | unsigned i; | 1040 | unsigned i; |
855 | 1041 | ||
856 | /* keep kstop machine from running */ | 1042 | /* keep kstop machine from running */ |
857 | preempt_disable(); | 1043 | preempt_disable(); |
858 | ftrace_filtered = 0; | 1044 | if (enable) |
1045 | ftrace_filtered = 0; | ||
859 | pg = ftrace_pages_start; | 1046 | pg = ftrace_pages_start; |
860 | while (pg) { | 1047 | while (pg) { |
861 | for (i = 0; i < pg->index; i++) { | 1048 | for (i = 0; i < pg->index; i++) { |
862 | rec = &pg->records[i]; | 1049 | rec = &pg->records[i]; |
863 | if (rec->flags & FTRACE_FL_FAILED) | 1050 | if (rec->flags & FTRACE_FL_FAILED) |
864 | continue; | 1051 | continue; |
865 | rec->flags &= ~FTRACE_FL_FILTER; | 1052 | rec->flags &= ~type; |
866 | } | 1053 | } |
867 | pg = pg->next; | 1054 | pg = pg->next; |
868 | } | 1055 | } |
@@ -870,7 +1057,7 @@ static void ftrace_filter_reset(void) | |||
870 | } | 1057 | } |
871 | 1058 | ||
872 | static int | 1059 | static int |
873 | ftrace_filter_open(struct inode *inode, struct file *file) | 1060 | ftrace_regex_open(struct inode *inode, struct file *file, int enable) |
874 | { | 1061 | { |
875 | struct ftrace_iterator *iter; | 1062 | struct ftrace_iterator *iter; |
876 | int ret = 0; | 1063 | int ret = 0; |
@@ -882,15 +1069,16 @@ ftrace_filter_open(struct inode *inode, struct file *file) | |||
882 | if (!iter) | 1069 | if (!iter) |
883 | return -ENOMEM; | 1070 | return -ENOMEM; |
884 | 1071 | ||
885 | mutex_lock(&ftrace_filter_lock); | 1072 | mutex_lock(&ftrace_regex_lock); |
886 | if ((file->f_mode & FMODE_WRITE) && | 1073 | if ((file->f_mode & FMODE_WRITE) && |
887 | !(file->f_flags & O_APPEND)) | 1074 | !(file->f_flags & O_APPEND)) |
888 | ftrace_filter_reset(); | 1075 | ftrace_filter_reset(enable); |
889 | 1076 | ||
890 | if (file->f_mode & FMODE_READ) { | 1077 | if (file->f_mode & FMODE_READ) { |
891 | iter->pg = ftrace_pages_start; | 1078 | iter->pg = ftrace_pages_start; |
892 | iter->pos = -1; | 1079 | iter->pos = -1; |
893 | iter->flags = FTRACE_ITER_FILTER; | 1080 | iter->flags = enable ? FTRACE_ITER_FILTER : |
1081 | FTRACE_ITER_NOTRACE; | ||
894 | 1082 | ||
895 | ret = seq_open(file, &show_ftrace_seq_ops); | 1083 | ret = seq_open(file, &show_ftrace_seq_ops); |
896 | if (!ret) { | 1084 | if (!ret) { |
@@ -900,13 +1088,25 @@ ftrace_filter_open(struct inode *inode, struct file *file) | |||
900 | kfree(iter); | 1088 | kfree(iter); |
901 | } else | 1089 | } else |
902 | file->private_data = iter; | 1090 | file->private_data = iter; |
903 | mutex_unlock(&ftrace_filter_lock); | 1091 | mutex_unlock(&ftrace_regex_lock); |
904 | 1092 | ||
905 | return ret; | 1093 | return ret; |
906 | } | 1094 | } |
907 | 1095 | ||
1096 | static int | ||
1097 | ftrace_filter_open(struct inode *inode, struct file *file) | ||
1098 | { | ||
1099 | return ftrace_regex_open(inode, file, 1); | ||
1100 | } | ||
1101 | |||
1102 | static int | ||
1103 | ftrace_notrace_open(struct inode *inode, struct file *file) | ||
1104 | { | ||
1105 | return ftrace_regex_open(inode, file, 0); | ||
1106 | } | ||
1107 | |||
908 | static ssize_t | 1108 | static ssize_t |
909 | ftrace_filter_read(struct file *file, char __user *ubuf, | 1109 | ftrace_regex_read(struct file *file, char __user *ubuf, |
910 | size_t cnt, loff_t *ppos) | 1110 | size_t cnt, loff_t *ppos) |
911 | { | 1111 | { |
912 | if (file->f_mode & FMODE_READ) | 1112 | if (file->f_mode & FMODE_READ) |
@@ -916,7 +1116,7 @@ ftrace_filter_read(struct file *file, char __user *ubuf, | |||
916 | } | 1116 | } |
917 | 1117 | ||
918 | static loff_t | 1118 | static loff_t |
919 | ftrace_filter_lseek(struct file *file, loff_t offset, int origin) | 1119 | ftrace_regex_lseek(struct file *file, loff_t offset, int origin) |
920 | { | 1120 | { |
921 | loff_t ret; | 1121 | loff_t ret; |
922 | 1122 | ||
@@ -936,13 +1136,14 @@ enum { | |||
936 | }; | 1136 | }; |
937 | 1137 | ||
938 | static void | 1138 | static void |
939 | ftrace_match(unsigned char *buff, int len) | 1139 | ftrace_match(unsigned char *buff, int len, int enable) |
940 | { | 1140 | { |
941 | char str[KSYM_SYMBOL_LEN]; | 1141 | char str[KSYM_SYMBOL_LEN]; |
942 | char *search = NULL; | 1142 | char *search = NULL; |
943 | struct ftrace_page *pg; | 1143 | struct ftrace_page *pg; |
944 | struct dyn_ftrace *rec; | 1144 | struct dyn_ftrace *rec; |
945 | int type = MATCH_FULL; | 1145 | int type = MATCH_FULL; |
1146 | unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
946 | unsigned i, match = 0, search_len = 0; | 1147 | unsigned i, match = 0, search_len = 0; |
947 | 1148 | ||
948 | for (i = 0; i < len; i++) { | 1149 | for (i = 0; i < len; i++) { |
@@ -966,7 +1167,8 @@ ftrace_match(unsigned char *buff, int len) | |||
966 | 1167 | ||
967 | /* keep kstop machine from running */ | 1168 | /* keep kstop machine from running */ |
968 | preempt_disable(); | 1169 | preempt_disable(); |
969 | ftrace_filtered = 1; | 1170 | if (enable) |
1171 | ftrace_filtered = 1; | ||
970 | pg = ftrace_pages_start; | 1172 | pg = ftrace_pages_start; |
971 | while (pg) { | 1173 | while (pg) { |
972 | for (i = 0; i < pg->index; i++) { | 1174 | for (i = 0; i < pg->index; i++) { |
@@ -997,7 +1199,7 @@ ftrace_match(unsigned char *buff, int len) | |||
997 | break; | 1199 | break; |
998 | } | 1200 | } |
999 | if (matched) | 1201 | if (matched) |
1000 | rec->flags |= FTRACE_FL_FILTER; | 1202 | rec->flags |= flag; |
1001 | } | 1203 | } |
1002 | pg = pg->next; | 1204 | pg = pg->next; |
1003 | } | 1205 | } |
@@ -1005,8 +1207,8 @@ ftrace_match(unsigned char *buff, int len) | |||
1005 | } | 1207 | } |
1006 | 1208 | ||
1007 | static ssize_t | 1209 | static ssize_t |
1008 | ftrace_filter_write(struct file *file, const char __user *ubuf, | 1210 | ftrace_regex_write(struct file *file, const char __user *ubuf, |
1009 | size_t cnt, loff_t *ppos) | 1211 | size_t cnt, loff_t *ppos, int enable) |
1010 | { | 1212 | { |
1011 | struct ftrace_iterator *iter; | 1213 | struct ftrace_iterator *iter; |
1012 | char ch; | 1214 | char ch; |
@@ -1016,7 +1218,7 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, | |||
1016 | if (!cnt || cnt < 0) | 1218 | if (!cnt || cnt < 0) |
1017 | return 0; | 1219 | return 0; |
1018 | 1220 | ||
1019 | mutex_lock(&ftrace_filter_lock); | 1221 | mutex_lock(&ftrace_regex_lock); |
1020 | 1222 | ||
1021 | if (file->f_mode & FMODE_READ) { | 1223 | if (file->f_mode & FMODE_READ) { |
1022 | struct seq_file *m = file->private_data; | 1224 | struct seq_file *m = file->private_data; |
@@ -1045,7 +1247,6 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, | |||
1045 | cnt--; | 1247 | cnt--; |
1046 | } | 1248 | } |
1047 | 1249 | ||
1048 | |||
1049 | if (isspace(ch)) { | 1250 | if (isspace(ch)) { |
1050 | file->f_pos += read; | 1251 | file->f_pos += read; |
1051 | ret = read; | 1252 | ret = read; |
@@ -1072,7 +1273,7 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, | |||
1072 | if (isspace(ch)) { | 1273 | if (isspace(ch)) { |
1073 | iter->filtered++; | 1274 | iter->filtered++; |
1074 | iter->buffer[iter->buffer_idx] = 0; | 1275 | iter->buffer[iter->buffer_idx] = 0; |
1075 | ftrace_match(iter->buffer, iter->buffer_idx); | 1276 | ftrace_match(iter->buffer, iter->buffer_idx, enable); |
1076 | iter->buffer_idx = 0; | 1277 | iter->buffer_idx = 0; |
1077 | } else | 1278 | } else |
1078 | iter->flags |= FTRACE_ITER_CONT; | 1279 | iter->flags |= FTRACE_ITER_CONT; |
@@ -1082,11 +1283,39 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, | |||
1082 | 1283 | ||
1083 | ret = read; | 1284 | ret = read; |
1084 | out: | 1285 | out: |
1085 | mutex_unlock(&ftrace_filter_lock); | 1286 | mutex_unlock(&ftrace_regex_lock); |
1086 | 1287 | ||
1087 | return ret; | 1288 | return ret; |
1088 | } | 1289 | } |
1089 | 1290 | ||
1291 | static ssize_t | ||
1292 | ftrace_filter_write(struct file *file, const char __user *ubuf, | ||
1293 | size_t cnt, loff_t *ppos) | ||
1294 | { | ||
1295 | return ftrace_regex_write(file, ubuf, cnt, ppos, 1); | ||
1296 | } | ||
1297 | |||
1298 | static ssize_t | ||
1299 | ftrace_notrace_write(struct file *file, const char __user *ubuf, | ||
1300 | size_t cnt, loff_t *ppos) | ||
1301 | { | ||
1302 | return ftrace_regex_write(file, ubuf, cnt, ppos, 0); | ||
1303 | } | ||
1304 | |||
1305 | static void | ||
1306 | ftrace_set_regex(unsigned char *buf, int len, int reset, int enable) | ||
1307 | { | ||
1308 | if (unlikely(ftrace_disabled)) | ||
1309 | return; | ||
1310 | |||
1311 | mutex_lock(&ftrace_regex_lock); | ||
1312 | if (reset) | ||
1313 | ftrace_filter_reset(enable); | ||
1314 | if (buf) | ||
1315 | ftrace_match(buf, len, enable); | ||
1316 | mutex_unlock(&ftrace_regex_lock); | ||
1317 | } | ||
1318 | |||
1090 | /** | 1319 | /** |
1091 | * ftrace_set_filter - set a function to filter on in ftrace | 1320 | * ftrace_set_filter - set a function to filter on in ftrace |
1092 | * @buf - the string that holds the function filter text. | 1321 | * @buf - the string that holds the function filter text. |
@@ -1098,24 +1327,31 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, | |||
1098 | */ | 1327 | */ |
1099 | void ftrace_set_filter(unsigned char *buf, int len, int reset) | 1328 | void ftrace_set_filter(unsigned char *buf, int len, int reset) |
1100 | { | 1329 | { |
1101 | if (unlikely(ftrace_disabled)) | 1330 | ftrace_set_regex(buf, len, reset, 1); |
1102 | return; | 1331 | } |
1103 | 1332 | ||
1104 | mutex_lock(&ftrace_filter_lock); | 1333 | /** |
1105 | if (reset) | 1334 | * ftrace_set_notrace - set a function to not trace in ftrace |
1106 | ftrace_filter_reset(); | 1335 | * @buf - the string that holds the function notrace text. |
1107 | if (buf) | 1336 | * @len - the length of the string. |
1108 | ftrace_match(buf, len); | 1337 | * @reset - non zero to reset all filters before applying this filter. |
1109 | mutex_unlock(&ftrace_filter_lock); | 1338 | * |
1339 | * Notrace Filters denote which functions should not be enabled when tracing | ||
1340 | * is enabled. If @buf is NULL and reset is set, all functions will be enabled | ||
1341 | * for tracing. | ||
1342 | */ | ||
1343 | void ftrace_set_notrace(unsigned char *buf, int len, int reset) | ||
1344 | { | ||
1345 | ftrace_set_regex(buf, len, reset, 0); | ||
1110 | } | 1346 | } |
1111 | 1347 | ||
1112 | static int | 1348 | static int |
1113 | ftrace_filter_release(struct inode *inode, struct file *file) | 1349 | ftrace_regex_release(struct inode *inode, struct file *file, int enable) |
1114 | { | 1350 | { |
1115 | struct seq_file *m = (struct seq_file *)file->private_data; | 1351 | struct seq_file *m = (struct seq_file *)file->private_data; |
1116 | struct ftrace_iterator *iter; | 1352 | struct ftrace_iterator *iter; |
1117 | 1353 | ||
1118 | mutex_lock(&ftrace_filter_lock); | 1354 | mutex_lock(&ftrace_regex_lock); |
1119 | if (file->f_mode & FMODE_READ) { | 1355 | if (file->f_mode & FMODE_READ) { |
1120 | iter = m->private; | 1356 | iter = m->private; |
1121 | 1357 | ||
@@ -1126,7 +1362,7 @@ ftrace_filter_release(struct inode *inode, struct file *file) | |||
1126 | if (iter->buffer_idx) { | 1362 | if (iter->buffer_idx) { |
1127 | iter->filtered++; | 1363 | iter->filtered++; |
1128 | iter->buffer[iter->buffer_idx] = 0; | 1364 | iter->buffer[iter->buffer_idx] = 0; |
1129 | ftrace_match(iter->buffer, iter->buffer_idx); | 1365 | ftrace_match(iter->buffer, iter->buffer_idx, enable); |
1130 | } | 1366 | } |
1131 | 1367 | ||
1132 | mutex_lock(&ftrace_sysctl_lock); | 1368 | mutex_lock(&ftrace_sysctl_lock); |
@@ -1137,10 +1373,71 @@ ftrace_filter_release(struct inode *inode, struct file *file) | |||
1137 | mutex_unlock(&ftrace_sysctl_lock); | 1373 | mutex_unlock(&ftrace_sysctl_lock); |
1138 | 1374 | ||
1139 | kfree(iter); | 1375 | kfree(iter); |
1140 | mutex_unlock(&ftrace_filter_lock); | 1376 | mutex_unlock(&ftrace_regex_lock); |
1141 | return 0; | 1377 | return 0; |
1142 | } | 1378 | } |
1143 | 1379 | ||
1380 | static int | ||
1381 | ftrace_filter_release(struct inode *inode, struct file *file) | ||
1382 | { | ||
1383 | return ftrace_regex_release(inode, file, 1); | ||
1384 | } | ||
1385 | |||
1386 | static int | ||
1387 | ftrace_notrace_release(struct inode *inode, struct file *file) | ||
1388 | { | ||
1389 | return ftrace_regex_release(inode, file, 0); | ||
1390 | } | ||
1391 | |||
1392 | static ssize_t | ||
1393 | ftraced_read(struct file *filp, char __user *ubuf, | ||
1394 | size_t cnt, loff_t *ppos) | ||
1395 | { | ||
1396 | /* don't worry about races */ | ||
1397 | char *buf = ftraced_stop ? "disabled\n" : "enabled\n"; | ||
1398 | int r = strlen(buf); | ||
1399 | |||
1400 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
1401 | } | ||
1402 | |||
1403 | static ssize_t | ||
1404 | ftraced_write(struct file *filp, const char __user *ubuf, | ||
1405 | size_t cnt, loff_t *ppos) | ||
1406 | { | ||
1407 | char buf[64]; | ||
1408 | long val; | ||
1409 | int ret; | ||
1410 | |||
1411 | if (cnt >= sizeof(buf)) | ||
1412 | return -EINVAL; | ||
1413 | |||
1414 | if (copy_from_user(&buf, ubuf, cnt)) | ||
1415 | return -EFAULT; | ||
1416 | |||
1417 | if (strncmp(buf, "enable", 6) == 0) | ||
1418 | val = 1; | ||
1419 | else if (strncmp(buf, "disable", 7) == 0) | ||
1420 | val = 0; | ||
1421 | else { | ||
1422 | buf[cnt] = 0; | ||
1423 | |||
1424 | ret = strict_strtoul(buf, 10, &val); | ||
1425 | if (ret < 0) | ||
1426 | return ret; | ||
1427 | |||
1428 | val = !!val; | ||
1429 | } | ||
1430 | |||
1431 | if (val) | ||
1432 | ftrace_enable_daemon(); | ||
1433 | else | ||
1434 | ftrace_disable_daemon(); | ||
1435 | |||
1436 | filp->f_pos += cnt; | ||
1437 | |||
1438 | return cnt; | ||
1439 | } | ||
1440 | |||
1144 | static struct file_operations ftrace_avail_fops = { | 1441 | static struct file_operations ftrace_avail_fops = { |
1145 | .open = ftrace_avail_open, | 1442 | .open = ftrace_avail_open, |
1146 | .read = seq_read, | 1443 | .read = seq_read, |
@@ -1148,59 +1445,57 @@ static struct file_operations ftrace_avail_fops = { | |||
1148 | .release = ftrace_avail_release, | 1445 | .release = ftrace_avail_release, |
1149 | }; | 1446 | }; |
1150 | 1447 | ||
1448 | static struct file_operations ftrace_failures_fops = { | ||
1449 | .open = ftrace_failures_open, | ||
1450 | .read = seq_read, | ||
1451 | .llseek = seq_lseek, | ||
1452 | .release = ftrace_avail_release, | ||
1453 | }; | ||
1454 | |||
1151 | static struct file_operations ftrace_filter_fops = { | 1455 | static struct file_operations ftrace_filter_fops = { |
1152 | .open = ftrace_filter_open, | 1456 | .open = ftrace_filter_open, |
1153 | .read = ftrace_filter_read, | 1457 | .read = ftrace_regex_read, |
1154 | .write = ftrace_filter_write, | 1458 | .write = ftrace_filter_write, |
1155 | .llseek = ftrace_filter_lseek, | 1459 | .llseek = ftrace_regex_lseek, |
1156 | .release = ftrace_filter_release, | 1460 | .release = ftrace_filter_release, |
1157 | }; | 1461 | }; |
1158 | 1462 | ||
1463 | static struct file_operations ftrace_notrace_fops = { | ||
1464 | .open = ftrace_notrace_open, | ||
1465 | .read = ftrace_regex_read, | ||
1466 | .write = ftrace_notrace_write, | ||
1467 | .llseek = ftrace_regex_lseek, | ||
1468 | .release = ftrace_notrace_release, | ||
1469 | }; | ||
1470 | |||
1471 | static struct file_operations ftraced_fops = { | ||
1472 | .open = tracing_open_generic, | ||
1473 | .read = ftraced_read, | ||
1474 | .write = ftraced_write, | ||
1475 | }; | ||
1476 | |||
1159 | /** | 1477 | /** |
1160 | * ftrace_force_update - force an update to all recording ftrace functions | 1478 | * ftrace_force_update - force an update to all recording ftrace functions |
1161 | * | ||
1162 | * The ftrace dynamic update daemon only wakes up once a second. | ||
1163 | * There may be cases where an update needs to be done immediately | ||
1164 | * for tests or internal kernel tracing to begin. This function | ||
1165 | * wakes the daemon to do an update and will not return until the | ||
1166 | * update is complete. | ||
1167 | */ | 1479 | */ |
1168 | int ftrace_force_update(void) | 1480 | int ftrace_force_update(void) |
1169 | { | 1481 | { |
1170 | unsigned long last_counter; | ||
1171 | DECLARE_WAITQUEUE(wait, current); | ||
1172 | int ret = 0; | 1482 | int ret = 0; |
1173 | 1483 | ||
1174 | if (unlikely(ftrace_disabled)) | 1484 | if (unlikely(ftrace_disabled)) |
1175 | return -ENODEV; | 1485 | return -ENODEV; |
1176 | 1486 | ||
1487 | mutex_lock(&ftrace_sysctl_lock); | ||
1177 | mutex_lock(&ftraced_lock); | 1488 | mutex_lock(&ftraced_lock); |
1178 | last_counter = ftraced_iteration_counter; | ||
1179 | 1489 | ||
1180 | set_current_state(TASK_INTERRUPTIBLE); | 1490 | /* |
1181 | add_wait_queue(&ftraced_waiters, &wait); | 1491 | * If ftraced_trigger is not set, then there is nothing |
1182 | 1492 | * to update. | |
1183 | if (unlikely(!ftraced_task)) { | 1493 | */ |
1184 | ret = -ENODEV; | 1494 | if (ftraced_trigger && !ftrace_update_code()) |
1185 | goto out; | 1495 | ret = -EBUSY; |
1186 | } | ||
1187 | |||
1188 | do { | ||
1189 | mutex_unlock(&ftraced_lock); | ||
1190 | wake_up_process(ftraced_task); | ||
1191 | schedule(); | ||
1192 | mutex_lock(&ftraced_lock); | ||
1193 | if (signal_pending(current)) { | ||
1194 | ret = -EINTR; | ||
1195 | break; | ||
1196 | } | ||
1197 | set_current_state(TASK_INTERRUPTIBLE); | ||
1198 | } while (last_counter == ftraced_iteration_counter); | ||
1199 | 1496 | ||
1200 | out: | ||
1201 | mutex_unlock(&ftraced_lock); | 1497 | mutex_unlock(&ftraced_lock); |
1202 | remove_wait_queue(&ftraced_waiters, &wait); | 1498 | mutex_unlock(&ftrace_sysctl_lock); |
1203 | set_current_state(TASK_RUNNING); | ||
1204 | 1499 | ||
1205 | return ret; | 1500 | return ret; |
1206 | } | 1501 | } |
@@ -1234,11 +1529,28 @@ static __init int ftrace_init_debugfs(void) | |||
1234 | pr_warning("Could not create debugfs " | 1529 | pr_warning("Could not create debugfs " |
1235 | "'available_filter_functions' entry\n"); | 1530 | "'available_filter_functions' entry\n"); |
1236 | 1531 | ||
1532 | entry = debugfs_create_file("failures", 0444, | ||
1533 | d_tracer, NULL, &ftrace_failures_fops); | ||
1534 | if (!entry) | ||
1535 | pr_warning("Could not create debugfs 'failures' entry\n"); | ||
1536 | |||
1237 | entry = debugfs_create_file("set_ftrace_filter", 0644, d_tracer, | 1537 | entry = debugfs_create_file("set_ftrace_filter", 0644, d_tracer, |
1238 | NULL, &ftrace_filter_fops); | 1538 | NULL, &ftrace_filter_fops); |
1239 | if (!entry) | 1539 | if (!entry) |
1240 | pr_warning("Could not create debugfs " | 1540 | pr_warning("Could not create debugfs " |
1241 | "'set_ftrace_filter' entry\n"); | 1541 | "'set_ftrace_filter' entry\n"); |
1542 | |||
1543 | entry = debugfs_create_file("set_ftrace_notrace", 0644, d_tracer, | ||
1544 | NULL, &ftrace_notrace_fops); | ||
1545 | if (!entry) | ||
1546 | pr_warning("Could not create debugfs " | ||
1547 | "'set_ftrace_notrace' entry\n"); | ||
1548 | |||
1549 | entry = debugfs_create_file("ftraced_enabled", 0644, d_tracer, | ||
1550 | NULL, &ftraced_fops); | ||
1551 | if (!entry) | ||
1552 | pr_warning("Could not create debugfs " | ||
1553 | "'ftraced_enabled' entry\n"); | ||
1242 | return 0; | 1554 | return 0; |
1243 | } | 1555 | } |
1244 | 1556 | ||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4dcc4e85c5d6..4a875600733b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/poll.h> | 27 | #include <linux/poll.h> |
28 | #include <linux/gfp.h> | 28 | #include <linux/gfp.h> |
29 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
30 | #include <linux/kprobes.h> | ||
30 | #include <linux/writeback.h> | 31 | #include <linux/writeback.h> |
31 | 32 | ||
32 | #include <linux/stacktrace.h> | 33 | #include <linux/stacktrace.h> |
@@ -42,11 +43,6 @@ static cpumask_t __read_mostly tracing_buffer_mask; | |||
42 | #define for_each_tracing_cpu(cpu) \ | 43 | #define for_each_tracing_cpu(cpu) \ |
43 | for_each_cpu_mask(cpu, tracing_buffer_mask) | 44 | for_each_cpu_mask(cpu, tracing_buffer_mask) |
44 | 45 | ||
45 | /* dummy trace to disable tracing */ | ||
46 | static struct tracer no_tracer __read_mostly = { | ||
47 | .name = "none", | ||
48 | }; | ||
49 | |||
50 | static int trace_alloc_page(void); | 46 | static int trace_alloc_page(void); |
51 | static int trace_free_page(void); | 47 | static int trace_free_page(void); |
52 | 48 | ||
@@ -134,6 +130,23 @@ static DECLARE_WAIT_QUEUE_HEAD(trace_wait); | |||
134 | /* trace_flags holds iter_ctrl options */ | 130 | /* trace_flags holds iter_ctrl options */ |
135 | unsigned long trace_flags = TRACE_ITER_PRINT_PARENT; | 131 | unsigned long trace_flags = TRACE_ITER_PRINT_PARENT; |
136 | 132 | ||
133 | static notrace void no_trace_init(struct trace_array *tr) | ||
134 | { | ||
135 | int cpu; | ||
136 | |||
137 | if(tr->ctrl) | ||
138 | for_each_online_cpu(cpu) | ||
139 | tracing_reset(tr->data[cpu]); | ||
140 | tracer_enabled = 0; | ||
141 | } | ||
142 | |||
143 | /* dummy trace to disable tracing */ | ||
144 | static struct tracer no_tracer __read_mostly = { | ||
145 | .name = "none", | ||
146 | .init = no_trace_init | ||
147 | }; | ||
148 | |||
149 | |||
137 | /** | 150 | /** |
138 | * trace_wake_up - wake up tasks waiting for trace input | 151 | * trace_wake_up - wake up tasks waiting for trace input |
139 | * | 152 | * |
@@ -249,24 +262,32 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) | |||
249 | tracing_record_cmdline(current); | 262 | tracing_record_cmdline(current); |
250 | } | 263 | } |
251 | 264 | ||
265 | #define CHECK_COND(cond) \ | ||
266 | if (unlikely(cond)) { \ | ||
267 | tracing_disabled = 1; \ | ||
268 | WARN_ON(1); \ | ||
269 | return -1; \ | ||
270 | } | ||
271 | |||
252 | /** | 272 | /** |
253 | * check_pages - integrity check of trace buffers | 273 | * check_pages - integrity check of trace buffers |
254 | * | 274 | * |
255 | * As a safty measure we check to make sure the data pages have not | 275 | * As a safty measure we check to make sure the data pages have not |
256 | * been corrupted. TODO: configure to disable this because it adds | 276 | * been corrupted. |
257 | * a bit of overhead. | ||
258 | */ | 277 | */ |
259 | void check_pages(struct trace_array_cpu *data) | 278 | int check_pages(struct trace_array_cpu *data) |
260 | { | 279 | { |
261 | struct page *page, *tmp; | 280 | struct page *page, *tmp; |
262 | 281 | ||
263 | BUG_ON(data->trace_pages.next->prev != &data->trace_pages); | 282 | CHECK_COND(data->trace_pages.next->prev != &data->trace_pages); |
264 | BUG_ON(data->trace_pages.prev->next != &data->trace_pages); | 283 | CHECK_COND(data->trace_pages.prev->next != &data->trace_pages); |
265 | 284 | ||
266 | list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) { | 285 | list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) { |
267 | BUG_ON(page->lru.next->prev != &page->lru); | 286 | CHECK_COND(page->lru.next->prev != &page->lru); |
268 | BUG_ON(page->lru.prev->next != &page->lru); | 287 | CHECK_COND(page->lru.prev->next != &page->lru); |
269 | } | 288 | } |
289 | |||
290 | return 0; | ||
270 | } | 291 | } |
271 | 292 | ||
272 | /** | 293 | /** |
@@ -280,7 +301,6 @@ void *head_page(struct trace_array_cpu *data) | |||
280 | { | 301 | { |
281 | struct page *page; | 302 | struct page *page; |
282 | 303 | ||
283 | check_pages(data); | ||
284 | if (list_empty(&data->trace_pages)) | 304 | if (list_empty(&data->trace_pages)) |
285 | return NULL; | 305 | return NULL; |
286 | 306 | ||
@@ -645,9 +665,6 @@ static char saved_cmdlines[SAVED_CMDLINES][TASK_COMM_LEN]; | |||
645 | static int cmdline_idx; | 665 | static int cmdline_idx; |
646 | static DEFINE_SPINLOCK(trace_cmdline_lock); | 666 | static DEFINE_SPINLOCK(trace_cmdline_lock); |
647 | 667 | ||
648 | /* trace in all context switches */ | ||
649 | atomic_t trace_record_cmdline_enabled __read_mostly; | ||
650 | |||
651 | /* temporary disable recording */ | 668 | /* temporary disable recording */ |
652 | atomic_t trace_record_cmdline_disabled __read_mostly; | 669 | atomic_t trace_record_cmdline_disabled __read_mostly; |
653 | 670 | ||
@@ -976,6 +993,30 @@ tracing_sched_wakeup_trace(struct trace_array *tr, | |||
976 | trace_wake_up(); | 993 | trace_wake_up(); |
977 | } | 994 | } |
978 | 995 | ||
996 | void | ||
997 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) | ||
998 | { | ||
999 | struct trace_array *tr = &global_trace; | ||
1000 | struct trace_array_cpu *data; | ||
1001 | unsigned long flags; | ||
1002 | long disabled; | ||
1003 | int cpu; | ||
1004 | |||
1005 | if (tracing_disabled || current_trace == &no_tracer || !tr->ctrl) | ||
1006 | return; | ||
1007 | |||
1008 | local_irq_save(flags); | ||
1009 | cpu = raw_smp_processor_id(); | ||
1010 | data = tr->data[cpu]; | ||
1011 | disabled = atomic_inc_return(&data->disabled); | ||
1012 | |||
1013 | if (likely(disabled == 1)) | ||
1014 | __trace_special(tr, data, arg1, arg2, arg3); | ||
1015 | |||
1016 | atomic_dec(&data->disabled); | ||
1017 | local_irq_restore(flags); | ||
1018 | } | ||
1019 | |||
979 | #ifdef CONFIG_FTRACE | 1020 | #ifdef CONFIG_FTRACE |
980 | static void | 1021 | static void |
981 | function_trace_call(unsigned long ip, unsigned long parent_ip) | 1022 | function_trace_call(unsigned long ip, unsigned long parent_ip) |
@@ -989,6 +1030,9 @@ function_trace_call(unsigned long ip, unsigned long parent_ip) | |||
989 | if (unlikely(!tracer_enabled)) | 1030 | if (unlikely(!tracer_enabled)) |
990 | return; | 1031 | return; |
991 | 1032 | ||
1033 | if (skip_trace(ip)) | ||
1034 | return; | ||
1035 | |||
992 | local_irq_save(flags); | 1036 | local_irq_save(flags); |
993 | cpu = raw_smp_processor_id(); | 1037 | cpu = raw_smp_processor_id(); |
994 | data = tr->data[cpu]; | 1038 | data = tr->data[cpu]; |
@@ -1213,6 +1257,20 @@ static void s_stop(struct seq_file *m, void *p) | |||
1213 | mutex_unlock(&trace_types_lock); | 1257 | mutex_unlock(&trace_types_lock); |
1214 | } | 1258 | } |
1215 | 1259 | ||
1260 | #define KRETPROBE_MSG "[unknown/kretprobe'd]" | ||
1261 | |||
1262 | #ifdef CONFIG_KRETPROBES | ||
1263 | static inline int kretprobed(unsigned long addr) | ||
1264 | { | ||
1265 | return addr == (unsigned long)kretprobe_trampoline; | ||
1266 | } | ||
1267 | #else | ||
1268 | static inline int kretprobed(unsigned long addr) | ||
1269 | { | ||
1270 | return 0; | ||
1271 | } | ||
1272 | #endif /* CONFIG_KRETPROBES */ | ||
1273 | |||
1216 | static int | 1274 | static int |
1217 | seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) | 1275 | seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address) |
1218 | { | 1276 | { |
@@ -1448,7 +1506,10 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) | |||
1448 | case TRACE_FN: | 1506 | case TRACE_FN: |
1449 | seq_print_ip_sym(s, entry->fn.ip, sym_flags); | 1507 | seq_print_ip_sym(s, entry->fn.ip, sym_flags); |
1450 | trace_seq_puts(s, " ("); | 1508 | trace_seq_puts(s, " ("); |
1451 | seq_print_ip_sym(s, entry->fn.parent_ip, sym_flags); | 1509 | if (kretprobed(entry->fn.parent_ip)) |
1510 | trace_seq_puts(s, KRETPROBE_MSG); | ||
1511 | else | ||
1512 | seq_print_ip_sym(s, entry->fn.parent_ip, sym_flags); | ||
1452 | trace_seq_puts(s, ")\n"); | 1513 | trace_seq_puts(s, ")\n"); |
1453 | break; | 1514 | break; |
1454 | case TRACE_CTX: | 1515 | case TRACE_CTX: |
@@ -1528,8 +1589,11 @@ static int print_trace_fmt(struct trace_iterator *iter) | |||
1528 | ret = trace_seq_printf(s, " <-"); | 1589 | ret = trace_seq_printf(s, " <-"); |
1529 | if (!ret) | 1590 | if (!ret) |
1530 | return 0; | 1591 | return 0; |
1531 | ret = seq_print_ip_sym(s, entry->fn.parent_ip, | 1592 | if (kretprobed(entry->fn.parent_ip)) |
1532 | sym_flags); | 1593 | ret = trace_seq_puts(s, KRETPROBE_MSG); |
1594 | else | ||
1595 | ret = seq_print_ip_sym(s, entry->fn.parent_ip, | ||
1596 | sym_flags); | ||
1533 | if (!ret) | 1597 | if (!ret) |
1534 | return 0; | 1598 | return 0; |
1535 | } | 1599 | } |
@@ -2608,7 +2672,7 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, | |||
2608 | { | 2672 | { |
2609 | unsigned long val; | 2673 | unsigned long val; |
2610 | char buf[64]; | 2674 | char buf[64]; |
2611 | int ret; | 2675 | int i, ret; |
2612 | 2676 | ||
2613 | if (cnt >= sizeof(buf)) | 2677 | if (cnt >= sizeof(buf)) |
2614 | return -EINVAL; | 2678 | return -EINVAL; |
@@ -2677,8 +2741,15 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, | |||
2677 | trace_free_page(); | 2741 | trace_free_page(); |
2678 | } | 2742 | } |
2679 | 2743 | ||
2744 | /* check integrity */ | ||
2745 | for_each_tracing_cpu(i) | ||
2746 | check_pages(global_trace.data[i]); | ||
2747 | |||
2680 | filp->f_pos += cnt; | 2748 | filp->f_pos += cnt; |
2681 | 2749 | ||
2750 | /* If check pages failed, return ENOMEM */ | ||
2751 | if (tracing_disabled) | ||
2752 | cnt = -ENOMEM; | ||
2682 | out: | 2753 | out: |
2683 | max_tr.entries = global_trace.entries; | 2754 | max_tr.entries = global_trace.entries; |
2684 | mutex_unlock(&trace_types_lock); | 2755 | mutex_unlock(&trace_types_lock); |
@@ -2969,8 +3040,6 @@ __init static int tracer_alloc_buffers(void) | |||
2969 | int ret = -ENOMEM; | 3040 | int ret = -ENOMEM; |
2970 | int i; | 3041 | int i; |
2971 | 3042 | ||
2972 | global_trace.ctrl = tracer_enabled; | ||
2973 | |||
2974 | /* TODO: make the number of buffers hot pluggable with CPUS */ | 3043 | /* TODO: make the number of buffers hot pluggable with CPUS */ |
2975 | tracing_nr_buffers = num_possible_cpus(); | 3044 | tracing_nr_buffers = num_possible_cpus(); |
2976 | tracing_buffer_mask = cpu_possible_map; | 3045 | tracing_buffer_mask = cpu_possible_map; |
@@ -3027,9 +3096,8 @@ __init static int tracer_alloc_buffers(void) | |||
3027 | } | 3096 | } |
3028 | max_tr.entries = global_trace.entries; | 3097 | max_tr.entries = global_trace.entries; |
3029 | 3098 | ||
3030 | pr_info("tracer: %d pages allocated for %ld", | 3099 | pr_info("tracer: %d pages allocated for %ld entries of %ld bytes\n", |
3031 | pages, trace_nr_entries); | 3100 | pages, trace_nr_entries, (long)TRACE_ENTRY_SIZE); |
3032 | pr_info(" entries of %ld bytes\n", (long)TRACE_ENTRY_SIZE); | ||
3033 | pr_info(" actual entries %ld\n", global_trace.entries); | 3101 | pr_info(" actual entries %ld\n", global_trace.entries); |
3034 | 3102 | ||
3035 | tracer_init_debugfs(); | 3103 | tracer_init_debugfs(); |
@@ -3040,6 +3108,7 @@ __init static int tracer_alloc_buffers(void) | |||
3040 | current_trace = &no_tracer; | 3108 | current_trace = &no_tracer; |
3041 | 3109 | ||
3042 | /* All seems OK, enable tracing */ | 3110 | /* All seems OK, enable tracing */ |
3111 | global_trace.ctrl = tracer_enabled; | ||
3043 | tracing_disabled = 0; | 3112 | tracing_disabled = 0; |
3044 | 3113 | ||
3045 | return 0; | 3114 | return 0; |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 0ef9ef74c806..4966e6a964fe 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -223,6 +223,8 @@ void trace_function(struct trace_array *tr, | |||
223 | 223 | ||
224 | void tracing_start_function_trace(void); | 224 | void tracing_start_function_trace(void); |
225 | void tracing_stop_function_trace(void); | 225 | void tracing_stop_function_trace(void); |
226 | void tracing_start_cmdline_record(void); | ||
227 | void tracing_stop_cmdline_record(void); | ||
226 | int register_tracer(struct tracer *type); | 228 | int register_tracer(struct tracer *type); |
227 | void unregister_tracer(struct tracer *type); | 229 | void unregister_tracer(struct tracer *type); |
228 | 230 | ||
@@ -231,8 +233,6 @@ extern unsigned long nsecs_to_usecs(unsigned long nsecs); | |||
231 | extern unsigned long tracing_max_latency; | 233 | extern unsigned long tracing_max_latency; |
232 | extern unsigned long tracing_thresh; | 234 | extern unsigned long tracing_thresh; |
233 | 235 | ||
234 | extern atomic_t trace_record_cmdline_enabled; | ||
235 | |||
236 | void update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu); | 236 | void update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu); |
237 | void update_max_tr_single(struct trace_array *tr, | 237 | void update_max_tr_single(struct trace_array *tr, |
238 | struct task_struct *tsk, int cpu); | 238 | struct task_struct *tsk, int cpu); |
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 0a084656d7cf..7ee7dcd76b7d 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
@@ -29,14 +29,14 @@ static void function_reset(struct trace_array *tr) | |||
29 | static void start_function_trace(struct trace_array *tr) | 29 | static void start_function_trace(struct trace_array *tr) |
30 | { | 30 | { |
31 | function_reset(tr); | 31 | function_reset(tr); |
32 | atomic_inc(&trace_record_cmdline_enabled); | 32 | tracing_start_cmdline_record(); |
33 | tracing_start_function_trace(); | 33 | tracing_start_function_trace(); |
34 | } | 34 | } |
35 | 35 | ||
36 | static void stop_function_trace(struct trace_array *tr) | 36 | static void stop_function_trace(struct trace_array *tr) |
37 | { | 37 | { |
38 | tracing_stop_function_trace(); | 38 | tracing_stop_function_trace(); |
39 | atomic_dec(&trace_record_cmdline_enabled); | 39 | tracing_stop_cmdline_record(); |
40 | } | 40 | } |
41 | 41 | ||
42 | static void function_trace_init(struct trace_array *tr) | 42 | static void function_trace_init(struct trace_array *tr) |
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 761f3ec66c50..421d6fe3650e 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c | |||
@@ -165,22 +165,6 @@ check_critical_timing(struct trace_array *tr, | |||
165 | 165 | ||
166 | update_max_tr_single(tr, current, cpu); | 166 | update_max_tr_single(tr, current, cpu); |
167 | 167 | ||
168 | if (!runqueue_is_locked()) { | ||
169 | if (tracing_thresh) { | ||
170 | printk(KERN_INFO "(%16s-%-5d|#%d): %lu us critical" | ||
171 | " section violates %lu us threshold.\n", | ||
172 | current->comm, current->pid, | ||
173 | raw_smp_processor_id(), | ||
174 | latency, nsecs_to_usecs(tracing_thresh)); | ||
175 | } else { | ||
176 | printk(KERN_INFO "(%16s-%-5d|#%d): new %lu us" | ||
177 | " maximum-latency critical section.\n", | ||
178 | current->comm, current->pid, | ||
179 | raw_smp_processor_id(), | ||
180 | latency); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | max_sequence++; | 168 | max_sequence++; |
185 | 169 | ||
186 | out_unlock: | 170 | out_unlock: |
diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c index d25ffa5eaf2b..93a662009151 100644 --- a/kernel/trace/trace_sched_switch.c +++ b/kernel/trace/trace_sched_switch.c | |||
@@ -29,6 +29,9 @@ sched_switch_func(void *private, void *__rq, struct task_struct *prev, | |||
29 | long disabled; | 29 | long disabled; |
30 | int cpu; | 30 | int cpu; |
31 | 31 | ||
32 | tracing_record_cmdline(prev); | ||
33 | tracing_record_cmdline(next); | ||
34 | |||
32 | if (!tracer_enabled) | 35 | if (!tracer_enabled) |
33 | return; | 36 | return; |
34 | 37 | ||
@@ -63,8 +66,6 @@ sched_switch_callback(void *probe_data, void *call_data, | |||
63 | prev = va_arg(*args, typeof(prev)); | 66 | prev = va_arg(*args, typeof(prev)); |
64 | next = va_arg(*args, typeof(next)); | 67 | next = va_arg(*args, typeof(next)); |
65 | 68 | ||
66 | tracing_record_cmdline(prev); | ||
67 | |||
68 | /* | 69 | /* |
69 | * If tracer_switch_func only points to the local | 70 | * If tracer_switch_func only points to the local |
70 | * switch func, it still needs the ptr passed to it. | 71 | * switch func, it still needs the ptr passed to it. |
@@ -125,30 +126,6 @@ wake_up_callback(void *probe_data, void *call_data, | |||
125 | wakeup_func(probe_data, __rq, task, curr); | 126 | wakeup_func(probe_data, __rq, task, curr); |
126 | } | 127 | } |
127 | 128 | ||
128 | void | ||
129 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) | ||
130 | { | ||
131 | struct trace_array *tr = ctx_trace; | ||
132 | struct trace_array_cpu *data; | ||
133 | unsigned long flags; | ||
134 | long disabled; | ||
135 | int cpu; | ||
136 | |||
137 | if (!tracer_enabled) | ||
138 | return; | ||
139 | |||
140 | local_irq_save(flags); | ||
141 | cpu = raw_smp_processor_id(); | ||
142 | data = tr->data[cpu]; | ||
143 | disabled = atomic_inc_return(&data->disabled); | ||
144 | |||
145 | if (likely(disabled == 1)) | ||
146 | __trace_special(tr, data, arg1, arg2, arg3); | ||
147 | |||
148 | atomic_dec(&data->disabled); | ||
149 | local_irq_restore(flags); | ||
150 | } | ||
151 | |||
152 | static void sched_switch_reset(struct trace_array *tr) | 129 | static void sched_switch_reset(struct trace_array *tr) |
153 | { | 130 | { |
154 | int cpu; | 131 | int cpu; |
@@ -219,7 +196,7 @@ static void tracing_sched_unregister(void) | |||
219 | &ctx_trace); | 196 | &ctx_trace); |
220 | } | 197 | } |
221 | 198 | ||
222 | void tracing_start_sched_switch(void) | 199 | static void tracing_start_sched_switch(void) |
223 | { | 200 | { |
224 | long ref; | 201 | long ref; |
225 | 202 | ||
@@ -228,7 +205,7 @@ void tracing_start_sched_switch(void) | |||
228 | tracing_sched_register(); | 205 | tracing_sched_register(); |
229 | } | 206 | } |
230 | 207 | ||
231 | void tracing_stop_sched_switch(void) | 208 | static void tracing_stop_sched_switch(void) |
232 | { | 209 | { |
233 | long ref; | 210 | long ref; |
234 | 211 | ||
@@ -237,18 +214,26 @@ void tracing_stop_sched_switch(void) | |||
237 | tracing_sched_unregister(); | 214 | tracing_sched_unregister(); |
238 | } | 215 | } |
239 | 216 | ||
217 | void tracing_start_cmdline_record(void) | ||
218 | { | ||
219 | tracing_start_sched_switch(); | ||
220 | } | ||
221 | |||
222 | void tracing_stop_cmdline_record(void) | ||
223 | { | ||
224 | tracing_stop_sched_switch(); | ||
225 | } | ||
226 | |||
240 | static void start_sched_trace(struct trace_array *tr) | 227 | static void start_sched_trace(struct trace_array *tr) |
241 | { | 228 | { |
242 | sched_switch_reset(tr); | 229 | sched_switch_reset(tr); |
243 | atomic_inc(&trace_record_cmdline_enabled); | ||
244 | tracer_enabled = 1; | 230 | tracer_enabled = 1; |
245 | tracing_start_sched_switch(); | 231 | tracing_start_cmdline_record(); |
246 | } | 232 | } |
247 | 233 | ||
248 | static void stop_sched_trace(struct trace_array *tr) | 234 | static void stop_sched_trace(struct trace_array *tr) |
249 | { | 235 | { |
250 | tracing_stop_sched_switch(); | 236 | tracing_stop_cmdline_record(); |
251 | atomic_dec(&trace_record_cmdline_enabled); | ||
252 | tracer_enabled = 0; | 237 | tracer_enabled = 0; |
253 | } | 238 | } |
254 | 239 | ||
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 5d2fb48e47f8..bf7e91caef57 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c | |||
@@ -30,6 +30,69 @@ static DEFINE_SPINLOCK(wakeup_lock); | |||
30 | 30 | ||
31 | static void __wakeup_reset(struct trace_array *tr); | 31 | static void __wakeup_reset(struct trace_array *tr); |
32 | 32 | ||
33 | #ifdef CONFIG_FTRACE | ||
34 | /* | ||
35 | * irqsoff uses its own tracer function to keep the overhead down: | ||
36 | */ | ||
37 | static void | ||
38 | wakeup_tracer_call(unsigned long ip, unsigned long parent_ip) | ||
39 | { | ||
40 | struct trace_array *tr = wakeup_trace; | ||
41 | struct trace_array_cpu *data; | ||
42 | unsigned long flags; | ||
43 | long disabled; | ||
44 | int resched; | ||
45 | int cpu; | ||
46 | |||
47 | if (likely(!wakeup_task)) | ||
48 | return; | ||
49 | |||
50 | resched = need_resched(); | ||
51 | preempt_disable_notrace(); | ||
52 | |||
53 | cpu = raw_smp_processor_id(); | ||
54 | data = tr->data[cpu]; | ||
55 | disabled = atomic_inc_return(&data->disabled); | ||
56 | if (unlikely(disabled != 1)) | ||
57 | goto out; | ||
58 | |||
59 | spin_lock_irqsave(&wakeup_lock, flags); | ||
60 | |||
61 | if (unlikely(!wakeup_task)) | ||
62 | goto unlock; | ||
63 | |||
64 | /* | ||
65 | * The task can't disappear because it needs to | ||
66 | * wake up first, and we have the wakeup_lock. | ||
67 | */ | ||
68 | if (task_cpu(wakeup_task) != cpu) | ||
69 | goto unlock; | ||
70 | |||
71 | trace_function(tr, data, ip, parent_ip, flags); | ||
72 | |||
73 | unlock: | ||
74 | spin_unlock_irqrestore(&wakeup_lock, flags); | ||
75 | |||
76 | out: | ||
77 | atomic_dec(&data->disabled); | ||
78 | |||
79 | /* | ||
80 | * To prevent recursion from the scheduler, if the | ||
81 | * resched flag was set before we entered, then | ||
82 | * don't reschedule. | ||
83 | */ | ||
84 | if (resched) | ||
85 | preempt_enable_no_resched_notrace(); | ||
86 | else | ||
87 | preempt_enable_notrace(); | ||
88 | } | ||
89 | |||
90 | static struct ftrace_ops trace_ops __read_mostly = | ||
91 | { | ||
92 | .func = wakeup_tracer_call, | ||
93 | }; | ||
94 | #endif /* CONFIG_FTRACE */ | ||
95 | |||
33 | /* | 96 | /* |
34 | * Should this new latency be reported/recorded? | 97 | * Should this new latency be reported/recorded? |
35 | */ | 98 | */ |
@@ -73,7 +136,7 @@ wakeup_sched_switch(void *private, void *rq, struct task_struct *prev, | |||
73 | if (next != wakeup_task) | 136 | if (next != wakeup_task) |
74 | return; | 137 | return; |
75 | 138 | ||
76 | /* The task we are waitng for is waking up */ | 139 | /* The task we are waiting for is waking up */ |
77 | data = tr->data[wakeup_cpu]; | 140 | data = tr->data[wakeup_cpu]; |
78 | 141 | ||
79 | /* disable local data, not wakeup_cpu data */ | 142 | /* disable local data, not wakeup_cpu data */ |
@@ -290,6 +353,7 @@ static void start_wakeup_tracer(struct trace_array *tr) | |||
290 | smp_wmb(); | 353 | smp_wmb(); |
291 | 354 | ||
292 | tracer_enabled = 1; | 355 | tracer_enabled = 1; |
356 | register_ftrace_function(&trace_ops); | ||
293 | 357 | ||
294 | return; | 358 | return; |
295 | fail_deprobe_wake_new: | 359 | fail_deprobe_wake_new: |
@@ -305,6 +369,7 @@ fail_deprobe: | |||
305 | static void stop_wakeup_tracer(struct trace_array *tr) | 369 | static void stop_wakeup_tracer(struct trace_array *tr) |
306 | { | 370 | { |
307 | tracer_enabled = 0; | 371 | tracer_enabled = 0; |
372 | unregister_ftrace_function(&trace_ops); | ||
308 | marker_probe_unregister("kernel_sched_schedule", | 373 | marker_probe_unregister("kernel_sched_schedule", |
309 | sched_switch_callback, | 374 | sched_switch_callback, |
310 | &wakeup_trace); | 375 | &wakeup_trace); |
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 3877dd9102f1..18c5423bc977 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c | |||
@@ -28,6 +28,7 @@ trace_test_buffer_cpu(struct trace_array *tr, struct trace_array_cpu *data) | |||
28 | page = list_entry(data->trace_pages.next, struct page, lru); | 28 | page = list_entry(data->trace_pages.next, struct page, lru); |
29 | entries = page_address(page); | 29 | entries = page_address(page); |
30 | 30 | ||
31 | check_pages(data); | ||
31 | if (head_page(data) != entries) | 32 | if (head_page(data) != entries) |
32 | goto failed; | 33 | goto failed; |
33 | 34 | ||