cpacfstats: Handle CPU hotplug

cpacfstatsd now correctly handles offline cpus and dynamically attaches to
cpus once they get online.  If events are enabled when a hotplug event
occurs, cpacfstatsd uses a pseudo-counter to notify user applications about
the occurence of this event and a potential data inaccuracy.

cpacfstats shows if a hotplug event has been detected since at least one
counter was activated.  As soon as all counters are deactivated, the
hotplug detection state is reset.

Signed-off-by: Juergen Christ <jchrist@linux.ibm.com>
Reviewed-By: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Juergen Christ
2022-04-12 18:08:17 +02:00
committed by Jan Höppner
parent 225964875c
commit 72b5e8b313
7 changed files with 425 additions and 181 deletions

View File

@@ -306,7 +306,6 @@ build options:
| | | hsavmcore |
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
| ncurses | `HAVE_NCURSES` | hyptop |
| pfm | `HAVE_PFM` | cpacfstats |
| net-snmp | `HAVE_SNMP` | osasnmpd |
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
@@ -320,6 +319,7 @@ build options:
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
| systemd | `HAVE_SYSTEMD` | hsavmcore |
| liblockfile | `HAVE_LOCKFILE` | ap-check |
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
This table lists additional build or install options:
@@ -403,14 +403,13 @@ the different tools are provided:
As of s390-tools-1.13.0, the minimum required kernel level is 2.6.38.
* cpacfstats:
For building the cpacfstats tools you need libpfm version 4 or
newer installed (libpfm-devel.rpm). Tip: you may skip the cpacfstats
build by adding `HAVE_PFN=0` to the make invocation. To run the
cpacfstats daemon the kernel needs to have performance
events enabled (check for `CONFIG_PERF_EVENTS=y`) and you need libpfm
version 4 or newer installed. A new group 'cpacfstats' needs to be
created and all users intending to use the tool should be added to
this group.
For building the cpacfstats tools you need libudev installed
(systemd-devel.rpm). Tip: you may skip the cpacfstats build by
adding `HAVE_LIBUDEV=0` to the make invocation. To run the
cpacfstats daemon the kernel needs to have performance events
enabled (check for `CONFIG_PERF_EVENTS=y`). A new group 'cpacfstats'
needs to be created and all users intending to use the tool should
be added to this group.
* zdev:
Depending on the boot loader and initial RAM-disk mechanism used by a

View File

@@ -1,11 +1,28 @@
include ../common.mak
ifeq (${HAVE_LIBUDEV},0)
all:
$(SKIP) HAVE_LIBUDEV=0
install:
$(SKIP) HAVE_LIBUDEV=0
else
check_dep:
$(call check_dep, \
"cpacfstatsd", \
"libudev.h", \
"systemd-devel", \
"HAVE_LIBUDEV=0")
ALL_CPPFLAGS += -DVERSION=$(VERSION)
all: cpacfstats cpacfstatsd
all: check_dep cpacfstats cpacfstatsd
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
cpacfstats: cpacfstats.o stats_sock.o
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
@@ -16,6 +33,8 @@ install: all
$(INSTALL) -m 644 cpacfstatsd.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 cpacfstats.1 $(DESTDIR)$(MANDIR)/man1
endif
clean:
rm -f *.o *~ cpacfstatsd cpacfstats

View File

@@ -91,9 +91,20 @@ static int recv_answer(int s, int *ctr, int *state, uint64_t *value)
return rc;
}
static void print_virtual_counter_answer(int ctr, int state, uint64_t value)
{
if (ctr == HOTPLUG_DETECTED) {
if (state >= 0 && value > 0)
printf(" hotplug detected\n");
}
}
static void print_answer(int ctr, int state, uint64_t value)
{
if (ctr > ALL_COUNTER) {
print_virtual_counter_answer(ctr, state, value);
return;
}
if (state < 0)
printf(" %s counter: error state %d\n",
counter_str[ctr], state);
@@ -232,6 +243,18 @@ int main(int argc, char *argv[])
}
print_answer(j, state, value);
}
/* receive hotplug state */
if (recv_answer(s, &j, &state, &value) != 0) {
eprint("Error on receiving hotplug state from daemon\n");
close(s);
exit(1);
}
if (state < 0) {
eprint("Received bad status code %d from daemon\n", state);
close(s);
exit(1);
}
print_answer(j, state, value);
/* close connection */
close(s);

