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 | 7 +
src/util/vircgroup.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++-
src/util/vircgroup.h | 12 ++
3 files changed, 423 insertions(+), 7 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 7be58ee..636c49d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -104,6 +104,12 @@ virCgroupGetMemorySoftLimit;
virCgroupGetMemoryUsage;
virCgroupGetMemSwapHardLimit;
virCgroupGetMemSwapUsage;
+virCgroupInit;
+virCgroupItemFree;
+virCgroupItemKeyPath;
+virCgroupItemNew;
+virCgroupItemPath;
+virCgroupItemType;
virCgroupKill;
virCgroupKillPainfully;
virCgroupKillRecursive;
@@ -123,6 +129,7 @@ virCgroupSetMemory;
virCgroupSetMemoryHardLimit;
virCgroupSetMemorySoftLimit;
virCgroupSetMemSwapHardLimit;
+virCgroupUninit;
# command.h
diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c
index 71d46c5..baa0af7 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,98 @@ struct virCgroupController {
char *placement;
};
+struct _virCgroupItem {
+ virObject object;
+
+ char *name;
+ char *path;
+
+ bool created; /* the path is created or not */
+
+ virCgroupItemPtr next;
+ virCgroupItemPtr parent;
+ virCgroupItemPtr children;
+
+ struct virCgroupController *controller;
+};
+
+static virClassPtr cgroupItemClass;
+
+static void cgroupItemDispose(void *obj);
+
+static int virCgroupItemOnceInit(void)
+{
+ if (!(cgroupItemClass = virClassNew("cgroupItem",
+ sizeof(virCgroupItem),
+ cgroupItemDispose)))
+ return -1;
+
+ return 0;
+}
+VIR_ONCE_GLOBAL_INIT(virCgroupItem);
+
+static struct virCgroupController cgroupControllers[VIR_CGROUP_CONTROLLER_LAST];
+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]);
+
+int virCgroupInit(void)
+{
+ int rc;
+ int i;
+
+ rc = virCgroupDetectMounts(&cgroupControllers);
+ if (rc < 0) {
+ VIR_ERROR(_("Failed to initialize cgroup controllers"));
+ return rc;
+ }
+
+ rc = virCgroupDetectPlacement(&cgroupControllers);
+
+ if (rc == 0) {
+ /* Check that for every mounted controller, we found our placement */
+ for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
+ if (!cgroupControllers[i].mountPoint)
+ continue;
+
+ if (!cgroupControllers[i].placement) {
+ VIR_ERROR(_("Could not find placement for controller %s at
%s"),
+ virCgroupControllerTypeToString(i),
+ cgroupControllers[i].placement);
+ rc = -ENOENT;
+ break;
+ }
+
+ VIR_DEBUG("Detected mount/mapping %i:%s at %s in %s", i,
+ virCgroupControllerTypeToString(i),
+ cgroupControllers[i].mountPoint,
+ cgroupControllers[i].placement);
+ }
+ } else {
+ VIR_ERROR(_("Failed to initialize cgroup controllers"));
+ }
+
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ rootCgroupItems[i] = virCgroupItemRootNew(i);
+ }
+
+ return rc;
+}
+
+void virCgroupUninit(void)
+{
+ int i;
+
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ if (rootCgroupItems[i]) {
+ virCgroupItemFree(rootCgroupItems[i]);
+ rootCgroupItems[i] = NULL;
+ }
+ }
+}
+
struct virCgroup {
char *path;
@@ -74,6 +168,307 @@ typedef enum {
* cpuacct and cpuset if possible. */
} virCgroupFlags;
+static virCgroupItemPtr virCgroupItemRootNew(int type)
+{
+ virCgroupItemPtr rootItem = NULL;
+
+ if (type >= VIR_CGROUP_CONTROLLER_LAST)
+ return NULL;
+
+ if (!cgroupControllers[type].mountPoint)
+ return NULL;
+
+ if (virCgroupItemInitialize() < 0)
+ return NULL;
+
+ if (!(rootItem = virObjectNew(cgroupItemClass)))
+ return NULL;
+
+ rootItem->name = strdup("/");
+ rootItem->next = NULL;
+ rootItem->parent = NULL;
+ rootItem->children = NULL;
+ rootItem->controller = &cgroupControllers[type];
+ rootItem->created = 1;
+
+ if (virAsprintf(&rootItem->path, "%s%s%s",
+ rootItem->controller->mountPoint,
+ rootItem->controller->placement,
+ rootItem->name) == -1) {
+ virObjectUnref(rootItem);
+ rootItem = NULL;
+ }
+
+ return rootItem;
+}
+
+static virCgroupItemPtr virCgroupHasChildByName(virCgroupItemPtr item,
+ const char *name)
+{
+ virCgroupItemPtr child = item->children;
+
+ while (child) {
+ if (STREQ(child->name, name)) {
+ virObjectRef(child);
+ return child;
+ }
+
+ child = child->next;
+ }
+
+ return NULL;
+}
+
+virCgroupItemPtr virCgroupItemNew(int type,
+ const char *name,
+ virCgroupItemPtr parent)
+{
+ virCgroupItemPtr cgroupItem = NULL;
+ virCgroupItemPtr *next = NULL;
+
+ if (type >= VIR_CGROUP_CONTROLLER_LAST)
+ return NULL;
+
+ if (virCgroupItemInitialize() < 0)
+ return NULL;
+
+ if (!cgroupControllers[type].mountPoint)
+ return NULL;
+
+ if (!parent)
+ parent = rootCgroupItems[type];
+
+ if (!parent)
+ 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->controller = parent->controller;
+ cgroupItem->created = false;
+
+ if (virAsprintf(&cgroupItem->path, "%s/%s",
+ parent->path,
+ cgroupItem->name) == -1) {
+ virObjectUnref(cgroupItem);
+ cgroupItem = NULL;
+ return NULL;
+ }
+
+ virObjectRef(cgroupItem->parent);
+
+ next = &parent->children;
+
+ while (*next)
+ next = &(*next)->next;
+
+ *next = cgroupItem;
+
+ return cgroupItem;
+}
+
+void virCgroupItemFree(virCgroupItemPtr cgroupItem)
+{
+ virObjectUnref(cgroupItem);
+}
+
+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);
+
+ if (cgroupItem->parent) {
+ next = &parent->children;
+
+ while (*next && *next != cgroupItem)
+ next = &(*next)->next;
+
+ if (*next == cgroupItem) {
+ *next = cgroupItem->next;
+ cgroupItem->next = NULL;
+ cgroupItem->parent = NULL;
+ virObjectUnref(parent);
+ }
+ }
+
+ VIR_FREE(cgroupItem->name);
+}
+
+int virCgroupItemType(virCgroupItemPtr cgroupItem)
+{
+ return cgroupItem->controller->type;
+}
+
+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 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 virCgroupRemoveRecursively(char *grppath);
+
+static int virCgroupItemMakePath(virCgroupItemPtr cgroupItem)
+{
+ int ret = 0;
+
+ if (!cgroupItem->created && access(cgroupItem->path, F_OK) == 0) {
+ if (virCgroupRemoveRecursively(cgroupItem->path) != 0) {
+ VIR_ERROR(_("failed to remove historical cgroup directory: %s"),
+ cgroupItem->path);
+ return -errno;
+ }
+ }
+
+ 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;
+ }
+
+ virCgroupItemCpusetInherit(cgroupItem);
+ }
+
+ return ret;
+}
+
+int virCgroupItemPath(virCgroupItemPtr cgroupItem,
+ bool create,
+ char **path)
+{
+ if (virAsprintf(path, "%s", cgroupItem->path) == -1)
+ return -ENOMEM;
+
+ if (create && virCgroupItemMakePath(cgroupItem) < 0) {
+ VIR_FREE(*path);
+ return -1;
+ }
+
+ return 0;
+}
+
+int virCgroupItemKeyPath(virCgroupItemPtr cgroupItem,
+ const char *key,
+ char **path)
+{
+ int ret = 0;
+ char *cgroupItemPath = NULL;
+
+ ret = virCgroupItemPath(cgroupItem, 1, &cgroupItemPath);
+ if (ret < 0)
+ return ret;
+
+ if (virAsprintf(path, "%s/%s", cgroupItemPath, key) == -1)
+ ret = -ENOMEM;
+
+ VIR_FREE(cgroupItemPath);
+ return ret;
+}
+
/**
* virCgroupFree:
*
@@ -134,6 +529,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;
@@ -171,7 +567,7 @@ no_memory:
* sub-path the current process is assigned to. ie not
* necessarily in the root
*/
-static int virCgroupDetectPlacement(struct virCgroupController
(*cgroupControllers)[VIR_CGROUP_CONTROLLER_LAST])
+static int virCgroupDetectPlacement(struct virCgroupController
(*controllers)[VIR_CGROUP_CONTROLLER_LAST])
{
int i;
FILE *mapping = NULL;
@@ -184,24 +580,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;
@@ -212,7 +608,7 @@ static int virCgroupDetectPlacement(struct virCgroupController
(*cgroupControlle
len = strlen(tmp);
}
if (typelen == len && STREQLEN(typestr, tmp, len) &&
- !((*cgroupControllers)[i].placement = strdup(STREQ(path,
"/") ? "" : path)))
+ !((*controllers)[i].placement = strdup(STREQ(path, "/") ?
"" : path)))
goto no_memory;
tmp = next;
@@ -230,6 +626,7 @@ no_memory:
}
+
static int virCgroupDetect(virCgroupPtr group)
{
int any = 0;
diff --git a/src/util/vircgroup.h b/src/util/vircgroup.h
index 05f2e54..0ba2430 100644
--- a/src/util/vircgroup.h
+++ b/src/util/vircgroup.h
@@ -30,6 +30,9 @@
struct virCgroup;
typedef struct virCgroup *virCgroupPtr;
+typedef struct _virCgroupItem virCgroupItem;
+typedef virCgroupItem *virCgroupItemPtr;
+
enum {
VIR_CGROUP_CONTROLLER_CPU,
VIR_CGROUP_CONTROLLER_CPUACCT,
@@ -166,4 +169,13 @@ int virCgroupKill(virCgroupPtr group, int signum);
int virCgroupKillRecursive(virCgroupPtr group, int signum);
int virCgroupKillPainfully(virCgroupPtr group);
+virCgroupItemPtr virCgroupItemNew(int type, const char *name, virCgroupItemPtr parent);
+void virCgroupItemFree(virCgroupItemPtr cgroupItem);
+int virCgroupItemType(virCgroupItemPtr cgroupItem);
+int virCgroupItemPath(virCgroupItemPtr cgroupItem, bool create, char **path);
+int virCgroupItemKeyPath(virCgroupItemPtr cgroupItem, const char *key, char **path);
+
+int virCgroupInit(void);
+void virCgroupUninit(void);
+
#endif /* __VIR_CGROUP_H__ */
--
1.8.0.1.240.ge8a1f5a