mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Introduce the new state 'unauthorized' to the three already existing states disabled, enabled, and unsupported to CPU-MF counters. CPU-MF counters are only available on LPARs. The intent is to differentiate whether a system simply does not support the CPU-MF counters like a z/VM guest or if they are supported like on LPAR but have to be authorized via HMC/SE. Signed-off-by: Finn Callies <fcallies@linux.ibm.com> Reviewed-by: Harald Freudenberger <freude@linux.ibm.com> [hoeppner@linux.ibm.com: Adapt commit message] Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
877 lines
20 KiB
C
877 lines
20 KiB
C
/*
|
|
* cpacfstats - display and maintain CPACF perf counters
|
|
*
|
|
* low level perf functions
|
|
*
|
|
* Copyright IBM Corp. 2015, 2022
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <asm/unistd.h>
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#define __STDC_FORMAT_MACROS
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <linux/perf_event.h>
|
|
#include <pthread.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/resource.h>
|
|
#include <unistd.h>
|
|
|
|
#include <libudev.h>
|
|
|
|
#include "cpacfstats.h"
|
|
#include "lib/libcpumf.h"
|
|
#include "../include/lib/zt_common.h"
|
|
|
|
/* correlation between counter and perf counter string */
|
|
static const struct {
|
|
char pmu[20];
|
|
char pfm_name[60];
|
|
enum ctr_e ctr;
|
|
} pmf_counter_name[ALL_COUNTER] = {
|
|
{"cpum_cf", "DEA_FUNCTIONS", DES_FUNCTIONS},
|
|
{"cpum_cf", "AES_FUNCTIONS", AES_FUNCTIONS},
|
|
{"cpum_cf", "SHA_FUNCTIONS", SHA_FUNCTIONS},
|
|
{"cpum_cf", "PRNG_FUNCTIONS", PRNG_FUNCTIONS},
|
|
{"cpum_cf", "ECC_FUNCTION_COUNT", ECC_FUNCTIONS}
|
|
};
|
|
|
|
static struct pmf_data {
|
|
int pmutype;
|
|
int eventid;
|
|
} pmf_counter_data[ALL_COUNTER];
|
|
|
|
struct percpucounter {
|
|
int ctr_fds[ALL_COUNTER];
|
|
int pai_user[MAX_NUM_PAI];
|
|
int pai_kernel[MAX_NUM_PAI];
|
|
unsigned int cpunum;
|
|
struct percpucounter *next;
|
|
};
|
|
|
|
static struct percpucounter *root;
|
|
pthread_mutex_t rootmux = PTHREAD_MUTEX_INITIALIZER;
|
|
static volatile int hotplugdetected;
|
|
static unsigned int enabledcounter;
|
|
pthread_t hotplugthread;
|
|
|
|
#define foreachcpu(PCPU) if (pthread_mutex_lock(&rootmux)) return -1; \
|
|
for ((PCPU) = root; (PCPU) != NULL; (PCPU) = (PCPU)->next)
|
|
#define endforeachcpu() pthread_mutex_unlock(&rootmux)
|
|
|
|
static int ctr_state[NUM_COUNTER];
|
|
|
|
static volatile int stoprequested;
|
|
|
|
static int paipmutype, paipmueventstart;
|
|
|
|
static struct percpucounter *allocpercpucounter(unsigned int cpunum)
|
|
{
|
|
struct percpucounter *ppc;
|
|
|
|
ppc = malloc(sizeof(struct percpucounter));
|
|
if (ppc) {
|
|
int i;
|
|
|
|
for (i = 0; i < ALL_COUNTER; ++i)
|
|
ppc->ctr_fds[i] = -1;
|
|
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
|
ppc->pai_user[i] = -1;
|
|
ppc->pai_kernel[i] = -1;
|
|
}
|
|
ppc->cpunum = cpunum;
|
|
ppc->next = NULL;
|
|
}
|
|
return ppc;
|
|
}
|
|
|
|
static void freepercpucounter(struct percpucounter *pcpu)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ALL_COUNTER; ++i)
|
|
(void)close(pcpu->ctr_fds[i]);
|
|
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
|
(void)close(pcpu->pai_user[i]);
|
|
(void)close(pcpu->pai_kernel[i]);
|
|
}
|
|
free(pcpu);
|
|
}
|
|
|
|
static struct percpucounter *findcpu(unsigned int cpunum, int unlinkflag)
|
|
{
|
|
struct percpucounter **prev = &root, *walk = root;
|
|
|
|
while (walk) {
|
|
if (walk->cpunum == cpunum) {
|
|
if (unlinkflag)
|
|
*prev = walk->next;
|
|
return walk;
|
|
}
|
|
prev = &(walk->next);
|
|
walk = walk->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int perf_supported(void)
|
|
{
|
|
return !access("/proc/sys/kernel/perf_event_paranoid", R_OK);
|
|
}
|
|
|
|
static int perf_counter_supported(const char *pmu, const char *counter)
|
|
{
|
|
char buf[PATH_MAX];
|
|
|
|
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s",
|
|
pmu, counter) >= PATH_MAX) {
|
|
eprint("overflow in path name");
|
|
return 0;
|
|
}
|
|
return !access(buf, R_OK);
|
|
}
|
|
|
|
/**
|
|
* Returns 1 if counters are authorized, -1 if counters are unauthorized,
|
|
* and 0 otherwise which indicates that the counters are unsupported
|
|
*/
|
|
static int cpumf_authorized(void)
|
|
{
|
|
unsigned vermin, vermax, auth;
|
|
int res = 0, found = 0;
|
|
size_t linesize = 0;
|
|
char *line = NULL;
|
|
FILE *f;
|
|
|
|
f = fopen("/proc/service_levels", "r");
|
|
if (f == NULL) {
|
|
eprint("Failed to open /proc/service_levels (%d:%s)\n",
|
|
errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
while (getline(&line, &linesize, f) >= 0) {
|
|
if (sscanf(line,
|
|
"CPU-MF: Counter facility: version=%d.%d authorization=%x",
|
|
&vermin, &vermax, &auth) == 3) {
|
|
if (auth & 0x8) {
|
|
res = 1;
|
|
} else {
|
|
eprint("CPU-MF counters not authorized.\n");
|
|
res = -1;
|
|
}
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
eprint("CPU-MF counters not available.\n");
|
|
free(line);
|
|
fclose(f);
|
|
return res;
|
|
}
|
|
|
|
static int perf_event_encode(int *pmutype, int *eventid,
|
|
const char *pmu, const char *event)
|
|
{
|
|
FILE *f;
|
|
char buf[PATH_MAX];
|
|
|
|
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s",
|
|
pmu, event) >= PATH_MAX) {
|
|
eprint("overflow in path name");
|
|
return -1;
|
|
}
|
|
f = fopen(buf, "r");
|
|
if (!f) {
|
|
eprint("Event %s for pmu %s not found (%d:%s)\n", event, pmu,
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (fscanf(f, "event=0x%x\n", eventid) != 1) {
|
|
fclose(f);
|
|
eprint("Event file %s has invalid format\n", buf);
|
|
return -1;
|
|
}
|
|
fclose(f);
|
|
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/type",
|
|
pmu) >= PATH_MAX) {
|
|
eprint("overflow in path name");
|
|
return -1;
|
|
}
|
|
f = fopen(buf, "r");
|
|
if (!f) {
|
|
eprint("Event %s for pmu %s not found (%d:%s)\n", event, pmu,
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (fscanf(f, "%d\n", pmutype) != 1) {
|
|
fclose(f);
|
|
eprint("Type file %s has invalid format\n", buf);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int activatecpu(unsigned int cpu, unsigned int *supported_counters)
|
|
{
|
|
struct perf_event_attr pfm_event;
|
|
struct percpucounter *ppc;
|
|
int fd, i, rc = 0;
|
|
|
|
ppc = allocpercpucounter(cpu);
|
|
if (ppc == NULL) {
|
|
eprint("Failed to allocate per cpu counter data");
|
|
return -1;
|
|
}
|
|
if (pthread_mutex_lock(&rootmux)) {
|
|
freepercpucounter(ppc);
|
|
return -1;
|
|
}
|
|
/* activate CPU-MF */
|
|
for (i = 0; i < ALL_COUNTER; ++i) {
|
|
if (ctr_state[i] == UNSUPPORTED || ctr_state[i] == UNAUTHORIZED)
|
|
continue;
|
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
|
pfm_event.size = sizeof(pfm_event);
|
|
pfm_event.type = pmf_counter_data[i].pmutype;
|
|
pfm_event.config = pmf_counter_data[i].eventid;
|
|
|
|
/* fetch file descriptor for this perf event
|
|
* the counter event should start disabled
|
|
*/
|
|
pfm_event.disabled = ctr_state[i] == DISABLED;
|
|
fd = perf_event_open(
|
|
&pfm_event,
|
|
-1, /* pid -1 means all processes */
|
|
cpu,
|
|
-1, /* group filedescriptor */
|
|
0); /* flags */
|
|
if (fd < 0) {
|
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
ctr_state[i] = UNSUPPORTED;
|
|
} else {
|
|
ppc->ctr_fds[i] = fd;
|
|
}
|
|
}
|
|
/* activate pai_user and pai_kernel */
|
|
/* invariant:
|
|
(ctr_state[PAI_USER] == UNSUPPORTED) ==
|
|
(ctr_state[PAI_KERNEL] == UNSUPPORTED) */
|
|
if (ctr_state[PAI_USER] != UNSUPPORTED) {
|
|
for (i = 1; i <= MAX_NUM_PAI; ++i) {
|
|
if (is_user_space(i - 1) != KERNEL_AND_USER_COUNTER ||
|
|
supported_counters[i - 1] != 1)
|
|
continue;
|
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
|
pfm_event.size = sizeof(pfm_event);
|
|
pfm_event.type = paipmutype;
|
|
pfm_event.config = paipmueventstart + i;
|
|
pfm_event.exclude_kernel = 1;
|
|
pfm_event.exclude_user = 0;
|
|
pfm_event.disabled = ctr_state[PAI_USER] == DISABLED;
|
|
fd = perf_event_open(&pfm_event, -1, cpu, -1, 0);
|
|
if (fd < 0) {
|
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
|
goto outevents;
|
|
} else {
|
|
ppc->pai_user[i - 1] = fd;
|
|
}
|
|
pfm_event.exclude_kernel = 0;
|
|
pfm_event.exclude_user = 1;
|
|
pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED;
|
|
fd = perf_event_open(&pfm_event, -1, cpu, -1, 0);
|
|
if (fd < 0) {
|
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
|
goto outevents;
|
|
} else {
|
|
ppc->pai_kernel[i - 1] = fd;
|
|
}
|
|
}
|
|
/*
|
|
* i can start at the index of the first PAI counter
|
|
* for which kernel space is needed
|
|
*/
|
|
for (i = FIRST_KERNEL_ONLY_COUNTER; i <= MAX_NUM_PAI; ++i) {
|
|
if (is_user_space(i - 1) == SUPPRESS_COUNTER ||
|
|
supported_counters[i - 1] != 1)
|
|
continue;
|
|
memset(&pfm_event, 0, sizeof(pfm_event));
|
|
pfm_event.size = sizeof(pfm_event);
|
|
pfm_event.type = paipmutype;
|
|
pfm_event.config = paipmueventstart + i;
|
|
pfm_event.exclude_kernel = 0;
|
|
pfm_event.exclude_user = 1;
|
|
pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED;
|
|
fd = perf_event_open(&pfm_event, -1, cpu, -1, 0);
|
|
if (fd < 0) {
|
|
eprint("Perf_event_open() failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
|
goto outevents;
|
|
} else {
|
|
ppc->pai_kernel[i - 1] = fd;
|
|
}
|
|
}
|
|
}
|
|
outevents:
|
|
ppc->next = root;
|
|
root = ppc;
|
|
if (enabledcounter)
|
|
hotplugdetected = 1;
|
|
pthread_mutex_unlock(&rootmux);
|
|
return rc;
|
|
}
|
|
|
|
static void deactivatecpu(unsigned int cpunum)
|
|
{
|
|
struct percpucounter *pcpu;
|
|
int i;
|
|
|
|
if (pthread_mutex_lock(&rootmux))
|
|
return;
|
|
pcpu = findcpu(cpunum, 1);
|
|
if (pcpu != NULL) {
|
|
for (i = 0; i < ALL_COUNTER; ++i)
|
|
(void)close(pcpu->ctr_fds[i]);
|
|
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
|
(void)close(pcpu->pai_user[i]);
|
|
(void)close(pcpu->pai_kernel[i]);
|
|
}
|
|
free(pcpu);
|
|
if (enabledcounter)
|
|
hotplugdetected = 1;
|
|
}
|
|
pthread_mutex_unlock(&rootmux);
|
|
}
|
|
|
|
static int addallcpus(unsigned int *supported_counters)
|
|
{
|
|
unsigned int start, end;
|
|
int scanned, rc = 0;
|
|
FILE *fp;
|
|
|
|
/* comma separated list of intervals */
|
|
if ((fp = fopen("/sys/devices/system/cpu/online", "r")) == NULL) {
|
|
eprint("Failed to get online cpus (%d:%s)\n",
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
while (!feof(fp)) {
|
|
/* scan all intervals of online cpus */
|
|
scanned = fscanf(fp, "%u-%u", &start, &end);
|
|
/* take care of singleton intervals */
|
|
if (scanned == 1)
|
|
end = start;
|
|
for (; start <= end; ++start) {
|
|
if (activatecpu(start, supported_counters)) {
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
/* Skip comma separator */
|
|
(void)fgetc(fp);
|
|
}
|
|
out:
|
|
fclose(fp);
|
|
return rc;
|
|
}
|
|
|
|
static int perf_load_counter_data(void)
|
|
{
|
|
int i, res = 0;
|
|
|
|
for (i = 0; i < ALL_COUNTER; ++i) {
|
|
if (ctr_state[i] != UNSUPPORTED && ctr_state[i] != UNAUTHORIZED)
|
|
res |= perf_event_encode(&pmf_counter_data[i].pmutype,
|
|
&pmf_counter_data[i].eventid,
|
|
pmf_counter_name[i].pmu,
|
|
pmf_counter_name[i].pfm_name);
|
|
}
|
|
if (ctr_state[PAI_USER] != UNSUPPORTED)
|
|
res |= perf_event_encode(&paipmutype, &paipmueventstart,
|
|
"pai_crypto", "CRYPTO_ALL");
|
|
return res;
|
|
}
|
|
|
|
static void *hotplughandler(void *supported_counters)
|
|
{
|
|
struct udev *hotplug;
|
|
struct udev_monitor *monitor;
|
|
struct pollfd item;
|
|
|
|
hotplug = udev_new();
|
|
if (!hotplug) {
|
|
eprint("Failed to create hotplug device\n");
|
|
return NULL;
|
|
}
|
|
monitor = udev_monitor_new_from_netlink(hotplug, "udev");
|
|
udev_monitor_filter_add_match_subsystem_devtype(monitor, "cpu", NULL);
|
|
udev_monitor_enable_receiving(monitor);
|
|
item.fd = udev_monitor_get_fd(monitor);
|
|
item.events = POLLIN;
|
|
item.revents = 0;
|
|
while (!stoprequested) {
|
|
struct udev_device *dev;
|
|
const char *path, *action;
|
|
unsigned int cpunum;
|
|
int rc, on, off;
|
|
|
|
errno = 0;
|
|
rc = poll(&item, 1, -1);
|
|
if (rc == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
break;
|
|
}
|
|
dev = udev_monitor_receive_device(monitor);
|
|
if (dev == NULL)
|
|
continue;
|
|
action = udev_device_get_action(dev);
|
|
if (action == NULL)
|
|
continue;
|
|
off = strcmp(action, "offline") == 0;
|
|
on = strcmp(action, "online") == 0;
|
|
if (!on && !off)
|
|
continue;
|
|
path = udev_device_get_devpath(dev);
|
|
if (sscanf(path, "/devices/system/cpu/cpu%u", &cpunum) != 1)
|
|
continue;
|
|
if (on && activatecpu(cpunum, (unsigned int *) supported_counters))
|
|
eprint("Failed to attach to hotplugged CPU %u\n", cpunum);
|
|
if (off)
|
|
deactivatecpu(cpunum);
|
|
}
|
|
udev_monitor_unref(monitor);
|
|
udev_unref(hotplug);
|
|
return NULL;
|
|
}
|
|
|
|
int perf_init(unsigned int *supported_counters)
|
|
{
|
|
static const char *cpum_cf[] = {
|
|
"DEA_FUNCTIONS",
|
|
"AES_FUNCTIONS",
|
|
"SHA_FUNCTIONS",
|
|
"PRNG_FUNCTIONS",
|
|
"ECC_FUNCTION_COUNT"
|
|
};
|
|
unsigned long maxfd;
|
|
struct rlimit rlim;
|
|
int cpumf_state;
|
|
int i, num;
|
|
FILE *f;
|
|
|
|
/* initialize performance monitoring library */
|
|
if (!perf_supported()) {
|
|
eprint("Performance counter not supported");
|
|
return -1;
|
|
}
|
|
|
|
/* We currently support all cpumf counters plus two virtual
|
|
* counters for PAI. */
|
|
num = ALL_COUNTER + 2;
|
|
|
|
cpumf_state = cpumf_authorized();
|
|
if (cpumf_state == 0) {
|
|
for (i = 0; i < ALL_COUNTER; ++i)
|
|
ctr_state[i] = UNSUPPORTED;
|
|
num -= ALL_COUNTER;
|
|
} else if (cpumf_state == -1) {
|
|
for (i = 0; i < ALL_COUNTER; ++i)
|
|
ctr_state[i] = UNAUTHORIZED;
|
|
num -= ALL_COUNTER;
|
|
} else {
|
|
for (i = 0; i < ALL_COUNTER; i++) {
|
|
if (!perf_counter_supported("cpum_cf", cpum_cf[i])) {
|
|
ctr_state[i] = UNSUPPORTED;
|
|
num--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!perf_counter_supported("pai_crypto", "CRYPTO_ALL")) {
|
|
ctr_state[PAI_USER] = UNSUPPORTED;
|
|
ctr_state[PAI_KERNEL] = UNSUPPORTED;
|
|
num -= 2;
|
|
}
|
|
|
|
if (num == 0)
|
|
eprint("No crypto counters supported!\n");
|
|
|
|
if (perf_load_counter_data())
|
|
return -1;
|
|
/* We have to adjust the number of FDs possible since we might
|
|
* need more than 1024 (the typical soft limit) */
|
|
f = fopen("/proc/sys/fs/nr_open", "r");
|
|
if (f == NULL) {
|
|
eprint("fopen failed for /proc/sys/fs/nr_open with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (fscanf(f, "%lu", &maxfd) != 1) {
|
|
fclose(f);
|
|
eprint("Failed to parse /proc/sys/fs/nr_open\n");
|
|
return -1;
|
|
}
|
|
fclose(f);
|
|
rlim.rlim_cur = maxfd;
|
|
rlim.rlim_max = maxfd;
|
|
if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
|
|
eprint("setrlimit failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (pthread_create(&hotplugthread, NULL, hotplughandler,
|
|
supported_counters)) {
|
|
eprint("Failed to start hotplug handler thread\n");
|
|
return -1;
|
|
}
|
|
return addallcpus(supported_counters);
|
|
}
|
|
|
|
|
|
void perf_stop(void)
|
|
{
|
|
stoprequested = 1;
|
|
}
|
|
|
|
|
|
void perf_close(void)
|
|
{
|
|
struct percpucounter *walk, *next;
|
|
|
|
pthread_kill(hotplugthread, SIGINT);
|
|
pthread_join(hotplugthread, NULL);
|
|
walk = root;
|
|
while (walk) {
|
|
next = walk->next;
|
|
freepercpucounter(walk);
|
|
walk = next;
|
|
}
|
|
}
|
|
|
|
|
|
static int enable_array(int *arr, int user, unsigned int *supported_counters)
|
|
{
|
|
int i, ec, rc = 0;
|
|
|
|
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
|
if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) ||
|
|
supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER)
|
|
continue;
|
|
ec = ioctl(arr[i], PERF_EVENT_IOC_ENABLE, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
int perf_enable_ctr(enum ctr_e ctr, unsigned int *supported_counters)
|
|
{
|
|
struct percpucounter *pcpu;
|
|
int ec, rc = 0;
|
|
|
|
if (ctr == ALL_COUNTER) {
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
rc = perf_enable_ctr(ctr, supported_counters);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
} else if (ctr < ALL_COUNTER) {
|
|
foreachcpu(pcpu) {
|
|
ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_ENABLE, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
ctr_state[ctr] = ENABLED;
|
|
++enabledcounter;
|
|
endforeachcpu();
|
|
} else if (ctr == PAI_USER) {
|
|
foreachcpu(pcpu) {
|
|
ec = enable_array(pcpu->pai_user, 1, supported_counters);
|
|
if (ec < 0)
|
|
rc = -1;
|
|
}
|
|
ctr_state[ctr] = ENABLED;
|
|
++enabledcounter;
|
|
endforeachcpu();
|
|
} else if (ctr == PAI_KERNEL) {
|
|
foreachcpu(pcpu) {
|
|
ec = enable_array(pcpu->pai_kernel, 0, supported_counters);
|
|
if (ec < 0)
|
|
rc = -1;
|
|
}
|
|
ctr_state[ctr] = ENABLED;
|
|
++enabledcounter;
|
|
endforeachcpu();
|
|
} else {
|
|
rc = -1;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int disable_array(int *arr, int user, unsigned int *supported_counters)
|
|
{
|
|
int i, ec, rc = 0;
|
|
|
|
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
|
if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) ||
|
|
supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER)
|
|
continue;
|
|
ec = ioctl(arr[i], PERF_EVENT_IOC_DISABLE, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
int perf_disable_ctr(enum ctr_e ctr, unsigned int *supported_counters)
|
|
{
|
|
struct percpucounter *pcpu;
|
|
int ec, rc = 0;
|
|
|
|
if (ctr == ALL_COUNTER) {
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
rc = perf_disable_ctr(ctr, supported_counters);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
} else if (ctr < ALL_COUNTER) {
|
|
foreachcpu(pcpu) {
|
|
ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_DISABLE, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
ctr_state[ctr] = DISABLED;
|
|
--enabledcounter;
|
|
if (enabledcounter == 0)
|
|
hotplugdetected = 0;
|
|
endforeachcpu();
|
|
} else if (ctr == PAI_USER) {
|
|
foreachcpu(pcpu) {
|
|
ec = disable_array(pcpu->pai_user, 1, supported_counters);
|
|
if (ec < 0)
|
|
rc = -1;
|
|
}
|
|
ctr_state[ctr] = DISABLED;
|
|
--enabledcounter;
|
|
if (enabledcounter == 0)
|
|
hotplugdetected = 0;
|
|
endforeachcpu();
|
|
} else if (ctr == PAI_KERNEL) {
|
|
foreachcpu(pcpu) {
|
|
ec = disable_array(pcpu->pai_kernel, 0, supported_counters);
|
|
if (ec < 0)
|
|
rc = -1;
|
|
}
|
|
ctr_state[ctr] = DISABLED;
|
|
--enabledcounter;
|
|
if (enabledcounter == 0)
|
|
hotplugdetected = 0;
|
|
endforeachcpu();
|
|
} else {
|
|
rc = -1;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int reset_array(int *arr, int user, unsigned int *supported_counters)
|
|
{
|
|
int ec, rc = 0, i;
|
|
|
|
for (i = 0; i < MAX_NUM_PAI; ++i) {
|
|
if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) ||
|
|
supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER)
|
|
continue;
|
|
ec = ioctl(arr[i], PERF_EVENT_IOC_RESET, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
int perf_reset_ctr(enum ctr_e ctr, uint64_t *value, unsigned int
|
|
*supported_counters)
|
|
{
|
|
struct percpucounter *pcpu;
|
|
int ec, rc = 0;
|
|
|
|
if (ctr == ALL_COUNTER) {
|
|
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
|
|
rc = perf_reset_ctr(ctr, value, supported_counters);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
} else if (ctr < ALL_COUNTER) {
|
|
foreachcpu(pcpu) {
|
|
ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_RESET, 0);
|
|
if (ec < 0) {
|
|
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
}
|
|
}
|
|
endforeachcpu();
|
|
} else if (ctr == PAI_USER) {
|
|
foreachcpu(pcpu) {
|
|
ec = reset_array(pcpu->pai_user, 1, supported_counters);
|
|
if (ec < 0)
|
|
rc = -1;
|
|
}
|
|
endforeachcpu();
|
|
} else if (ctr == PAI_KERNEL) {
|
|
foreachcpu(pcpu) {
|
|
ec = reset_array(pcpu->pai_kernel, 0, supported_counters);
|
|
if (ec < 0)
|
|
rc = -1;
|
|
}
|
|
endforeachcpu();
|
|
} else {
|
|
rc = -1;
|
|
}
|
|
if (rc == 0)
|
|
rc = perf_read_ctr(ctr, value, supported_counters);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int perf_read_ctr(enum ctr_e ctr, uint64_t *value, unsigned int
|
|
*supported_counters)
|
|
{
|
|
struct percpucounter *pcpu;
|
|
int ec, rc = 0;
|
|
uint64_t val;
|
|
|
|
if (!value)
|
|
return -1;
|
|
if (ctr == HOTPLUG_DETECTED) {
|
|
*value = hotplugdetected;
|
|
return 0;
|
|
}
|
|
if (ctr == PAI_USER) {
|
|
int c = 0;
|
|
|
|
for (int i = 0; i < MAX_NUM_PAI; i++) {
|
|
if (is_user_space(i) == KERNEL_AND_USER_COUNTER &&
|
|
supported_counters[i] == 1)
|
|
c++;
|
|
}
|
|
|
|
*value = c;
|
|
return 0;
|
|
}
|
|
if (ctr == PAI_KERNEL) {
|
|
int c = 0;
|
|
|
|
for (int i = 0; i < MAX_NUM_PAI; i++) {
|
|
if (supported_counters[i] == 1)
|
|
c++;
|
|
}
|
|
|
|
*value = c;
|
|
return 0;
|
|
}
|
|
if (ctr >= ALL_COUNTER)
|
|
return -1;
|
|
*value = 0;
|
|
|
|
foreachcpu(pcpu) {
|
|
ec = read(pcpu->ctr_fds[ctr], &val, sizeof(val));
|
|
if (ec != sizeof(val)) {
|
|
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
} else {
|
|
*value += val;
|
|
}
|
|
}
|
|
endforeachcpu();
|
|
|
|
return rc;
|
|
}
|
|
|
|
int perf_ecc_supported(void)
|
|
{
|
|
return ctr_state[ECC_FUNCTIONS] != UNSUPPORTED;
|
|
}
|
|
|
|
int perf_ctr_state(enum ctr_e ctr) {
|
|
if (ctr < NUM_COUNTER)
|
|
return ctr_state[ctr];
|
|
return UNSUPPORTED;
|
|
}
|
|
|
|
|
|
int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value)
|
|
{
|
|
struct percpucounter *pcpu;
|
|
int *arr, ec, rc = 0;
|
|
uint64_t val;
|
|
|
|
*value = 0;
|
|
|
|
if (is_user_space(ctrnum) == SUPPRESS_COUNTER ||
|
|
(user && is_user_space(ctrnum) == KERNEL_ONLY_COUNTER))
|
|
return -1;
|
|
|
|
foreachcpu(pcpu) {
|
|
arr = user ? pcpu->pai_user : pcpu->pai_kernel;
|
|
ec = read(arr[ctrnum], &val, sizeof(val));
|
|
if (ec != sizeof(val)) {
|
|
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
|
|
errno, strerror(errno));
|
|
rc = -1;
|
|
} else {
|
|
*value += val;
|
|
}
|
|
}
|
|
endforeachcpu();
|
|
return rc;
|
|
}
|