2008-10-03 17:58:02 +00:00
|
|
|
/*
|
|
|
|
* cgroup.c: Tools for managing cgroups
|
|
|
|
*
|
|
|
|
* Copyright IBM Corp. 2008
|
|
|
|
*
|
|
|
|
* See COPYING.LIB for the License of this software
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Dan Smith <danms@us.ibm.com>
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <mntent.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "cgroup.h"
|
2008-11-06 16:36:07 +00:00
|
|
|
#include "logging.h"
|
2008-10-03 17:58:02 +00:00
|
|
|
|
|
|
|
#define CGROUP_MAX_VAL 512
|
|
|
|
|
2009-07-09 13:09:38 +00:00
|
|
|
enum {
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
2009-07-09 13:09:38 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_CPUACCT,
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
2009-07-09 13:09:38 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
|
|
|
|
|
|
VIR_CGROUP_CONTROLLER_LAST
|
2008-10-03 17:58:02 +00:00
|
|
|
};
|
|
|
|
|
2009-07-09 13:09:38 +00:00
|
|
|
VIR_ENUM_DECL(virCgroupController);
|
|
|
|
VIR_ENUM_IMPL(virCgroupController, VIR_CGROUP_CONTROLLER_LAST,
|
2009-07-09 13:10:41 +00:00
|
|
|
"cpu", "cpuacct", "cpuset", "memory", "devices");
|
|
|
|
|
|
|
|
struct virCgroupController {
|
|
|
|
int type;
|
|
|
|
char *mountPoint;
|
|
|
|
char *placement;
|
|
|
|
};
|
2009-07-09 13:09:38 +00:00
|
|
|
|
|
|
|
struct virCgroup {
|
|
|
|
char *path;
|
2009-07-09 13:10:41 +00:00
|
|
|
|
|
|
|
struct virCgroupController controllers[VIR_CGROUP_CONTROLLER_LAST];
|
2008-10-03 17:58:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupFree:
|
|
|
|
*
|
|
|
|
* @group: The group structure to free
|
|
|
|
*/
|
|
|
|
void virCgroupFree(virCgroupPtr *group)
|
|
|
|
{
|
2009-07-09 13:10:41 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (*group == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
|
|
|
VIR_FREE((*group)->controllers[i].mountPoint);
|
|
|
|
VIR_FREE((*group)->controllers[i].placement);
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
2009-07-09 13:10:41 +00:00
|
|
|
|
|
|
|
VIR_FREE((*group)->path);
|
|
|
|
VIR_FREE(*group);
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Process /proc/mounts figuring out what controllers are
|
|
|
|
* mounted and where
|
|
|
|
*/
|
|
|
|
static int virCgroupDetectMounts(virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2009-07-09 13:10:41 +00:00
|
|
|
int i;
|
2009-03-16 10:41:37 +00:00
|
|
|
FILE *mounts = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
struct mntent entry;
|
|
|
|
char buf[CGROUP_MAX_VAL];
|
|
|
|
|
|
|
|
mounts = fopen("/proc/mounts", "r");
|
|
|
|
if (mounts == NULL) {
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_ERROR0("Unable to open /proc/mounts");
|
|
|
|
return -ENOENT;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
while (getmntent_r(mounts, &entry, buf, sizeof(buf)) != NULL) {
|
2009-07-09 13:10:41 +00:00
|
|
|
if (STRNEQ(entry.mnt_type, "cgroup"))
|
|
|
|
continue;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
|
|
|
const char *typestr = virCgroupControllerTypeToString(i);
|
|
|
|
int typelen = strlen(typestr);
|
|
|
|
char *tmp = entry.mnt_opts;
|
|
|
|
while (tmp) {
|
|
|
|
char *next = strchr(tmp, ',');
|
|
|
|
int len;
|
|
|
|
if (next) {
|
|
|
|
len = next-tmp;
|
|
|
|
next++;
|
|
|
|
} else {
|
|
|
|
len = strlen(tmp);
|
|
|
|
}
|
|
|
|
if (typelen == len && STREQLEN(typestr, tmp, len) &&
|
|
|
|
!(group->controllers[i].mountPoint = strdup(entry.mnt_dir)))
|
|
|
|
goto no_memory;
|
|
|
|
tmp = next;
|
|
|
|
}
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(mounts);
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
return 0;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
no_memory:
|
|
|
|
if (mounts)
|
|
|
|
fclose(mounts);
|
|
|
|
return -ENOMEM;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Process /proc/self/cgroup figuring out what cgroup
|
|
|
|
* sub-path the current process is assigned to. ie not
|
|
|
|
* neccessarily in the root
|
2008-10-03 17:58:02 +00:00
|
|
|
*/
|
2009-07-09 13:10:41 +00:00
|
|
|
static int virCgroupDetectPlacement(virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
int i;
|
2009-07-09 13:10:41 +00:00
|
|
|
FILE *mapping = NULL;
|
|
|
|
char line[1024];
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
mapping = fopen("/proc/self/cgroup", "r");
|
|
|
|
if (mapping == NULL) {
|
|
|
|
VIR_ERROR0("Unable to open /proc/self/cgroup");
|
|
|
|
return -ENOENT;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
while (fgets(line, sizeof(line), mapping) != NULL) {
|
|
|
|
char *controllers = strchr(line, ':');
|
|
|
|
char *path = controllers ? strchr(controllers+1, ':') : NULL;
|
|
|
|
char *nl = path ? strchr(path, '\n') : NULL;
|
|
|
|
|
|
|
|
if (!controllers || !path)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (nl)
|
|
|
|
*nl = '\0';
|
|
|
|
|
|
|
|
*path = '\0';
|
|
|
|
controllers++;
|
|
|
|
path++;
|
|
|
|
|
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
|
|
|
const char *typestr = virCgroupControllerTypeToString(i);
|
|
|
|
int typelen = strlen(typestr);
|
|
|
|
char *tmp = controllers;
|
|
|
|
while (tmp) {
|
|
|
|
char *next = strchr(tmp, ',');
|
|
|
|
int len;
|
|
|
|
if (next) {
|
|
|
|
len = next-tmp;
|
|
|
|
next++;
|
|
|
|
} else {
|
|
|
|
len = strlen(tmp);
|
|
|
|
}
|
|
|
|
if (typelen == len && STREQLEN(typestr, tmp, len) &&
|
|
|
|
!(group->controllers[i].placement = strdup(STREQ(path, "/") ? "" : path)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
tmp = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(mapping);
|
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
return 0;
|
2009-07-09 13:10:41 +00:00
|
|
|
|
|
|
|
no_memory:
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
static int virCgroupDetect(virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2009-07-09 13:10:41 +00:00
|
|
|
int any = 0;
|
|
|
|
int rc;
|
|
|
|
int i;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupDetectMounts(group);
|
|
|
|
if (rc < 0) {
|
|
|
|
VIR_ERROR("Failed to detect mounts for %s", group->path);
|
|
|
|
return rc;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
/* Check that at least 1 controller is available */
|
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
|
|
|
if (group->controllers[i].mountPoint != NULL)
|
|
|
|
any = 1;
|
|
|
|
}
|
|
|
|
if (!any)
|
|
|
|
return -ENXIO;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupDetectPlacement(group);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
if (rc == 0) {
|
|
|
|
/* Check that for every mounted controller, we found our placement */
|
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
|
|
|
if (!group->controllers[i].mountPoint)
|
|
|
|
continue;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
if (!group->controllers[i].placement) {
|
|
|
|
VIR_ERROR("Could not find placement for controller %s at %s",
|
|
|
|
virCgroupControllerTypeToString(i),
|
|
|
|
group->controllers[i].placement);
|
|
|
|
rc = -ENOENT;
|
|
|
|
break;
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_DEBUG("Detected mount/mapping %i:%s at %s in %s", i,
|
|
|
|
virCgroupControllerTypeToString(i),
|
|
|
|
group->controllers[i].mountPoint,
|
|
|
|
group->controllers[i].placement);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
VIR_ERROR("Failed to detect mapping for %s", group->path);
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
|
|
|
|
static int virCgroupPathOfController(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
char **path)
|
|
|
|
{
|
|
|
|
if (group->controllers[controller].mountPoint == NULL)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
if (group->controllers[controller].placement == NULL)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
if (virAsprintf(path, "%s%s%s/%s",
|
|
|
|
group->controllers[controller].mountPoint,
|
|
|
|
group->controllers[controller].placement,
|
|
|
|
STREQ(group->path, "/") ? "" : group->path,
|
|
|
|
key ? key : "") == -1)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
static int virCgroupSetValueStr(virCgroupPtr group,
|
2009-07-09 13:10:41 +00:00
|
|
|
int controller,
|
2008-10-03 17:58:02 +00:00
|
|
|
const char *key,
|
|
|
|
const char *value)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
char *keypath = NULL;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupPathOfController(group, controller, key, &keypath);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc != 0)
|
|
|
|
return rc;
|
|
|
|
|
2009-07-09 13:10:06 +00:00
|
|
|
VIR_DEBUG("Set value %s", keypath);
|
|
|
|
rc = virFileWriteStr(keypath, value);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
DEBUG("Failed to write value '%s': %m", value);
|
|
|
|
rc = -errno;
|
2009-07-09 13:10:06 +00:00
|
|
|
} else {
|
|
|
|
rc = 0;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(keypath);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int virCgroupGetValueStr(virCgroupPtr group,
|
2009-07-09 13:10:41 +00:00
|
|
|
int controller,
|
2008-10-03 17:58:02 +00:00
|
|
|
const char *key,
|
|
|
|
char **value)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *keypath = NULL;
|
|
|
|
|
2009-07-09 13:10:06 +00:00
|
|
|
*value = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupPathOfController(group, controller, key, &keypath);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
DEBUG("No path of %s, %s", group->path, key);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:06 +00:00
|
|
|
VIR_DEBUG("Get value %s", keypath);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:06 +00:00
|
|
|
rc = virFileReadAll(keypath, 1024, value);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
DEBUG("Failed to read %s: %m\n", keypath);
|
|
|
|
rc = -errno;
|
2009-07-09 13:10:06 +00:00
|
|
|
} else {
|
|
|
|
rc = 0;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(keypath);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:06 +00:00
|
|
|
static int virCgroupSetValueU64(virCgroupPtr group,
|
2009-07-09 13:10:41 +00:00
|
|
|
int controller,
|
2009-07-09 13:10:06 +00:00
|
|
|
const char *key,
|
|
|
|
uint64_t value)
|
|
|
|
{
|
|
|
|
char *strval = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (virAsprintf(&strval, "%" PRIu64, value) == -1)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupSetValueStr(group, controller, key, strval);
|
2009-07-09 13:10:06 +00:00
|
|
|
|
|
|
|
VIR_FREE(strval);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-08 16:28:48 +00:00
|
|
|
#if 0
|
|
|
|
/* This is included for completeness, but not yet used */
|
|
|
|
|
|
|
|
static int virCgroupSetValueI64(virCgroupPtr group,
|
2009-07-09 13:10:41 +00:00
|
|
|
int controller,
|
2008-10-03 17:58:02 +00:00
|
|
|
const char *key,
|
2008-10-08 16:28:48 +00:00
|
|
|
int64_t value)
|
|
|
|
{
|
|
|
|
char *strval = NULL;
|
|
|
|
int rc;
|
|
|
|
|
2008-12-23 13:03:29 +00:00
|
|
|
if (virAsprintf(&strval, "%" PRIi64, value) == -1)
|
2008-10-08 16:28:48 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupSetValueStr(group, controller, key, strval);
|
2008-10-08 16:28:48 +00:00
|
|
|
|
|
|
|
VIR_FREE(strval);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int virCgroupGetValueI64(virCgroupPtr group,
|
2009-07-09 13:10:41 +00:00
|
|
|
int controller,
|
2008-10-08 16:28:48 +00:00
|
|
|
const char *key,
|
|
|
|
int64_t *value)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
char *strval = NULL;
|
|
|
|
int rc = 0;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupGetValueStr(group, controller, key, &strval);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc != 0)
|
|
|
|
goto out;
|
|
|
|
|
2008-10-08 16:28:48 +00:00
|
|
|
if (sscanf(strval, "%" SCNi64, value) != 1)
|
2008-10-03 17:58:02 +00:00
|
|
|
rc = -EINVAL;
|
|
|
|
out:
|
|
|
|
VIR_FREE(strval);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2008-10-08 16:28:48 +00:00
|
|
|
#endif
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2008-10-08 16:28:48 +00:00
|
|
|
static int virCgroupGetValueU64(virCgroupPtr group,
|
2009-07-09 13:10:41 +00:00
|
|
|
int controller,
|
2008-10-03 17:58:02 +00:00
|
|
|
const char *key,
|
2008-10-08 16:28:48 +00:00
|
|
|
uint64_t *value)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
char *strval = NULL;
|
|
|
|
int rc = 0;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupGetValueStr(group, controller, key, &strval);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc != 0)
|
|
|
|
goto out;
|
|
|
|
|
2008-10-08 16:28:48 +00:00
|
|
|
if (sscanf(strval, "%" SCNu64, value) != 1)
|
2008-10-03 17:58:02 +00:00
|
|
|
rc = -EINVAL;
|
|
|
|
out:
|
|
|
|
VIR_FREE(strval);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
static int virCgroupCpuSetInherit(virCgroupPtr parent, virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int rc = 0;
|
|
|
|
const char *inherit_values[] = {
|
|
|
|
"cpuset.cpus",
|
|
|
|
"cpuset.mems",
|
|
|
|
};
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_DEBUG("Setting up inheritance %s -> %s", parent->path, group->path);
|
|
|
|
for (i = 0; i < ARRAY_CARDINALITY(inherit_values) ; i++) {
|
|
|
|
char *value;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupGetValueStr(parent,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
inherit_values[i],
|
|
|
|
&value);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc != 0) {
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_ERROR("Failed to get %s %d", inherit_values[i], rc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Inherit %s = %s", inherit_values[i], value);
|
|
|
|
|
|
|
|
rc = virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
inherit_values[i],
|
|
|
|
value);
|
|
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
VIR_ERROR("Failed to set %s %d", inherit_values[i], rc);
|
2008-10-03 17:58:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
static int virCgroupMakeGroup(virCgroupPtr parent, virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int rc = 0;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_DEBUG("Make group %s", group->path);
|
2009-07-09 13:09:38 +00:00
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
2008-10-03 17:58:02 +00:00
|
|
|
char *path = NULL;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
/* Skip over controllers that aren't mounted */
|
|
|
|
if (!group->controllers[i].mountPoint)
|
2008-10-03 17:58:02 +00:00
|
|
|
continue;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupPathOfController(group, i, "", &path);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_DEBUG("Make controller %s", path);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (access(path, F_OK) != 0) {
|
2009-05-08 10:24:11 +00:00
|
|
|
if (mkdir(path, 0755) < 0) {
|
2008-10-03 17:58:02 +00:00
|
|
|
rc = -errno;
|
|
|
|
VIR_FREE(path);
|
|
|
|
break;
|
|
|
|
}
|
2009-07-09 13:10:41 +00:00
|
|
|
if (group->controllers[VIR_CGROUP_CONTROLLER_CPUSET].mountPoint != NULL &&
|
|
|
|
(i == VIR_CGROUP_CONTROLLER_CPUSET ||
|
|
|
|
STREQ(group->controllers[i].mountPoint, group->controllers[VIR_CGROUP_CONTROLLER_CPUSET].mountPoint))) {
|
|
|
|
rc = virCgroupCpuSetInherit(parent, group);
|
|
|
|
if (rc != 0)
|
|
|
|
break;
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
|
|
|
|
static int virCgroupNew(const char *path,
|
|
|
|
virCgroupPtr *group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
char *typpath = NULL;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_DEBUG("New group %s", path);
|
|
|
|
*group = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
if (VIR_ALLOC((*group)) != 0) {
|
2008-10-03 17:58:02 +00:00
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
if (!((*group)->path = strdup(path))) {
|
2008-10-03 17:58:02 +00:00
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupDetect(*group);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
err:
|
2009-07-09 13:10:41 +00:00
|
|
|
virCgroupFree(group);
|
|
|
|
*group = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
|
|
|
VIR_FREE(typpath);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
static int virCgroupAppRoot(int privileged,
|
|
|
|
virCgroupPtr *group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2009-07-10 10:40:04 +00:00
|
|
|
virCgroupPtr rootgrp = NULL;
|
|
|
|
int rc;
|
2009-07-09 13:10:41 +00:00
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
rc = virCgroupNew("/", &rootgrp);
|
|
|
|
if (rc != 0)
|
|
|
|
return rc;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
if (privileged) {
|
|
|
|
rc = virCgroupNew("/libvirt", group);
|
|
|
|
} else {
|
|
|
|
char *rootname;
|
|
|
|
char *username;
|
|
|
|
username = virGetUserName(NULL, getuid());
|
|
|
|
if (!username) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
rc = virAsprintf(&rootname, "/libvirt-%s", username);
|
|
|
|
VIR_FREE(username);
|
|
|
|
if (rc < 0) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2009-07-09 13:10:41 +00:00
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
rc = virCgroupNew(rootname, group);
|
|
|
|
VIR_FREE(rootname);
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
if (rc != 0)
|
2009-07-10 10:40:04 +00:00
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
rc = virCgroupMakeGroup(rootgrp, *group);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
cleanup:
|
|
|
|
virCgroupFree(&rootgrp);
|
2008-10-03 17:58:02 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
/**
|
|
|
|
* virCgroupRemove:
|
|
|
|
*
|
|
|
|
* @group: The group to be removed
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int virCgroupRemove(virCgroupPtr group)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
int i;
|
|
|
|
char *grppath = NULL;
|
|
|
|
|
2009-07-09 13:09:38 +00:00
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
2009-07-09 13:10:41 +00:00
|
|
|
/* Skip over controllers not mounted */
|
|
|
|
if (!group->controllers[i].mountPoint)
|
2008-10-03 17:58:02 +00:00
|
|
|
continue;
|
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
if (virCgroupPathOfController(group,
|
|
|
|
i,
|
|
|
|
NULL,
|
|
|
|
&grppath) != 0)
|
|
|
|
continue;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
DEBUG("Removing cgroup %s", grppath);
|
|
|
|
if (rmdir(grppath) != 0 && errno != ENOENT) {
|
|
|
|
rc = -errno;
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
VIR_FREE(grppath);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupAddTask:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to add a task to
|
|
|
|
* @pid: The pid of the task to add
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int virCgroupAddTask(virCgroupPtr group, pid_t pid)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
int i;
|
|
|
|
|
2009-07-09 13:09:38 +00:00
|
|
|
for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
|
2009-07-09 13:10:41 +00:00
|
|
|
/* Skip over controllers not mounted */
|
|
|
|
if (!group->controllers[i].mountPoint)
|
|
|
|
continue;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 13:10:41 +00:00
|
|
|
rc = virCgroupSetValueU64(group, i, "tasks", (unsigned long long)pid);
|
2008-10-03 17:58:02 +00:00
|
|
|
if (rc != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
/**
|
2009-07-10 10:40:04 +00:00
|
|
|
* virCgroupForDriver:
|
2008-10-03 17:58:02 +00:00
|
|
|
*
|
2009-07-10 10:40:04 +00:00
|
|
|
* @name: name of this driver (e.g., xen, qemu, lxc)
|
2008-10-03 17:58:02 +00:00
|
|
|
* @group: Pointer to returned virCgroupPtr
|
|
|
|
*
|
|
|
|
* Returns 0 on success
|
|
|
|
*/
|
2009-07-10 10:40:04 +00:00
|
|
|
int virCgroupForDriver(const char *name,
|
|
|
|
virCgroupPtr *group,
|
|
|
|
int privileged,
|
|
|
|
int create)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
int rc;
|
2009-07-10 10:40:04 +00:00
|
|
|
char *path = NULL;
|
2009-07-09 13:10:41 +00:00
|
|
|
virCgroupPtr rootgrp = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
rc = virCgroupAppRoot(privileged, &rootgrp);
|
2009-07-09 13:10:41 +00:00
|
|
|
if (rc != 0)
|
2008-10-03 17:58:02 +00:00
|
|
|
goto out;
|
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
if (virAsprintf(&path, "%s/%s", rootgrp->path, name) < 0) {
|
|
|
|
rc = -ENOMEM;
|
2009-07-09 13:10:41 +00:00
|
|
|
goto out;
|
2009-07-10 10:40:04 +00:00
|
|
|
}
|
2009-07-09 13:10:41 +00:00
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
rc = virCgroupNew(path, group);
|
|
|
|
VIR_FREE(path);
|
|
|
|
|
|
|
|
if (rc == 0 &&
|
|
|
|
create) {
|
|
|
|
rc = virCgroupMakeGroup(rootgrp, *group);
|
|
|
|
if (rc != 0)
|
|
|
|
virCgroupFree(group);
|
|
|
|
}
|
2009-07-09 13:10:41 +00:00
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
out:
|
2009-07-09 13:10:41 +00:00
|
|
|
virCgroupFree(&rootgrp);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-07-10 10:40:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupForDomain:
|
|
|
|
*
|
|
|
|
* @driver: group for driver owning the domain
|
|
|
|
* @name: name of the domain
|
|
|
|
* @group: Pointer to returned virCgroupPtr
|
|
|
|
*
|
|
|
|
* Returns 0 on success
|
|
|
|
*/
|
|
|
|
int virCgroupForDomain(virCgroupPtr driver,
|
|
|
|
const char *name,
|
|
|
|
virCgroupPtr *group,
|
|
|
|
int create)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "%s/%s", driver->path, name) < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rc = virCgroupNew(path, group);
|
|
|
|
VIR_FREE(path);
|
|
|
|
|
|
|
|
if (rc == 0 &&
|
|
|
|
create) {
|
|
|
|
rc = virCgroupMakeGroup(driver, *group);
|
|
|
|
if (rc != 0)
|
|
|
|
virCgroupFree(group);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
/**
|
|
|
|
* virCgroupSetMemory:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to change memory for
|
|
|
|
* @kb: The memory amount in kilobytes
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int virCgroupSetMemory(virCgroupPtr group, unsigned long kb)
|
|
|
|
{
|
|
|
|
return virCgroupSetValueU64(group,
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
2008-10-03 17:58:02 +00:00
|
|
|
"memory.limit_in_bytes",
|
|
|
|
kb << 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupDenyAllDevices:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to deny devices for
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int virCgroupDenyAllDevices(virCgroupPtr group)
|
|
|
|
{
|
|
|
|
return virCgroupSetValueStr(group,
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
|
|
"devices.deny",
|
|
|
|
"a");
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupAllowDevice:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to allow a device for
|
|
|
|
* @type: The device type (i.e., 'c' or 'b')
|
|
|
|
* @major: The major number of the device
|
|
|
|
* @minor: The minor number of the device
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int virCgroupAllowDevice(virCgroupPtr group,
|
|
|
|
char type,
|
|
|
|
int major,
|
|
|
|
int minor)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *devstr = NULL;
|
|
|
|
|
2008-12-23 13:03:29 +00:00
|
|
|
if (virAsprintf(&devstr, "%c %i:%i rwm", type, major, minor) == -1) {
|
2008-10-03 17:58:02 +00:00
|
|
|
rc = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = virCgroupSetValueStr(group,
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
2008-10-03 17:58:02 +00:00
|
|
|
"devices.allow",
|
|
|
|
devstr);
|
|
|
|
out:
|
|
|
|
VIR_FREE(devstr);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2008-10-08 16:28:48 +00:00
|
|
|
|
2008-10-21 16:46:47 +00:00
|
|
|
/**
|
|
|
|
* virCgroupAllowDeviceMajor:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to allow an entire device major type for
|
|
|
|
* @type: The device type (i.e., 'c' or 'b')
|
|
|
|
* @major: The major number of the device type
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int virCgroupAllowDeviceMajor(virCgroupPtr group,
|
|
|
|
char type,
|
|
|
|
int major)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *devstr = NULL;
|
|
|
|
|
2008-12-23 13:03:29 +00:00
|
|
|
if (virAsprintf(&devstr, "%c %i:* rwm", type, major) == -1) {
|
2008-10-21 16:46:47 +00:00
|
|
|
rc = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = virCgroupSetValueStr(group,
|
2009-07-09 13:10:41 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
2008-10-21 16:46:47 +00:00
|
|
|
"devices.allow",
|
|
|
|
devstr);
|
|
|
|
out:
|
|
|
|
VIR_FREE(devstr);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2008-10-08 16:28:48 +00:00
|
|
|
int virCgroupSetCpuShares(virCgroupPtr group, unsigned long shares)
|
|
|
|
{
|
2009-07-09 13:10:41 +00:00
|
|
|
return virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
|
|
|
"cpu.shares", (uint64_t)shares);
|
2008-10-08 16:28:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int virCgroupGetCpuShares(virCgroupPtr group, unsigned long *shares)
|
|
|
|
{
|
2009-07-09 13:10:41 +00:00
|
|
|
return virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
|
|
|
"cpu.shares", (uint64_t *)shares);
|
2008-10-08 16:28:48 +00:00
|
|
|
}
|
2009-03-06 14:44:04 +00:00
|
|
|
|
|
|
|
int virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage)
|
|
|
|
{
|
2009-07-09 13:10:41 +00:00
|
|
|
return virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUACCT,
|
|
|
|
"cpuacct.usage", (uint64_t *)usage);
|
2009-03-06 14:44:04 +00:00
|
|
|
}
|