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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list