This patch adds a new structure, virCgroupItem, to represent
a cgroup directory(named cgroup item). cgroup directory is
created when needed and removed if no one is using it.
---
src/libvirt_private.syms | 2 +
src/util/vircgroup.c | 541 ++++++++++++++++++++++++++++++++++++++++++++++-
src/util/vircgroup.h | 8 +
3 files changed, 545 insertions(+), 6 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c589236..f5138af 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -73,6 +73,8 @@ virCapabilitiesSetMacPrefix;
# cgroup.h
+virCgroup2Free;
+virCgroup2New;
virCgroupAddTask;
virCgroupAddTaskController;
virCgroupAllowDevice;
diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c
index 71d46c5..dbc9688 100644
--- a/src/util/vircgroup.c
+++ b/src/util/vircgroup.c
@@ -37,6 +37,7 @@
#include <libgen.h>
#include <dirent.h>
+#include "virobject.h"
#include "internal.h"
#include "virutil.h"
#include "viralloc.h"
@@ -45,6 +46,7 @@
#include "virfile.h"
#include "virhash.h"
#include "virhashcode.h"
+#include "virthread.h"
#define CGROUP_MAX_VAL 512
@@ -58,6 +60,91 @@ struct virCgroupController {
char *placement;
};
+struct _virCgroupItem;
+typedef struct _virCgroupItem virCgroupItem;
+typedef virCgroupItem *virCgroupItemPtr;
+
+struct _virCgroupItem {
+ virObjectLockable object;
+
+ char *name;
+ char *path;
+
+ bool created; /* the path is created or not */
+
+ virCgroupItemPtr next;
+ virCgroupItemPtr parent;
+ virCgroupItemPtr children;
+
+ int type;
+ struct virCgroupController controllers[VIR_CGROUP_CONTROLLER_LAST];
+};
+
+struct virCgroup2 {
+ virCgroupItemPtr items[VIR_CGROUP_CONTROLLER_LAST];
+};
+
+static virClassPtr cgroupItemClass;
+
+static void cgroupItemDispose(void *obj);
+
+static int virCgroupItemOnceInit(void)
+{
+ if (!(cgroupItemClass = virClassNew(virClassForObjectLockable(),
+ "cgroupItem",
+ sizeof(virCgroupItem),
+ cgroupItemDispose)))
+ return -1;
+
+ return 0;
+}
+VIR_ONCE_GLOBAL_INIT(virCgroupItem);
+
+static virCgroupItemPtr rootCgroupItems[VIR_CGROUP_CONTROLLER_LAST];
+
+static virCgroupItemPtr virCgroupItemRootNew(int type);
+static int virCgroupDetectMounts(struct virCgroupController
(*controllers)[VIR_CGROUP_CONTROLLER_LAST]);
+static int virCgroupDetectPlacement(struct virCgroupController
(*controllers)[VIR_CGROUP_CONTROLLER_LAST]);
+
+static int virCgroupControllersInit(struct virCgroupController
(*controllers)[VIR_CGROUP_CONTROLLER_LAST])
+{
+ int rc;
+ int i;
+
+ rc = virCgroupDetectMounts(controllers);
+ if (rc < 0) {
+ VIR_ERROR(_("Failed to initialize cgroup controllers"));
+ return rc;
+ }
+
+ rc = virCgroupDetectPlacement(controllers);
+
+ if (rc == 0) {
+ /* Check that for every mounted controller, we found our placement */
+ for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
+ if (!(*controllers)[i].mountPoint)
+ continue;
+
+ if (!(*controllers)[i].placement) {
+ VIR_ERROR(_("Could not find placement for controller %s at
%s"),
+ virCgroupControllerTypeToString(i),
+ (*controllers)[i].placement);
+ rc = -ENOENT;
+ break;
+ }
+
+ VIR_DEBUG("Detected mount/mapping %i:%s at %s in %s", i,
+ virCgroupControllerTypeToString(i),
+ (*controllers)[i].mountPoint,
+ (*controllers)[i].placement);
+ }
+ } else {
+ VIR_ERROR(_("Failed to initialize cgroup controllers"));
+ }
+
+ return rc;
+}
+
struct virCgroup {
char *path;
@@ -74,6 +161,362 @@ typedef enum {
* cpuacct and cpuset if possible. */
} virCgroupFlags;
+static virCgroupItemPtr virCgroupItemRootNew(int type)
+{
+ virCgroupItemPtr rootItem = NULL;
+
+ if (type >= VIR_CGROUP_CONTROLLER_LAST || type < 0)
+ return NULL;
+
+ if (virCgroupItemInitialize() < 0)
+ return NULL;
+
+ if (!(rootItem = virObjectNew(cgroupItemClass)))
+ return NULL;
+
+ if (virCgroupControllersInit(&rootItem->controllers) != 0) {
+ virObjectUnref(rootItem);
+ rootItem = NULL;
+ return NULL;
+ }
+
+ rootItem->name = strdup("/");
+ rootItem->next = NULL;
+ rootItem->parent = NULL;
+ rootItem->children = NULL;
+ rootItem->created = 1;
+ rootItem->type = type;
+ if (virAsprintf(&rootItem->path, "%s%s",
+ rootItem->controllers[rootItem->type].mountPoint,
+ rootItem->controllers[rootItem->type].placement) < 0) {
+ virObjectUnref(rootItem);
+ rootItem = NULL;
+ }
+
+ return rootItem;
+}
+
+static virCgroupItemPtr virCgroupHasChildByName(virCgroupItemPtr item,
+ const char *name)
+{
+ virCgroupItemPtr child;
+
+ virObjectLock(item);
+
+ child = item->children;
+
+ while (child) {
+ if (STREQ(child->name, name)) {
+ virObjectRef(child);
+ goto out;
+ }
+
+ child = child->next;
+ }
+
+out:
+ virObjectUnlock(item);
+ return child;
+}
+
+static virCgroupItemPtr virCgroupItemNew(const char *name,
+ virCgroupItemPtr parent)
+{
+ virCgroupItemPtr cgroupItem = NULL;
+ virCgroupItemPtr *next = NULL;
+
+ if (!parent)
+ return NULL;
+
+ if (virCgroupItemInitialize() < 0)
+ return NULL;
+
+ cgroupItem = virCgroupHasChildByName(parent, name);
+ if (cgroupItem)
+ return cgroupItem;
+
+ if (!(cgroupItem = virObjectNew(cgroupItemClass)))
+ return NULL;
+
+ cgroupItem->name = strdup(name);
+ cgroupItem->parent = parent;
+ cgroupItem->next = NULL;
+ cgroupItem->children = NULL;
+ cgroupItem->created = false;
+ cgroupItem->type = parent->type;
+
+ if (virAsprintf(&cgroupItem->path, "%s/%s",
+ parent->path,
+ cgroupItem->name) == -1) {
+ virObjectUnref(cgroupItem);
+ cgroupItem = NULL;
+ return NULL;
+ }
+
+ virObjectRef(parent);
+
+ virObjectLock(parent);
+
+ next = &parent->children;
+
+ while (*next)
+ next = &(*next)->next;
+
+ *next = cgroupItem;
+
+ virObjectUnlock(parent);
+
+ return cgroupItem;
+}
+
+static void virCgroupItemFree(virCgroupItemPtr *cgroupItem)
+{
+ if (!virObjectUnref(*cgroupItem))
+ *cgroupItem = NULL;
+}
+
+static void cgroupItemDispose(void *obj)
+{
+ virCgroupItemPtr cgroupItem = obj;
+ virCgroupItemPtr parent = cgroupItem->parent;
+ virCgroupItemPtr *next;
+
+ sa_assert(cgroupItem->children == NULL);
+
+ if (cgroupItem->created &&
+ access(cgroupItem->path, F_OK) == 0)
+ rmdir(cgroupItem->path);
+ VIR_FREE(cgroupItem->path);
+
+ if (parent) {
+ virObjectLock(parent);
+
+ next = &parent->children;
+
+ while (*next && *next != cgroupItem)
+ next = &(*next)->next;
+
+ if (*next == cgroupItem) {
+ *next = cgroupItem->next;
+ cgroupItem->next = NULL;
+ cgroupItem->parent = NULL;
+ } else {
+ /* BUG */
+ }
+
+ virObjectUnlock(parent);
+ virObjectUnref(parent);
+ }
+
+ VIR_FREE(cgroupItem->name);
+}
+
+static int virCgroupItemKeyPath(virCgroupItemPtr cgroupItem,
+ const char *key,
+ char **path);
+
+static int virCgroupItemGetValueStr(virCgroupItemPtr cgroupItem,
+ const char *key,
+ char **value)
+{
+ int rc;
+ char *keypath = NULL;
+
+ *value = NULL;
+
+ rc = virCgroupItemKeyPath(cgroupItem, key, &keypath);
+ if (rc != 0)
+ return rc;
+
+ VIR_DEBUG("Get value %s", keypath);
+
+ rc = virFileReadAll(keypath, 1024*1024, value);
+ if (rc < 0) {
+ rc = -errno;
+ VIR_DEBUG("Failed to read %s: %m\n", keypath);
+ } else {
+ /* Terminated with '\n' has sometimes harmful effects to the caller */
+ if ((*value)[rc - 1] == '\n')
+ (*value)[rc - 1] = '\0';
+
+ rc = 0;
+ }
+
+ VIR_FREE(keypath);
+
+ return rc;
+}
+
+static int virCgroupItemSetValueStr(virCgroupItemPtr cgroupItem,
+ const char *key,
+ const char *value)
+{
+ int rc = 0;
+ char *keypath = NULL;
+
+ rc = virCgroupItemKeyPath(cgroupItem, key, &keypath);
+ if (rc != 0)
+ return rc;
+
+ VIR_DEBUG("Set value '%s' to '%s'", keypath, value);
+ rc = virFileWriteStr(keypath, value, 0);
+ if (rc < 0) {
+ rc = -errno;
+ VIR_DEBUG("Failed to write value '%s': %m", value);
+ } else {
+ rc = 0;
+ }
+
+ VIR_FREE(keypath);
+ return rc;
+}
+
+static int virCgroupRemoveRecursively(char *grppath);
+static int virCgroupItemCpusetInherit(virCgroupItemPtr cgroupItem);
+static int virCgroupItemSetMemoryUseHierarchy(virCgroupItemPtr item);
+
+static int virCgroupItemMakePath(virCgroupItemPtr cgroupItem)
+{
+ int ret = 0;
+
+ if (!cgroupItem->created && access(cgroupItem->path, F_OK) == 0) {
+ VIR_WARN(_("removing historical cgroup directory: %s"),
+ cgroupItem->path);
+
+ if (virCgroupRemoveRecursively(cgroupItem->path) != 0) {
+ VIR_WARN(_("failed to remove historical cgroup directory: %s"),
+ cgroupItem->path);
+ }
+ }
+
+ cgroupItem->created = true;
+
+ if (access(cgroupItem->path, F_OK) != 0) {
+ if (cgroupItem->parent &&
+ (ret = virCgroupItemMakePath(cgroupItem->parent)) < 0)
+ return ret;
+
+ if (mkdir(cgroupItem->path, 0755) < 0) {
+ return -errno;
+ }
+
+ if (cgroupItem->type == VIR_CGROUP_CONTROLLER_CPUSET)
+ virCgroupItemCpusetInherit(cgroupItem);
+
+ if (cgroupItem->type == VIR_CGROUP_CONTROLLER_MEMORY)
+ virCgroupItemSetMemoryUseHierarchy(cgroupItem);
+ }
+
+ return ret;
+}
+
+static int virCgroupItemPath(virCgroupItemPtr cgroupItem,
+ bool create,
+ char **path)
+{
+ if (path) {
+ if (virAsprintf(path, "%s", cgroupItem->path) == -1)
+ return -ENOMEM;
+ }
+
+ if (create && virCgroupItemMakePath(cgroupItem) < 0) {
+ if (path)
+ VIR_FREE(*path);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static int virCgroupItemKeyPath(virCgroupItemPtr cgroupItem,
+ const char *key,
+ char **path)
+{
+ int ret = 0;
+ char *cgroupItemPath = NULL;
+
+ ret = virCgroupItemPath(cgroupItem, true, &cgroupItemPath);
+ if (ret < 0)
+ return ret;
+
+ if (virAsprintf(path, "%s/%s", cgroupItemPath, key) == -1)
+ ret = -ENOMEM;
+
+ VIR_FREE(cgroupItemPath);
+ return ret;
+}
+
+int virCgroup2New(const char *name, virCgroup2Ptr parent, virCgroup2Ptr *cgroup)
+{
+ virCgroup2Ptr newCgroup = NULL;
+ virCgroupItemPtr *parentCgroupItems;
+ int ret = -1;
+ int i = 0;
+
+ if (VIR_ALLOC(newCgroup) < 0) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (!parent) {
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ if (!rootCgroupItems[i])
+ rootCgroupItems[i] = virCgroupItemRootNew(i);
+ if (!rootCgroupItems[i])
+ goto error;
+ virObjectRef(rootCgroupItems[i]);
+ }
+
+ parentCgroupItems = rootCgroupItems;
+ } else {
+ parentCgroupItems = parent->items;
+ }
+
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ newCgroup->items[i] = virCgroupItemNew(name, parentCgroupItems[i]);
+ if (!newCgroup->items[i])
+ goto error;
+ }
+
+ *cgroup = newCgroup;
+ newCgroup = NULL;
+ ret = 0;
+
+error:
+ if (!parent) {
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ if (rootCgroupItems[i])
+ virCgroupItemFree(&rootCgroupItems[i]);
+ }
+ }
+
+ if (newCgroup) {
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ if (newCgroup->items[i])
+ virCgroupItemFree(&newCgroup->items[i]);
+ }
+ VIR_FREE(newCgroup);
+ }
+
+ return ret;
+}
+
+void virCgroup2Free(virCgroup2Ptr *cgroup)
+{
+ int i;
+
+ if (!cgroup || !*cgroup)
+ return;
+
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ virCgroupItemFree(&(*cgroup)->items[i]);
+ }
+
+ VIR_FREE(*cgroup);
+ *cgroup = NULL;
+}
+
/**
* virCgroupFree:
*
@@ -109,6 +552,7 @@ bool virCgroupMounted(virCgroupPtr cgroup, int controller)
}
#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
+
/*
* Process /proc/mounts figuring out what controllers are
* mounted and where
@@ -134,6 +578,7 @@ static int virCgroupDetectMounts(struct virCgroupController
(*controllers)[VIR_C
const char *typestr = virCgroupControllerTypeToString(i);
int typelen = strlen(typestr);
char *tmp = entry.mnt_opts;
+ (*controllers)[i].type = i;
while (tmp) {
char *next = strchr(tmp, ',');
int len;
@@ -184,24 +629,24 @@ static int virCgroupDetectPlacement(struct virCgroupController
(*cgroupControlle
}
while (fgets(line, sizeof(line), mapping) != NULL) {
- char *controllers = strchr(line, ':');
- char *path = controllers ? strchr(controllers+1, ':') : NULL;
+ char *controllersStr = strchr(line, ':');
+ char *path = controllersStr ? strchr(controllersStr+1, ':') : NULL;
char *nl = path ? strchr(path, '\n') : NULL;
- if (!controllers || !path)
+ if (!controllersStr || !path)
continue;
if (nl)
*nl = '\0';
*path = '\0';
- controllers++;
+ controllersStr++;
path++;
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
const char *typestr = virCgroupControllerTypeToString(i);
int typelen = strlen(typestr);
- char *tmp = controllers;
+ char *tmp = controllersStr;
while (tmp) {
char *next = strchr(tmp, ',');
int len;
@@ -230,6 +675,7 @@ no_memory:
}
+
static int virCgroupDetect(virCgroupPtr group)
{
int any = 0;
@@ -315,6 +761,23 @@ int virCgroupPathOfController(virCgroupPtr group,
return 0;
}
+static int virCgroup2SetValueStr(virCgroup2Ptr group,
+ int controller,
+ const char *key,
+ const char *value)
+{
+ return virCgroupItemSetValueStr(group->items[controller],
+ key, value);
+}
+
+static int virCgroup2GetValueStr(virCgroup2Ptr group,
+ int controller,
+ const char *key,
+ char **value)
+{
+ return virCgroupItemGetValueStr(group->items[controller],
+ key, value);
+}
static int virCgroupSetValueStr(virCgroupPtr group,
int controller,
@@ -455,7 +918,6 @@ out:
return rc;
}
-
#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
static int virCgroupCpuSetInherit(virCgroupPtr parent, virCgroupPtr group)
{
@@ -496,6 +958,41 @@ static int virCgroupCpuSetInherit(virCgroupPtr parent, virCgroupPtr
group)
return rc;
}
+static int virCgroupItemCpusetInherit(virCgroupItemPtr cgroupItem)
+{
+ int i;
+ char *value;
+ int rc = 0;
+ const char *inherit_values[] = {
+ "cpuset.cpus",
+ "cpuset.mems",
+ };
+
+ for (i = 0; i < ARRAY_CARDINALITY(inherit_values) ; i++) {
+ rc = virCgroupItemGetValueStr(cgroupItem->parent,
+ inherit_values[i],
+ &value);
+ if (rc != 0) {
+ VIR_ERROR(_("Failed to get %s %d"), inherit_values[i], rc);
+ break;
+ }
+
+ VIR_DEBUG("Inherit %s = %s", inherit_values[i], value);
+
+ rc = virCgroupItemSetValueStr(cgroupItem,
+ inherit_values[i],
+ value);
+ VIR_FREE(value);
+
+ if (rc != 0) {
+ VIR_ERROR(_("Failed to set %s %d"), inherit_values[i], rc);
+ break;
+ }
+ }
+
+ return rc;
+}
+
static int virCgroupSetMemoryUseHierarchy(virCgroupPtr group)
{
int rc = 0;
@@ -526,6 +1023,38 @@ static int virCgroupSetMemoryUseHierarchy(virCgroupPtr group)
return rc;
}
+static int virCgroupItemSetMemoryUseHierarchy(virCgroupItemPtr item)
+{
+ int rc = 0;
+ char *value = NULL;
+ const char *filename = "memory.use_hierarchy";
+
+ rc = virCgroupItemGetValueStr(item,
+ filename, &value);
+ if (rc != 0) {
+ VIR_ERROR(_("Failed to read %s/%s (%d)"), item->path, filename,
rc);
+ return rc;
+ }
+
+ /* Setting twice causes error, so if already enabled, skip setting */
+ if (STREQ(value, "1")) {
+ rc = 0;
+ goto out;
+ }
+
+ VIR_DEBUG("Setting up %s/%s", item->path, filename);
+ rc = virCgroupItemSetValueStr(item,
+ filename, "1");
+
+ if (rc != 0) {
+ VIR_ERROR(_("Failed to set %s/%s (%d)"), item->path, filename, rc);
+ }
+
+out:
+ VIR_FREE(value);
+ return rc;
+}
+
static int virCgroupMakeGroup(virCgroupPtr parent,
virCgroupPtr group,
bool create,
diff --git a/src/util/vircgroup.h b/src/util/vircgroup.h
index 2ed6ff9..f93c185 100644
--- a/src/util/vircgroup.h
+++ b/src/util/vircgroup.h
@@ -30,6 +30,10 @@
struct virCgroup;
typedef struct virCgroup *virCgroupPtr;
+struct virCgroup2;
+typedef struct virCgroup2 virCgroup2;
+typedef virCgroup2 *virCgroup2Ptr;
+
enum {
VIR_CGROUP_CONTROLLER_CPU,
VIR_CGROUP_CONTROLLER_CPUACCT,
@@ -166,4 +170,8 @@ int virCgroupKill(virCgroupPtr group, int signum);
int virCgroupKillRecursive(virCgroupPtr group, int signum);
int virCgroupKillPainfully(virCgroupPtr group);
+
+int virCgroup2New(const char *name, virCgroup2Ptr parent, virCgroup2Ptr *cgroup);
+void virCgroup2Free(virCgroup2Ptr *cgroup);
+
#endif /* __VIR_CGROUP_H__ */
--
1.8.0.1.240.ge8a1f5a