This patch adds some helpers to support cpu hotplug in libvirt.
virHotplugGetCpuidFromMsg():
Get cpuid from cpu hotplug netlink message. The message is of the following
format:
{online|offline}@/devices/system/cpu/cpuxx (xx is cpuid)
virHotplugUpdateCgroupCpuset():
Update the cpuset.cpus in the specified cgroup when a cpu is hotpluged.
virHotplugSetCpusetToLastest():
Copy the cpuset from root to the specified cgroup.
This function could be used when there are cpu houplug events occurred
when libvirtd is not running.
Signed-off-by: Tang Chen <tangchen(a)cn.fujitsu.com>
---
include/libvirt/virterror.h | 2 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 6 ++
src/util/cgroup.c | 4 +-
src/util/cgroup.h | 3 +
src/util/hotplug.c | 240 +++++++++++++++++++++++++++++++++++++++++++
src/util/hotplug.h | 44 ++++++++
src/util/virterror.c | 3 +-
8 files changed, 300 insertions(+), 3 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 5140c38..aabda04 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 6f14763..2ff7e0e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -94,6 +94,7 @@ virCgroupKillPainfully;
virCgroupKillRecursive;
virCgroupMounted;
virCgroupMoveTask;
+virCgroupNew;
virCgroupPathOfController;
virCgroupRemove;
virCgroupSetBlkioDeviceWeight;
@@ -643,6 +644,11 @@ virHookInitialize;
virHookPresent;
+# hotplug.h
+virHotplugUpdateCgroupCpuset;
+virHotplugSetCpusetToLastest;
+
+
# interface_conf.h
virInterfaceAssignDef;
virInterfaceDefFormat;
diff --git a/src/util/cgroup.c b/src/util/cgroup.c
index 5dc0764..393d439 100644
--- a/src/util/cgroup.c
+++ b/src/util/cgroup.c
@@ -608,8 +608,8 @@ static int virCgroupMakeGroup(virCgroupPtr parent, virCgroupPtr
group,
}
-static int virCgroupNew(const char *path,
- virCgroupPtr *group)
+int virCgroupNew(const char *path,
+ virCgroupPtr *group)
{
int rc = 0;
char *typpath = NULL;
diff --git a/src/util/cgroup.h b/src/util/cgroup.h
index 68ac232..e294495 100644
--- a/src/util/cgroup.h
+++ b/src/util/cgroup.h
@@ -68,6 +68,9 @@ int virCgroupPathOfController(virCgroupPtr group,
const char *key,
char **path);
+int virCgroupNew(const char *path,
+ virCgroupPtr *group);
+
int virCgroupAddTask(virCgroupPtr group, pid_t pid);
int virCgroupAddTaskController(virCgroupPtr group,
diff --git a/src/util/hotplug.c b/src/util/hotplug.c
new file mode 100644
index 0000000..7c44299
--- /dev/null
+++ b/src/util/hotplug.c
@@ -0,0 +1,240 @@
+/*
+ * 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 "buf.h"
+#include "virterror_internal.h"
+#include "virnetlink.h"
+#include "logging.h"
+#include "memory.h"
+#include "util.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"
+
+/**
+ * virHotplugGetCpuidFromMsg:
+ *
+ * @msg: The buffer containing the received netlink message
+ * @length: The length of 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 virHotplugGetCpuidFromMsg(unsigned char *msg,
+ int length,
+ char **cpuid)
+{
+ char *p = NULL;
+ size_t len_online = MIN(length, strlen(CPU_ONLINE_MSG));
+ size_t len_offline = MIN(length, 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 0;
+ }
+
+memory_error:
+ virReportOOMError();
+ return -1;
+}
+
+/**
+ * virHotplugUpdateCgroupCpuset:
+ *
+ * @msg: The buffer containing the received netlink message
+ * @length: The length of the received netlink message
+ * @cgroup: 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.
+ *
+ * Return 0 on success.
+ */
+int
+virHotplugUpdateCgroupCpuset(unsigned char *msg,
+ int length,
+ virCgroupPtr cgroup)
+{
+ int rc = 0;
+ char *cpuid = NULL;
+ char *cpus = NULL;
+
+ if (!cgroup) {
+ virReportSystemError(EINVAL,
+ "%s",
+ _("invalid argument"));
+ return -EINVAL;
+ }
+
+ if (VIR_ALLOC_N(cpus, 1024) < 0) {
+ virReportOOMError();
+ return -ENOMEM;
+ }
+
+ rc = virHotplugGetCpuidFromMsg(msg, length, &cpuid);
+ if (rc < 0)
+ goto error;
+
+ /**
+ * If virHotplugGetCpuidFromMsg() 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"));
+ rc = -1;
+ goto error;
+ }
+
+ if (virAsprintf(&cpus, "%s,%s", cpus, cpuid) < 0) {
+ virReportOOMError();
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ if (virCgroupSetCpusetCpus(cgroup, cpus) < 0) {
+ virReportSystemError(errno,
+ "%s",
+ _("Unable to set cpuset.cpus"));
+ rc = -1;
+ goto error;
+ }
+
+ } else if (msg == strstr((const char *)msg, "offline")) {
+ /* For now, nothing to do. */
+ VIR_DEBUG("CPU %s offline message received.", cpuid);
+ }
+
+error:
+ VIR_FREE(cpuid);
+ VIR_FREE(cpus);
+
+ return rc;
+}
+
+/**
+ * virHotplugSetCpusetToLastest:
+ *
+ * @cgroup: The cgroup to be reset
+ *
+ * This function copies the cpuset from root to the specified cgroup.
+ *
+ * Return 0 on success or errno on error.
+ */
+int
+virHotplugSetCpusetToLastest(virCgroupPtr cgroup)
+{
+ int rc = 0;
+ char *cpus = NULL;
+ virCgroupPtr mpgrp = NULL;
+
+ /* mpgrp here is just the cpuset controller's mount point,
+ * which always contains all the online cpus.
+ */
+ rc = virCgroupNew("/", &mpgrp);
+ if (rc < 0)
+ goto out;
+
+ rc = virCgroupGetCpusetCpus(mpgrp, &cpus);
+ if (rc < 0)
+ goto error;
+
+ rc = virCgroupSetCpusetCpus(cgroup, cpus);
+ if (rc < 0)
+ goto error;
+
+error:
+ virCgroupFree(&mpgrp);
+ VIR_FREE(cpus);
+
+out:
+ return rc;
+}
+
+#else
+
+static const char *unsupported = N_("Not a linux system.");
+
+/**
+ * virHotplugUpdateCgroupCpuset: This function is called when libvirtd
+ * receives netlink message from kernel, and modifies cpuset.cpus of
+ * specified cgroup.
+ */
+int
+virHotplugUpdateCgroupCpuset(unsigned char *msg ATTRIBUTE_UNUSED,
+ int length ATTRIBUTE_UNUSED,
+ virCgroupPtr cgroup ATTRIBUTE_UNUSED)
+{
+ VIR_DEBUG("%s", _(unsupported));
+ return 0;
+}
+
+/**
+ * virHotplugSetCpusetToLastest: This function copies the cpuset from
+ * root to the specified cgroup.
+ */
+int
+virHotplugSetCpusetToLastest(virCgroupPtr cgroup 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..ef3e279
--- /dev/null
+++ b/src/util/hotplug.h
@@ -0,0 +1,44 @@
+/*
+ * 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"
+# include "cgroup.h"
+
+/**
+ * virHotplugUpdateCgroupCpuset: This function is called when libvirtd
+ * receives netlink message from kernel, and modifies cpuset.cpus of
+ * specified cgroup.
+ */
+int
+virHotplugUpdateCgroupCpuset(unsigned char *msg, int length,
+ virCgroupPtr opaque);
+
+/**
+ * virHotplugSetCpusetToLastest: This function copies the cpuset from
+ * root to the specified cgroup.
+ */
+int
+virHotplugSetCpusetToLastest(virCgroupPtr cgroup);
+
+#endif
diff --git a/src/util/virterror.c b/src/util/virterror.c
index 7caa69e..eff52ad 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