This patch adds a callback for cpu hotplug event.
The cpu hotplug netlink message is of the following format:
{online|offline}@/devices/system/cpu/cpuxx (xx is cpuid)
When a cpu online message is received, the callback will
get the new added cpuid from the message, and adds it to
the cpuset.cpus of a specific cgroup, such as libvirtd,
qemu driver, or lxc driver's cpuset cgroup.
When a cpu offline message is received, nothing to for now.
Signed-off-by: Tang Chen <tangchen(a)cn.fujitsu.com>
---
include/libvirt/virterror.h | 2 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 5 +
src/util/cgroup.c | 6 +-
src/util/cgroup.h | 4 +
src/util/hotplug.c | 221 +++++++++++++++++++++++++++++++++++++++++++
src/util/hotplug.h | 32 +++++++
src/util/virterror.c | 3 +-
8 files changed, 270 insertions(+), 4 deletions(-)
create mode 100644 src/util/hotplug.c
create mode 100644 src/util/hotplug.h
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 69c64aa..5e10338 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -114,6 +114,8 @@ typedef enum {
VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */
+ VIR_FROM_HOTPLUG = 51, /* Error from Hotplug driver */
+
# ifdef VIR_ENUM_SENTINELS
VIR_ERR_DOMAIN_LAST
# endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 95e1bea..c65ee37 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,6 +60,7 @@ UTIL_SOURCES = \
util/event.c util/event.h \
util/event_poll.c util/event_poll.h \
util/hooks.c util/hooks.h \
+ util/hotplug.c util/hotplug.h \
util/iptables.c util/iptables.h \
util/ebtables.c util/ebtables.h \
util/dnsmasq.c util/dnsmasq.h \
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 27eb43e..97f9c7b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -64,6 +64,7 @@ virCgroupAddTaskController;
virCgroupAllowDevice;
virCgroupAllowDeviceMajor;
virCgroupAllowDevicePath;
+virCgroupAppRoot;
virCgroupControllerTypeFromString;
virCgroupControllerTypeToString;
virCgroupDenyAllDevices;
@@ -643,6 +644,10 @@ virHookInitialize;
virHookPresent;
+# hotplug.h
+virCpuHotplugRegisterCallback;
+
+
# interface_conf.h
virInterfaceAssignDef;
virInterfaceDefFormat;
diff --git a/src/util/cgroup.c b/src/util/cgroup.c
index 8541c7f..df5f31a 100644
--- a/src/util/cgroup.c
+++ b/src/util/cgroup.c
@@ -641,9 +641,9 @@ err:
return rc;
}
-static int virCgroupAppRoot(int privileged,
- virCgroupPtr *group,
- int create)
+int virCgroupAppRoot(int privileged,
+ virCgroupPtr *group,
+ int create)
{
virCgroupPtr rootgrp = NULL;
int rc;
diff --git a/src/util/cgroup.h b/src/util/cgroup.h
index 68ac232..ef9b022 100644
--- a/src/util/cgroup.h
+++ b/src/util/cgroup.h
@@ -44,6 +44,10 @@ enum {
VIR_ENUM_DECL(virCgroupController);
+int virCgroupAppRoot(int privileged,
+ virCgroupPtr *group,
+ int create);
+
int virCgroupForDriver(const char *name,
virCgroupPtr *group,
int privileged,
diff --git a/src/util/hotplug.c b/src/util/hotplug.c
new file mode 100644
index 0000000..d5ffd67
--- /dev/null
+++ b/src/util/hotplug.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012 FUJITSU, Inc.
+ *
+ * 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:
+ * Tang Chen <tangchen(a)cn.fujitsu.com>
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hotplug.h"
+#include "cgroup.h"
+#include "virterror_internal.h"
+#include "virnetlink.h"
+#include "logging.h"
+#include "memory.h"
+
+#define VIR_FROM_THIS VIR_FROM_HOTPLUG
+
+#ifdef __linux__
+
+/**
+ * CPU hotplug message is of the following format:
+ * {online|offline}@/devices/system/cpu/cpuxx (xx is cpuid)
+ */
+# define CPU_ONLINE_MSG "online@/devices/system/cpu/cpu"
+# define CPU_OFFLINE_MSG "offline@/devices/system/cpu/cpu"
+
+/**
+ * getCpuidFromNetlinkMsg:
+ *
+ * @msg: The buffer containing the received netlink message
+ * @cpuid: Contains the cpuid in the message
+ *
+ * This function get the cpuid from the following netlink message,
+ * {online|offline}@/devices/system/cpu/cpuxx (xx is cpuid)
+ *
+ * Returns 0 on success, or -1 on error.
+ */
+static int getCpuidFromNetlinkMsg(unsigned char *msg,
+ char **cpuid)
+{
+ char *p = NULL;
+ size_t len_online = strlen(CPU_ONLINE_MSG);
+ size_t len_offline = strlen(CPU_OFFLINE_MSG);
+
+ if (VIR_ALLOC_N(*cpuid, 64) < 0)
+ goto memory_error;
+
+ /**
+ * For now, we aren't sure if the message is a '/0' ended string.
+ * So we only test the first len_online or len_offline characters.
+ */
+ if (strncmp((const char *)msg, CPU_ONLINE_MSG, len_online) == 0 ||
+ strncmp((const char *)msg, CPU_OFFLINE_MSG, len_offline) == 0) {
+ p = strrchr((const char *)msg, '/');
+ p = p + 4;
+ strcpy(*cpuid, p);
+ return 0;
+ } else {
+ VIR_DEBUG("Event is not a cpu hotplug event.");
+ VIR_FREE(*cpuid);
+ return -1;
+ }
+
+memory_error:
+ virReportOOMError();
+ return -1;
+}
+
+/**
+ * virCpuHotplugCallback:
+ *
+ * @msg: The buffer containing the received netlink message
+ * @length: The length of the received netlink message
+ * @peer: The netling sockaddr containing the peer information
+ * @handled: Contains information if the message has been replied to yet
+ * @opaque: Contains a cgroup identifier to be modified
+ *
+ * Cpu hotplug netlink event handler. It is called when libvirtd
+ * receives netlink message from kernel, and modifies cpuset.cpus
+ * of specified cgroup.
+ */
+static void
+virCpuHotplugCallback(unsigned char *msg,
+ int length,
+ struct sockaddr_nl *peer,
+ bool *handled,
+ void *opaque)
+{
+ char *cpuid = NULL;
+ char *cpus = NULL;
+ virCgroupPtr cgroup = opaque;
+
+ if (!cgroup)
+ return;
+
+ if (VIR_ALLOC_N(cpus, 1024) < 0) {
+ virReportOOMError();
+ return;
+ }
+
+ if (getCpuidFromNetlinkMsg(msg, &cpuid) < 0) {
+ goto error_2;
+ }
+
+ /**
+ * If getCpuidFromNetlinkMsg() succeeds, we are sure the
+ * netlink message is a '/0' ended string.
+ */
+ VIR_DEBUG("netlink (cpu hotplug): %s", msg);
+
+ if (msg == strstr((const char *)msg, "online")) {
+ VIR_DEBUG("CPU %s online message received.", cpuid);
+
+ if (virCgroupGetCpusetCpus(cgroup, &cpus) < 0) {
+ virReportSystemError(errno,
+ "%s",
+ _("Unable to get cpuset.cpus"));
+ goto error_1;
+ }
+
+ if (virAsprintf(&cpus, "%s,%s", cpus, cpuid) < 0) {
+ virReportOOMError();
+ goto error_1;
+ }
+
+ if (virCgroupSetCpusetCpus(cgroup, cpus) < 0) {
+ virReportSystemError(errno,
+ "%s",
+ _("Unable to set cpuset.cpus"));
+ goto error_1;
+ }
+
+ } else if (msg == strstr((const char *)msg, "offline")) {
+ VIR_DEBUG("CPU %s offline message received.", cpuid);
+ }
+
+error_1:
+ VIR_FREE(cpuid);
+
+error_2:
+ VIR_FREE(cpus);
+
+ return;
+}
+
+/**
+ * virCpuHotplugDestroyCallback:
+ *
+ * @watch: watch whose handle to remove
+ * @macaddr: macaddr whose handle to remove
+ * @opaque: Contains user data to pass to the callback
+ *
+ * This function is called when a netlink message handler is terminated.
+ * For now, nothing to do.
+ */
+static void
+virCpuHotplugDestroyCallback(int watch ATTRIBUTE_UNUSED,
+ const virMacAddrPtr macaddr ATTRIBUTE_UNUSED,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ VIR_DEBUG("CPU hotplug netlink handler has been removed.");
+}
+
+/**
+ * virCpuHotplugRegisterCallback:
+ *
+ * @opaque: Contains user data to pass to the callback, which is a
+ * cgroup identifier to be modified
+ *
+ * Register a callback for cpu hotplug event.
+ *
+ * Returns -1 if the file handle cannot be registered, number of
+ * monitor upon success.
+ */
+int
+virCpuHotplugRegisterCallback(void *opaque)
+{
+ int ret = 0;
+ virCgroupPtr cgroup = (virCgroupPtr)opaque;
+
+ if (virNetlinkEventServiceIsRunning(NETLINK_KOBJECT_UEVENT))
+ ret = virNetlinkEventAddClient(virCpuHotplugCallback,
+ virCpuHotplugDestroyCallback,
+ cgroup, NULL, NETLINK_KOBJECT_UEVENT);
+
+ return ret;
+}
+
+#else
+
+static const char *unsupported = N_("Not a linux system.");
+
+/**
+ * virCpuHotplugRegisterCallback: Register a callback for cpu hotplug event.
+ */
+int
+virCpuHotplugRegisterCallback(void *opaque ATTRIBUTE_UNUSED)
+{
+ VIR_DEBUG("%s", _(unsupported));
+ return 0;
+}
+
+#endif /* __linux__ */
diff --git a/src/util/hotplug.h b/src/util/hotplug.h
new file mode 100644
index 0000000..8f2cdf3
--- /dev/null
+++ b/src/util/hotplug.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 FUJITSU, Inc.
+ *
+ * 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:
+ * Tang Chen <tangchen(a)cn.fujitsu.com>
+ */
+
+#ifndef __HOTPLUG_H_
+# define __HOTPLUG_H_
+
+# include "internal.h"
+
+/**
+ * virCpuHotplugRegisterCallback: Register a callback for cpu hotplug event.
+ */
+int virCpuHotplugRegisterCallback(void *opaque);
+
+#endif
diff --git a/src/util/virterror.c b/src/util/virterror.c
index 3ee2ae0..1ae26f9 100644
--- a/src/util/virterror.c
+++ b/src/util/virterror.c
@@ -115,7 +115,8 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Parallels Cloud Server",
"Device Config",
- "SSH transport layer" /* 50 */
+ "SSH transport layer", /* 50 */
+ "Hotplug driver" /* 51 */
)
--
1.7.10.1