View File

@@ -23,7 +23,8 @@ int eprint(const char *format, ...);
/*
* Counter names
* ALL_COUNTER must always be the last member of the enum
* ALL_COUNTER specifies the number of physical counters. Virtual
* counters might be added afterwards.
*/
enum ctr_e {
DES_FUNCTIONS = 0,
@@ -31,7 +32,8 @@ enum ctr_e {
SHA_FUNCTIONS,
PRNG_FUNCTIONS,
ECC_FUNCTIONS,
ALL_COUNTER
ALL_COUNTER,
HOTPLUG_DETECTED
};
enum type_e {
@@ -53,7 +55,7 @@ enum state_e {
};
/*
* query send from clent to daemon
* query send from client to daemon
* Consist of:
* enum counter
* enum command
@@ -102,17 +104,19 @@ struct msg {
} __packed;
int open_socket(int mode);
int send_msg(int sfd, struct msg *m);
int recv_msg(int sfd, struct msg *m);
int send_msg(int sfd, struct msg *m, int timeout);
int recv_msg(int sfd, struct msg *m, int timeout);
/* perf_crypto.c */
int perf_init(void);
void perf_stop(void);
void perf_close(void);
int perf_enable_ctr(enum ctr_e ctr);
int perf_disable_ctr(enum ctr_e ctr);
int perf_reset_ctr(enum ctr_e ctr);
int perf_read_ctr(enum ctr_e ctr, uint64_t *value);
int perf_ecc_supported(void);
int perf_ctr_state(enum ctr_e ctr);
#endif

View File

@@ -28,6 +28,7 @@
#include "lib/zt_common.h"
#include "cpacfstats.h"
static volatile int stopsig;
static const char *const name = "cpacfstatsd";
@@ -43,9 +44,6 @@ static const char *const usage =
static int daemonized;
static int ctr_state[ALL_COUNTER];
static int recv_query(int s, enum ctr_e *ctr, enum cmd_e *cmd)
{
struct msg m;
@@ -89,32 +87,35 @@ static int send_answer(int s, int ctr, int state, uint64_t value)
static int do_enable(int s, enum ctr_e ctr)
{
uint64_t value;
uint64_t value = 0;
int i, rc = 0;
int state;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (ctr_state[i] == DISABLED) {
state = perf_ctr_state(i);
if (state == DISABLED) {
rc = perf_enable_ctr(i);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
ctr_state[i] = ENABLED;
state = ENABLED;
}
if (ctr_state[i] == UNSUPPORTED) {
send_answer(s, i, UNSUPPORTED, 0);
} else {
if (state != UNSUPPORTED) {
rc = perf_read_ctr(i, &value);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
send_answer(s, i, ENABLED, value);
}
send_answer(s, i, state, value);
}
}
if (rc == 0) {
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
send_answer(s, HOTPLUG_DETECTED, rc, value);
}
return rc;
}
@@ -122,67 +123,75 @@ static int do_enable(int s, enum ctr_e ctr)
static int do_disable(int s, enum ctr_e ctr)
{
int i, rc = 0;
uint64_t value;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (ctr_state[i] == ENABLED) {
if (perf_ctr_state(i) == ENABLED) {
rc = perf_disable_ctr(i);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
ctr_state[i] = 0;
}
send_answer(s, i, ctr_state[i], 0);
send_answer(s, i, perf_ctr_state(i), 0);
}
}
if (rc == 0) {
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
send_answer(s, HOTPLUG_DETECTED, rc, value);
}
return rc;
}
static int do_reset(int s, enum ctr_e ctr)
{
int i, rc = 0;
int i, rc = 0, state;
uint64_t value;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (ctr_state[i] == ENABLED) {
state = perf_ctr_state(i);
if (state == ENABLED) {
rc = perf_reset_ctr(i);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
send_answer(s, i, ENABLED, 0);
} else {
send_answer(s, i, ctr_state[i], 0);
}
send_answer(s, i, state, 0);
}
}
if (rc == 0) {
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
send_answer(s, HOTPLUG_DETECTED, rc, value);
}
return rc;
}
static int do_print(int s, enum ctr_e ctr)
{
int i, rc = 0;
uint64_t value;
int i, rc = 0, state;
uint64_t value = 0;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (ctr_state[i] == ENABLED) {
state = perf_ctr_state(i);
if (state == ENABLED) {
rc = perf_read_ctr(i, &value);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
send_answer(s, i, ENABLED, value);
} else {
send_answer(s, i, ctr_state[i], 0);
}
send_answer(s, i, state, value);
}
}
if (rc == 0) {
rc = perf_read_ctr(HOTPLUG_DETECTED, &value);
send_answer(s, HOTPLUG_DETECTED, rc, value);
}
return rc;
}
@@ -324,18 +333,8 @@ static void remove_pidfile(void)
void signalhandler(int sig)
{
if (sig == SIGTERM)
eprint("Caught signal SIGTERM, terminating...\n");
else if (sig == SIGINT)
eprint("Caught signal SIGINT, terminating...\n");
else
eprint("Caught signal %d, terminating...\n", sig);
remove_sock();
perf_close();
remove_pidfile();
exit(0);
perf_stop();
stopsig = sig;
}
@@ -416,9 +415,6 @@ int main(int argc, char *argv[])
}
atexit(perf_close);
if (!perf_ecc_supported())
ctr_state[ECC_FUNCTIONS] = UNSUPPORTED;
sfd = open_socket(SERVER);
if (sfd < 0) {
eprint("Couldn't initialize server socket\n");
@@ -442,7 +438,7 @@ int main(int argc, char *argv[])
eprint("Running\n");
while (1) {
while (!stopsig) {
enum ctr_e ctr;
enum cmd_e cmd;
int s;
@@ -480,5 +476,13 @@ cleanup:
close(s);
}
if (stopsig == SIGTERM)
eprint("Caught signal SIGTERM, terminating...\n");
else if (stopsig == SIGINT)
eprint("Caught signal SIGINT, terminating...\n");
else
eprint("Caught signal %d, terminating...\n", stopsig);
remove_pidfile();
return 0;
}

View File

@@ -16,6 +16,9 @@
#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>
@@ -23,14 +26,19 @@
#include <sys/ioctl.h>
#include <unistd.h>
#include <libudev.h>
#include "cpacfstats.h"
#include "../include/lib/zt_common.h"
#define NUM_COUNTER ALL_COUNTER
/* 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] = {
} pmf_counter_name[NUM_COUNTER] = {
{"cpum_cf", "DEA_FUNCTIONS", DES_FUNCTIONS},
{"cpum_cf", "AES_FUNCTIONS", AES_FUNCTIONS},
{"cpum_cf", "SHA_FUNCTIONS", SHA_FUNCTIONS},
@@ -38,25 +46,71 @@ static const struct {
{"cpum_cf", "ECC_FUNCTION_COUNT", ECC_FUNCTIONS}
};
/*
* We need one filedescriptor per CPU per counter.
* So perf_init builds this:
*
* ctr_fds - is an array of pointers to file descriptor arrays.
* Each file descriptor array has space for number of logical CPUs + 1
* filedescriptors (int values). The last element of each file descriptor
* array is always 0, assuming there will never appear a filedescriptor
* with value 0:
*
* ctr_fds:
* ctr_fds[0] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
* ctr_fds[1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
* ...
* ctr_fds[ALL_COUNTER-1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
*/
static int *ctr_fds[ALL_COUNTER];
static struct pmf_data {
int pmutype;
int eventid;
} pmf_counter_data[NUM_COUNTER];
static int ecc_supported;
struct percpucounter {
int ctr_fds[NUM_COUNTER];
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 struct percpucounter *allocpercpucounter(unsigned int cpunum)
{
struct percpucounter *ppc;
ppc = malloc(sizeof(struct percpucounter));
if (ppc) {
int i;
for (i = 0; i < NUM_COUNTER; ++i)
ppc->ctr_fds[i] = -1;
ppc->cpunum = cpunum;
ppc->next = NULL;
}
return ppc;
}
static void freepercpucounter(struct percpucounter *pcpu)
{
int i;
for (i = 0; i < NUM_COUNTER; ++i)
(void)close(pcpu->ctr_fds[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 long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
@@ -85,12 +139,10 @@ static int perf_counter_supported(const char *pmu, const char *counter)
return !access(buf, R_OK);
}
static int perf_event_encode(struct perf_event_attr *attr,
static int perf_event_encode(int *pmutype, int *eventid,
const char *pmu, const char *event)
{
FILE *f;
int eventid;
int pmutype;
char buf[PATH_MAX];
if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s",
@@ -104,7 +156,7 @@ static int perf_event_encode(struct perf_event_attr *attr,
errno, strerror(errno));
return -1;
}
if (fscanf(f, "event=0x%x\n", &eventid) != 1) {
if (fscanf(f, "event=0x%x\n", eventid) != 1) {
fclose(f);
eprint("Event file %s has invalid format\n", buf);
return -1;
@@ -121,21 +173,185 @@ static int perf_event_encode(struct perf_event_attr *attr,
errno, strerror(errno));
return -1;
}
if (fscanf(f, "%d\n", &pmutype) != 1) {
if (fscanf(f, "%d\n", pmutype) != 1) {
fclose(f);
eprint("Type file %s has invalid format\n", buf);
return -1;
}
attr->type = pmutype;
attr->config = eventid;
return 0;
}
static int activatecpu(unsigned int cpu)
{
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;
}
for (i = 0; i < NUM_COUNTER; ++i) {
if (ctr_state[i] == UNSUPPORTED)
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;
}
}
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 < NUM_COUNTER; ++i)
(void)close(pcpu->ctr_fds[i]);
free(pcpu);
if (enabledcounter)
hotplugdetected = 1;
}
pthread_mutex_unlock(&rootmux);
}
static int addallcpus(void)
{
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)) {
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 < NUM_COUNTER; ++i) {
if (ctr_state[i] != UNSUPPORTED)
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);
}
return res;
}
static void *hotplughandler(void *UNUSED(unused))
{
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))
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(void)
{
int i, cpus, ctr, cpu, *fds;
memset(ctr_fds, 0, sizeof(ctr_fds));
int ecc_supported;
/* initialize performance monitoring library */
if (!perf_supported()) {
@@ -145,97 +361,44 @@ int perf_init(void)
/* Check if ECC is supported on current hardware */
ecc_supported = perf_counter_supported("cpum_cf", "ECC_FUNCTION_COUNT");
if (!ecc_supported)
ctr_state[ECC_FUNCTIONS] = UNSUPPORTED;
/* get number of logical processors */
cpus = sysconf(_SC_NPROCESSORS_ONLN);
/* for each counter */
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
/* Skip ECC counters completely if unsupported */
if (ctr == ECC_FUNCTIONS && !ecc_supported)
continue;
/*
* allocate an array of ints to store for each CPU
* one filedescriptor + a terminating 0
*/
fds = (int *) calloc(sizeof(int), cpus+1);
if (!fds) {
eprint("Malloc() of %d byte failed, errno=%d [%s]\n",
(int)(sizeof(int) * (cpus+1)),
errno, strerror(errno));
return -1;
}
ctr_fds[ctr] = fds;
/* search for the counter's corresponding pfm name */
for (i = ALL_COUNTER-1; i >= 0; i--)
if ((int) pmf_counter_name[i].ctr == ctr)
break;
if (i < 0) {
eprint("Pfm ctr name not found for counter %d, please adjust pmf_counter_name[] in %s\n",
ctr, __FILE__);
return -1;
}
for (cpu = 0; cpu < cpus; cpu++) {
struct perf_event_attr pfm_event;
int fd;
memset(&pfm_event, 0, sizeof(pfm_event));
pfm_event.size = sizeof(pfm_event);
if (perf_event_encode(&pfm_event,
pmf_counter_name[i].pmu,
pmf_counter_name[i].pfm_name)) {
eprint("Failed to initialize counter %s for pmu %s\n",
pmf_counter_name[i].pfm_name,
pmf_counter_name[i].pmu);
return -1;
}
/* fetch file descriptor for this perf event
* the counter event should start disabled
*/
pfm_event.disabled = 1;
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));
return -1;
}
fds[cpu] = fd;
}
if (perf_load_counter_data())
return -1;
if (pthread_create(&hotplugthread, NULL, hotplughandler, NULL)) {
eprint("Failed to start hotplug handler thread\n");
return -1;
}
return addallcpus();
}
return 0;
void perf_stop(void)
{
stoprequested = 1;
}
void perf_close(void)
{
int ctr, *fds;
struct percpucounter *walk, *next;
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
close(*fds);
*fds = 0;
}
free(ctr_fds[ctr]);
ctr_fds[ctr] = NULL;
pthread_kill(hotplugthread, SIGINT);
pthread_join(hotplugthread, NULL);
walk = root;
while (walk) {
next = walk->next;
freepercpucounter(walk);
walk = next;
}
}
int perf_enable_ctr(enum ctr_e ctr)
{
int *fds, ec, rc = 0;
struct percpucounter *pcpu;
int ec, rc = 0;
if (ctr == ALL_COUNTER) {
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
@@ -243,15 +406,20 @@ int perf_enable_ctr(enum ctr_e ctr)
if (rc != 0)
return rc;
}
} else {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = ioctl(*fds, PERF_EVENT_IOC_ENABLE, 0);
} 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 {
rc = -1;
}
return rc;
@@ -260,7 +428,8 @@ int perf_enable_ctr(enum ctr_e ctr)
int perf_disable_ctr(enum ctr_e ctr)
{
int *fds, ec, rc = 0;
struct percpucounter *pcpu;
int ec, rc = 0;
if (ctr == ALL_COUNTER) {
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
@@ -268,15 +437,22 @@ int perf_disable_ctr(enum ctr_e ctr)
if (rc != 0)
return rc;
}
} else {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = ioctl(*fds, PERF_EVENT_IOC_DISABLE, 0);
} 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 {
rc = -1;
}
return rc;
@@ -285,7 +461,8 @@ int perf_disable_ctr(enum ctr_e ctr)
int perf_reset_ctr(enum ctr_e ctr)
{
int *fds, ec, rc = 0;
struct percpucounter *pcpu;
int ec, rc = 0;
if (ctr == ALL_COUNTER) {
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
@@ -293,15 +470,18 @@ int perf_reset_ctr(enum ctr_e ctr)
if (rc != 0)
return rc;
}
} else {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = ioctl(*fds, PERF_EVENT_IOC_RESET, 0);
} 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 {
rc = -1;
}
return rc;
@@ -310,15 +490,22 @@ int perf_reset_ctr(enum ctr_e ctr)
int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
{
int *fds, ec, rc = -1;
struct percpucounter *pcpu;
int ec, rc = -1;
uint64_t val;
if (!value)
return -1;
if (ctr == HOTPLUG_DETECTED) {
*value = hotplugdetected;
return 0;
}
if (ctr >= ALL_COUNTER)
return -1;
*value = 0;
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = read(*fds, &val, sizeof(val));
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));
@@ -327,11 +514,18 @@ int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
rc = 0;
}
}
endforeachcpu();
return rc;
}
int perf_ecc_supported(void)
{
return ecc_supported;
return ctr_state[ECC_FUNCTIONS] != UNSUPPORTED;
}
int perf_ctr_state(enum ctr_e ctr) {
if (ctr < ALL_COUNTER)
return ctr_state[ctr];
return UNSUPPORTED;
}

View File

@@ -13,6 +13,7 @@
#include <endian.h>
#include <errno.h>
#include <grp.h>
#include <poll.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/socket.h>
@@ -244,7 +245,7 @@ int recv_msg(int sfd, struct msg *m, int timeout)
return -1;
}
n = timeout ? __timedread(sfd, ((char *)m) + sizeof(m->head), len) :
n = timeout ? __timedread(sfd, ((char *)m) + sizeof(m->head), len, timeout) :
__read(sfd, ((char *)m) + sizeof(m->head), len);
if (n != len) {
eprint("Recv() error: recv()=%d expected %d, errno=%d [%s]\n",