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(a)intel.com>
---
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(a)intel.com>
+ */
+#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(a)intel.com>
+ */
+
+#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