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