diff options
Diffstat (limited to 'arch/arm/kernel/kprobes-test.c')
-rw-r--r-- | arch/arm/kernel/kprobes-test.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/arch/arm/kernel/kprobes-test.c b/arch/arm/kernel/kprobes-test.c index fe169e934d42..e5740c7c64e5 100644 --- a/arch/arm/kernel/kprobes-test.c +++ b/arch/arm/kernel/kprobes-test.c | |||
@@ -185,6 +185,9 @@ | |||
185 | #include "kprobes-test.h" | 185 | #include "kprobes-test.h" |
186 | 186 | ||
187 | 187 | ||
188 | #define BENCHMARKING 1 | ||
189 | |||
190 | |||
188 | /* | 191 | /* |
189 | * Test basic API | 192 | * Test basic API |
190 | */ | 193 | */ |
@@ -444,6 +447,157 @@ static int run_api_tests(long (*func)(long, long)) | |||
444 | 447 | ||
445 | 448 | ||
446 | /* | 449 | /* |
450 | * Benchmarking | ||
451 | */ | ||
452 | |||
453 | #if BENCHMARKING | ||
454 | |||
455 | static void __naked benchmark_nop(void) | ||
456 | { | ||
457 | __asm__ __volatile__ ( | ||
458 | "nop \n\t" | ||
459 | "bx lr" | ||
460 | ); | ||
461 | } | ||
462 | |||
463 | #ifdef CONFIG_THUMB2_KERNEL | ||
464 | #define wide ".w" | ||
465 | #else | ||
466 | #define wide | ||
467 | #endif | ||
468 | |||
469 | static void __naked benchmark_pushpop1(void) | ||
470 | { | ||
471 | __asm__ __volatile__ ( | ||
472 | "stmdb"wide" sp!, {r3-r11,lr} \n\t" | ||
473 | "ldmia"wide" sp!, {r3-r11,pc}" | ||
474 | ); | ||
475 | } | ||
476 | |||
477 | static void __naked benchmark_pushpop2(void) | ||
478 | { | ||
479 | __asm__ __volatile__ ( | ||
480 | "stmdb"wide" sp!, {r0-r8,lr} \n\t" | ||
481 | "ldmia"wide" sp!, {r0-r8,pc}" | ||
482 | ); | ||
483 | } | ||
484 | |||
485 | static void __naked benchmark_pushpop3(void) | ||
486 | { | ||
487 | __asm__ __volatile__ ( | ||
488 | "stmdb"wide" sp!, {r4,lr} \n\t" | ||
489 | "ldmia"wide" sp!, {r4,pc}" | ||
490 | ); | ||
491 | } | ||
492 | |||
493 | static void __naked benchmark_pushpop4(void) | ||
494 | { | ||
495 | __asm__ __volatile__ ( | ||
496 | "stmdb"wide" sp!, {r0,lr} \n\t" | ||
497 | "ldmia"wide" sp!, {r0,pc}" | ||
498 | ); | ||
499 | } | ||
500 | |||
501 | |||
502 | #ifdef CONFIG_THUMB2_KERNEL | ||
503 | |||
504 | static void __naked benchmark_pushpop_thumb(void) | ||
505 | { | ||
506 | __asm__ __volatile__ ( | ||
507 | "push.n {r0-r7,lr} \n\t" | ||
508 | "pop.n {r0-r7,pc}" | ||
509 | ); | ||
510 | } | ||
511 | |||
512 | #endif | ||
513 | |||
514 | static int __kprobes | ||
515 | benchmark_pre_handler(struct kprobe *p, struct pt_regs *regs) | ||
516 | { | ||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static int benchmark(void(*fn)(void)) | ||
521 | { | ||
522 | unsigned n, i, t, t0; | ||
523 | |||
524 | for (n = 1000; ; n *= 2) { | ||
525 | t0 = sched_clock(); | ||
526 | for (i = n; i > 0; --i) | ||
527 | fn(); | ||
528 | t = sched_clock() - t0; | ||
529 | if (t >= 250000000) | ||
530 | break; /* Stop once we took more than 0.25 seconds */ | ||
531 | } | ||
532 | return t / n; /* Time for one iteration in nanoseconds */ | ||
533 | }; | ||
534 | |||
535 | static int kprobe_benchmark(void(*fn)(void), unsigned offset) | ||
536 | { | ||
537 | struct kprobe k = { | ||
538 | .addr = (kprobe_opcode_t *)((uintptr_t)fn + offset), | ||
539 | .pre_handler = benchmark_pre_handler, | ||
540 | }; | ||
541 | |||
542 | int ret = register_kprobe(&k); | ||
543 | if (ret < 0) { | ||
544 | pr_err("FAIL: register_kprobe failed with %d\n", ret); | ||
545 | return ret; | ||
546 | } | ||
547 | |||
548 | ret = benchmark(fn); | ||
549 | |||
550 | unregister_kprobe(&k); | ||
551 | return ret; | ||
552 | }; | ||
553 | |||
554 | struct benchmarks { | ||
555 | void (*fn)(void); | ||
556 | unsigned offset; | ||
557 | const char *title; | ||
558 | }; | ||
559 | |||
560 | static int run_benchmarks(void) | ||
561 | { | ||
562 | int ret; | ||
563 | struct benchmarks list[] = { | ||
564 | {&benchmark_nop, 0, "nop"}, | ||
565 | /* | ||
566 | * benchmark_pushpop{1,3} will have the optimised | ||
567 | * instruction emulation, whilst benchmark_pushpop{2,4} will | ||
568 | * be the equivalent unoptimised instructions. | ||
569 | */ | ||
570 | {&benchmark_pushpop1, 0, "stmdb sp!, {r3-r11,lr}"}, | ||
571 | {&benchmark_pushpop1, 4, "ldmia sp!, {r3-r11,pc}"}, | ||
572 | {&benchmark_pushpop2, 0, "stmdb sp!, {r0-r8,lr}"}, | ||
573 | {&benchmark_pushpop2, 4, "ldmia sp!, {r0-r8,pc}"}, | ||
574 | {&benchmark_pushpop3, 0, "stmdb sp!, {r4,lr}"}, | ||
575 | {&benchmark_pushpop3, 4, "ldmia sp!, {r4,pc}"}, | ||
576 | {&benchmark_pushpop4, 0, "stmdb sp!, {r0,lr}"}, | ||
577 | {&benchmark_pushpop4, 4, "ldmia sp!, {r0,pc}"}, | ||
578 | #ifdef CONFIG_THUMB2_KERNEL | ||
579 | {&benchmark_pushpop_thumb, 0, "push.n {r0-r7,lr}"}, | ||
580 | {&benchmark_pushpop_thumb, 2, "pop.n {r0-r7,pc}"}, | ||
581 | #endif | ||
582 | {0} | ||
583 | }; | ||
584 | |||
585 | struct benchmarks *b; | ||
586 | for (b = list; b->fn; ++b) { | ||
587 | ret = kprobe_benchmark(b->fn, b->offset); | ||
588 | if (ret < 0) | ||
589 | return ret; | ||
590 | pr_info(" %dns for kprobe %s\n", ret, b->title); | ||
591 | } | ||
592 | |||
593 | pr_info("\n"); | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | #endif /* BENCHMARKING */ | ||
598 | |||
599 | |||
600 | /* | ||
447 | * Decoding table self-consistency tests | 601 | * Decoding table self-consistency tests |
448 | */ | 602 | */ |
449 | 603 | ||
@@ -1526,6 +1680,13 @@ static int __init run_all_tests(void) | |||
1526 | goto out; | 1680 | goto out; |
1527 | } | 1681 | } |
1528 | 1682 | ||
1683 | #if BENCHMARKING | ||
1684 | pr_info("Benchmarks\n"); | ||
1685 | ret = run_benchmarks(); | ||
1686 | if (ret) | ||
1687 | goto out; | ||
1688 | #endif | ||
1689 | |||
1529 | #if __LINUX_ARM_ARCH__ >= 7 | 1690 | #if __LINUX_ARM_ARCH__ >= 7 |
1530 | /* We are able to run all test cases so coverage should be complete */ | 1691 | /* We are able to run all test cases so coverage should be complete */ |
1531 | if (coverage_fail) { | 1692 | if (coverage_fail) { |