Home | History | Annotate | Download | only in ebb
      1 /*
      2  * Copyright 2014, 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 "ebb.h"
     14 
     15 
     16 /*
     17  * Run a calibrated instruction loop and count instructions executed using
     18  * EBBs. Make sure the counts look right.
     19  */
     20 
     21 extern void thirty_two_instruction_loop(uint64_t loops);
     22 
     23 static bool counters_frozen = true;
     24 
     25 static int do_count_loop(struct event *event, uint64_t instructions,
     26 			 uint64_t overhead, bool report)
     27 {
     28 	int64_t difference, expected;
     29 	double percentage;
     30 
     31 	clear_ebb_stats();
     32 
     33 	counters_frozen = false;
     34 	mb();
     35 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
     36 
     37 	thirty_two_instruction_loop(instructions >> 5);
     38 
     39 	counters_frozen = true;
     40 	mb();
     41 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
     42 
     43 	count_pmc(4, sample_period);
     44 
     45 	event->result.value = ebb_state.stats.pmc_count[4-1];
     46 	expected = instructions + overhead;
     47 	difference = event->result.value - expected;
     48 	percentage = (double)difference / event->result.value * 100;
     49 
     50 	if (report) {
     51 		printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead);
     52 		printf("Expected %lu\n", expected);
     53 		printf("Actual   %llu\n", event->result.value);
     54 		printf("Delta    %ld, %f%%\n", difference, percentage);
     55 		printf("Took %d EBBs\n", ebb_state.stats.ebb_count);
     56 	}
     57 
     58 	if (difference < 0)
     59 		difference = -difference;
     60 
     61 	/* Tolerate a difference of up to 0.0001 % */
     62 	difference *= 10000 * 100;
     63 	if (difference / event->result.value)
     64 		return -1;
     65 
     66 	return 0;
     67 }
     68 
     69 /* Count how many instructions it takes to do a null loop */
     70 static uint64_t determine_overhead(struct event *event)
     71 {
     72 	uint64_t current, overhead;
     73 	int i;
     74 
     75 	do_count_loop(event, 0, 0, false);
     76 	overhead = event->result.value;
     77 
     78 	for (i = 0; i < 100; i++) {
     79 		do_count_loop(event, 0, 0, false);
     80 		current = event->result.value;
     81 		if (current < overhead) {
     82 			printf("Replacing overhead %lu with %lu\n", overhead, current);
     83 			overhead = current;
     84 		}
     85 	}
     86 
     87 	return overhead;
     88 }
     89 
     90 static void pmc4_ebb_callee(void)
     91 {
     92 	uint64_t val;
     93 
     94 	val = mfspr(SPRN_BESCR);
     95 	if (!(val & BESCR_PMEO)) {
     96 		ebb_state.stats.spurious++;
     97 		goto out;
     98 	}
     99 
    100 	ebb_state.stats.ebb_count++;
    101 	count_pmc(4, sample_period);
    102 out:
    103 	if (counters_frozen)
    104 		reset_ebb_with_clear_mask(MMCR0_PMAO);
    105 	else
    106 		reset_ebb();
    107 }
    108 
    109 int instruction_count(void)
    110 {
    111 	struct event event;
    112 	uint64_t overhead;
    113 
    114 	SKIP_IF(!ebb_is_supported());
    115 
    116 	event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL");
    117 	event_leader_ebb_init(&event);
    118 	event.attr.exclude_kernel = 1;
    119 	event.attr.exclude_hv = 1;
    120 	event.attr.exclude_idle = 1;
    121 
    122 	FAIL_IF(event_open(&event));
    123 	FAIL_IF(ebb_event_enable(&event));
    124 
    125 	sample_period = COUNTER_OVERFLOW;
    126 
    127 	setup_ebb_handler(pmc4_ebb_callee);
    128 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
    129 	ebb_global_enable();
    130 
    131 	overhead = determine_overhead(&event);
    132 	printf("Overhead of null loop: %lu instructions\n", overhead);
    133 
    134 	/* Run for 1M instructions */
    135 	FAIL_IF(do_count_loop(&event, 0x100000, overhead, true));
    136 
    137 	/* Run for 10M instructions */
    138 	FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true));
    139 
    140 	/* Run for 100M instructions */
    141 	FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true));
    142 
    143 	/* Run for 1G instructions */
    144 	FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true));
    145 
    146 	/* Run for 16G instructions */
    147 	FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true));
    148 
    149 	/* Run for 64G instructions */
    150 	FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true));
    151 
    152 	/* Run for 128G instructions */
    153 	FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true));
    154 
    155 	ebb_global_disable();
    156 	event_close(&event);
    157 
    158 	printf("Finished OK\n");
    159 
    160 	return 0;
    161 }
    162 
    163 int main(void)
    164 {
    165 	return test_harness(instruction_count, "instruction_count");
    166 }
    167