Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2014, Michael Ellerman, IBM Corp.
0004  */
0005 
0006 #define _GNU_SOURCE
0007 
0008 #include <stdio.h>
0009 #include <stdbool.h>
0010 #include <string.h>
0011 #include <sys/prctl.h>
0012 
0013 #include "ebb.h"
0014 
0015 
0016 /*
0017  * Run a calibrated instruction loop and count instructions executed using
0018  * EBBs. Make sure the counts look right.
0019  */
0020 
0021 extern void thirty_two_instruction_loop(uint64_t loops);
0022 
0023 static bool counters_frozen = true;
0024 
0025 static int do_count_loop(struct event *event, uint64_t instructions,
0026              uint64_t overhead, bool report)
0027 {
0028     int64_t difference, expected;
0029     double percentage;
0030 
0031     clear_ebb_stats();
0032 
0033     counters_frozen = false;
0034     mb();
0035     mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
0036 
0037     thirty_two_instruction_loop(instructions >> 5);
0038 
0039     counters_frozen = true;
0040     mb();
0041     mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
0042 
0043     count_pmc(4, sample_period);
0044 
0045     event->result.value = ebb_state.stats.pmc_count[4-1];
0046     expected = instructions + overhead;
0047     difference = event->result.value - expected;
0048     percentage = (double)difference / event->result.value * 100;
0049 
0050     if (report) {
0051         printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead);
0052         printf("Expected %lu\n", expected);
0053         printf("Actual   %llu\n", event->result.value);
0054         printf("Delta    %ld, %f%%\n", difference, percentage);
0055         printf("Took %d EBBs\n", ebb_state.stats.ebb_count);
0056     }
0057 
0058     if (difference < 0)
0059         difference = -difference;
0060 
0061     /* Tolerate a difference of up to 0.0001 % */
0062     difference *= 10000 * 100;
0063     if (difference / event->result.value)
0064         return -1;
0065 
0066     return 0;
0067 }
0068 
0069 /* Count how many instructions it takes to do a null loop */
0070 static uint64_t determine_overhead(struct event *event)
0071 {
0072     uint64_t current, overhead;
0073     int i;
0074 
0075     do_count_loop(event, 0, 0, false);
0076     overhead = event->result.value;
0077 
0078     for (i = 0; i < 100; i++) {
0079         do_count_loop(event, 0, 0, false);
0080         current = event->result.value;
0081         if (current < overhead) {
0082             printf("Replacing overhead %lu with %lu\n", overhead, current);
0083             overhead = current;
0084         }
0085     }
0086 
0087     return overhead;
0088 }
0089 
0090 static void pmc4_ebb_callee(void)
0091 {
0092     uint64_t val;
0093 
0094     val = mfspr(SPRN_BESCR);
0095     if (!(val & BESCR_PMEO)) {
0096         ebb_state.stats.spurious++;
0097         goto out;
0098     }
0099 
0100     ebb_state.stats.ebb_count++;
0101     count_pmc(4, sample_period);
0102 out:
0103     if (counters_frozen)
0104         reset_ebb_with_clear_mask(MMCR0_PMAO);
0105     else
0106         reset_ebb();
0107 }
0108 
0109 int instruction_count(void)
0110 {
0111     struct event event;
0112     uint64_t overhead;
0113 
0114     SKIP_IF(!ebb_is_supported());
0115 
0116     event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL");
0117     event_leader_ebb_init(&event);
0118     event.attr.exclude_kernel = 1;
0119     event.attr.exclude_hv = 1;
0120     event.attr.exclude_idle = 1;
0121 
0122     FAIL_IF(event_open(&event));
0123     FAIL_IF(ebb_event_enable(&event));
0124 
0125     sample_period = COUNTER_OVERFLOW;
0126 
0127     setup_ebb_handler(pmc4_ebb_callee);
0128     mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
0129     ebb_global_enable();
0130 
0131     overhead = determine_overhead(&event);
0132     printf("Overhead of null loop: %lu instructions\n", overhead);
0133 
0134     /* Run for 1M instructions */
0135     FAIL_IF(do_count_loop(&event, 0x100000, overhead, true));
0136 
0137     /* Run for 10M instructions */
0138     FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true));
0139 
0140     /* Run for 100M instructions */
0141     FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true));
0142 
0143     /* Run for 1G instructions */
0144     FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true));
0145 
0146     /* Run for 16G instructions */
0147     FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true));
0148 
0149     /* Run for 64G instructions */
0150     FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true));
0151 
0152     /* Run for 128G instructions */
0153     FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true));
0154 
0155     ebb_global_disable();
0156     event_close(&event);
0157 
0158     printf("Finished OK\n");
0159 
0160     return 0;
0161 }
0162 
0163 int main(void)
0164 {
0165     test_harness_set_timeout(300);
0166     return test_harness(instruction_count, "instruction_count");
0167 }