This patch implement a set of interfaces for perf event. Based on these interfaces, we can implement internal driver API for perf, and get the results of perf conuter you care about. Signed-off-by: Qiaowei Ren <qiaowei.ren@xxxxxxxxx> --- include/libvirt/virterror.h | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 12 ++ src/util/virerror.c | 1 + src/util/virperf.c | 298 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virperf.h | 61 +++++++++ 6 files changed, 374 insertions(+) create mode 100644 src/util/virperf.c create mode 100644 src/util/virperf.h diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 0539e48..7e87162 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -128,6 +128,7 @@ typedef enum { VIR_FROM_THREAD = 61, /* Error from thread utils */ VIR_FROM_ADMIN = 62, /* Error from admin backend */ VIR_FROM_LOGGING = 63, /* Error from log manager */ + VIR_FROM_PERF = 64, /* Error from perf */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/src/Makefile.am b/src/Makefile.am index 7219f7c..26ac935 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -98,6 +98,7 @@ UTIL_SOURCES = \ util/virauthconfig.c util/virauthconfig.h \ util/virbitmap.c util/virbitmap.h \ util/virbuffer.c util/virbuffer.h \ + util/virperf.c util/virperf.h \ util/vircgroup.c util/vircgroup.h util/vircgrouppriv.h \ util/virclosecallbacks.c util/virclosecallbacks.h \ util/vircommand.c util/vircommand.h util/vircommandpriv.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index dd085c3..5e64fa6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2001,6 +2001,18 @@ virPCIGetVirtualFunctions; virPCIIsVirtualFunction; +# util/virperf.h +virPerfNew; +virPerfFree; +virPerfEventTypeFromString; +virPerfEventTypeToString; +virPerfEventEnable; +virPerfEventDisable; +virPerfEventIsEnabled; +virPerfGetEventFd; +virPerfGetCmtScale; + + # util/virpidfile.h virPidFileAcquire; virPidFileAcquirePath; diff --git a/src/util/virerror.c b/src/util/virerror.c index 098211a..7e6918d 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -135,6 +135,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Thread jobs", "Admin Interface", "Log Manager", + "Perf", ) diff --git a/src/util/virperf.c b/src/util/virperf.c new file mode 100644 index 0000000..9c40307 --- /dev/null +++ b/src/util/virperf.c @@ -0,0 +1,298 @@ +/* + * virperf.c: methods for managing perf events + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Ren Qiaowei <qiaowei.ren@xxxxxxxxx> + */ +#include <config.h> + +#include <sys/ioctl.h> +#if defined HAVE_SYS_SYSCALL_H +# include <sys/syscall.h> +#endif + +#include "virperf.h" +#include "viralloc.h" +#include "virerror.h" +#include "virlog.h" +#include "virfile.h" +#include "virstring.h" +#include "virtypedparam.h" + +VIR_LOG_INIT("util.perf"); + +#define VIR_FROM_THIS VIR_FROM_PERF + +VIR_ENUM_IMPL(virPerfEvent, VIR_PERF_EVENT_LAST, + "cmt"); + +struct virPerfEvent { + int type; + int fd; + bool enabled; + union { + /* cmt */ + struct { + int scale; + } cmt; + } efields; +}; +typedef struct virPerfEvent *virPerfEventPtr; + +struct virPerf { + struct virPerfEvent events[VIR_PERF_EVENT_LAST]; +}; + +virPerfPtr +virPerfNew(void) +{ + size_t i; + virPerfPtr perf; + + if (VIR_ALLOC(perf) < 0) + return NULL; + + for (i = 0; i < VIR_PERF_EVENT_LAST; i++) { + perf->events[i].type = i; + perf->events[i].fd = -1; + perf->events[i].enabled = false; + } + + return perf; +} + +void +virPerfFree(virPerfPtr perf) +{ + size_t i; + + if (perf == NULL) + return; + + for (i = 0; i < VIR_PERF_EVENT_LAST; i++) { + if (perf->events[i].enabled) + virPerfEventDisable(perf, i); + } + + VIR_FREE(perf); +} + +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) + +# include <linux/perf_event.h> + +static virPerfEventPtr +virPerfGetEvent(virPerfPtr perf, + virPerfEventType type) +{ + if (type >= VIR_PERF_EVENT_LAST) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Event '%d' is not supported"), + type); + return NULL; + } + + return perf->events + type; +} + +static int +virPerfCmtEnable(virPerfEventPtr event, + pid_t pid) +{ + struct perf_event_attr cmt_attr; + int event_type; + FILE *fp = NULL; + FILE *fp_scale = NULL; + int scale = 0; + + fp = fopen("/sys/devices/intel_cqm/type", "r"); + if (!fp) { + virReportSystemError(errno, "%s", + _("CMT is not available on this host")); + return -1; + } + if (fscanf(fp, "%d", &event_type) != 1) { + virReportSystemError(errno, "%s", + _("Unable to read event type file.")); + goto cleanup; + } + + fp_scale = fopen("/sys/devices/intel_cqm/events/llc_occupancy.scale", "r"); + if (!fp_scale) { + virReportSystemError(errno, "%s", + _("Unable to open CMT scale file")); + goto cleanup; + } + if (fscanf(fp_scale, "%d", &scale) != 1) { + virReportSystemError(errno, "%s", + _("Unable to read CMT scale file")); + goto cleanup; + } + event->efields.cmt.scale = scale; + + memset(&cmt_attr, 0, sizeof(cmt_attr)); + cmt_attr.size = sizeof(cmt_attr); + cmt_attr.type = event_type; + cmt_attr.config = 1; + cmt_attr.inherit = 1; + cmt_attr.disabled = 1; + cmt_attr.enable_on_exec = 0; + + event->fd = syscall(__NR_perf_event_open, &cmt_attr, pid, -1, -1, 0); + if (event->fd < 0) { + virReportSystemError(errno, + _("Unable to open perf type=%d for pid=%d"), + event_type, pid); + goto cleanup; + } + + if (ioctl(event->fd, PERF_EVENT_IOC_ENABLE) < 0) { + virReportSystemError(errno, "%s", + _("Unable to enable perf event for CMT")); + goto cleanup; + } + + event->enabled = true; + return 0; + + cleanup: + if (fp) + VIR_FORCE_FCLOSE(fp); + if (fp_scale) + VIR_FORCE_FCLOSE(fp_scale); + if (event->fd >= 0) + VIR_FORCE_CLOSE(event->fd); + return -1; +} + +int +virPerfEventEnable(virPerfPtr perf, + virPerfEventType type, + pid_t pid) +{ + virPerfEventPtr event = virPerfGetEvent(perf, type); + if (event == NULL) + return -1; + + switch (type) { + case VIR_PERF_EVENT_CMT: + if (virPerfCmtEnable(event, pid)) + return -1; + break; + case VIR_PERF_EVENT_LAST: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected perf event type=%d"), type); + return -1; + } + + return 0; +} + +int +virPerfEventDisable(virPerfPtr perf, + virPerfEventType type) +{ + virPerfEventPtr event = virPerfGetEvent(perf, type); + if (event == NULL) + return -1; + + if (ioctl(event->fd, PERF_EVENT_IOC_DISABLE) < 0) { + virReportSystemError(errno, + _("Unable to disable perf event type=%d"), + event->type); + return -1; + } + + event->enabled = false; + VIR_FORCE_CLOSE(event->fd); + return 0; +} + +bool virPerfEventIsEnabled(virPerfPtr perf, + virPerfEventType type) +{ + virPerfEventPtr event = virPerfGetEvent(perf, type); + if (event == NULL) + return false; + + return event->enabled; +} + +int virPerfGetEventFd(virPerfPtr perf, + virPerfEventType type) +{ + virPerfEventPtr event = virPerfGetEvent(perf, type); + if (event == NULL) + return false; + + return event->fd; +} + +int +virPerfGetCmtScale(virPerfPtr perf) +{ + virPerfEventPtr event = virPerfGetEvent(perf, VIR_PERF_EVENT_CMT); + if (event == NULL || !event->enabled) + return -1; + + return event->efields.cmt.scale; +} + +#else +int +virPerfEventEnable(virPerfPtr perf ATTRIBUTE_UNUSED, + pid_t pid ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENXIO, "%s", + _("Perf not supported on this platform")); + return -1; +} + +int +virPerfEventDisable(virPerfPtr perf ATTRIBUTE_UNUSED, + int event ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENXIO, "%s", + _("Perf not supported on this platform")); + return -1; +} + +bool +virPerfEventIsEnabled(virPerfPtr perf, + virPerfEventType type) +{ + return false; +} + +int +virPerfGetEventFd(virPerfPtr perf, + virPerfEventType type) +{ + virReportSystemError(ENXIO, "%s", + _("Perf not supported on this platform")); + return -1; +} + +int +virPerfGetCmtScale(virPerfPtr perf) +{ + virReportSystemError(ENXIO, "%s", + _("Perf not supported on this platform")); + return -1; +} + +#endif diff --git a/src/util/virperf.h b/src/util/virperf.h new file mode 100644 index 0000000..ad60464 --- /dev/null +++ b/src/util/virperf.h @@ -0,0 +1,61 @@ +/* + * virperf.h: methods for managing perf events + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Ren Qiaowei <qiaowei.ren@xxxxxxxxx> + */ + +#ifndef __VIR_PERF_H__ +# define __VIR_PERF_H__ + +# include "virutil.h" + +typedef enum { + VIR_PERF_EVENT_CMT, + + VIR_PERF_EVENT_LAST +} virPerfEventType; + +VIR_ENUM_DECL(virPerfEvent); + +# define VIR_PERF_PARAMETERS \ + VIR_PERF_PARAM_CMT, VIR_TYPED_PARAM_BOOLEAN, \ + NULL + +struct virPerf; +typedef struct virPerf *virPerfPtr; + +virPerfPtr virPerfNew(void); + +void virPerfFree(virPerfPtr perf); + +int virPerfEventEnable(virPerfPtr perf, + virPerfEventType type, + pid_t pid); + +int virPerfEventDisable(virPerfPtr perf, + virPerfEventType type); + +bool virPerfEventIsEnabled(virPerfPtr perf, + virPerfEventType type); + +int virPerfGetEventFd(virPerfPtr perf, + virPerfEventType type); + +int virPerfGetCmtScale(virPerfPtr perf); + +#endif /* __VIR_PERF_H__ */ -- 1.9.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list