| /* |
| * PMU support for the CPU-measurement facilities |
| * |
| * Copyright IBM Corp. 2012, 2014 |
| * Contributed by Hendrik Brueckner <brueckner@linux.vnet.ibm.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
| * of the Software, and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in all |
| * copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
| * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
| * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE |
| * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| #include <ctype.h> |
| #include <sys/types.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| /* private library and arch headers */ |
| #include "pfmlib_priv.h" |
| #include "pfmlib_s390x_priv.h" |
| #include "pfmlib_perf_event_priv.h" |
| #include "events/s390x_cpumf_events.h" |
| |
| |
| #define CPUM_CF_DEVICE_DIR "/sys/bus/event_source/devices/cpum_cf" |
| #define CPUM_SF_DEVICE_DIR "/sys/bus/event_source/devices/cpum_sf" |
| #define SYS_INFO "/proc/sysinfo" |
| #define SERVICE_LEVEL "/proc/service_levels" |
| #define CF_VERSION_STR "CPU-MF: Counter facility: version=" |
| |
| |
| /* CPU-measurement counter list (pmu events) */ |
| static pme_cpumf_ctr_t *cpumcf_pe = NULL; |
| |
| /* Detect the CPU-measurement counter and sampling facilities */ |
| static int pfm_cpumcf_detect(void *this) |
| { |
| if (access(CPUM_CF_DEVICE_DIR, R_OK)) |
| return PFM_ERR_NOTSUPP; |
| return PFM_SUCCESS; |
| } |
| |
| static int pfm_cpumsf_detect(void *this) |
| { |
| if (access(CPUM_SF_DEVICE_DIR, R_OK)) |
| return PFM_ERR_NOTSUPP; |
| return PFM_SUCCESS; |
| } |
| |
| /* Parses the machine type that identifies an IBM mainframe. |
| * These kind of information are from /proc/sysinfo. |
| */ |
| static long get_machine_type(void) |
| { |
| long machine_type; |
| size_t buflen, len; |
| char *buffer, *tmp; |
| FILE *fp; |
| |
| machine_type = 0; |
| fp = fopen(SYS_INFO, "r"); |
| if (fp == NULL) |
| goto out; |
| |
| buffer = NULL; |
| while (pfmlib_getl(&buffer, &buflen, fp) != -1) { |
| /* skip empty lines */ |
| if (*buffer == '\n') |
| continue; |
| |
| /* look for 'Type:' entry */ |
| if (!strncmp("Type:", buffer, 5)) { |
| tmp = buffer + 5; /* set ptr after ':' */ |
| /* skip leading blanks */ |
| while (isspace(*tmp)) |
| tmp++; |
| /* skip trailing blanks */ |
| len = strlen(tmp); |
| while (len > 0 && isspace(tmp[len])) |
| len--; |
| tmp[len+1] = '\0'; |
| machine_type = strtol(tmp, NULL, 10); |
| break; |
| } |
| } |
| fclose(fp); |
| free(buffer); |
| out: |
| return machine_type; |
| } |
| |
| static void get_cf_version(unsigned int *cfvn, unsigned int *csvn) |
| { |
| int rc; |
| FILE *fp; |
| char *buffer; |
| size_t buflen; |
| |
| *cfvn = *csvn = 0; |
| fp = fopen(SERVICE_LEVEL, "r"); |
| if (fp == NULL) |
| return; |
| |
| buffer = NULL; |
| while (pfmlib_getl(&buffer, &buflen, fp) != -1) { |
| /* skip empty lines */ |
| if (*buffer == '\n') |
| continue; |
| |
| /* look for 'CPU-MF: Counter facility: version=' entry */ |
| if (!strncmp(CF_VERSION_STR, buffer, strlen(CF_VERSION_STR))) { |
| rc = sscanf(buffer + strlen(CF_VERSION_STR), "%u.%u", |
| cfvn, csvn); |
| if (rc != 2) |
| *cfvn = *csvn = 0; |
| break; |
| } |
| } |
| fclose(fp); |
| free(buffer); |
| } |
| |
| /* Initialize the PMU representation for CPUMF. |
| * |
| * Set up the PMU events array based on |
| * - generic (basic, problem-state, and crypto-activaty) counter sets |
| * - the extended counter depending on the machine type |
| */ |
| static int pfm_cpumcf_init(void *this) |
| { |
| pfmlib_pmu_t *pmu = this; |
| unsigned int cfvn, csvn; |
| const pme_cpumf_ctr_t *cfvn_set, *csvn_set, *ext_set; |
| size_t cfvn_set_count, csvn_set_count, ext_set_count, pme_count; |
| |
| /* obtain counter first/second version number */ |
| get_cf_version(&cfvn, &csvn); |
| |
| /* counters based on first version number */ |
| switch (cfvn) |
| { |
| case 1: |
| cfvn_set = cpumcf_fvn1_counters; |
| cfvn_set_count = LIBPFM_ARRAY_SIZE(cpumcf_fvn1_counters); |
| break; |
| case 3: |
| cfvn_set = cpumcf_fvn3_counters; |
| cfvn_set_count = LIBPFM_ARRAY_SIZE(cpumcf_fvn3_counters); |
| break; |
| default: |
| cfvn_set = NULL; |
| cfvn_set_count = 0; |
| break; |
| } |
| |
| /* counters based on second version number */ |
| csvn_set = cpumcf_svn_generic_counters; |
| csvn_set_count = LIBPFM_ARRAY_SIZE(cpumcf_svn_generic_counters); |
| if (csvn < 6) /* Crypto counter set enlarged for SVN == 6 7 and 8 */ |
| csvn_set_count -= CPUMF_SVN6_ECC; |
| |
| /* check and assign a machine-specific extended counter set */ |
| switch (get_machine_type()) { |
| case 2097: /* IBM System z10 EC */ |
| case 2098: /* IBM System z10 BC */ |
| ext_set = cpumcf_z10_counters, |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_z10_counters); |
| break; |
| case 2817: /* IBM zEnterprise 196 */ |
| case 2818: /* IBM zEnterprise 114 */ |
| ext_set = cpumcf_z196_counters; |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_z196_counters); |
| break; |
| case 2827: /* IBM zEnterprise EC12 */ |
| case 2828: /* IBM zEnterprise BC12 */ |
| ext_set = cpumcf_zec12_counters; |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_zec12_counters); |
| break; |
| case 2964: /* IBM z13 */ |
| case 2965: /* IBM z13s */ |
| ext_set = cpumcf_z13_counters; |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_z13_counters); |
| break; |
| case 3906: /* IBM z14 */ |
| case 3907: /* IBM z14 ZR1 */ |
| ext_set = cpumcf_z14_counters; |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_z14_counters); |
| break; |
| case 8561: /* IBM Machine types 8561 and 8562 */ |
| case 8562: |
| ext_set = cpumcf_z15_counters; |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_z15_counters); |
| break; |
| case 3931: /* IBM Machine types 3931 and 3932 */ |
| case 3932: |
| ext_set = cpumcf_z16_counters; |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_z16_counters); |
| break; |
| case 9175: /* IBM Machine types 9175 and 9176 */ |
| case 9176: |
| ext_set = cpumcf_z17_counters; |
| ext_set_count = LIBPFM_ARRAY_SIZE(cpumcf_z17_counters); |
| break; |
| default: |
| /* No extended counter set for this machine type or there |
| * was an error retrieving the machine type */ |
| ext_set = NULL; |
| ext_set_count = 0; |
| break; |
| } |
| |
| cpumcf_pe = calloc(cfvn_set_count + csvn_set_count + ext_set_count, |
| sizeof(*cpumcf_pe)); |
| if (cpumcf_pe == NULL) |
| return PFM_ERR_NOMEM; |
| |
| pme_count = 0; |
| memcpy(cpumcf_pe, cfvn_set, sizeof(*cpumcf_pe) * cfvn_set_count); |
| pme_count += cfvn_set_count; |
| memcpy((void *) (cpumcf_pe + pme_count), csvn_set, |
| sizeof(*cpumcf_pe) * csvn_set_count); |
| pme_count += csvn_set_count; |
| if (ext_set_count) |
| memcpy((void *) (cpumcf_pe + pme_count), |
| ext_set, sizeof(*cpumcf_pe) * ext_set_count); |
| pme_count += ext_set_count; |
| |
| pmu->pe = cpumcf_pe; |
| pmu->pme_count = pme_count; |
| |
| /* CPUM-CF provides fixed counters only. The number of installed |
| * counters depends on the version and hardware model up to |
| * CPUMF_COUNTER_MAX. |
| */ |
| pmu->num_fixed_cntrs = pme_count; |
| |
| return PFM_SUCCESS; |
| } |
| |
| static void pfm_cpumcf_exit(void *this) |
| { |
| pfmlib_pmu_t *pmu = this; |
| |
| pmu->pme_count = 0; |
| pmu->pe = NULL; |
| |
| free(cpumcf_pe); |
| } |
| |
| static int pfm_cpumf_get_encoding(void *this, pfmlib_event_desc_t *e) |
| { |
| const pme_cpumf_ctr_t *pe = this_pe(this); |
| |
| e->count = 1; /* number of encoded entries in e->codes */ |
| e->codes[0] = pe[e->event].ctrnum; |
| evt_strcat(e->fstr, "%s", pe[e->event].name); |
| |
| return PFM_SUCCESS; |
| } |
| |
| static int pfm_cpumf_get_event_first(void *this) |
| { |
| pfmlib_pmu_t *pmu = this; |
| |
| return !!pmu->pme_count ? 0 : -1; |
| } |
| |
| static int pfm_cpumf_get_event_next(void *this, int idx) |
| { |
| pfmlib_pmu_t *pmu = this; |
| |
| if (idx >= (pmu->pme_count - 1)) |
| return -1; |
| return idx + 1; |
| } |
| |
| static int pfm_cpumf_event_is_valid(void *this, int idx) |
| { |
| pfmlib_pmu_t *pmu = this; |
| |
| return (idx >= 0 && idx < pmu->pme_count); |
| } |
| |
| static int pfm_cpumf_validate_table(void *this, FILE *fp) |
| { |
| pfmlib_pmu_t *pmu = this; |
| const pme_cpumf_ctr_t *pe = this_pe(this); |
| int i, rc; |
| |
| rc = PFM_ERR_INVAL; |
| for (i = 0; i < pmu->pme_count; i++) { |
| if (!pe[i].name) { |
| fprintf(fp, "pmu: %s event: %i: No name\n", |
| pmu->name, i); |
| goto failed; |
| } |
| if (!pe[i].desc) { |
| fprintf(fp, "pmu: %s event: %i: No description\n", |
| pmu->name, i); |
| goto failed; |
| } |
| } |
| |
| rc = PFM_SUCCESS; |
| failed: |
| return rc; |
| } |
| |
| static int pfm_cpumcf_validate_table(void *this, FILE *fp) |
| { |
| pfmlib_pmu_t *pmu = this; |
| |
| if (pmu->pme_count > CPUMF_COUNTER_MAX) { |
| fprintf(fp, "pmu: %s: pme number exceeded maximum\n", |
| pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| |
| return pfm_cpumf_validate_table(this, fp); |
| } |
| |
| static int pfm_cpumf_get_event_info(void *this, int idx, |
| pfm_event_info_t *info) |
| { |
| pfmlib_pmu_t *pmu = this; |
| const pme_cpumf_ctr_t *pe = this_pe(this); |
| |
| if (idx >= pmu->pme_count) |
| return PFM_ERR_INVAL; |
| |
| info->name = pe[idx].name; |
| info->desc = pe[idx].desc; |
| info->code = pe[idx].ctrnum; |
| info->equiv = NULL; |
| info->idx = idx; |
| info->pmu = pmu->pmu; |
| info->is_precise = 0; |
| |
| info->nattrs = 0; /* attributes are not supported */ |
| |
| return PFM_SUCCESS; |
| } |
| |
| static int pfm_cpumf_get_event_attr_info(void *this, int idx, int umask_idx, |
| pfmlib_event_attr_info_t *info) |
| { |
| /* Attributes are not supported */ |
| return PFM_ERR_ATTR; |
| } |
| |
| pfmlib_pmu_t s390x_cpum_cf_support = { |
| .desc = "CPU-measurement counter facility", |
| .name = "cpum_cf", |
| .pmu = PFM_PMU_S390X_CPUM_CF, |
| .type = PFM_PMU_TYPE_CORE, |
| .flags = PFMLIB_PMU_FL_ARCH_DFL, |
| .supported_plm = PFM_PLM3, |
| |
| .num_cntrs = 0, /* no general-purpose counters */ |
| .num_fixed_cntrs = CPUMF_COUNTER_MAX, /* fixed counters only */ |
| .max_encoding = 1, |
| |
| .pe = NULL, |
| .pme_count = 0, |
| |
| .pmu_detect = pfm_cpumcf_detect, |
| .pmu_init = pfm_cpumcf_init, |
| .pmu_terminate = pfm_cpumcf_exit, |
| |
| .get_event_encoding[PFM_OS_NONE] = pfm_cpumf_get_encoding, |
| PFMLIB_ENCODE_PERF(pfm_s390x_get_perf_encoding), |
| .get_event_first = pfm_cpumf_get_event_first, |
| .get_event_next = pfm_cpumf_get_event_next, |
| .event_is_valid = pfm_cpumf_event_is_valid, |
| .validate_table = pfm_cpumcf_validate_table, |
| .get_event_info = pfm_cpumf_get_event_info, |
| .get_event_attr_info = pfm_cpumf_get_event_attr_info, |
| PFMLIB_VALID_PERF_PATTRS(pfm_s390x_perf_validate_pattrs), |
| }; |
| |
| pfmlib_pmu_t s390x_cpum_sf_support = { |
| .desc = "CPU-measurement sampling facility", |
| .name = "cpum_sf", |
| .pmu = PFM_PMU_S390X_CPUM_SF, |
| .type = PFM_PMU_TYPE_CORE, |
| .flags = PFMLIB_PMU_FL_ARCH_DFL, |
| |
| .num_cntrs = 0, /* no general-purpose counters */ |
| .num_fixed_cntrs = 2, /* fixed counters only */ |
| .max_encoding = 1, |
| |
| .pe = cpumsf_counters, |
| .pme_count = LIBPFM_ARRAY_SIZE(cpumsf_counters), |
| |
| .pmu_detect = pfm_cpumsf_detect, |
| |
| .get_event_encoding[PFM_OS_NONE] = pfm_cpumf_get_encoding, |
| PFMLIB_ENCODE_PERF(pfm_s390x_get_perf_encoding), |
| .get_event_first = pfm_cpumf_get_event_first, |
| .get_event_next = pfm_cpumf_get_event_next, |
| .event_is_valid = pfm_cpumf_event_is_valid, |
| .validate_table = pfm_cpumf_validate_table, |
| .get_event_info = pfm_cpumf_get_event_info, |
| .get_event_attr_info = pfm_cpumf_get_event_attr_info, |
| PFMLIB_VALID_PERF_PATTRS(pfm_s390x_perf_validate_pattrs), |
| }; |