diff --git a/README.md b/README.md index 43d7d52e..acf934f4 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/cpacfstats/Makefile b/cpacfstats/Makefile index 944e5c3d..aa92e0c8 100644 --- a/cpacfstats/Makefile +++ b/cpacfstats/Makefile @@ -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 diff --git a/cpacfstats/cpacfstats.c b/cpacfstats/cpacfstats.c index 98fd22e8..c063c67d 100644 --- a/cpacfstats/cpacfstats.c +++ b/cpacfstats/cpacfstats.c @@ -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); diff --git a/cpacfstats/cpacfstats.h b/cpacfstats/cpacfstats.h index a17903aa..c39bc274 100644 --- a/cpacfstats/cpacfstats.h +++ b/cpacfstats/cpacfstats.h @@ -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 diff --git a/cpacfstats/cpacfstatsd.c b/cpacfstats/cpacfstatsd.c index 87c1763d..43738d60 100644 --- a/cpacfstats/cpacfstatsd.c +++ b/cpacfstats/cpacfstatsd.c @@ -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; } diff --git a/cpacfstats/perf_crypto.c b/cpacfstats/perf_crypto.c index a1e2ddca..be14dfc9 100644 --- a/cpacfstats/perf_crypto.c +++ b/cpacfstats/perf_crypto.c @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -23,14 +26,19 @@ #include #include +#include + #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; } diff --git a/cpacfstats/stats_sock.c b/cpacfstats/stats_sock.c index f3ad5a92..2c1823ca 100644 --- a/cpacfstats/stats_sock.c +++ b/cpacfstats/stats_sock.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -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",