diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 208 |
1 files changed, 101 insertions, 107 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 369fb78bd4ab..fed1ebc3a133 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -297,6 +297,19 @@ static struct ftrace_page *ftrace_pages; | |||
297 | 297 | ||
298 | static struct dyn_ftrace *ftrace_free_records; | 298 | static struct dyn_ftrace *ftrace_free_records; |
299 | 299 | ||
300 | /* | ||
301 | * This is a double for. Do not use 'break' to break out of the loop, | ||
302 | * you must use a goto. | ||
303 | */ | ||
304 | #define do_for_each_ftrace_rec(pg, rec) \ | ||
305 | for (pg = ftrace_pages_start; pg; pg = pg->next) { \ | ||
306 | int _____i; \ | ||
307 | for (_____i = 0; _____i < pg->index; _____i++) { \ | ||
308 | rec = &pg->records[_____i]; | ||
309 | |||
310 | #define while_for_each_ftrace_rec() \ | ||
311 | } \ | ||
312 | } | ||
300 | 313 | ||
301 | #ifdef CONFIG_KPROBES | 314 | #ifdef CONFIG_KPROBES |
302 | 315 | ||
@@ -341,7 +354,6 @@ void ftrace_release(void *start, unsigned long size) | |||
341 | struct ftrace_page *pg; | 354 | struct ftrace_page *pg; |
342 | unsigned long s = (unsigned long)start; | 355 | unsigned long s = (unsigned long)start; |
343 | unsigned long e = s + size; | 356 | unsigned long e = s + size; |
344 | int i; | ||
345 | 357 | ||
346 | if (ftrace_disabled || !start) | 358 | if (ftrace_disabled || !start) |
347 | return; | 359 | return; |
@@ -349,14 +361,11 @@ void ftrace_release(void *start, unsigned long size) | |||
349 | /* should not be called from interrupt context */ | 361 | /* should not be called from interrupt context */ |
350 | spin_lock(&ftrace_lock); | 362 | spin_lock(&ftrace_lock); |
351 | 363 | ||
352 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 364 | do_for_each_ftrace_rec(pg, rec) { |
353 | for (i = 0; i < pg->index; i++) { | 365 | if ((rec->ip >= s) && (rec->ip < e)) |
354 | rec = &pg->records[i]; | 366 | ftrace_free_rec(rec); |
367 | } while_for_each_ftrace_rec(); | ||
355 | 368 | ||
356 | if ((rec->ip >= s) && (rec->ip < e)) | ||
357 | ftrace_free_rec(rec); | ||
358 | } | ||
359 | } | ||
360 | spin_unlock(&ftrace_lock); | 369 | spin_unlock(&ftrace_lock); |
361 | } | 370 | } |
362 | 371 | ||
@@ -523,41 +532,37 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | |||
523 | 532 | ||
524 | static void ftrace_replace_code(int enable) | 533 | static void ftrace_replace_code(int enable) |
525 | { | 534 | { |
526 | int i, failed; | 535 | int failed; |
527 | struct dyn_ftrace *rec; | 536 | struct dyn_ftrace *rec; |
528 | struct ftrace_page *pg; | 537 | struct ftrace_page *pg; |
529 | 538 | ||
530 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 539 | do_for_each_ftrace_rec(pg, rec) { |
531 | for (i = 0; i < pg->index; i++) { | 540 | /* |
532 | rec = &pg->records[i]; | 541 | * Skip over free records and records that have |
533 | 542 | * failed. | |
534 | /* | 543 | */ |
535 | * Skip over free records and records that have | 544 | if (rec->flags & FTRACE_FL_FREE || |
536 | * failed. | 545 | rec->flags & FTRACE_FL_FAILED) |
537 | */ | 546 | continue; |
538 | if (rec->flags & FTRACE_FL_FREE || | ||
539 | rec->flags & FTRACE_FL_FAILED) | ||
540 | continue; | ||
541 | |||
542 | /* ignore updates to this record's mcount site */ | ||
543 | if (get_kprobe((void *)rec->ip)) { | ||
544 | freeze_record(rec); | ||
545 | continue; | ||
546 | } else { | ||
547 | unfreeze_record(rec); | ||
548 | } | ||
549 | 547 | ||
550 | failed = __ftrace_replace_code(rec, enable); | 548 | /* ignore updates to this record's mcount site */ |
551 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { | 549 | if (get_kprobe((void *)rec->ip)) { |
552 | rec->flags |= FTRACE_FL_FAILED; | 550 | freeze_record(rec); |
553 | if ((system_state == SYSTEM_BOOTING) || | 551 | continue; |
554 | !core_kernel_text(rec->ip)) { | 552 | } else { |
555 | ftrace_free_rec(rec); | 553 | unfreeze_record(rec); |
556 | } else | ||
557 | ftrace_bug(failed, rec->ip); | ||
558 | } | ||
559 | } | 554 | } |
560 | } | 555 | |
556 | failed = __ftrace_replace_code(rec, enable); | ||
557 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { | ||
558 | rec->flags |= FTRACE_FL_FAILED; | ||
559 | if ((system_state == SYSTEM_BOOTING) || | ||
560 | !core_kernel_text(rec->ip)) { | ||
561 | ftrace_free_rec(rec); | ||
562 | } else | ||
563 | ftrace_bug(failed, rec->ip); | ||
564 | } | ||
565 | } while_for_each_ftrace_rec(); | ||
561 | } | 566 | } |
562 | 567 | ||
563 | static int | 568 | static int |
@@ -956,22 +961,17 @@ static void ftrace_filter_reset(int enable) | |||
956 | struct ftrace_page *pg; | 961 | struct ftrace_page *pg; |
957 | struct dyn_ftrace *rec; | 962 | struct dyn_ftrace *rec; |
958 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 963 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; |
959 | unsigned i; | ||
960 | 964 | ||
961 | /* should not be called from interrupt context */ | 965 | /* should not be called from interrupt context */ |
962 | spin_lock(&ftrace_lock); | 966 | spin_lock(&ftrace_lock); |
963 | if (enable) | 967 | if (enable) |
964 | ftrace_filtered = 0; | 968 | ftrace_filtered = 0; |
965 | pg = ftrace_pages_start; | 969 | do_for_each_ftrace_rec(pg, rec) { |
966 | while (pg) { | 970 | if (rec->flags & FTRACE_FL_FAILED) |
967 | for (i = 0; i < pg->index; i++) { | 971 | continue; |
968 | rec = &pg->records[i]; | 972 | rec->flags &= ~type; |
969 | if (rec->flags & FTRACE_FL_FAILED) | 973 | } while_for_each_ftrace_rec(); |
970 | continue; | 974 | |
971 | rec->flags &= ~type; | ||
972 | } | ||
973 | pg = pg->next; | ||
974 | } | ||
975 | spin_unlock(&ftrace_lock); | 975 | spin_unlock(&ftrace_lock); |
976 | } | 976 | } |
977 | 977 | ||
@@ -1094,44 +1094,39 @@ ftrace_match(unsigned char *buff, int len, int enable) | |||
1094 | spin_lock(&ftrace_lock); | 1094 | spin_lock(&ftrace_lock); |
1095 | if (enable) | 1095 | if (enable) |
1096 | ftrace_filtered = 1; | 1096 | ftrace_filtered = 1; |
1097 | pg = ftrace_pages_start; | 1097 | do_for_each_ftrace_rec(pg, rec) { |
1098 | while (pg) { | 1098 | int matched = 0; |
1099 | for (i = 0; i < pg->index; i++) { | 1099 | char *ptr; |
1100 | int matched = 0; | 1100 | |
1101 | char *ptr; | 1101 | if (rec->flags & FTRACE_FL_FAILED) |
1102 | 1102 | continue; | |
1103 | rec = &pg->records[i]; | 1103 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); |
1104 | if (rec->flags & FTRACE_FL_FAILED) | 1104 | switch (type) { |
1105 | continue; | 1105 | case MATCH_FULL: |
1106 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | 1106 | if (strcmp(str, buff) == 0) |
1107 | switch (type) { | 1107 | matched = 1; |
1108 | case MATCH_FULL: | 1108 | break; |
1109 | if (strcmp(str, buff) == 0) | 1109 | case MATCH_FRONT_ONLY: |
1110 | matched = 1; | 1110 | if (memcmp(str, buff, match) == 0) |
1111 | break; | 1111 | matched = 1; |
1112 | case MATCH_FRONT_ONLY: | 1112 | break; |
1113 | if (memcmp(str, buff, match) == 0) | 1113 | case MATCH_MIDDLE_ONLY: |
1114 | matched = 1; | 1114 | if (strstr(str, search)) |
1115 | break; | 1115 | matched = 1; |
1116 | case MATCH_MIDDLE_ONLY: | 1116 | break; |
1117 | if (strstr(str, search)) | 1117 | case MATCH_END_ONLY: |
1118 | matched = 1; | 1118 | ptr = strstr(str, search); |
1119 | break; | 1119 | if (ptr && (ptr[search_len] == 0)) |
1120 | case MATCH_END_ONLY: | 1120 | matched = 1; |
1121 | ptr = strstr(str, search); | 1121 | break; |
1122 | if (ptr && (ptr[search_len] == 0)) | ||
1123 | matched = 1; | ||
1124 | break; | ||
1125 | } | ||
1126 | if (matched) { | ||
1127 | if (not) | ||
1128 | rec->flags &= ~flag; | ||
1129 | else | ||
1130 | rec->flags |= flag; | ||
1131 | } | ||
1132 | } | 1122 | } |
1133 | pg = pg->next; | 1123 | if (matched) { |
1134 | } | 1124 | if (not) |
1125 | rec->flags &= ~flag; | ||
1126 | else | ||
1127 | rec->flags |= flag; | ||
1128 | } | ||
1129 | } while_for_each_ftrace_rec(); | ||
1135 | spin_unlock(&ftrace_lock); | 1130 | spin_unlock(&ftrace_lock); |
1136 | } | 1131 | } |
1137 | 1132 | ||
@@ -1452,7 +1447,7 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer) | |||
1452 | struct dyn_ftrace *rec; | 1447 | struct dyn_ftrace *rec; |
1453 | struct ftrace_page *pg; | 1448 | struct ftrace_page *pg; |
1454 | int found = 0; | 1449 | int found = 0; |
1455 | int i, j; | 1450 | int j; |
1456 | 1451 | ||
1457 | if (ftrace_disabled) | 1452 | if (ftrace_disabled) |
1458 | return -ENODEV; | 1453 | return -ENODEV; |
@@ -1460,27 +1455,26 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer) | |||
1460 | /* should not be called from interrupt context */ | 1455 | /* should not be called from interrupt context */ |
1461 | spin_lock(&ftrace_lock); | 1456 | spin_lock(&ftrace_lock); |
1462 | 1457 | ||
1463 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 1458 | do_for_each_ftrace_rec(pg, rec) { |
1464 | for (i = 0; i < pg->index; i++) { | 1459 | |
1465 | rec = &pg->records[i]; | 1460 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) |
1466 | 1461 | continue; | |
1467 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) | 1462 | |
1468 | continue; | 1463 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); |
1469 | 1464 | if (strcmp(str, buffer) == 0) { | |
1470 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | 1465 | /* Return 1 if we add it to the array */ |
1471 | if (strcmp(str, buffer) == 0) { | 1466 | found = 1; |
1472 | found = 1; | 1467 | for (j = 0; j < idx; j++) |
1473 | for (j = 0; j < idx; j++) | 1468 | if (array[j] == rec->ip) { |
1474 | if (array[j] == rec->ip) { | 1469 | found = 0; |
1475 | found = 0; | 1470 | break; |
1476 | break; | 1471 | } |
1477 | } | 1472 | if (found) |
1478 | if (found) | 1473 | array[idx] = rec->ip; |
1479 | array[idx] = rec->ip; | 1474 | goto out; |
1480 | break; | ||
1481 | } | ||
1482 | } | 1475 | } |
1483 | } | 1476 | } while_for_each_ftrace_rec(); |
1477 | out: | ||
1484 | spin_unlock(&ftrace_lock); | 1478 | spin_unlock(&ftrace_lock); |
1485 | 1479 | ||
1486 | return found ? 0 : -EINVAL; | 1480 | return found ? 0 : -EINVAL; |