Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: Add test of PMU instruction counting on powerpc

This commit adds a test of instruction counting using the PMU on powerpc.

Although the bulk of the code is architecture agnostic, the code needs to
run a precisely sized loop which is implemented in assembler.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Michael Ellerman and committed by
Benjamin Herrenschmidt
cb96143d 2fae0d7c

+349 -1
+1 -1
tools/testing/selftests/powerpc/Makefile
··· 13 13 14 14 export CC CFLAGS 15 15 16 - TARGETS = 16 + TARGETS = pmu 17 17 18 18 endif 19 19
+23
tools/testing/selftests/powerpc/pmu/Makefile
··· 1 + noarg: 2 + $(MAKE) -C ../ 3 + 4 + PROGS := count_instructions 5 + EXTRA_SOURCES := ../harness.c event.c 6 + 7 + all: $(PROGS) 8 + 9 + $(PROGS): $(EXTRA_SOURCES) 10 + 11 + # loop.S can only be built 64-bit 12 + count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES) 13 + $(CC) $(CFLAGS) -m64 -o $@ $^ 14 + 15 + run_tests: all 16 + @-for PROG in $(PROGS); do \ 17 + ./$$PROG; \ 18 + done; 19 + 20 + clean: 21 + rm -f $(PROGS) loop.o 22 + 23 + .PHONY: all run_tests clean
+135
tools/testing/selftests/powerpc/pmu/count_instructions.c
··· 1 + /* 2 + * Copyright 2013, Michael Ellerman, IBM Corp. 3 + * Licensed under GPLv2. 4 + */ 5 + 6 + #define _GNU_SOURCE 7 + 8 + #include <stdio.h> 9 + #include <stdbool.h> 10 + #include <string.h> 11 + #include <sys/prctl.h> 12 + 13 + #include "event.h" 14 + #include "utils.h" 15 + 16 + extern void thirty_two_instruction_loop(u64 loops); 17 + 18 + static void setup_event(struct event *e, u64 config, char *name) 19 + { 20 + event_init_opts(e, config, PERF_TYPE_HARDWARE, name); 21 + 22 + e->attr.disabled = 1; 23 + e->attr.exclude_kernel = 1; 24 + e->attr.exclude_hv = 1; 25 + e->attr.exclude_idle = 1; 26 + } 27 + 28 + static int do_count_loop(struct event *events, u64 instructions, 29 + u64 overhead, bool report) 30 + { 31 + s64 difference, expected; 32 + double percentage; 33 + 34 + prctl(PR_TASK_PERF_EVENTS_ENABLE); 35 + 36 + /* Run for 1M instructions */ 37 + thirty_two_instruction_loop(instructions >> 5); 38 + 39 + prctl(PR_TASK_PERF_EVENTS_DISABLE); 40 + 41 + event_read(&events[0]); 42 + event_read(&events[1]); 43 + 44 + expected = instructions + overhead; 45 + difference = events[0].result.value - expected; 46 + percentage = (double)difference / events[0].result.value * 100; 47 + 48 + if (report) { 49 + event_report(&events[0]); 50 + event_report(&events[1]); 51 + 52 + printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead); 53 + printf("Expected %llu\n", expected); 54 + printf("Actual %llu\n", events[0].result.value); 55 + printf("Delta %lld, %f%%\n", difference, percentage); 56 + } 57 + 58 + event_reset(&events[0]); 59 + event_reset(&events[1]); 60 + 61 + if (difference < 0) 62 + difference = -difference; 63 + 64 + /* Tolerate a difference below 0.0001 % */ 65 + difference *= 10000 * 100; 66 + if (difference / events[0].result.value) 67 + return -1; 68 + 69 + return 0; 70 + } 71 + 72 + /* Count how many instructions it takes to do a null loop */ 73 + static u64 determine_overhead(struct event *events) 74 + { 75 + u64 current, overhead; 76 + int i; 77 + 78 + do_count_loop(events, 0, 0, false); 79 + overhead = events[0].result.value; 80 + 81 + for (i = 0; i < 100; i++) { 82 + do_count_loop(events, 0, 0, false); 83 + current = events[0].result.value; 84 + if (current < overhead) { 85 + printf("Replacing overhead %llu with %llu\n", overhead, current); 86 + overhead = current; 87 + } 88 + } 89 + 90 + return overhead; 91 + } 92 + 93 + static int count_instructions(void) 94 + { 95 + struct event events[2]; 96 + u64 overhead; 97 + 98 + setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions"); 99 + setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles"); 100 + 101 + if (event_open(&events[0])) { 102 + perror("perf_event_open"); 103 + return -1; 104 + } 105 + 106 + if (event_open_with_group(&events[1], events[0].fd)) { 107 + perror("perf_event_open"); 108 + return -1; 109 + } 110 + 111 + overhead = determine_overhead(events); 112 + printf("Overhead of null loop: %llu instructions\n", overhead); 113 + 114 + /* Run for 1M instructions */ 115 + FAIL_IF(do_count_loop(events, 0x100000, overhead, true)); 116 + 117 + /* Run for 10M instructions */ 118 + FAIL_IF(do_count_loop(events, 0xa00000, overhead, true)); 119 + 120 + /* Run for 100M instructions */ 121 + FAIL_IF(do_count_loop(events, 0x6400000, overhead, true)); 122 + 123 + /* Run for 1G instructions */ 124 + FAIL_IF(do_count_loop(events, 0x40000000, overhead, true)); 125 + 126 + event_close(&events[0]); 127 + event_close(&events[1]); 128 + 129 + return 0; 130 + } 131 + 132 + int main(void) 133 + { 134 + return test_harness(count_instructions, "count_instructions"); 135 + }
+105
tools/testing/selftests/powerpc/pmu/event.c
··· 1 + /* 2 + * Copyright 2013, Michael Ellerman, IBM Corp. 3 + * Licensed under GPLv2. 4 + */ 5 + 6 + #define _GNU_SOURCE 7 + #include <unistd.h> 8 + #include <sys/syscall.h> 9 + #include <string.h> 10 + #include <stdio.h> 11 + #include <sys/ioctl.h> 12 + 13 + #include "event.h" 14 + 15 + 16 + int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, 17 + int group_fd, unsigned long flags) 18 + { 19 + return syscall(__NR_perf_event_open, attr, pid, cpu, 20 + group_fd, flags); 21 + } 22 + 23 + void event_init_opts(struct event *e, u64 config, int type, char *name) 24 + { 25 + memset(e, 0, sizeof(*e)); 26 + 27 + e->name = name; 28 + 29 + e->attr.type = type; 30 + e->attr.config = config; 31 + e->attr.size = sizeof(e->attr); 32 + /* This has to match the structure layout in the header */ 33 + e->attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | \ 34 + PERF_FORMAT_TOTAL_TIME_RUNNING; 35 + } 36 + 37 + void event_init_named(struct event *e, u64 config, char *name) 38 + { 39 + event_init_opts(e, config, PERF_TYPE_RAW, name); 40 + } 41 + 42 + #define PERF_CURRENT_PID 0 43 + #define PERF_NO_CPU -1 44 + #define PERF_NO_GROUP -1 45 + 46 + int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd) 47 + { 48 + e->fd = perf_event_open(&e->attr, pid, cpu, group_fd, 0); 49 + if (e->fd == -1) { 50 + perror("perf_event_open"); 51 + return -1; 52 + } 53 + 54 + return 0; 55 + } 56 + 57 + int event_open_with_group(struct event *e, int group_fd) 58 + { 59 + return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd); 60 + } 61 + 62 + int event_open(struct event *e) 63 + { 64 + return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP); 65 + } 66 + 67 + void event_close(struct event *e) 68 + { 69 + close(e->fd); 70 + } 71 + 72 + int event_reset(struct event *e) 73 + { 74 + return ioctl(e->fd, PERF_EVENT_IOC_RESET); 75 + } 76 + 77 + int event_read(struct event *e) 78 + { 79 + int rc; 80 + 81 + rc = read(e->fd, &e->result, sizeof(e->result)); 82 + if (rc != sizeof(e->result)) { 83 + fprintf(stderr, "read error on event %p!\n", e); 84 + return -1; 85 + } 86 + 87 + return 0; 88 + } 89 + 90 + void event_report_justified(struct event *e, int name_width, int result_width) 91 + { 92 + printf("%*s: result %*llu ", name_width, e->name, result_width, 93 + e->result.value); 94 + 95 + if (e->result.running == e->result.enabled) 96 + printf("running/enabled %llu\n", e->result.running); 97 + else 98 + printf("running %llu enabled %llu\n", e->result.running, 99 + e->result.enabled); 100 + } 101 + 102 + void event_report(struct event *e) 103 + { 104 + event_report_justified(e, 0, 0); 105 + }
+39
tools/testing/selftests/powerpc/pmu/event.h
··· 1 + /* 2 + * Copyright 2013, Michael Ellerman, IBM Corp. 3 + * Licensed under GPLv2. 4 + */ 5 + 6 + #ifndef _SELFTESTS_POWERPC_PMU_EVENT_H 7 + #define _SELFTESTS_POWERPC_PMU_EVENT_H 8 + 9 + #include <unistd.h> 10 + #include <linux/perf_event.h> 11 + 12 + #include "utils.h" 13 + 14 + 15 + struct event { 16 + struct perf_event_attr attr; 17 + char *name; 18 + int fd; 19 + /* This must match the read_format we use */ 20 + struct { 21 + u64 value; 22 + u64 running; 23 + u64 enabled; 24 + } result; 25 + }; 26 + 27 + void event_init(struct event *e, u64 config); 28 + void event_init_named(struct event *e, u64 config, char *name); 29 + void event_init_opts(struct event *e, u64 config, int type, char *name); 30 + int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd); 31 + int event_open_with_group(struct event *e, int group_fd); 32 + int event_open(struct event *e); 33 + void event_close(struct event *e); 34 + int event_reset(struct event *e); 35 + int event_read(struct event *e); 36 + void event_report_justified(struct event *e, int name_width, int result_width); 37 + void event_report(struct event *e); 38 + 39 + #endif /* _SELFTESTS_POWERPC_PMU_EVENT_H */
+46
tools/testing/selftests/powerpc/pmu/loop.S
··· 1 + /* 2 + * Copyright 2013, Michael Ellerman, IBM Corp. 3 + * Licensed under GPLv2. 4 + */ 5 + 6 + .text 7 + 8 + .global thirty_two_instruction_loop 9 + .type .thirty_two_instruction_loop,@function 10 + .section ".opd","aw",@progbits 11 + thirty_two_instruction_loop: 12 + .quad .thirty_two_instruction_loop, .TOC.@tocbase, 0 13 + .previous 14 + .thirty_two_instruction_loop: 15 + cmpwi %r3,0 16 + beqlr 17 + addi %r4,%r3,1 18 + addi %r4,%r4,1 19 + addi %r4,%r4,1 20 + addi %r4,%r4,1 21 + addi %r4,%r4,1 22 + addi %r4,%r4,1 23 + addi %r4,%r4,1 24 + addi %r4,%r4,1 25 + addi %r4,%r4,1 26 + addi %r4,%r4,1 27 + addi %r4,%r4,1 28 + addi %r4,%r4,1 29 + addi %r4,%r4,1 30 + addi %r4,%r4,1 31 + addi %r4,%r4,1 32 + addi %r4,%r4,1 33 + addi %r4,%r4,1 34 + addi %r4,%r4,1 35 + addi %r4,%r4,1 36 + addi %r4,%r4,1 37 + addi %r4,%r4,1 38 + addi %r4,%r4,1 39 + addi %r4,%r4,1 40 + addi %r4,%r4,1 41 + addi %r4,%r4,1 42 + addi %r4,%r4,1 43 + addi %r4,%r4,1 44 + addi %r4,%r4,1 # 28 addi's 45 + subi %r3,%r3,1 46 + b .thirty_two_instruction_loop