2008-10-03 17:58:02 +00:00
|
|
|
/*
|
2012-12-03 15:03:47 +00:00
|
|
|
* vircgroup.c: methods for managing control cgroups
|
2008-10-03 17:58:02 +00:00
|
|
|
*
|
2014-02-25 17:18:54 -07:00
|
|
|
* Copyright (C) 2010-2014 Red Hat, Inc.
|
2008-10-03 17:58:02 +00:00
|
|
|
* Copyright IBM Corp. 2008
|
|
|
|
*
|
2012-07-27 17:39:53 +08:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 16:30:55 -06:00
|
|
|
* License along with this library. If not, see
|
2012-07-27 17:39:53 +08:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2008-10-03 17:58:02 +00:00
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Dan Smith <danms@us.ibm.com>
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2010-04-23 11:34:17 +02:00
|
|
|
#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
|
2010-03-09 19:22:22 +01:00
|
|
|
# include <mntent.h>
|
2009-10-07 11:18:31 +01:00
|
|
|
#endif
|
2013-04-05 12:48:47 +01:00
|
|
|
#if defined HAVE_SYS_MOUNT_H
|
|
|
|
# include <sys/mount.h>
|
|
|
|
#endif
|
2008-10-03 17:58:02 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2011-02-22 17:33:59 +00:00
|
|
|
#include <signal.h>
|
2010-06-24 01:00:55 +09:00
|
|
|
#include <dirent.h>
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-03-28 14:32:23 +00:00
|
|
|
#define __VIR_CGROUP_ALLOW_INCLUDE_PRIV_H__
|
|
|
|
#include "vircgrouppriv.h"
|
|
|
|
|
2012-12-13 17:44:57 +00:00
|
|
|
#include "virutil.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2013-04-05 12:48:47 +01:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2011-07-19 12:32:58 -06:00
|
|
|
#include "virfile.h"
|
2012-01-25 16:13:59 +00:00
|
|
|
#include "virhash.h"
|
2012-01-18 16:10:43 +00:00
|
|
|
#include "virhashcode.h"
|
2013-04-26 10:23:51 +01:00
|
|
|
#include "virstring.h"
|
2013-07-22 16:34:51 +01:00
|
|
|
#include "virsystemd.h"
|
2014-02-14 18:49:01 +01:00
|
|
|
#include "virtypedparam.h"
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2014-02-14 18:49:02 +01:00
|
|
|
#include "nodeinfo.h"
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("util.cgroup");
|
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
#define CGROUP_MAX_VAL 512
|
|
|
|
|
2013-04-05 12:48:47 +01:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_CGROUP
|
|
|
|
|
2014-02-14 18:49:01 +01:00
|
|
|
#define CGROUP_NB_TOTAL_CPU_STAT_PARAM 3
|
2014-02-14 18:49:02 +01:00
|
|
|
#define CGROUP_NB_PER_CPU_STAT_PARAM 1
|
2014-02-14 18:49:01 +01:00
|
|
|
|
2013-08-12 14:02:26 -06:00
|
|
|
#if defined(__linux__) && defined(HAVE_GETMNTENT_R) && \
|
2013-08-12 11:47:13 -06:00
|
|
|
defined(_DIRENT_HAVE_D_TYPE) && defined(_SC_CLK_TCK)
|
2013-08-12 14:02:26 -06:00
|
|
|
# define VIR_CGROUP_SUPPORTED
|
|
|
|
#endif
|
|
|
|
|
2009-07-09 14:09:38 +01:00
|
|
|
VIR_ENUM_IMPL(virCgroupController, VIR_CGROUP_CONTROLLER_LAST,
|
2009-09-21 23:31:22 +09:00
|
|
|
"cpu", "cpuacct", "cpuset", "memory", "devices",
|
2013-07-25 19:13:44 +01:00
|
|
|
"freezer", "blkio", "net_cls", "perf_event",
|
|
|
|
"name=systemd");
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2011-07-21 15:12:55 +08:00
|
|
|
typedef enum {
|
|
|
|
VIR_CGROUP_NONE = 0, /* create subdir under each cgroup if possible. */
|
|
|
|
VIR_CGROUP_MEM_HIERACHY = 1 << 0, /* call virCgroupSetMemoryUseHierarchy
|
|
|
|
* before creating subcgroups and
|
|
|
|
* attaching tasks
|
|
|
|
*/
|
|
|
|
} virCgroupFlags;
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2014-07-08 17:34:56 +02:00
|
|
|
/**
|
|
|
|
* virCgroupGetDevicePermsString:
|
|
|
|
*
|
|
|
|
* @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits
|
|
|
|
*
|
|
|
|
* Returns string corresponding to the appropriate bits set.
|
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
virCgroupGetDevicePermsString(int perms)
|
|
|
|
{
|
|
|
|
if (perms & VIR_CGROUP_DEVICE_READ) {
|
|
|
|
if (perms & VIR_CGROUP_DEVICE_WRITE) {
|
|
|
|
if (perms & VIR_CGROUP_DEVICE_MKNOD)
|
|
|
|
return "rwm";
|
|
|
|
else
|
|
|
|
return "rw";
|
|
|
|
} else {
|
|
|
|
if (perms & VIR_CGROUP_DEVICE_MKNOD)
|
|
|
|
return "rm";
|
|
|
|
else
|
|
|
|
return "r";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (perms & VIR_CGROUP_DEVICE_WRITE) {
|
|
|
|
if (perms & VIR_CGROUP_DEVICE_MKNOD)
|
|
|
|
return "wm";
|
|
|
|
else
|
|
|
|
return "w";
|
|
|
|
} else {
|
|
|
|
if (perms & VIR_CGROUP_DEVICE_MKNOD)
|
|
|
|
return "m";
|
|
|
|
else
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:27 +04:00
|
|
|
#ifdef VIR_CGROUP_SUPPORTED
|
2013-08-12 13:52:06 -06:00
|
|
|
bool
|
|
|
|
virCgroupAvailable(void)
|
2013-07-04 16:49:24 +01:00
|
|
|
{
|
2013-07-24 16:30:33 +04:00
|
|
|
bool ret = false;
|
2013-07-04 16:49:24 +01:00
|
|
|
FILE *mounts = NULL;
|
|
|
|
struct mntent entry;
|
|
|
|
char buf[CGROUP_MAX_VAL];
|
|
|
|
|
|
|
|
if (!virFileExists("/proc/cgroups"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!(mounts = fopen("/proc/mounts", "r")))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
while (getmntent_r(mounts, &entry, buf, sizeof(buf)) != NULL) {
|
2013-09-11 19:15:52 +01:00
|
|
|
/* We're looking for at least one 'cgroup' fs mount,
|
|
|
|
* which is *not* a named mount. */
|
|
|
|
if (STREQ(entry.mnt_type, "cgroup") &&
|
|
|
|
!strstr(entry.mnt_opts, "name=")) {
|
2013-07-04 16:49:24 +01:00
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FORCE_FCLOSE(mounts);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 15:01:30 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupPartitionNeedsEscaping(const char *path)
|
|
|
|
{
|
|
|
|
FILE *fp = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
char *line = NULL;
|
|
|
|
size_t buflen;
|
|
|
|
|
|
|
|
/* If it starts with 'cgroup.' or a '_' of any
|
|
|
|
* of the controller names from /proc/cgroups,
|
|
|
|
* then we must prefix a '_'
|
|
|
|
*/
|
|
|
|
if (STRPREFIX(path, "cgroup."))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (path[0] == '_' ||
|
|
|
|
path[0] == '.')
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (!(fp = fopen("/proc/cgroups", "r"))) {
|
|
|
|
/* The API contract is that we return ENXIO
|
|
|
|
* if cgroups are not available on a host */
|
|
|
|
if (errno == ENOENT)
|
|
|
|
errno = ENXIO;
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Cannot open /proc/cgroups"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Data looks like this:
|
|
|
|
* #subsys_name hierarchy num_cgroups enabled
|
|
|
|
* cpuset 2 4 1
|
|
|
|
* cpu 3 48 1
|
|
|
|
* cpuacct 3 48 1
|
|
|
|
* memory 4 4 1
|
|
|
|
* devices 5 4 1
|
|
|
|
* freezer 6 4 1
|
|
|
|
* net_cls 7 1 1
|
|
|
|
*/
|
|
|
|
while (getline(&line, &buflen, fp) > 0) {
|
|
|
|
char *tmp;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (STRPREFIX(line, "#subsys_name"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
tmp = strchrnul(line, ' ');
|
|
|
|
*tmp = '\0';
|
|
|
|
len = tmp - line;
|
|
|
|
|
|
|
|
if (STRPREFIX(path, line) &&
|
|
|
|
path[len] == '.') {
|
|
|
|
ret = 1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ferror(fp)) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Error while reading /proc/cgroups"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-08-12 15:01:30 -06:00
|
|
|
VIR_FREE(line);
|
|
|
|
VIR_FORCE_FCLOSE(fp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupPartitionEscape(char **path)
|
|
|
|
{
|
|
|
|
size_t len = strlen(*path) + 1;
|
|
|
|
int rc;
|
|
|
|
char escape = '_';
|
|
|
|
|
|
|
|
if ((rc = virCgroupPartitionNeedsEscaping(*path)) <= 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (VIR_INSERT_ELEMENT(*path, 0, len, escape) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-07-29 08:55:05 -06:00
|
|
|
|
|
|
|
|
2013-07-24 17:38:08 +01:00
|
|
|
static bool
|
2013-07-24 17:41:44 +01:00
|
|
|
virCgroupValidateMachineGroup(virCgroupPtr group,
|
|
|
|
const char *name,
|
|
|
|
const char *drivername,
|
2013-07-22 16:34:51 +01:00
|
|
|
const char *partition,
|
2013-07-24 17:41:44 +01:00
|
|
|
bool stripEmulatorSuffix)
|
2013-07-23 15:26:21 +01:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
bool valid = false;
|
|
|
|
char *partname;
|
2013-07-22 16:34:51 +01:00
|
|
|
char *scopename;
|
2013-07-23 15:26:21 +01:00
|
|
|
|
|
|
|
if (virAsprintf(&partname, "%s.libvirt-%s",
|
|
|
|
name, drivername) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupPartitionEscape(&partname) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-07-22 16:34:51 +01:00
|
|
|
if (!partition)
|
|
|
|
partition = "/machine";
|
|
|
|
|
|
|
|
if (!(scopename = virSystemdMakeScopeName(name, drivername, partition)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupPartitionEscape(&scopename) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-07-23 15:26:21 +01:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
|
|
|
char *tmp;
|
|
|
|
|
2013-07-25 19:13:44 +01:00
|
|
|
if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
|
|
|
|
continue;
|
|
|
|
|
2013-07-23 15:26:21 +01:00
|
|
|
if (!group->controllers[i].placement)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
tmp = strrchr(group->controllers[i].placement, '/');
|
|
|
|
if (!tmp)
|
|
|
|
goto cleanup;
|
2013-07-24 17:41:44 +01:00
|
|
|
|
|
|
|
if (stripEmulatorSuffix &&
|
|
|
|
(i == VIR_CGROUP_CONTROLLER_CPU ||
|
|
|
|
i == VIR_CGROUP_CONTROLLER_CPUACCT ||
|
|
|
|
i == VIR_CGROUP_CONTROLLER_CPUSET)) {
|
|
|
|
if (STREQ(tmp, "/emulator"))
|
|
|
|
*tmp = '\0';
|
|
|
|
tmp = strrchr(group->controllers[i].placement, '/');
|
|
|
|
if (!tmp)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-07-23 15:26:21 +01:00
|
|
|
tmp++;
|
|
|
|
|
|
|
|
if (STRNEQ(tmp, name) &&
|
2013-07-22 16:34:51 +01:00
|
|
|
STRNEQ(tmp, partname) &&
|
|
|
|
STRNEQ(tmp, scopename)) {
|
2013-08-12 13:52:06 -06:00
|
|
|
VIR_DEBUG("Name '%s' for controller '%s' does not match "
|
|
|
|
"'%s', '%s' or '%s'",
|
|
|
|
tmp, virCgroupControllerTypeToString(i),
|
|
|
|
name, partname, scopename);
|
2013-07-23 15:26:21 +01:00
|
|
|
goto cleanup;
|
2013-07-24 17:41:44 +01:00
|
|
|
}
|
2013-07-23 15:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
valid = true;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(partname);
|
2013-07-22 16:34:51 +01:00
|
|
|
VIR_FREE(scopename);
|
2013-07-23 15:26:21 +01:00
|
|
|
return valid;
|
|
|
|
}
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2010-10-29 17:32:16 +08:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupCopyMounts(virCgroupPtr group,
|
|
|
|
virCgroupPtr parent)
|
2013-03-22 11:11:34 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2013-03-22 11:11:34 +00:00
|
|
|
if (!parent->controllers[i].mountPoint)
|
|
|
|
continue;
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (VIR_STRDUP(group->controllers[i].mountPoint,
|
|
|
|
parent->controllers[i].mountPoint) < 0)
|
|
|
|
return -1;
|
2013-04-05 11:28:04 +01:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (VIR_STRDUP(group->controllers[i].linkPoint,
|
|
|
|
parent->controllers[i].linkPoint) < 0)
|
|
|
|
return -1;
|
2013-03-22 11:11:34 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
/*
|
|
|
|
* Process /proc/mounts figuring out what controllers are
|
|
|
|
* mounted and where
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupDetectMounts(virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t 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) {
|
2013-07-04 16:49:24 +01:00
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Unable to open /proc/mounts"));
|
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
while (getmntent_r(mounts, &entry, buf, sizeof(buf)) != NULL) {
|
2009-07-09 14:10:41 +01:00
|
|
|
if (STRNEQ(entry.mnt_type, "cgroup"))
|
|
|
|
continue;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2009-07-09 14:10:41 +01:00
|
|
|
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);
|
|
|
|
}
|
2011-08-31 16:46:13 +01:00
|
|
|
/* NB, the same controller can appear >1 time in mount list
|
|
|
|
* due to bind mounts from one location to another. Pick the
|
|
|
|
* first entry only
|
|
|
|
*/
|
2009-07-09 14:10:41 +01:00
|
|
|
if (typelen == len && STREQLEN(typestr, tmp, len) &&
|
2013-04-05 11:28:04 +01:00
|
|
|
!group->controllers[i].mountPoint) {
|
|
|
|
char *linksrc;
|
|
|
|
struct stat sb;
|
|
|
|
char *tmp2;
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (VIR_STRDUP(group->controllers[i].mountPoint,
|
|
|
|
entry.mnt_dir) < 0)
|
2013-05-24 09:19:34 +02:00
|
|
|
goto error;
|
2013-04-05 11:28:04 +01:00
|
|
|
|
|
|
|
tmp2 = strrchr(entry.mnt_dir, '/');
|
|
|
|
if (!tmp2) {
|
2013-07-04 16:49:24 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Missing '/' separator in cgroup mount '%s'"),
|
|
|
|
entry.mnt_dir);
|
2013-04-05 11:28:04 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2013-09-10 14:31:53 +01:00
|
|
|
|
2013-04-05 11:28:04 +01:00
|
|
|
/* If it is a co-mount it has a filename like "cpu,cpuacct"
|
|
|
|
* and we must identify the symlink path */
|
|
|
|
if (strchr(tmp2 + 1, ',')) {
|
2013-09-10 14:31:53 +01:00
|
|
|
*tmp2 = '\0';
|
2013-04-05 11:28:04 +01:00
|
|
|
if (virAsprintf(&linksrc, "%s/%s",
|
|
|
|
entry.mnt_dir, typestr) < 0)
|
2013-05-24 09:19:34 +02:00
|
|
|
goto error;
|
2013-04-05 11:28:04 +01:00
|
|
|
*tmp2 = '/';
|
|
|
|
|
|
|
|
if (lstat(linksrc, &sb) < 0) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
VIR_WARN("Controller %s co-mounted at %s is missing symlink at %s",
|
|
|
|
typestr, entry.mnt_dir, linksrc);
|
|
|
|
VIR_FREE(linksrc);
|
|
|
|
} else {
|
2013-07-04 16:49:24 +01:00
|
|
|
virReportSystemError(errno,
|
2013-08-12 13:52:06 -06:00
|
|
|
_("Cannot stat %s"),
|
|
|
|
linksrc);
|
2013-04-05 11:28:04 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!S_ISLNK(sb.st_mode)) {
|
|
|
|
VIR_WARN("Expecting a symlink at %s for controller %s",
|
|
|
|
linksrc, typestr);
|
|
|
|
} else {
|
|
|
|
group->controllers[i].linkPoint = linksrc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-09 14:10:41 +01:00
|
|
|
tmp = next;
|
|
|
|
}
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2010-11-16 21:13:29 -05:00
|
|
|
VIR_FORCE_FCLOSE(mounts);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
return 0;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
error:
|
2010-11-16 21:13:29 -05:00
|
|
|
VIR_FORCE_FCLOSE(mounts);
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupCopyPlacement(virCgroupPtr group,
|
|
|
|
const char *path,
|
|
|
|
virCgroupPtr parent)
|
2013-03-22 11:11:34 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2013-03-22 11:11:34 +00:00
|
|
|
if (!group->controllers[i].mountPoint)
|
|
|
|
continue;
|
|
|
|
|
2013-07-25 19:13:44 +01:00
|
|
|
if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
|
|
|
|
continue;
|
|
|
|
|
2013-03-22 11:11:34 +00:00
|
|
|
if (path[0] == '/') {
|
2013-07-04 16:49:24 +01:00
|
|
|
if (VIR_STRDUP(group->controllers[i].placement, path) < 0)
|
|
|
|
return -1;
|
2013-03-22 11:11:34 +00:00
|
|
|
} else {
|
|
|
|
/*
|
2014-03-17 10:38:38 +01:00
|
|
|
* parent == "/" + path="" => "/"
|
|
|
|
* parent == "/libvirt.service" + path == "" => "/libvirt.service"
|
|
|
|
* parent == "/libvirt.service" + path == "foo" => "/libvirt.service/foo"
|
2013-03-22 11:11:34 +00:00
|
|
|
*/
|
|
|
|
if (virAsprintf(&group->controllers[i].placement,
|
|
|
|
"%s%s%s",
|
|
|
|
parent->controllers[i].placement,
|
|
|
|
(STREQ(parent->controllers[i].placement, "/") ||
|
|
|
|
STREQ(path, "") ? "" : "/"),
|
|
|
|
path) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
2013-03-22 11:11:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
/*
|
2013-03-22 11:11:34 +00:00
|
|
|
* virCgroupDetectPlacement:
|
|
|
|
* @group: the group to process
|
|
|
|
* @path: the relative path to append, not starting with '/'
|
|
|
|
*
|
2009-07-09 14:10:41 +01:00
|
|
|
* Process /proc/self/cgroup figuring out what cgroup
|
|
|
|
* sub-path the current process is assigned to. ie not
|
2013-03-22 11:11:34 +00:00
|
|
|
* necessarily in the root. The contents of this file
|
|
|
|
* looks like
|
|
|
|
*
|
|
|
|
* 9:perf_event:/
|
|
|
|
* 8:blkio:/
|
|
|
|
* 7:net_cls:/
|
|
|
|
* 6:freezer:/
|
|
|
|
* 5:devices:/
|
|
|
|
* 4:memory:/
|
|
|
|
* 3:cpuacct,cpu:/
|
|
|
|
* 2:cpuset:/
|
|
|
|
* 1:name=systemd:/user/berrange/2
|
|
|
|
*
|
|
|
|
* It then appends @path to each detected path.
|
2008-10-03 17:58:02 +00:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupDetectPlacement(virCgroupPtr group,
|
|
|
|
pid_t pid,
|
|
|
|
const char *path)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2009-07-09 14:10:41 +01:00
|
|
|
FILE *mapping = NULL;
|
|
|
|
char line[1024];
|
2013-07-04 16:49:24 +01:00
|
|
|
int ret = -1;
|
2013-07-19 11:13:05 +01:00
|
|
|
char *procfile;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-25 19:13:44 +01:00
|
|
|
VIR_DEBUG("Detecting placement for pid %lld path %s",
|
|
|
|
(unsigned long long)pid, path);
|
2013-07-19 11:13:05 +01:00
|
|
|
if (pid == -1) {
|
|
|
|
if (VIR_STRDUP(procfile, "/proc/self/cgroup") < 0)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
if (virAsprintf(&procfile, "/proc/%llu/cgroup",
|
|
|
|
(unsigned long long)pid) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
mapping = fopen(procfile, "r");
|
2009-07-09 14:10:41 +01:00
|
|
|
if (mapping == NULL) {
|
2013-07-19 11:13:05 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to open '%s'"),
|
|
|
|
procfile);
|
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
while (fgets(line, sizeof(line), mapping) != NULL) {
|
|
|
|
char *controllers = strchr(line, ':');
|
2013-03-22 11:11:34 +00:00
|
|
|
char *selfpath = controllers ? strchr(controllers + 1, ':') : NULL;
|
|
|
|
char *nl = selfpath ? strchr(selfpath, '\n') : NULL;
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2013-03-22 11:11:34 +00:00
|
|
|
if (!controllers || !selfpath)
|
2009-07-09 14:10:41 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (nl)
|
|
|
|
*nl = '\0';
|
|
|
|
|
2013-03-22 11:11:34 +00:00
|
|
|
*selfpath = '\0';
|
2009-07-09 14:10:41 +01:00
|
|
|
controllers++;
|
2013-03-22 11:11:34 +00:00
|
|
|
selfpath++;
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2009-07-09 14:10:41 +01:00
|
|
|
const char *typestr = virCgroupControllerTypeToString(i);
|
|
|
|
int typelen = strlen(typestr);
|
|
|
|
char *tmp = controllers;
|
2013-07-25 19:13:44 +01:00
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
while (tmp) {
|
|
|
|
char *next = strchr(tmp, ',');
|
|
|
|
int len;
|
|
|
|
if (next) {
|
2013-03-22 11:11:34 +00:00
|
|
|
len = next - tmp;
|
2009-07-09 14:10:41 +01:00
|
|
|
next++;
|
|
|
|
} else {
|
|
|
|
len = strlen(tmp);
|
|
|
|
}
|
2013-03-22 11:11:34 +00:00
|
|
|
|
|
|
|
/*
|
2014-03-17 10:38:38 +01:00
|
|
|
* selfpath == "/" + path="" -> "/"
|
|
|
|
* selfpath == "/libvirt.service" + path == "" -> "/libvirt.service"
|
|
|
|
* selfpath == "/libvirt.service" + path == "foo" -> "/libvirt.service/foo"
|
2013-03-22 11:11:34 +00:00
|
|
|
*/
|
2013-07-25 13:12:30 +01:00
|
|
|
if (typelen == len && STREQLEN(typestr, tmp, len) &&
|
2013-07-25 19:13:44 +01:00
|
|
|
group->controllers[i].mountPoint != NULL &&
|
|
|
|
group->controllers[i].placement == NULL) {
|
|
|
|
if (i == VIR_CGROUP_CONTROLLER_SYSTEMD) {
|
|
|
|
if (VIR_STRDUP(group->controllers[i].placement,
|
|
|
|
selfpath) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
if (virAsprintf(&group->controllers[i].placement,
|
|
|
|
"%s%s%s", selfpath,
|
|
|
|
(STREQ(selfpath, "/") ||
|
|
|
|
STREQ(path, "") ? "" : "/"),
|
|
|
|
path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-03-22 11:11:34 +00:00
|
|
|
}
|
2009-07-09 14:10:41 +01:00
|
|
|
|
|
|
|
tmp = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
ret = 0;
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-19 11:13:05 +01:00
|
|
|
VIR_FREE(procfile);
|
2010-11-16 21:13:29 -05:00
|
|
|
VIR_FORCE_FCLOSE(mapping);
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupDetect(virCgroupPtr group,
|
|
|
|
pid_t pid,
|
|
|
|
int controllers,
|
|
|
|
const char *path,
|
|
|
|
virCgroupPtr parent)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
|
|
|
size_t j;
|
2013-03-22 11:11:34 +00:00
|
|
|
VIR_DEBUG("group=%p controllers=%d path=%s parent=%p",
|
|
|
|
group, controllers, path, parent);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (parent) {
|
|
|
|
if (virCgroupCopyMounts(group, parent) < 0)
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if (virCgroupDetectMounts(group) < 0)
|
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-03-21 13:27:13 +00:00
|
|
|
if (controllers >= 0) {
|
2013-05-23 15:26:31 +02:00
|
|
|
VIR_DEBUG("Filtering controllers %d", controllers);
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2013-03-22 11:11:34 +00:00
|
|
|
VIR_DEBUG("Controller '%s' wanted=%s, mount='%s'",
|
2013-03-21 13:27:13 +00:00
|
|
|
virCgroupControllerTypeToString(i),
|
2013-03-22 11:11:34 +00:00
|
|
|
(1 << i) & controllers ? "yes" : "no",
|
|
|
|
NULLSTR(group->controllers[i].mountPoint));
|
2013-03-21 13:27:13 +00:00
|
|
|
if (((1 << i) & controllers)) {
|
2013-05-23 15:26:31 +02:00
|
|
|
/* Remove non-existent controllers */
|
2013-03-21 13:27:13 +00:00
|
|
|
if (!group->controllers[i].mountPoint) {
|
2013-05-23 15:26:31 +02:00
|
|
|
VIR_DEBUG("Requested controller '%s' not mounted, ignoring",
|
2013-03-21 13:27:13 +00:00
|
|
|
virCgroupControllerTypeToString(i));
|
2013-05-23 15:26:31 +02:00
|
|
|
controllers &= ~(1 << i);
|
2013-03-21 13:27:13 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Check whether a request to disable a controller
|
|
|
|
* clashes with co-mounting of controllers */
|
2013-05-21 15:58:16 +08:00
|
|
|
for (j = 0; j < VIR_CGROUP_CONTROLLER_LAST; j++) {
|
2013-03-21 13:27:13 +00:00
|
|
|
if (j == i)
|
|
|
|
continue;
|
|
|
|
if (!((1 << j) & controllers))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (STREQ_NULLABLE(group->controllers[i].mountPoint,
|
|
|
|
group->controllers[j].mountPoint)) {
|
2013-07-04 16:49:24 +01:00
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Controller '%s' is not wanted, but '%s' is co-mounted"),
|
|
|
|
virCgroupControllerTypeToString(i),
|
|
|
|
virCgroupControllerTypeToString(j));
|
|
|
|
return -1;
|
2013-03-21 13:27:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
VIR_FREE(group->controllers[i].mountPoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Auto-detecting controllers");
|
|
|
|
controllers = 0;
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2013-03-21 13:27:13 +00:00
|
|
|
VIR_DEBUG("Controller '%s' present=%s",
|
|
|
|
virCgroupControllerTypeToString(i),
|
|
|
|
group->controllers[i].mountPoint ? "yes" : "no");
|
|
|
|
if (group->controllers[i].mountPoint == NULL)
|
|
|
|
continue;
|
|
|
|
controllers |= (1 << i);
|
|
|
|
}
|
2009-07-09 14:10:41 +01:00
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-03-21 13:27:13 +00:00
|
|
|
/* Check that at least 1 controller is available */
|
2013-03-22 11:11:34 +00:00
|
|
|
if (!controllers) {
|
2013-07-04 16:49:24 +01:00
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("At least one cgroup controller is required"));
|
|
|
|
return -1;
|
2013-03-22 11:11:34 +00:00
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-25 19:13:44 +01:00
|
|
|
/* In some cases we can copy part of the placement info
|
|
|
|
* based on the parent cgroup...
|
|
|
|
*/
|
|
|
|
if ((parent || path[0] == '/') &&
|
|
|
|
virCgroupCopyPlacement(group, path, parent) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* ... but use /proc/cgroups to fill in the rest */
|
|
|
|
if (virCgroupDetectPlacement(group, pid, path) < 0)
|
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
/* 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
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (!group->controllers[i].placement) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Could not find placement for controller %s at %s"),
|
|
|
|
virCgroupControllerTypeToString(i),
|
|
|
|
group->controllers[i].placement);
|
|
|
|
return -1;
|
2009-07-09 14:10:41 +01:00
|
|
|
}
|
2013-07-04 16:49:24 +01:00
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
VIR_DEBUG("Detected mount/mapping %zu:%s at %s in %s for pid %llu", i,
|
2013-07-04 16:49:24 +01:00
|
|
|
virCgroupControllerTypeToString(i),
|
|
|
|
group->controllers[i].mountPoint,
|
2013-07-19 11:13:05 +01:00
|
|
|
group->controllers[i].placement,
|
|
|
|
(unsigned long long)pid);
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
return 0;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupSetValueStr(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
const char *value)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
char *keypath = NULL;
|
2013-10-11 21:41:23 +08:00
|
|
|
char *tmp = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupPathOfController(group, controller, key, &keypath) < 0)
|
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2010-06-23 16:17:15 +01:00
|
|
|
VIR_DEBUG("Set value '%s' to '%s'", keypath, value);
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virFileWriteStr(keypath, value, 0) < 0) {
|
2013-10-11 21:41:23 +08:00
|
|
|
if (errno == EINVAL &&
|
|
|
|
(tmp = strrchr(keypath, '/'))) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Invalid value '%s' for '%s'"),
|
|
|
|
value, tmp + 1);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-07-08 11:08:46 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to write to '%s'"), keypath);
|
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(keypath);
|
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupGetValueStr(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
char **value)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
char *keypath = NULL;
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1, rc;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 14:10:06 +01:00
|
|
|
*value = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupPathOfController(group, controller, key, &keypath) < 0)
|
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 14:10:06 +01:00
|
|
|
VIR_DEBUG("Get value %s", keypath);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if ((rc = virFileReadAll(keypath, 1024*1024, value)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to read from '%s'"), keypath);
|
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
/* Terminated with '\n' has sometimes harmful effects to the caller */
|
|
|
|
if (rc > 0 && (*value)[rc - 1] == '\n')
|
|
|
|
(*value)[rc - 1] = '\0';
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(keypath);
|
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupSetValueU64(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
unsigned long long int value)
|
2009-07-09 14:10:06 +01:00
|
|
|
{
|
|
|
|
char *strval = NULL;
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret;
|
2009-07-09 14:10:06 +01:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virAsprintf(&strval, "%llu", value) < 0)
|
|
|
|
return -1;
|
2009-07-09 14:10:06 +01:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = virCgroupSetValueStr(group, controller, key, strval);
|
2009-07-09 14:10:06 +01:00
|
|
|
|
|
|
|
VIR_FREE(strval);
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
return ret;
|
2009-07-09 14:10:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupSetValueI64(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
long long int value)
|
2008-10-08 16:28:48 +00:00
|
|
|
{
|
|
|
|
char *strval = NULL;
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret;
|
2008-10-08 16:28:48 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virAsprintf(&strval, "%lld", value) < 0)
|
|
|
|
return -1;
|
2008-10-08 16:28:48 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = virCgroupSetValueStr(group, controller, key, strval);
|
2008-10-08 16:28:48 +00:00
|
|
|
|
|
|
|
VIR_FREE(strval);
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
return ret;
|
2008-10-08 16:28:48 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupGetValueI64(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
long long int *value)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
char *strval = NULL;
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupGetValueStr(group, controller, key, &strval) < 0)
|
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virStrToLong_ll(strval, NULL, 10, value) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to parse '%s' as an integer"),
|
|
|
|
strval);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(strval);
|
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupGetValueU64(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
unsigned long long int *value)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
char *strval = NULL;
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupGetValueStr(group, controller, key, &strval) < 0)
|
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virStrToLong_ull(strval, NULL, 10, value) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to parse '%s' as an integer"),
|
|
|
|
strval);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(strval);
|
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupCpuSetInherit(virCgroupPtr parent, virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2008-10-03 17:58:02 +00:00
|
|
|
const char *inherit_values[] = {
|
|
|
|
"cpuset.cpus",
|
|
|
|
"cpuset.mems",
|
|
|
|
};
|
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
VIR_DEBUG("Setting up inheritance %s -> %s", parent->path, group->path);
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < ARRAY_CARDINALITY(inherit_values); i++) {
|
2009-07-09 14:10:41 +01:00
|
|
|
char *value;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupGetValueStr(parent,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
inherit_values[i],
|
|
|
|
&value) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
2009-07-09 14:10:41 +01:00
|
|
|
|
|
|
|
VIR_DEBUG("Inherit %s = %s", inherit_values[i], value);
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
inherit_values[i],
|
|
|
|
value) < 0) {
|
|
|
|
VIR_FREE(value);
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(value);
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
return 0;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupSetMemoryUseHierarchy(virCgroupPtr group)
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
{
|
|
|
|
unsigned long long value;
|
|
|
|
const char *filename = "memory.use_hierarchy";
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
filename, &value) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
|
|
|
|
/* Setting twice causes error, so if already enabled, skip setting */
|
|
|
|
if (value == 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
VIR_DEBUG("Setting up %s/%s", group->path, filename);
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
filename, 1) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
return 0;
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCgroupMakeGroup(virCgroupPtr parent,
|
|
|
|
virCgroupPtr group,
|
|
|
|
bool create,
|
|
|
|
unsigned int flags)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2013-07-04 16:49:24 +01:00
|
|
|
int ret = -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
VIR_DEBUG("Make group %s", group->path);
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2008-10-03 17:58:02 +00:00
|
|
|
char *path = NULL;
|
|
|
|
|
2013-07-25 19:13:44 +01:00
|
|
|
/* We must never mkdir() in systemd's hierarchy */
|
|
|
|
if (i == VIR_CGROUP_CONTROLLER_SYSTEMD) {
|
|
|
|
VIR_DEBUG("Not creating systemd controller group");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
/* Skip over controllers that aren't mounted */
|
2013-04-03 18:06:41 +01:00
|
|
|
if (!group->controllers[i].mountPoint) {
|
|
|
|
VIR_DEBUG("Skipping unmounted controller %s",
|
|
|
|
virCgroupControllerTypeToString(i));
|
2008-10-03 17:58:02 +00:00
|
|
|
continue;
|
2013-04-03 18:06:41 +01:00
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupPathOfController(group, i, "", &path) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
2013-07-08 11:08:46 +01:00
|
|
|
|
2011-02-14 14:23:11 -07:00
|
|
|
/* As of Feb 2011, clang can't see that the above function
|
|
|
|
* call did not modify group. */
|
|
|
|
sa_assert(group->controllers[i].mountPoint);
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
VIR_DEBUG("Make controller %s", path);
|
2013-09-13 15:32:43 +02:00
|
|
|
if (!virFileExists(path)) {
|
2009-11-12 14:11:43 +00:00
|
|
|
if (!create ||
|
|
|
|
mkdir(path, 0755) < 0) {
|
2014-03-20 07:14:01 +00:00
|
|
|
if (errno == EEXIST) {
|
|
|
|
VIR_FREE(path);
|
|
|
|
continue;
|
|
|
|
}
|
2011-03-07 11:49:12 +08:00
|
|
|
/* With a kernel that doesn't support multi-level directory
|
|
|
|
* for blkio controller, libvirt will fail and disable all
|
|
|
|
* other controllers even though they are available. So
|
|
|
|
* treat blkio as unmounted if mkdir fails. */
|
|
|
|
if (i == VIR_CGROUP_CONTROLLER_BLKIO) {
|
2013-04-03 18:06:41 +01:00
|
|
|
VIR_DEBUG("Ignoring mkdir failure with blkio controller. Kernel probably too old");
|
2011-03-07 11:49:12 +08:00
|
|
|
VIR_FREE(group->controllers[i].mountPoint);
|
|
|
|
VIR_FREE(path);
|
|
|
|
continue;
|
|
|
|
} else {
|
2013-07-04 16:49:24 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Failed to create controller %s for group"),
|
|
|
|
virCgroupControllerTypeToString(i));
|
2011-03-07 11:49:12 +08:00
|
|
|
VIR_FREE(path);
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
2011-03-07 11:49:12 +08:00
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
2009-07-09 14:10:41 +01:00
|
|
|
if (group->controllers[VIR_CGROUP_CONTROLLER_CPUSET].mountPoint != NULL &&
|
|
|
|
(i == VIR_CGROUP_CONTROLLER_CPUSET ||
|
2013-08-12 13:52:06 -06:00
|
|
|
STREQ(group->controllers[i].mountPoint,
|
|
|
|
group->controllers[VIR_CGROUP_CONTROLLER_CPUSET].mountPoint))) {
|
2013-07-04 16:49:24 +01:00
|
|
|
if (virCgroupCpuSetInherit(parent, group) < 0) {
|
2010-05-04 04:04:51 +09:00
|
|
|
VIR_FREE(path);
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
2010-05-04 04:04:51 +09:00
|
|
|
}
|
2009-07-09 14:10:41 +01:00
|
|
|
}
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
/*
|
|
|
|
* Note that virCgroupSetMemoryUseHierarchy should always be
|
|
|
|
* called prior to creating subcgroups and attaching tasks.
|
|
|
|
*/
|
2011-07-21 15:12:55 +08:00
|
|
|
if ((flags & VIR_CGROUP_MEM_HIERACHY) &&
|
|
|
|
(group->controllers[VIR_CGROUP_CONTROLLER_MEMORY].mountPoint != NULL) &&
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
(i == VIR_CGROUP_CONTROLLER_MEMORY ||
|
2013-08-12 13:52:06 -06:00
|
|
|
STREQ(group->controllers[i].mountPoint,
|
|
|
|
group->controllers[VIR_CGROUP_CONTROLLER_MEMORY].mountPoint))) {
|
2013-07-04 16:49:24 +01:00
|
|
|
if (virCgroupSetMemoryUseHierarchy(group) < 0) {
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
VIR_FREE(path);
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
cgroup: Enable memory.use_hierarchy of cgroup for domain
Through conversation with Kumar L Srikanth-B22348, I found
that the function of getting memory usage (e.g., virsh dominfo)
doesn't work for lxc with ns subsystem of cgroup enabled.
This is because of features of ns and memory subsystems.
Ns creates child cgroup on every process fork and as a result
processes in a container are not assigned in a cgroup for
domain (e.g., libvirt/lxc/test1/). For example, libvirt_lxc
and init (or somewhat specified in XML) are assigned into
libvirt/lxc/test1/8839/ and libvirt/lxc/test1/8839/8849/,
respectively. On the other hand, memory subsystem accounts
memory usage within a group of processes by default, i.e.,
it does not take any child (and descendant) groups into
account. With the two features, virsh dominfo which just
checks memory usage of a cgroup for domain always returns
zero because the cgroup has no process.
Setting memory.use_hierarchy of a group allows to account
(and limit) memory usage of every descendant groups of the group.
By setting it of a cgroup for domain, we can get proper memory
usage of lxc with ns subsystem enabled. (To be exact, the
setting is required only when memory and ns subsystems are
enabled at the same time, e.g., mount -t cgroup none /cgroup.)
2010-06-24 01:00:56 +09:00
|
|
|
}
|
|
|
|
}
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(path);
|
|
|
|
}
|
|
|
|
|
2013-04-03 18:06:41 +01:00
|
|
|
VIR_DEBUG("Done making controllers for group");
|
2013-07-04 16:49:24 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-04 16:49:24 +01:00
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 14:10:41 +01:00
|
|
|
|
2013-03-22 11:11:34 +00:00
|
|
|
/**
|
|
|
|
* virCgroupNew:
|
|
|
|
* @path: path for the new group
|
|
|
|
* @parent: parent group, or NULL
|
|
|
|
* @controllers: bitmask of controllers to activate
|
|
|
|
*
|
|
|
|
* Create a new cgroup storing it in @group.
|
|
|
|
*
|
|
|
|
* If @path starts with a '/' it is treated as an
|
|
|
|
* absolute path, and @parent is ignored. Otherwise
|
|
|
|
* it is treated as being relative to @parent. If
|
|
|
|
* @parent is NULL, then the placement of the current
|
|
|
|
* process is used.
|
|
|
|
*
|
2013-07-04 16:49:24 +01:00
|
|
|
* Returns 0 on success, -1 on error
|
2013-03-22 11:11:34 +00:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupNew(pid_t pid,
|
|
|
|
const char *path,
|
|
|
|
virCgroupPtr parent,
|
|
|
|
int controllers,
|
|
|
|
virCgroupPtr *group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2013-03-22 11:11:34 +00:00
|
|
|
VIR_DEBUG("parent=%p path=%s controllers=%d",
|
|
|
|
parent, path, controllers);
|
2009-07-09 14:10:41 +01:00
|
|
|
*group = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (VIR_ALLOC((*group)) < 0)
|
|
|
|
goto error;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-03-22 11:11:34 +00:00
|
|
|
if (path[0] == '/' || !parent) {
|
2013-07-04 16:49:24 +01:00
|
|
|
if (VIR_STRDUP((*group)->path, path) < 0)
|
|
|
|
goto error;
|
2013-03-22 11:11:34 +00:00
|
|
|
} else {
|
|
|
|
if (virAsprintf(&(*group)->path, "%s%s%s",
|
|
|
|
parent->path,
|
|
|
|
STREQ(parent->path, "") ? "" : "/",
|
2013-07-04 16:49:24 +01:00
|
|
|
path) < 0)
|
|
|
|
goto error;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
if (virCgroupDetect(*group, pid, controllers, path, parent) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
goto error;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
return 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
error:
|
2009-07-09 14:10:41 +01:00
|
|
|
virCgroupFree(group);
|
|
|
|
*group = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
return -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
2009-07-10 11:40:04 +01:00
|
|
|
|
2013-03-21 13:27:13 +00:00
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
/**
|
|
|
|
* virCgroupAddTask:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to add a task to
|
|
|
|
* @pid: The pid of the task to add
|
|
|
|
*
|
2013-07-08 11:08:46 +01:00
|
|
|
* Returns: 0 on success, -1 on error
|
2008-10-03 17:58:02 +00:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupAddTask(virCgroupPtr group, pid_t pid)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2009-07-09 14:10:41 +01:00
|
|
|
/* Skip over controllers not mounted */
|
|
|
|
if (!group->controllers[i].mountPoint)
|
|
|
|
continue;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-25 19:13:44 +01:00
|
|
|
/* We must never add tasks in systemd's hierarchy */
|
|
|
|
if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
|
|
|
|
continue;
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
if (virCgroupSetValueU64(group, i, "tasks", pid) < 0)
|
2013-07-08 11:08:46 +01:00
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2012-08-21 17:18:25 +08:00
|
|
|
/**
|
|
|
|
* virCgroupAddTaskController:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to add a task to
|
|
|
|
* @pid: The pid of the task to add
|
|
|
|
* @controller: The cgroup controller to be operated on
|
|
|
|
*
|
2013-07-08 11:08:46 +01:00
|
|
|
* Returns: 0 on success or -1 on error
|
2012-08-21 17:18:25 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupAddTaskController(virCgroupPtr group, pid_t pid, int controller)
|
2012-08-21 17:18:25 +08:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
if (controller < 0 || controller >= VIR_CGROUP_CONTROLLER_LAST) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Controller %d out of range"), controller);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-08-21 17:18:25 +08:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (!group->controllers[controller].mountPoint) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Controller '%s' not mounted"),
|
|
|
|
virCgroupControllerTypeToString(controller));
|
|
|
|
return -1;
|
|
|
|
}
|
2012-08-21 17:18:25 +08:00
|
|
|
|
|
|
|
return virCgroupSetValueU64(group, controller, "tasks",
|
|
|
|
(unsigned long long)pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupAddTaskStrController(virCgroupPtr group,
|
|
|
|
const char *pidstr,
|
|
|
|
int controller)
|
2012-08-21 17:18:25 +08:00
|
|
|
{
|
|
|
|
char *str = NULL, *cur = NULL, *next = NULL;
|
|
|
|
unsigned long long p = 0;
|
|
|
|
int rc = 0;
|
|
|
|
char *endp;
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (VIR_STRDUP(str, pidstr) < 0)
|
|
|
|
return -1;
|
2012-08-21 17:18:25 +08:00
|
|
|
|
|
|
|
cur = str;
|
|
|
|
while (*cur != '\0') {
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virStrToLong_ull(cur, &endp, 10, &p) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot parse '%s' as an integer"), cur);
|
2012-08-21 17:18:25 +08:00
|
|
|
goto cleanup;
|
2013-07-08 11:08:46 +01:00
|
|
|
}
|
2012-08-21 17:18:25 +08:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupAddTaskController(group, p, controller) < 0) {
|
|
|
|
/* A thread that exits between when we first read the source
|
|
|
|
* tasks and now is not fatal. */
|
|
|
|
if (virLastErrorIsSystemErrno(ESRCH))
|
|
|
|
virResetLastError();
|
|
|
|
else
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-08-21 17:18:25 +08:00
|
|
|
|
|
|
|
next = strchr(cur, '\n');
|
|
|
|
if (next) {
|
|
|
|
cur = next + 1;
|
|
|
|
*next = '\0';
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2012-08-21 17:18:25 +08:00
|
|
|
VIR_FREE(str);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2012-08-21 17:18:25 +08:00
|
|
|
/**
|
|
|
|
* virCgroupMoveTask:
|
|
|
|
*
|
|
|
|
* @src_group: The source cgroup where all tasks are removed from
|
|
|
|
* @dest_group: The destination where all tasks are added to
|
|
|
|
*
|
2013-07-08 11:08:46 +01:00
|
|
|
* Returns: 0 on success or -1 on failure
|
2012-08-21 17:18:25 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupMoveTask(virCgroupPtr src_group, virCgroupPtr dest_group)
|
2012-08-21 17:18:25 +08:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2012-08-21 17:18:25 +08:00
|
|
|
char *content = NULL;
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2012-08-21 17:18:25 +08:00
|
|
|
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2013-03-21 13:27:13 +00:00
|
|
|
if (!src_group->controllers[i].mountPoint ||
|
|
|
|
!dest_group->controllers[i].mountPoint)
|
|
|
|
continue;
|
2012-08-21 17:18:25 +08:00
|
|
|
|
2013-07-25 19:13:44 +01:00
|
|
|
/* We must never move tasks in systemd's hierarchy */
|
|
|
|
if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
|
|
|
|
continue;
|
|
|
|
|
cgroup: be robust against cgroup movement races
https://bugzilla.redhat.com/show_bug.cgi?id=965169 documents a
problem starting domains when cgroups are enabled; I was able
to reliably reproduce the race about 5% of the time when I added
hooks to domain startup by 3 seconds (as that seemed to be about
the length of time that qemu created and then closed a temporary
thread, probably related to aio handling of initially opening
a disk image). The problem has existed since we introduced
virCgroupMoveTask in commit 9102829 (v0.10.0).
There are some inherent TOCTTOU races when moving tasks between
kernel cgroups, precisely because threads can be created or
completed in the window between when we read a thread id from the
source and when we write to the destination. As the goal of
virCgroupMoveTask is merely to move ALL tasks into the new
cgroup, it is sufficient to iterate until no more threads are
being created in the old group, and ignoring any threads that
die before we can move them.
It would be nicer to start the threads in the right cgroup to
begin with, but by default, all child threads are created in
the same cgroup as their parent, and we don't want vcpu child
threads in the emulator cgroup, so I don't see any good way
of avoiding the move. It would also be nice if the kernel were
to implement something like rename() as a way to atomically move
a group of threads from one cgroup to another, instead of forcing
a window where we have to read and parse the source, then format
and write back into the destination.
* src/util/vircgroup.c (virCgroupAddTaskStrController): Ignore
ESRCH, because a thread ended between read and write attempts.
(virCgroupMoveTask): Loop until all threads have moved.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-20 20:30:30 -06:00
|
|
|
/* New threads are created in the same group as their parent;
|
|
|
|
* but if a thread is created after we first read we aren't
|
|
|
|
* aware that it needs to move. Therefore, we must iterate
|
|
|
|
* until content is empty. */
|
|
|
|
while (1) {
|
2013-06-26 14:55:27 +02:00
|
|
|
VIR_FREE(content);
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupGetValueStr(src_group, i, "tasks", &content) < 0)
|
|
|
|
return -1;
|
|
|
|
|
cgroup: be robust against cgroup movement races
https://bugzilla.redhat.com/show_bug.cgi?id=965169 documents a
problem starting domains when cgroups are enabled; I was able
to reliably reproduce the race about 5% of the time when I added
hooks to domain startup by 3 seconds (as that seemed to be about
the length of time that qemu created and then closed a temporary
thread, probably related to aio handling of initially opening
a disk image). The problem has existed since we introduced
virCgroupMoveTask in commit 9102829 (v0.10.0).
There are some inherent TOCTTOU races when moving tasks between
kernel cgroups, precisely because threads can be created or
completed in the window between when we read a thread id from the
source and when we write to the destination. As the goal of
virCgroupMoveTask is merely to move ALL tasks into the new
cgroup, it is sufficient to iterate until no more threads are
being created in the old group, and ignoring any threads that
die before we can move them.
It would be nicer to start the threads in the right cgroup to
begin with, but by default, all child threads are created in
the same cgroup as their parent, and we don't want vcpu child
threads in the emulator cgroup, so I don't see any good way
of avoiding the move. It would also be nice if the kernel were
to implement something like rename() as a way to atomically move
a group of threads from one cgroup to another, instead of forcing
a window where we have to read and parse the source, then format
and write back into the destination.
* src/util/vircgroup.c (virCgroupAddTaskStrController): Ignore
ESRCH, because a thread ended between read and write attempts.
(virCgroupMoveTask): Loop until all threads have moved.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-20 20:30:30 -06:00
|
|
|
if (!*content)
|
|
|
|
break;
|
2012-08-21 17:18:25 +08:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupAddTaskStrController(dest_group, content, i) < 0)
|
cgroup: be robust against cgroup movement races
https://bugzilla.redhat.com/show_bug.cgi?id=965169 documents a
problem starting domains when cgroups are enabled; I was able
to reliably reproduce the race about 5% of the time when I added
hooks to domain startup by 3 seconds (as that seemed to be about
the length of time that qemu created and then closed a temporary
thread, probably related to aio handling of initially opening
a disk image). The problem has existed since we introduced
virCgroupMoveTask in commit 9102829 (v0.10.0).
There are some inherent TOCTTOU races when moving tasks between
kernel cgroups, precisely because threads can be created or
completed in the window between when we read a thread id from the
source and when we write to the destination. As the goal of
virCgroupMoveTask is merely to move ALL tasks into the new
cgroup, it is sufficient to iterate until no more threads are
being created in the old group, and ignoring any threads that
die before we can move them.
It would be nicer to start the threads in the right cgroup to
begin with, but by default, all child threads are created in
the same cgroup as their parent, and we don't want vcpu child
threads in the emulator cgroup, so I don't see any good way
of avoiding the move. It would also be nice if the kernel were
to implement something like rename() as a way to atomically move
a group of threads from one cgroup to another, instead of forcing
a window where we have to read and parse the source, then format
and write back into the destination.
* src/util/vircgroup.c (virCgroupAddTaskStrController): Ignore
ESRCH, because a thread ended between read and write attempts.
(virCgroupMoveTask): Loop until all threads have moved.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-20 20:30:30 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-03-21 13:27:13 +00:00
|
|
|
}
|
2012-08-21 17:18:25 +08:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2012-08-21 17:18:25 +08:00
|
|
|
VIR_FREE(content);
|
2013-07-08 11:08:46 +01:00
|
|
|
return ret;
|
2012-08-21 17:18:25 +08:00
|
|
|
}
|
2009-07-10 11:40:04 +01:00
|
|
|
|
2013-03-28 18:08:39 +00:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupSetPartitionSuffix(const char *path, char **res)
|
2013-04-26 10:23:51 +01:00
|
|
|
{
|
2013-07-04 16:49:24 +01:00
|
|
|
char **tokens;
|
2013-04-26 10:23:51 +01:00
|
|
|
size_t i;
|
2013-05-10 12:05:00 -06:00
|
|
|
int ret = -1;
|
2013-04-26 10:23:51 +01:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (!(tokens = virStringSplit(path, "/", 0)))
|
2013-05-10 12:05:00 -06:00
|
|
|
return ret;
|
2013-04-26 10:23:51 +01:00
|
|
|
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; tokens[i] != NULL; i++) {
|
2013-04-26 10:23:51 +01:00
|
|
|
/* Whitelist the 3 top level fixed dirs
|
|
|
|
* NB i == 0 is "", since we have leading '/'
|
|
|
|
*/
|
|
|
|
if (i == 1 &&
|
|
|
|
(STREQ(tokens[i], "machine") ||
|
|
|
|
STREQ(tokens[i], "system") ||
|
|
|
|
STREQ(tokens[i], "user"))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* If there is no suffix set already, then
|
|
|
|
* add ".partition"
|
|
|
|
*/
|
|
|
|
if (STRNEQ(tokens[i], "") &&
|
|
|
|
!strchr(tokens[i], '.')) {
|
|
|
|
if (VIR_REALLOC_N(tokens[i],
|
2013-07-04 16:49:24 +01:00
|
|
|
strlen(tokens[i]) + strlen(".partition") + 1) < 0)
|
2013-04-26 10:23:51 +01:00
|
|
|
goto cleanup;
|
|
|
|
strcat(tokens[i], ".partition");
|
|
|
|
}
|
2013-04-26 10:50:24 +01:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (virCgroupPartitionEscape(&(tokens[i])) < 0)
|
2013-04-26 10:50:24 +01:00
|
|
|
goto cleanup;
|
2013-04-26 10:23:51 +01:00
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (!(*res = virStringJoin((const char **)tokens, "/")))
|
2013-04-26 10:23:51 +01:00
|
|
|
goto cleanup;
|
2013-05-10 12:05:00 -06:00
|
|
|
|
|
|
|
ret = 0;
|
2013-04-26 10:23:51 +01:00
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-04-26 10:23:51 +01:00
|
|
|
virStringFreeList(tokens);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2013-03-28 18:08:39 +00:00
|
|
|
/**
|
|
|
|
* virCgroupNewPartition:
|
|
|
|
* @path: path for the partition
|
|
|
|
* @create: true to create the cgroup tree
|
|
|
|
* @controllers: mask of controllers to create
|
|
|
|
*
|
|
|
|
* Creates a new cgroup to represent the resource
|
|
|
|
* partition path identified by @name.
|
|
|
|
*
|
2013-07-04 16:49:24 +01:00
|
|
|
* Returns 0 on success, -1 on failure
|
2013-03-28 18:08:39 +00:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupNewPartition(const char *path,
|
|
|
|
bool create,
|
|
|
|
int controllers,
|
|
|
|
virCgroupPtr *group)
|
2013-03-28 18:08:39 +00:00
|
|
|
{
|
2013-07-04 16:49:24 +01:00
|
|
|
int ret = -1;
|
2013-03-28 18:08:39 +00:00
|
|
|
char *parentPath = NULL;
|
|
|
|
virCgroupPtr parent = NULL;
|
2013-12-06 11:38:14 +08:00
|
|
|
char *newPath = NULL;
|
2013-03-28 18:08:39 +00:00
|
|
|
VIR_DEBUG("path=%s create=%d controllers=%x",
|
|
|
|
path, create, controllers);
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (path[0] != '/') {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Partition path '%s' must start with '/'"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-03-28 18:08:39 +00:00
|
|
|
|
2013-12-06 11:38:14 +08:00
|
|
|
if (virCgroupSetPartitionSuffix(path, &newPath) < 0)
|
2013-04-26 10:23:51 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-12-06 11:38:14 +08:00
|
|
|
if (virCgroupNew(-1, newPath, NULL, controllers, group) < 0)
|
2013-03-28 18:08:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-12-06 11:38:14 +08:00
|
|
|
if (STRNEQ(newPath, "/")) {
|
2013-03-28 18:08:39 +00:00
|
|
|
char *tmp;
|
2013-12-06 11:38:14 +08:00
|
|
|
if (VIR_STRDUP(parentPath, newPath) < 0)
|
2013-03-28 18:08:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
tmp = strrchr(parentPath, '/');
|
|
|
|
tmp++;
|
|
|
|
*tmp = '\0';
|
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
if (virCgroupNew(-1, parentPath, NULL, controllers, &parent) < 0)
|
2013-03-28 18:08:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (virCgroupMakeGroup(parent, *group, create, VIR_CGROUP_NONE) < 0) {
|
2013-03-28 18:08:39 +00:00
|
|
|
virCgroupRemove(*group);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
ret = 0;
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-04 16:49:24 +01:00
|
|
|
if (ret != 0)
|
2013-03-28 18:08:39 +00:00
|
|
|
virCgroupFree(group);
|
|
|
|
virCgroupFree(&parent);
|
|
|
|
VIR_FREE(parentPath);
|
2013-12-06 11:38:14 +08:00
|
|
|
VIR_FREE(newPath);
|
2013-07-04 16:49:24 +01:00
|
|
|
return ret;
|
2013-03-28 18:08:39 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2012-11-12 15:02:26 +08:00
|
|
|
/**
|
2013-03-28 16:33:22 +00:00
|
|
|
* virCgroupNewSelf:
|
2012-11-12 15:02:26 +08:00
|
|
|
*
|
|
|
|
* @group: Pointer to returned virCgroupPtr
|
|
|
|
*
|
2013-03-21 11:53:14 +00:00
|
|
|
* Obtain a cgroup representing the config of the
|
|
|
|
* current process
|
|
|
|
*
|
2013-07-04 16:49:24 +01:00
|
|
|
* Returns 0 on success, or -1 on error
|
2012-11-12 15:02:26 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupNewSelf(virCgroupPtr *group)
|
2012-11-12 15:02:26 +08:00
|
|
|
{
|
2013-07-24 17:31:25 +01:00
|
|
|
return virCgroupNewDetect(-1, -1, group);
|
2012-11-12 15:02:26 +08:00
|
|
|
}
|
2013-07-19 11:13:05 +01:00
|
|
|
|
2009-07-10 11:40:04 +01:00
|
|
|
|
2013-03-28 18:08:39 +00:00
|
|
|
/**
|
|
|
|
* virCgroupNewDomainPartition:
|
|
|
|
*
|
|
|
|
* @partition: partition holding the domain
|
|
|
|
* @driver: name of the driver
|
|
|
|
* @name: name of the domain
|
|
|
|
* @group: Pointer to returned virCgroupPtr
|
|
|
|
*
|
2013-07-04 16:49:24 +01:00
|
|
|
* Returns 0 on success, or -1 on error
|
2013-03-28 18:08:39 +00:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupNewDomainPartition(virCgroupPtr partition,
|
|
|
|
const char *driver,
|
|
|
|
const char *name,
|
|
|
|
bool create,
|
|
|
|
virCgroupPtr *group)
|
2013-03-28 18:08:39 +00:00
|
|
|
{
|
2013-07-04 16:49:24 +01:00
|
|
|
int ret = -1;
|
2013-04-16 08:16:37 -04:00
|
|
|
char *grpname = NULL;
|
2013-03-28 18:08:39 +00:00
|
|
|
|
2013-04-26 09:52:47 +01:00
|
|
|
if (virAsprintf(&grpname, "%s.libvirt-%s",
|
2013-03-28 18:08:39 +00:00
|
|
|
name, driver) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
2013-03-28 18:08:39 +00:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (virCgroupPartitionEscape(&grpname) < 0)
|
|
|
|
goto cleanup;
|
2013-04-26 10:50:24 +01:00
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
if (virCgroupNew(-1, grpname, partition, -1, group) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a cgroup with memory.use_hierarchy enabled to
|
|
|
|
* surely account memory usage of lxc with ns subsystem
|
|
|
|
* enabled. (To be exact, memory and ns subsystems are
|
|
|
|
* enabled at the same time.)
|
|
|
|
*
|
|
|
|
* The reason why doing it here, not a upper group, say
|
|
|
|
* a group for driver, is to avoid overhead to track
|
|
|
|
* cumulative usage that we don't need.
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
if (virCgroupMakeGroup(partition, *group, create,
|
|
|
|
VIR_CGROUP_MEM_HIERACHY) < 0) {
|
2013-07-04 16:49:24 +01:00
|
|
|
virCgroupRemove(*group);
|
|
|
|
virCgroupFree(group);
|
|
|
|
goto cleanup;
|
2013-03-28 18:08:39 +00:00
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-04-16 08:16:37 -04:00
|
|
|
VIR_FREE(grpname);
|
2013-07-04 16:49:24 +01:00
|
|
|
return ret;
|
2013-03-28 18:08:39 +00:00
|
|
|
}
|
2009-07-10 11:40:04 +01:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-07-21 15:12:55 +08:00
|
|
|
/**
|
2013-03-28 16:33:22 +00:00
|
|
|
* virCgroupNewVcpu:
|
2011-07-21 15:12:55 +08:00
|
|
|
*
|
2013-03-28 16:33:22 +00:00
|
|
|
* @domain: group for the domain
|
2011-07-21 15:12:55 +08:00
|
|
|
* @vcpuid: id of the vcpu
|
2013-03-28 16:33:22 +00:00
|
|
|
* @create: true to create if not already existing
|
2011-07-21 15:12:55 +08:00
|
|
|
* @group: Pointer to returned virCgroupPtr
|
|
|
|
*
|
2013-07-04 16:49:24 +01:00
|
|
|
* Returns 0 on success, or -1 on error
|
2011-07-21 15:12:55 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupNewVcpu(virCgroupPtr domain,
|
|
|
|
int vcpuid,
|
|
|
|
bool create,
|
|
|
|
virCgroupPtr *group)
|
2011-07-21 15:12:55 +08:00
|
|
|
{
|
2013-07-04 16:49:24 +01:00
|
|
|
int ret = -1;
|
|
|
|
char *name = NULL;
|
2013-03-21 13:27:13 +00:00
|
|
|
int controllers;
|
2011-07-21 15:12:55 +08:00
|
|
|
|
2013-03-22 11:11:34 +00:00
|
|
|
if (virAsprintf(&name, "vcpu%d", vcpuid) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
2011-07-21 15:12:55 +08:00
|
|
|
|
2013-03-21 13:27:13 +00:00
|
|
|
controllers = ((1 << VIR_CGROUP_CONTROLLER_CPU) |
|
|
|
|
(1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
|
|
|
|
(1 << VIR_CGROUP_CONTROLLER_CPUSET));
|
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
if (virCgroupNew(-1, name, domain, controllers, group) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
2011-07-21 15:12:55 +08:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (virCgroupMakeGroup(domain, *group, create, VIR_CGROUP_NONE) < 0) {
|
|
|
|
virCgroupRemove(*group);
|
|
|
|
virCgroupFree(group);
|
|
|
|
goto cleanup;
|
2011-07-21 15:12:55 +08:00
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
ret = 0;
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-04 16:49:24 +01:00
|
|
|
VIR_FREE(name);
|
|
|
|
return ret;
|
2011-07-21 15:12:55 +08:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2012-08-21 17:18:24 +08:00
|
|
|
/**
|
2013-03-28 16:33:22 +00:00
|
|
|
* virCgroupNewEmulator:
|
2012-08-21 17:18:24 +08:00
|
|
|
*
|
2013-03-28 16:33:22 +00:00
|
|
|
* @domain: group for the domain
|
|
|
|
* @create: true to create if not already existing
|
2012-08-21 17:18:24 +08:00
|
|
|
* @group: Pointer to returned virCgroupPtr
|
|
|
|
*
|
2013-07-04 16:49:24 +01:00
|
|
|
* Returns: 0 on success or -1 on error
|
2012-08-21 17:18:24 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupNewEmulator(virCgroupPtr domain,
|
|
|
|
bool create,
|
|
|
|
virCgroupPtr *group)
|
2012-08-21 17:18:24 +08:00
|
|
|
{
|
2013-07-04 16:49:24 +01:00
|
|
|
int ret = -1;
|
2013-03-21 13:27:13 +00:00
|
|
|
int controllers;
|
2012-08-21 17:18:24 +08:00
|
|
|
|
2013-03-21 13:27:13 +00:00
|
|
|
controllers = ((1 << VIR_CGROUP_CONTROLLER_CPU) |
|
|
|
|
(1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
|
|
|
|
(1 << VIR_CGROUP_CONTROLLER_CPUSET));
|
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
if (virCgroupNew(-1, "emulator", domain, controllers, group) < 0)
|
2013-07-04 16:49:24 +01:00
|
|
|
goto cleanup;
|
2012-08-21 17:18:24 +08:00
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
if (virCgroupMakeGroup(domain, *group, create, VIR_CGROUP_NONE) < 0) {
|
|
|
|
virCgroupRemove(*group);
|
|
|
|
virCgroupFree(group);
|
|
|
|
goto cleanup;
|
2012-08-21 17:18:24 +08:00
|
|
|
}
|
|
|
|
|
2013-07-04 16:49:24 +01:00
|
|
|
ret = 0;
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-04 16:49:24 +01:00
|
|
|
return ret;
|
2012-08-21 17:18:24 +08:00
|
|
|
}
|
2013-07-04 16:49:24 +01:00
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
|
2014-09-03 09:04:07 -04:00
|
|
|
/**
|
|
|
|
* virCgroupNewIOThread:
|
|
|
|
*
|
|
|
|
* @domain: group for the domain
|
|
|
|
* @iothreadid: id of the iothread
|
|
|
|
* @create: true to create if not already existing
|
|
|
|
* @group: Pointer to returned virCgroupPtr
|
|
|
|
*
|
|
|
|
* Returns 0 on success, or -1 on error
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupNewIOThread(virCgroupPtr domain,
|
|
|
|
int iothreadid,
|
|
|
|
bool create,
|
|
|
|
virCgroupPtr *group)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
char *name = NULL;
|
|
|
|
int controllers;
|
|
|
|
|
|
|
|
if (virAsprintf(&name, "iothread%d", iothreadid) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
controllers = ((1 << VIR_CGROUP_CONTROLLER_CPU) |
|
|
|
|
(1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
|
|
|
|
(1 << VIR_CGROUP_CONTROLLER_CPUSET));
|
|
|
|
|
|
|
|
if (virCgroupNew(-1, name, domain, controllers, group) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupMakeGroup(domain, *group, create, VIR_CGROUP_NONE) < 0) {
|
|
|
|
virCgroupRemove(*group);
|
|
|
|
virCgroupFree(group);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupNewDetect(pid_t pid,
|
|
|
|
int controllers,
|
|
|
|
virCgroupPtr *group)
|
2013-07-19 11:13:05 +01:00
|
|
|
{
|
2013-07-24 17:31:25 +01:00
|
|
|
return virCgroupNew(pid, "", NULL, controllers, group);
|
2013-07-19 11:13:05 +01:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2013-07-24 17:36:42 +01:00
|
|
|
/*
|
|
|
|
* Returns 0 on success (but @group may be NULL), -1 on fatal error
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupNewDetectMachine(const char *name,
|
|
|
|
const char *drivername,
|
|
|
|
pid_t pid,
|
|
|
|
const char *partition,
|
|
|
|
int controllers,
|
|
|
|
virCgroupPtr *group)
|
2013-07-24 17:36:42 +01:00
|
|
|
{
|
2013-07-24 17:31:25 +01:00
|
|
|
if (virCgroupNewDetect(pid, controllers, group) < 0) {
|
2013-07-24 17:36:42 +01:00
|
|
|
if (virCgroupNewIgnoreError())
|
|
|
|
return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
if (!virCgroupValidateMachineGroup(*group, name, drivername, partition,
|
|
|
|
true)) {
|
2013-07-24 17:41:44 +01:00
|
|
|
VIR_DEBUG("Failed to validate machine name for '%s' driver '%s'",
|
|
|
|
name, drivername);
|
2013-07-24 17:36:42 +01:00
|
|
|
virCgroupFree(group);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2013-07-22 16:34:51 +01:00
|
|
|
/*
|
|
|
|
* Returns 0 on success, -1 on fatal error, -2 on systemd not available
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virCgroupNewMachineSystemd(const char *name,
|
|
|
|
const char *drivername,
|
|
|
|
bool privileged,
|
|
|
|
const unsigned char *uuid,
|
|
|
|
const char *rootdir,
|
|
|
|
pid_t pidleader,
|
|
|
|
bool isContainer,
|
|
|
|
const char *partition,
|
|
|
|
int controllers,
|
|
|
|
virCgroupPtr *group)
|
2013-07-18 16:55:37 +01:00
|
|
|
{
|
|
|
|
int ret = -1;
|
2013-07-22 16:34:51 +01:00
|
|
|
int rv;
|
|
|
|
virCgroupPtr init, parent = NULL;
|
|
|
|
char *path = NULL;
|
|
|
|
char *offset;
|
|
|
|
|
|
|
|
VIR_DEBUG("Trying to setup machine '%s' via systemd", name);
|
|
|
|
if ((rv = virSystemdCreateMachine(name,
|
|
|
|
drivername,
|
|
|
|
privileged,
|
|
|
|
uuid,
|
|
|
|
rootdir,
|
|
|
|
pidleader,
|
|
|
|
isContainer,
|
|
|
|
partition)) < 0)
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
if (controllers != -1)
|
|
|
|
controllers |= (1 << VIR_CGROUP_CONTROLLER_SYSTEMD);
|
|
|
|
|
|
|
|
VIR_DEBUG("Detecting systemd placement");
|
|
|
|
if (virCgroupNewDetect(pidleader,
|
|
|
|
controllers,
|
|
|
|
&init) < 0)
|
|
|
|
return -1;
|
2013-07-18 16:55:37 +01:00
|
|
|
|
2013-07-22 16:34:51 +01:00
|
|
|
path = init->controllers[VIR_CGROUP_CONTROLLER_SYSTEMD].placement;
|
|
|
|
init->controllers[VIR_CGROUP_CONTROLLER_SYSTEMD].placement = NULL;
|
|
|
|
virCgroupFree(&init);
|
|
|
|
|
|
|
|
if (!path || STREQ(path, "/") || path[0] != '/') {
|
|
|
|
VIR_DEBUG("Systemd didn't setup its controller");
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = path;
|
|
|
|
|
|
|
|
if (virCgroupNew(pidleader,
|
|
|
|
"",
|
|
|
|
NULL,
|
|
|
|
controllers,
|
|
|
|
&parent) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
virCgroupPtr tmp;
|
|
|
|
char *t = strchr(offset + 1, '/');
|
|
|
|
if (t)
|
|
|
|
*t = '\0';
|
|
|
|
|
|
|
|
if (virCgroupNew(pidleader,
|
|
|
|
path,
|
|
|
|
parent,
|
|
|
|
controllers,
|
|
|
|
&tmp) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupMakeGroup(parent, tmp, true, VIR_CGROUP_NONE) < 0) {
|
|
|
|
virCgroupFree(&tmp);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (t) {
|
|
|
|
*t = '/';
|
|
|
|
offset = t;
|
|
|
|
virCgroupFree(&parent);
|
|
|
|
parent = tmp;
|
|
|
|
} else {
|
|
|
|
*group = tmp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCgroupAddTask(*group, pidleader) < 0) {
|
|
|
|
virErrorPtr saved = virSaveLastError();
|
|
|
|
virCgroupRemove(*group);
|
|
|
|
virCgroupFree(group);
|
|
|
|
if (saved) {
|
|
|
|
virSetError(saved);
|
|
|
|
virFreeError(saved);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virCgroupFree(&parent);
|
|
|
|
VIR_FREE(path);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-07-18 16:55:37 +01:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2014-09-25 13:32:58 +02:00
|
|
|
/*
|
|
|
|
* Returns 0 on success, -1 on fatal error
|
|
|
|
*/
|
|
|
|
int virCgroupTerminateMachine(const char *name,
|
|
|
|
const char *drivername,
|
|
|
|
bool privileged)
|
|
|
|
{
|
|
|
|
return virSystemdTerminateMachine(name, drivername, privileged);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-22 16:34:51 +01:00
|
|
|
static int
|
|
|
|
virCgroupNewMachineManual(const char *name,
|
|
|
|
const char *drivername,
|
|
|
|
pid_t pidleader,
|
|
|
|
const char *partition,
|
|
|
|
int controllers,
|
|
|
|
virCgroupPtr *group)
|
|
|
|
{
|
|
|
|
virCgroupPtr parent = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Fallback to non-systemd setup");
|
2013-07-18 16:55:37 +01:00
|
|
|
if (virCgroupNewPartition(partition,
|
|
|
|
STREQ(partition, "/machine"),
|
|
|
|
controllers,
|
|
|
|
&parent) < 0) {
|
|
|
|
if (virCgroupNewIgnoreError())
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCgroupNewDomainPartition(parent,
|
|
|
|
drivername,
|
|
|
|
name,
|
|
|
|
true,
|
|
|
|
group) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupAddTask(*group, pidleader) < 0) {
|
|
|
|
virErrorPtr saved = virSaveLastError();
|
|
|
|
virCgroupRemove(*group);
|
|
|
|
virCgroupFree(group);
|
|
|
|
if (saved) {
|
|
|
|
virSetError(saved);
|
|
|
|
virFreeError(saved);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
done:
|
2013-07-18 16:55:37 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-18 16:55:37 +01:00
|
|
|
virCgroupFree(&parent);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupNewMachine(const char *name,
|
|
|
|
const char *drivername,
|
|
|
|
bool privileged,
|
|
|
|
const unsigned char *uuid,
|
|
|
|
const char *rootdir,
|
|
|
|
pid_t pidleader,
|
|
|
|
bool isContainer,
|
|
|
|
const char *partition,
|
|
|
|
int controllers,
|
|
|
|
virCgroupPtr *group)
|
2013-07-22 16:34:51 +01:00
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
*group = NULL;
|
|
|
|
|
|
|
|
if ((rv = virCgroupNewMachineSystemd(name,
|
|
|
|
drivername,
|
|
|
|
privileged,
|
|
|
|
uuid,
|
|
|
|
rootdir,
|
|
|
|
pidleader,
|
|
|
|
isContainer,
|
|
|
|
partition,
|
|
|
|
controllers,
|
|
|
|
group)) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (rv == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return virCgroupNewMachineManual(name,
|
|
|
|
drivername,
|
|
|
|
pidleader,
|
|
|
|
partition,
|
|
|
|
controllers,
|
|
|
|
group);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
bool
|
|
|
|
virCgroupNewIgnoreError(void)
|
2013-07-04 16:49:24 +01:00
|
|
|
{
|
|
|
|
if (virLastErrorIsSystemErrno(ENXIO) ||
|
|
|
|
virLastErrorIsSystemErrno(EPERM) ||
|
|
|
|
virLastErrorIsSystemErrno(EACCES)) {
|
|
|
|
virResetLastError();
|
|
|
|
VIR_DEBUG("No cgroups present/configured/accessible, ignoring error");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2013-08-12 15:20:59 -06:00
|
|
|
/**
|
|
|
|
* virCgroupFree:
|
|
|
|
*
|
|
|
|
* @group: The group structure to free
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virCgroupFree(virCgroupPtr *group)
|
|
|
|
{
|
|
|
|
size_t 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].linkPoint);
|
|
|
|
VIR_FREE((*group)->controllers[i].placement);
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE((*group)->path);
|
|
|
|
VIR_FREE(*group);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupHasController: query whether a cgroup controller is present
|
|
|
|
*
|
|
|
|
* @cgroup: The group structure to be queried, or NULL
|
|
|
|
* @controller: cgroup subsystem id
|
|
|
|
*
|
|
|
|
* Returns true if a cgroup controller is mounted and is associated
|
|
|
|
* with this cgroup object.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virCgroupHasController(virCgroupPtr cgroup, int controller)
|
|
|
|
{
|
|
|
|
if (!cgroup)
|
|
|
|
return false;
|
|
|
|
if (controller < 0 || controller >= VIR_CGROUP_CONTROLLER_LAST)
|
|
|
|
return false;
|
|
|
|
return cgroup->controllers[controller].mountPoint != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupPathOfController(virCgroupPtr group,
|
|
|
|
int controller,
|
|
|
|
const char *key,
|
|
|
|
char **path)
|
|
|
|
{
|
|
|
|
if (controller == -1) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
|
|
|
/* Reject any controller with a placement
|
|
|
|
* of '/' to avoid doing bad stuff to the root
|
|
|
|
* cgroup
|
|
|
|
*/
|
|
|
|
if (group->controllers[i].mountPoint &&
|
|
|
|
group->controllers[i].placement &&
|
|
|
|
STRNEQ(group->controllers[i].placement, "/")) {
|
|
|
|
controller = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (controller == -1) {
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("No controllers are mounted"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group->controllers[controller].mountPoint == NULL) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Controller '%s' is not mounted"),
|
|
|
|
virCgroupControllerTypeToString(controller));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group->controllers[controller].placement == NULL) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Controller '%s' is not enabled for group"),
|
|
|
|
virCgroupControllerTypeToString(controller));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(path, "%s%s/%s",
|
|
|
|
group->controllers[controller].mountPoint,
|
|
|
|
group->controllers[controller].placement,
|
|
|
|
key ? key : "") < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-14 18:48:59 +01:00
|
|
|
/**
|
|
|
|
* virCgroupGetBlkioIoServiced:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get throughput for
|
|
|
|
* @bytes_read: Pointer to returned bytes read
|
|
|
|
* @bytes_write: Pointer to returned bytes written
|
|
|
|
* @requests_read: Pointer to returned read io ops
|
|
|
|
* @requests_write: Pointer to returned write io ops
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupGetBlkioIoServiced(virCgroupPtr group,
|
|
|
|
long long *bytes_read,
|
|
|
|
long long *bytes_write,
|
|
|
|
long long *requests_read,
|
|
|
|
long long *requests_write)
|
|
|
|
{
|
|
|
|
long long stats_val;
|
|
|
|
char *str1 = NULL, *str2 = NULL, *p1, *p2;
|
|
|
|
size_t i;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
const char *value_names[] = {
|
|
|
|
"Read ",
|
|
|
|
"Write "
|
|
|
|
};
|
|
|
|
long long *bytes_ptrs[] = {
|
|
|
|
bytes_read,
|
|
|
|
bytes_write
|
|
|
|
};
|
|
|
|
long long *requests_ptrs[] = {
|
|
|
|
requests_read,
|
|
|
|
requests_write
|
|
|
|
};
|
|
|
|
|
|
|
|
*bytes_read = 0;
|
|
|
|
*bytes_write = 0;
|
|
|
|
*requests_read = 0;
|
|
|
|
*requests_write = 0;
|
|
|
|
|
|
|
|
if (virCgroupGetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.io_service_bytes", &str1) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupGetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.io_serviced", &str2) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* sum up all entries of the same kind, from all devices */
|
|
|
|
for (i = 0; i < ARRAY_CARDINALITY(value_names); i++) {
|
|
|
|
p1 = str1;
|
|
|
|
p2 = str2;
|
|
|
|
|
|
|
|
while ((p1 = strstr(p1, value_names[i]))) {
|
|
|
|
p1 += strlen(value_names[i]);
|
|
|
|
if (virStrToLong_ll(p1, &p1, 10, &stats_val) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot parse byte %sstat '%s'"),
|
|
|
|
value_names[i],
|
|
|
|
p1);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stats_val < 0 ||
|
|
|
|
(stats_val > 0 && *bytes_ptrs[i] > (LLONG_MAX - stats_val)))
|
|
|
|
{
|
|
|
|
virReportError(VIR_ERR_OVERFLOW,
|
|
|
|
_("Sum of byte %sstat overflows"),
|
|
|
|
value_names[i]);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*bytes_ptrs[i] += stats_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((p2 = strstr(p2, value_names[i]))) {
|
|
|
|
p2 += strlen(value_names[i]);
|
|
|
|
if (virStrToLong_ll(p2, &p2, 10, &stats_val) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot parse %srequest stat '%s'"),
|
|
|
|
value_names[i],
|
|
|
|
p2);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stats_val < 0 ||
|
|
|
|
(stats_val > 0 && *requests_ptrs[i] > (LLONG_MAX - stats_val)))
|
|
|
|
{
|
|
|
|
virReportError(VIR_ERR_OVERFLOW,
|
|
|
|
_("Sum of %srequest stat overflows"),
|
|
|
|
value_names[i]);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*requests_ptrs[i] += stats_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2014-02-14 18:48:59 +01:00
|
|
|
VIR_FREE(str2);
|
|
|
|
VIR_FREE(str1);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupGetBlkioIoDeviceServiced:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get throughput for
|
|
|
|
* @path: The device to get throughput for
|
|
|
|
* @bytes_read: Pointer to returned bytes read
|
|
|
|
* @bytes_write: Pointer to returned bytes written
|
|
|
|
* @requests_read: Pointer to returned read io ops
|
|
|
|
* @requests_write: Pointer to returned write io ops
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupGetBlkioIoDeviceServiced(virCgroupPtr group,
|
|
|
|
const char *path,
|
|
|
|
long long *bytes_read,
|
|
|
|
long long *bytes_write,
|
|
|
|
long long *requests_read,
|
|
|
|
long long *requests_write)
|
|
|
|
{
|
|
|
|
char *str1 = NULL, *str2 = NULL, *str3 = NULL, *p1, *p2;
|
|
|
|
struct stat sb;
|
|
|
|
size_t i;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
const char *value_names[] = {
|
|
|
|
"Read ",
|
|
|
|
"Write "
|
|
|
|
};
|
|
|
|
long long *bytes_ptrs[] = {
|
|
|
|
bytes_read,
|
|
|
|
bytes_write
|
|
|
|
};
|
|
|
|
long long *requests_ptrs[] = {
|
|
|
|
requests_read,
|
|
|
|
requests_write
|
|
|
|
};
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISBLK(sb.st_mode)) {
|
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Path '%s' must be a block device"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCgroupGetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.io_service_bytes", &str1) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupGetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.io_serviced", &str2) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virAsprintf(&str3, "%d:%d ", major(sb.st_rdev), minor(sb.st_rdev)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(p1 = strstr(str1, str3))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot find byte stats for block device '%s'"),
|
|
|
|
str3);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(p2 = strstr(str2, str3))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot find request stats for block device '%s'"),
|
|
|
|
str3);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_CARDINALITY(value_names); i++) {
|
|
|
|
if (!(p1 = strstr(p1, value_names[i]))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot find byte %sstats for block device '%s'"),
|
|
|
|
value_names[i], str3);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ll(p1 + strlen(value_names[i]), &p1, 10, bytes_ptrs[i]) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot parse %sstat '%s'"),
|
|
|
|
value_names[i], p1 + strlen(value_names[i]));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(p2 = strstr(p2, value_names[i]))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot find request %sstats for block device '%s'"),
|
|
|
|
value_names[i], str3);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ll(p2 + strlen(value_names[i]), &p2, 10, requests_ptrs[i]) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot parse %sstat '%s'"),
|
|
|
|
value_names[i], p2 + strlen(value_names[i]));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2014-02-14 18:48:59 +01:00
|
|
|
VIR_FREE(str3);
|
|
|
|
VIR_FREE(str2);
|
|
|
|
VIR_FREE(str1);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-08 14:56:39 +08:00
|
|
|
/**
|
|
|
|
* virCgroupSetBlkioWeight:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to change io weight for
|
|
|
|
* @weight: The Weight for this cgroup
|
|
|
|
*
|
2013-07-08 11:08:46 +01:00
|
|
|
* Returns: 0 on success, -1 on error
|
2011-02-08 14:56:39 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetBlkioWeight(virCgroupPtr group, unsigned int weight)
|
2011-02-08 14:56:39 +08:00
|
|
|
{
|
|
|
|
return virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.weight",
|
|
|
|
weight);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-02-08 14:56:39 +08:00
|
|
|
/**
|
|
|
|
* virCgroupGetBlkioWeight:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get weight for
|
|
|
|
* @Weight: Pointer to returned weight
|
|
|
|
*
|
2013-07-08 11:08:46 +01:00
|
|
|
* Returns: 0 on success, -1 on error
|
2011-02-08 14:56:39 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetBlkioWeight(virCgroupPtr group, unsigned int *weight)
|
2011-02-08 14:56:39 +08:00
|
|
|
{
|
|
|
|
unsigned long long tmp;
|
|
|
|
int ret;
|
|
|
|
ret = virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.weight", &tmp);
|
|
|
|
if (ret == 0)
|
|
|
|
*weight = tmp;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-11 16:29:50 +08:00
|
|
|
/**
|
|
|
|
* virCgroupSetBlkioDeviceReadIops:
|
|
|
|
* @group: The cgroup to change block io setting for
|
|
|
|
* @path: The path of device
|
|
|
|
* @riops: The new device read iops throttle, or 0 to clear
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceReadIops(virCgroupPtr group,
|
|
|
|
const char *path,
|
|
|
|
unsigned int riops)
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
struct stat sb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISBLK(sb.st_mode)) {
|
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Path '%s' must be a block device"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&str, "%d:%d %u", major(sb.st_rdev),
|
|
|
|
minor(sb.st_rdev), riops) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.read_iops_device",
|
|
|
|
str);
|
|
|
|
|
|
|
|
VIR_FREE(str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-11-08 19:00:34 +08:00
|
|
|
/**
|
2013-12-11 16:29:50 +08:00
|
|
|
* virCgroupSetBlkioDeviceWriteIops:
|
|
|
|
* @group: The cgroup to change block io setting for
|
|
|
|
* @path: The path of device
|
|
|
|
* @wiops: The new device write iops throttle, or 0 to clear
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceWriteIops(virCgroupPtr group,
|
|
|
|
const char *path,
|
|
|
|
unsigned int wiops)
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
struct stat sb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISBLK(sb.st_mode)) {
|
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Path '%s' must be a block device"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&str, "%d:%d %u", major(sb.st_rdev),
|
|
|
|
minor(sb.st_rdev), wiops) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.write_iops_device",
|
|
|
|
str);
|
|
|
|
|
|
|
|
VIR_FREE(str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupSetBlkioDeviceReadBps:
|
|
|
|
* @group: The cgroup to change block io setting for
|
|
|
|
* @path: The path of device
|
|
|
|
* @rbps: The new device read bps throttle, or 0 to clear
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceReadBps(virCgroupPtr group,
|
|
|
|
const char *path,
|
|
|
|
unsigned long long rbps)
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
struct stat sb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISBLK(sb.st_mode)) {
|
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Path '%s' must be a block device"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&str, "%d:%d %llu", major(sb.st_rdev),
|
|
|
|
minor(sb.st_rdev), rbps) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.read_bps_device",
|
|
|
|
str);
|
|
|
|
|
|
|
|
VIR_FREE(str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupSetBlkioDeviceWriteBps:
|
|
|
|
* @group: The cgroup to change block io setting for
|
|
|
|
* @path: The path of device
|
|
|
|
* @wbps: The new device write bps throttle, or 0 to clear
|
2011-11-08 19:00:34 +08:00
|
|
|
*
|
2013-12-11 16:29:50 +08:00
|
|
|
* Returns: 0 on success, -1 on error
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceWriteBps(virCgroupPtr group,
|
|
|
|
const char *path,
|
|
|
|
unsigned long long wbps)
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
struct stat sb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISBLK(sb.st_mode)) {
|
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Path '%s' must be a block device"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&str, "%d:%d %llu", major(sb.st_rdev),
|
|
|
|
minor(sb.st_rdev), wbps) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.throttle.write_bps_device",
|
|
|
|
str);
|
|
|
|
|
|
|
|
VIR_FREE(str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupSetBlkioDeviceWeight:
|
|
|
|
* @group: The cgroup to change block io setting for
|
|
|
|
* @path: The path of device
|
2013-10-11 21:41:23 +08:00
|
|
|
* @weight: The new device weight (100-1000),
|
|
|
|
* (10-1000) after kernel 2.6.39, or 0 to clear
|
2011-11-08 19:00:34 +08:00
|
|
|
*
|
|
|
|
* device_weight is treated as a write-only parameter, so
|
|
|
|
* there isn't a getter counterpart.
|
|
|
|
*
|
2013-07-08 11:08:46 +01:00
|
|
|
* Returns: 0 on success, -1 on error
|
2011-11-08 19:00:34 +08:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceWeight(virCgroupPtr group,
|
|
|
|
const char *path,
|
|
|
|
unsigned int weight)
|
2011-11-08 19:00:34 +08:00
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
struct stat sb;
|
|
|
|
int ret;
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-11-08 19:00:34 +08:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (!S_ISBLK(sb.st_mode)) {
|
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Path '%s' must be a block device"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-11-08 19:00:34 +08:00
|
|
|
|
|
|
|
if (virAsprintf(&str, "%d:%d %d", major(sb.st_rdev), minor(sb.st_rdev),
|
|
|
|
weight) < 0)
|
2013-07-08 11:08:46 +01:00
|
|
|
return -1;
|
2011-11-08 19:00:34 +08:00
|
|
|
|
|
|
|
ret = virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_BLKIO,
|
|
|
|
"blkio.weight_device",
|
|
|
|
str);
|
|
|
|
VIR_FREE(str);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-08-11 16:04:28 +04:00
|
|
|
|
2011-11-08 19:00:34 +08:00
|
|
|
|
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
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetMemory(virCgroupPtr group, unsigned long long kb)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2011-01-13 14:48:11 +05:30
|
|
|
unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (kb > maxkb) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Memory '%llu' must be less than %llu"),
|
|
|
|
kb, maxkb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kb == maxkb)
|
2011-01-13 14:48:11 +05:30
|
|
|
return virCgroupSetValueI64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.limit_in_bytes",
|
|
|
|
-1);
|
|
|
|
else
|
|
|
|
return virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.limit_in_bytes",
|
|
|
|
kb << 10);
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2009-10-07 15:26:23 +02:00
|
|
|
/**
|
|
|
|
* virCgroupGetMemoryUsage:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to change memory for
|
|
|
|
* @kb: Pointer to returned used memory in kilobytes
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetMemoryUsage(virCgroupPtr group, unsigned long *kb)
|
2009-10-07 15:26:23 +02:00
|
|
|
{
|
2009-10-08 10:26:42 -04:00
|
|
|
long long unsigned int usage_in_bytes;
|
2009-10-07 15:26:23 +02:00
|
|
|
int ret;
|
|
|
|
ret = virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.usage_in_bytes", &usage_in_bytes);
|
|
|
|
if (ret == 0)
|
|
|
|
*kb = (unsigned long) usage_in_bytes >> 10;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2010-10-12 16:50:53 +02:00
|
|
|
/**
|
|
|
|
* virCgroupSetMemoryHardLimit:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to change memory hard limit for
|
|
|
|
* @kb: The memory amount in kilobytes
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetMemoryHardLimit(virCgroupPtr group, unsigned long long kb)
|
2010-10-12 16:50:53 +02:00
|
|
|
{
|
|
|
|
return virCgroupSetMemory(group, kb);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2010-10-12 16:50:53 +02:00
|
|
|
/**
|
|
|
|
* virCgroupGetMemoryHardLimit:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get the memory hard limit for
|
|
|
|
* @kb: The memory amount in kilobytes
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetMemoryHardLimit(virCgroupPtr group, unsigned long long *kb)
|
2010-10-12 16:50:53 +02:00
|
|
|
{
|
|
|
|
long long unsigned int limit_in_bytes;
|
2013-12-04 16:54:29 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.limit_in_bytes", &limit_in_bytes) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
*kb = limit_in_bytes >> 10;
|
|
|
|
if (*kb > VIR_DOMAIN_MEMORY_PARAM_UNLIMITED)
|
|
|
|
*kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
2010-10-12 16:50:53 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2010-10-12 16:50:53 +02:00
|
|
|
/**
|
|
|
|
* virCgroupSetMemorySoftLimit:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to change memory soft limit for
|
|
|
|
* @kb: The memory amount in kilobytes
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetMemorySoftLimit(virCgroupPtr group, unsigned long long kb)
|
2010-10-12 16:50:53 +02:00
|
|
|
{
|
2011-01-13 14:48:11 +05:30
|
|
|
unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (kb > maxkb) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Memory '%llu' must be less than %llu"),
|
|
|
|
kb, maxkb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kb == maxkb)
|
2011-01-13 14:48:11 +05:30
|
|
|
return virCgroupSetValueI64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.soft_limit_in_bytes",
|
|
|
|
-1);
|
|
|
|
else
|
|
|
|
return virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.soft_limit_in_bytes",
|
|
|
|
kb << 10);
|
2010-10-12 16:50:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupGetMemorySoftLimit:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get the memory soft limit for
|
|
|
|
* @kb: The memory amount in kilobytes
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetMemorySoftLimit(virCgroupPtr group, unsigned long long *kb)
|
2010-10-12 16:50:53 +02:00
|
|
|
{
|
|
|
|
long long unsigned int limit_in_bytes;
|
2013-12-04 16:54:29 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.soft_limit_in_bytes", &limit_in_bytes) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
*kb = limit_in_bytes >> 10;
|
|
|
|
if (*kb > VIR_DOMAIN_MEMORY_PARAM_UNLIMITED)
|
|
|
|
*kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
2010-10-12 16:50:53 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2010-10-12 16:50:53 +02:00
|
|
|
/**
|
2011-03-16 10:37:12 +05:30
|
|
|
* virCgroupSetMemSwapHardLimit:
|
2010-10-12 16:50:53 +02:00
|
|
|
*
|
2011-03-16 10:37:12 +05:30
|
|
|
* @group: The cgroup to change mem+swap hard limit for
|
|
|
|
* @kb: The mem+swap amount in kilobytes
|
2010-10-12 16:50:53 +02:00
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetMemSwapHardLimit(virCgroupPtr group, unsigned long long kb)
|
2010-10-12 16:50:53 +02:00
|
|
|
{
|
2011-01-13 14:48:11 +05:30
|
|
|
unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (kb > maxkb) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Memory '%llu' must be less than %llu"),
|
|
|
|
kb, maxkb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kb == maxkb)
|
2011-01-13 14:48:11 +05:30
|
|
|
return virCgroupSetValueI64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.memsw.limit_in_bytes",
|
|
|
|
-1);
|
|
|
|
else
|
|
|
|
return virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.memsw.limit_in_bytes",
|
|
|
|
kb << 10);
|
2010-10-12 16:50:53 +02:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2010-10-12 16:50:53 +02:00
|
|
|
/**
|
2011-03-16 10:37:12 +05:30
|
|
|
* virCgroupGetMemSwapHardLimit:
|
2010-10-12 16:50:53 +02:00
|
|
|
*
|
2011-03-16 10:37:12 +05:30
|
|
|
* @group: The cgroup to get mem+swap hard limit for
|
|
|
|
* @kb: The mem+swap amount in kilobytes
|
2010-10-12 16:50:53 +02:00
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetMemSwapHardLimit(virCgroupPtr group, unsigned long long *kb)
|
2010-10-12 16:50:53 +02:00
|
|
|
{
|
|
|
|
long long unsigned int limit_in_bytes;
|
2013-12-04 16:54:29 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.memsw.limit_in_bytes", &limit_in_bytes) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
*kb = limit_in_bytes >> 10;
|
|
|
|
if (*kb > VIR_DOMAIN_MEMORY_PARAM_UNLIMITED)
|
|
|
|
*kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
2010-10-12 16:50:53 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2012-11-12 15:02:25 +08:00
|
|
|
/**
|
|
|
|
* virCgroupGetMemSwapUsage:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get mem+swap usage for
|
|
|
|
* @kb: The mem+swap amount in kilobytes
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetMemSwapUsage(virCgroupPtr group, unsigned long long *kb)
|
2012-11-12 15:02:25 +08:00
|
|
|
{
|
|
|
|
long long unsigned int usage_in_bytes;
|
|
|
|
int ret;
|
|
|
|
ret = virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY,
|
|
|
|
"memory.memsw.usage_in_bytes", &usage_in_bytes);
|
|
|
|
if (ret == 0)
|
|
|
|
*kb = usage_in_bytes >> 10;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-12-20 16:34:58 +08:00
|
|
|
/**
|
|
|
|
* virCgroupSetCpusetMems:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to set cpuset.mems for
|
|
|
|
* @mems: the numa nodes to set
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetCpusetMems(virCgroupPtr group, const char *mems)
|
2011-12-20 16:34:58 +08:00
|
|
|
{
|
|
|
|
return virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
"cpuset.mems",
|
|
|
|
mems);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-12-20 16:34:58 +08:00
|
|
|
/**
|
|
|
|
* virCgroupGetCpusetMems:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get cpuset.mems for
|
|
|
|
* @mems: the numa nodes to get
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetCpusetMems(virCgroupPtr group, char **mems)
|
2011-12-20 16:34:58 +08:00
|
|
|
{
|
|
|
|
return virCgroupGetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
"cpuset.mems",
|
|
|
|
mems);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2012-08-21 17:18:30 +08:00
|
|
|
/**
|
|
|
|
* virCgroupSetCpusetCpus:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to set cpuset.cpus for
|
|
|
|
* @cpus: the cpus to set
|
|
|
|
*
|
|
|
|
* Retuens: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetCpusetCpus(virCgroupPtr group, const char *cpus)
|
2012-08-21 17:18:30 +08:00
|
|
|
{
|
|
|
|
return virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
"cpuset.cpus",
|
|
|
|
cpus);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2012-08-21 17:18:30 +08:00
|
|
|
/**
|
|
|
|
* virCgroupGetCpusetCpus:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get cpuset.cpus for
|
|
|
|
* @cpus: the cpus to get
|
|
|
|
*
|
|
|
|
* Retuens: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetCpusetCpus(virCgroupPtr group, char **cpus)
|
2012-08-21 17:18:30 +08:00
|
|
|
{
|
|
|
|
return virCgroupGetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUSET,
|
|
|
|
"cpuset.cpus",
|
|
|
|
cpus);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2008-10-03 17:58:02 +00:00
|
|
|
/**
|
|
|
|
* virCgroupDenyAllDevices:
|
|
|
|
*
|
2011-03-08 20:13:18 -07:00
|
|
|
* @group: The cgroup to deny all permissions, for all devices
|
2008-10-03 17:58:02 +00:00
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupDenyAllDevices(virCgroupPtr group)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
|
|
|
return virCgroupSetValueStr(group,
|
2009-07-09 14:10:41 +01:00
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
|
|
"devices.deny",
|
|
|
|
"a");
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 10:02:29 +02:00
|
|
|
/**
|
|
|
|
* virCgroupAllowAllDevices:
|
|
|
|
*
|
|
|
|
* Allows the permissiong for all devices by setting lines similar
|
|
|
|
* to these ones (obviously the 'm' permission is an example):
|
|
|
|
*
|
|
|
|
* 'b *:* m'
|
|
|
|
* 'c *:* m'
|
|
|
|
*
|
|
|
|
* @group: The cgroup to allow devices for
|
|
|
|
* @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to allow
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupAllowAllDevices(virCgroupPtr group, int perms)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virCgroupAllowDevice(group, 'b', -1, -1, perms) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupAllowDevice(group, 'c', -1, -1, perms) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
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')
|
2014-07-18 10:02:29 +02:00
|
|
|
* @major: The major number of the device, a negative value means '*'
|
|
|
|
* @minor: The minor number of the device, a negative value means '*'
|
2011-03-08 20:13:18 -07:00
|
|
|
* @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to allow
|
2008-10-03 17:58:02 +00:00
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupAllowDevice(virCgroupPtr group, char type, int major, int minor,
|
|
|
|
int perms)
|
2008-10-03 17:58:02 +00:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2008-10-03 17:58:02 +00:00
|
|
|
char *devstr = NULL;
|
2014-07-18 10:02:29 +02:00
|
|
|
char *majorstr = NULL;
|
|
|
|
char *minorstr = NULL;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2014-07-18 10:02:29 +02:00
|
|
|
if ((major < 0 && VIR_STRDUP(majorstr, "*") < 0) ||
|
|
|
|
virAsprintf(&majorstr, "%i", major) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((minor < 0 && VIR_STRDUP(minorstr, "*") < 0) ||
|
|
|
|
virAsprintf(&minorstr, "%i", minor) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virAsprintf(&devstr, "%c %s:%s %s", type, majorstr, minorstr,
|
2014-06-20 13:33:16 +02:00
|
|
|
virCgroupGetDevicePermsString(perms)) < 0)
|
2013-07-08 11:08:46 +01:00
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
|
|
"devices.allow",
|
|
|
|
devstr) < 0)
|
|
|
|
goto cleanup;
|
2008-10-03 17:58:02 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(devstr);
|
2014-07-18 10:02:29 +02:00
|
|
|
VIR_FREE(majorstr);
|
|
|
|
VIR_FREE(minorstr);
|
2013-07-08 11:08:46 +01:00
|
|
|
return ret;
|
2008-10-03 17:58:02 +00:00
|
|
|
}
|
2008-10-08 16:28:48 +00:00
|
|
|
|
2013-08-12 13:52:06 -06: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
|
2011-03-08 20:13:18 -07:00
|
|
|
* @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to allow
|
2008-10-21 16:46:47 +00:00
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupAllowDeviceMajor(virCgroupPtr group, char type, int major,
|
|
|
|
int perms)
|
2008-10-21 16:46:47 +00:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2008-10-21 16:46:47 +00:00
|
|
|
char *devstr = NULL;
|
|
|
|
|
2014-06-20 13:33:16 +02:00
|
|
|
if (virAsprintf(&devstr, "%c %i:* %s", type, major,
|
|
|
|
virCgroupGetDevicePermsString(perms)) < 0)
|
2013-07-08 11:08:46 +01:00
|
|
|
goto cleanup;
|
2008-10-21 16:46:47 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
|
|
"devices.allow",
|
|
|
|
devstr) < 0)
|
|
|
|
goto cleanup;
|
2008-10-21 16:46:47 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(devstr);
|
|
|
|
return ret;
|
2008-10-21 16:46:47 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2009-07-09 14:11:49 +01:00
|
|
|
/**
|
|
|
|
* virCgroupAllowDevicePath:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to allow the device for
|
|
|
|
* @path: the device to allow
|
2011-03-08 20:13:18 -07:00
|
|
|
* @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to allow
|
2009-07-09 14:11:49 +01:00
|
|
|
*
|
|
|
|
* Queries the type of device and its major/minor number, and
|
|
|
|
* adds that to the cgroup ACL
|
|
|
|
*
|
2011-02-16 17:05:54 -07:00
|
|
|
* Returns: 0 on success, 1 if path exists but is not a device, or
|
2013-07-08 11:08:46 +01:00
|
|
|
* -1 on error
|
2009-07-09 14:11:49 +01:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupAllowDevicePath(virCgroupPtr group, const char *path, int perms)
|
2009-07-09 14:11:49 +01:00
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-07-09 14:11:49 +01:00
|
|
|
|
|
|
|
if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
|
2011-02-16 17:05:54 -07:00
|
|
|
return 1;
|
2009-07-09 14:11:49 +01:00
|
|
|
|
|
|
|
return virCgroupAllowDevice(group,
|
|
|
|
S_ISCHR(sb.st_mode) ? 'c' : 'b',
|
|
|
|
major(sb.st_rdev),
|
2011-03-08 20:13:18 -07:00
|
|
|
minor(sb.st_rdev),
|
|
|
|
perms);
|
2009-07-09 14:11:49 +01:00
|
|
|
}
|
2009-10-07 11:18:31 +01:00
|
|
|
|
2009-07-09 14:11:49 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupDenyDevice:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to deny 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
|
2011-03-08 20:13:18 -07:00
|
|
|
* @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to deny
|
2009-07-09 14:11:49 +01:00
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupDenyDevice(virCgroupPtr group, char type, int major, int minor,
|
|
|
|
int perms)
|
2009-07-09 14:11:49 +01:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2009-07-09 14:11:49 +01:00
|
|
|
char *devstr = NULL;
|
|
|
|
|
2014-06-20 13:33:16 +02:00
|
|
|
if (virAsprintf(&devstr, "%c %i:%i %s", type, major, minor,
|
|
|
|
virCgroupGetDevicePermsString(perms)) < 0)
|
2013-07-08 11:08:46 +01:00
|
|
|
goto cleanup;
|
2009-07-09 14:11:49 +01:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
|
|
"devices.deny",
|
|
|
|
devstr) < 0)
|
|
|
|
goto cleanup;
|
2009-07-09 14:11:49 +01:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(devstr);
|
|
|
|
return ret;
|
2009-07-09 14:11:49 +01:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2009-07-09 14:11:49 +01:00
|
|
|
/**
|
|
|
|
* virCgroupDenyDeviceMajor:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to deny an entire device major type for
|
|
|
|
* @type: The device type (i.e., 'c' or 'b')
|
|
|
|
* @major: The major number of the device type
|
2011-03-08 20:13:18 -07:00
|
|
|
* @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to deny
|
2009-07-09 14:11:49 +01:00
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupDenyDeviceMajor(virCgroupPtr group, char type, int major,
|
|
|
|
int perms)
|
2009-07-09 14:11:49 +01:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
int ret = -1;
|
2009-07-09 14:11:49 +01:00
|
|
|
char *devstr = NULL;
|
|
|
|
|
2014-06-20 13:33:16 +02:00
|
|
|
if (virAsprintf(&devstr, "%c %i:* %s", type, major,
|
|
|
|
virCgroupGetDevicePermsString(perms)) < 0)
|
2013-07-08 11:08:46 +01:00
|
|
|
goto cleanup;
|
2009-07-09 14:11:49 +01:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
|
|
"devices.deny",
|
|
|
|
devstr) < 0)
|
|
|
|
goto cleanup;
|
2009-07-09 14:11:49 +01:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(devstr);
|
|
|
|
return ret;
|
2009-07-09 14:11:49 +01:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupDenyDevicePath(virCgroupPtr group, const char *path, int perms)
|
2009-07-09 14:11:49 +01:00
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Path '%s' is not accessible"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-07-09 14:11:49 +01:00
|
|
|
|
|
|
|
if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
|
2011-02-16 17:05:54 -07:00
|
|
|
return 1;
|
2009-07-09 14:11:49 +01:00
|
|
|
|
|
|
|
return virCgroupDenyDevice(group,
|
|
|
|
S_ISCHR(sb.st_mode) ? 'c' : 'b',
|
|
|
|
major(sb.st_rdev),
|
2011-03-08 20:13:18 -07:00
|
|
|
minor(sb.st_rdev),
|
|
|
|
perms);
|
2009-07-09 14:11:49 +01:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2014-04-03 17:53:43 +02:00
|
|
|
/* This function gets the sums of cpu time consumed by all vcpus.
|
|
|
|
* For example, if there are 4 physical cpus, and 2 vcpus in a domain,
|
|
|
|
* then for each vcpu, the cpuacct.usage_percpu looks like this:
|
|
|
|
* t0 t1 t2 t3
|
|
|
|
* and we have 2 groups of such data:
|
|
|
|
* v\p 0 1 2 3
|
|
|
|
* 0 t00 t01 t02 t03
|
|
|
|
* 1 t10 t11 t12 t13
|
|
|
|
* for each pcpu, the sum is cpu time consumed by all vcpus.
|
|
|
|
* s0 = t00 + t10
|
|
|
|
* s1 = t01 + t11
|
|
|
|
* s2 = t02 + t12
|
|
|
|
* s3 = t03 + t13
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virCgroupGetPercpuVcpuSum(virCgroupPtr group,
|
|
|
|
unsigned int nvcpupids,
|
|
|
|
unsigned long long *sum_cpu_time,
|
|
|
|
unsigned int num)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
char *buf = NULL;
|
|
|
|
virCgroupPtr group_vcpu = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < nvcpupids; i++) {
|
|
|
|
char *pos;
|
|
|
|
unsigned long long tmp;
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
if (virCgroupNewVcpu(group, i, false, &group_vcpu) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupGetCpuacctPercpuUsage(group_vcpu, &buf) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
pos = buf;
|
|
|
|
for (j = 0; j < num; j++) {
|
|
|
|
if (virStrToLong_ull(pos, &pos, 10, &tmp) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpuacct parse error"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
sum_cpu_time[j] += tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
virCgroupFree(&group_vcpu);
|
|
|
|
VIR_FREE(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virCgroupFree(&group_vcpu);
|
|
|
|
VIR_FREE(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-14 18:49:02 +01:00
|
|
|
int
|
|
|
|
virCgroupGetPercpuStats(virCgroupPtr group,
|
|
|
|
virTypedParameterPtr params,
|
|
|
|
unsigned int nparams,
|
|
|
|
int start_cpu,
|
2014-04-03 17:53:43 +02:00
|
|
|
unsigned int ncpus,
|
|
|
|
unsigned int nvcpupids)
|
2014-02-14 18:49:02 +01:00
|
|
|
{
|
|
|
|
int rv = -1;
|
|
|
|
size_t i;
|
2014-04-03 19:53:13 +02:00
|
|
|
int need_cpus, total_cpus;
|
2014-02-14 18:49:02 +01:00
|
|
|
char *pos;
|
|
|
|
char *buf = NULL;
|
2014-04-03 17:53:43 +02:00
|
|
|
unsigned long long *sum_cpu_time = NULL;
|
2014-02-14 18:49:02 +01:00
|
|
|
virTypedParameterPtr ent;
|
|
|
|
int param_idx;
|
|
|
|
unsigned long long cpu_time;
|
|
|
|
|
|
|
|
/* return the number of supported params */
|
2014-04-03 17:53:43 +02:00
|
|
|
if (nparams == 0 && ncpus != 0) {
|
|
|
|
if (nvcpupids == 0)
|
|
|
|
return CGROUP_NB_PER_CPU_STAT_PARAM;
|
|
|
|
else
|
|
|
|
return CGROUP_NB_PER_CPU_STAT_PARAM + 1;
|
|
|
|
}
|
2014-02-14 18:49:02 +01:00
|
|
|
|
|
|
|
/* To parse account file, we need to know how many cpus are present. */
|
2014-04-03 19:23:25 +02:00
|
|
|
if ((total_cpus = nodeGetCPUCount()) < 0)
|
2014-02-14 18:49:02 +01:00
|
|
|
return rv;
|
|
|
|
|
2014-04-03 19:23:25 +02:00
|
|
|
if (ncpus == 0)
|
|
|
|
return total_cpus;
|
2014-02-14 18:49:02 +01:00
|
|
|
|
2014-04-03 19:38:54 +02:00
|
|
|
if (start_cpu >= total_cpus) {
|
2014-02-14 18:49:02 +01:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("start_cpu %d larger than maximum of %d"),
|
2014-04-03 19:38:54 +02:00
|
|
|
start_cpu, total_cpus - 1);
|
2014-02-14 18:49:02 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we get percpu cputime accounting info. */
|
|
|
|
if (virCgroupGetCpuacctPercpuUsage(group, &buf))
|
|
|
|
goto cleanup;
|
|
|
|
pos = buf;
|
|
|
|
|
|
|
|
/* return percpu cputime in index 0 */
|
|
|
|
param_idx = 0;
|
|
|
|
|
|
|
|
/* number of cpus to compute */
|
2014-04-03 19:23:25 +02:00
|
|
|
need_cpus = MIN(total_cpus, start_cpu + ncpus);
|
2014-02-14 18:49:02 +01:00
|
|
|
|
2014-04-03 19:23:25 +02:00
|
|
|
for (i = 0; i < need_cpus; i++) {
|
2014-02-14 18:49:02 +01:00
|
|
|
if (virStrToLong_ull(pos, &pos, 10, &cpu_time) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpuacct parse error"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (i < start_cpu)
|
|
|
|
continue;
|
|
|
|
ent = ¶ms[(i - start_cpu) * nparams + param_idx];
|
|
|
|
if (virTypedParameterAssign(ent, VIR_DOMAIN_CPU_STATS_CPUTIME,
|
|
|
|
VIR_TYPED_PARAM_ULLONG, cpu_time) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-04-03 17:53:43 +02:00
|
|
|
if (nvcpupids == 0 || param_idx + 1 >= nparams)
|
|
|
|
goto success;
|
|
|
|
/* return percpu vcputime in index 1 */
|
|
|
|
param_idx++;
|
|
|
|
|
2014-04-03 19:23:25 +02:00
|
|
|
if (VIR_ALLOC_N(sum_cpu_time, need_cpus) < 0)
|
2014-04-03 17:53:43 +02:00
|
|
|
goto cleanup;
|
2014-04-03 19:23:25 +02:00
|
|
|
if (virCgroupGetPercpuVcpuSum(group, nvcpupids, sum_cpu_time, need_cpus) < 0)
|
2014-04-03 17:53:43 +02:00
|
|
|
goto cleanup;
|
|
|
|
|
2014-04-03 19:23:25 +02:00
|
|
|
for (i = start_cpu; i < need_cpus; i++) {
|
2014-04-03 17:53:43 +02:00
|
|
|
if (virTypedParameterAssign(¶ms[(i - start_cpu) * nparams +
|
|
|
|
param_idx],
|
|
|
|
VIR_DOMAIN_CPU_STATS_VCPUTIME,
|
|
|
|
VIR_TYPED_PARAM_ULLONG,
|
2014-04-03 19:23:25 +02:00
|
|
|
sum_cpu_time[i]) < 0)
|
2014-04-03 17:53:43 +02:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
success:
|
2014-04-04 09:26:23 +02:00
|
|
|
rv = param_idx + 1;
|
2014-02-14 18:49:02 +01:00
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2014-04-03 17:53:43 +02:00
|
|
|
VIR_FREE(sum_cpu_time);
|
2014-02-14 18:49:02 +01:00
|
|
|
VIR_FREE(buf);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2014-02-14 18:49:01 +01:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetDomainTotalCpuStats(virCgroupPtr group,
|
|
|
|
virTypedParameterPtr params,
|
|
|
|
int nparams)
|
|
|
|
{
|
|
|
|
unsigned long long cpu_time;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (nparams == 0) /* return supported number of params */
|
|
|
|
return CGROUP_NB_TOTAL_CPU_STAT_PARAM;
|
|
|
|
/* entry 0 is cputime */
|
|
|
|
ret = virCgroupGetCpuacctUsage(group, &cpu_time);
|
|
|
|
if (ret < 0) {
|
|
|
|
virReportSystemError(-ret, "%s", _("unable to get cpu account"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virTypedParameterAssign(¶ms[0], VIR_DOMAIN_CPU_STATS_CPUTIME,
|
|
|
|
VIR_TYPED_PARAM_ULLONG, cpu_time) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (nparams > 1) {
|
|
|
|
unsigned long long user;
|
|
|
|
unsigned long long sys;
|
|
|
|
|
|
|
|
ret = virCgroupGetCpuacctStat(group, &user, &sys);
|
|
|
|
if (ret < 0) {
|
|
|
|
virReportSystemError(-ret, "%s", _("unable to get cpu account"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virTypedParameterAssign(¶ms[1],
|
|
|
|
VIR_DOMAIN_CPU_STATS_USERTIME,
|
|
|
|
VIR_TYPED_PARAM_ULLONG, user) < 0)
|
|
|
|
return -1;
|
|
|
|
if (nparams > 2 &&
|
|
|
|
virTypedParameterAssign(¶ms[2],
|
|
|
|
VIR_DOMAIN_CPU_STATS_SYSTEMTIME,
|
|
|
|
VIR_TYPED_PARAM_ULLONG, sys) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (nparams > CGROUP_NB_TOTAL_CPU_STAT_PARAM)
|
|
|
|
nparams = CGROUP_NB_TOTAL_CPU_STAT_PARAM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nparams;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetCpuShares(virCgroupPtr group, unsigned long long shares)
|
2008-10-08 16:28:48 +00:00
|
|
|
{
|
2009-07-09 14:10:41 +01:00
|
|
|
return virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
2009-10-07 11:18:31 +01:00
|
|
|
"cpu.shares", shares);
|
2008-10-08 16:28:48 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares)
|
2008-10-08 16:28:48 +00:00
|
|
|
{
|
2009-07-09 14:10:41 +01:00
|
|
|
return virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
2009-10-07 11:18:31 +01:00
|
|
|
"cpu.shares", shares);
|
2008-10-08 16:28:48 +00:00
|
|
|
}
|
2009-03-06 14:44:04 +00:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-07-21 15:21:05 +08:00
|
|
|
/**
|
|
|
|
* virCgroupSetCpuCfsPeriod:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to change cpu.cfs_period_us for
|
|
|
|
* @cfs_period: The bandwidth period in usecs
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetCpuCfsPeriod(virCgroupPtr group, unsigned long long cfs_period)
|
2011-07-21 15:21:05 +08:00
|
|
|
{
|
2013-10-09 15:09:11 +08:00
|
|
|
/* The cfs_period should be greater or equal than 1ms, and less or equal
|
2011-07-21 15:21:05 +08:00
|
|
|
* than 1s.
|
|
|
|
*/
|
2013-07-08 11:08:46 +01:00
|
|
|
if (cfs_period < 1000 || cfs_period > 1000000) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("cfs_period '%llu' must be in range (1000, 1000000)"),
|
|
|
|
cfs_period);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-07-21 15:21:05 +08:00
|
|
|
|
|
|
|
return virCgroupSetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
|
|
|
"cpu.cfs_period_us", cfs_period);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-07-21 15:21:05 +08:00
|
|
|
/**
|
|
|
|
* virCgroupGetCpuCfsPeriod:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get cpu.cfs_period_us for
|
|
|
|
* @cfs_period: Pointer to the returned bandwidth period in usecs
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupGetCpuCfsPeriod(virCgroupPtr group, unsigned long long *cfs_period)
|
2011-07-21 15:21:05 +08:00
|
|
|
{
|
|
|
|
return virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
|
|
|
"cpu.cfs_period_us", cfs_period);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-07-21 15:21:05 +08:00
|
|
|
/**
|
|
|
|
* virCgroupSetCpuCfsQuota:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to change cpu.cfs_quota_us for
|
|
|
|
* @cfs_quota: the cpu bandwidth (in usecs) that this tg will be allowed to
|
|
|
|
* consume over period
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupSetCpuCfsQuota(virCgroupPtr group, long long cfs_quota)
|
2011-07-21 15:21:05 +08:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
/* The cfs_quota should be greater or equal than 1ms */
|
|
|
|
if (cfs_quota >= 0 &&
|
|
|
|
(cfs_quota < 1000 ||
|
|
|
|
cfs_quota > ULLONG_MAX / 1000)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("cfs_quota '%lld' must be in range (1000, %llu)"),
|
|
|
|
cfs_quota, ULLONG_MAX / 1000);
|
|
|
|
return -1;
|
2011-07-21 15:21:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return virCgroupSetValueI64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
|
|
|
"cpu.cfs_quota_us", cfs_quota);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuacctPercpuUsage(virCgroupPtr group, char **usage)
|
2012-03-02 10:54:23 +08:00
|
|
|
{
|
|
|
|
return virCgroupGetValueStr(group, VIR_CGROUP_CONTROLLER_CPUACCT,
|
|
|
|
"cpuacct.usage_percpu", usage);
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2013-08-12 15:20:59 -06:00
|
|
|
int
|
|
|
|
virCgroupRemoveRecursively(char *grppath)
|
|
|
|
{
|
|
|
|
DIR *grpdir;
|
|
|
|
struct dirent *ent;
|
|
|
|
int rc = 0;
|
2014-04-25 14:45:49 -06:00
|
|
|
int direrr;
|
2013-08-12 15:20:59 -06:00
|
|
|
|
|
|
|
grpdir = opendir(grppath);
|
|
|
|
if (grpdir == NULL) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
rc = -errno;
|
|
|
|
VIR_ERROR(_("Unable to open %s (%d)"), grppath, errno);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2014-04-25 14:45:49 -06:00
|
|
|
/* This is best-effort cleanup: we want to log failures with just
|
|
|
|
* VIR_ERROR instead of normal virReportError */
|
|
|
|
while ((direrr = virDirRead(grpdir, &ent, NULL)) > 0) {
|
2013-08-12 15:20:59 -06:00
|
|
|
char *path;
|
|
|
|
|
|
|
|
if (ent->d_name[0] == '.') continue;
|
|
|
|
if (ent->d_type != DT_DIR) continue;
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "%s/%s", grppath, ent->d_name) == -1) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = virCgroupRemoveRecursively(path);
|
|
|
|
VIR_FREE(path);
|
|
|
|
if (rc != 0)
|
|
|
|
break;
|
|
|
|
}
|
2014-04-25 14:45:49 -06:00
|
|
|
if (direrr < 0) {
|
|
|
|
rc = -errno;
|
|
|
|
VIR_ERROR(_("Failed to readdir for %s (%d)"), grppath, errno);
|
|
|
|
}
|
|
|
|
|
2013-08-12 15:20:59 -06:00
|
|
|
closedir(grpdir);
|
|
|
|
|
|
|
|
VIR_DEBUG("Removing cgroup %s", grppath);
|
|
|
|
if (rmdir(grppath) != 0 && errno != ENOENT) {
|
|
|
|
rc = -errno;
|
|
|
|
VIR_ERROR(_("Unable to remove %s (%d)"), grppath, errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCgroupRemove:
|
|
|
|
*
|
|
|
|
* @group: The group to be removed
|
|
|
|
*
|
|
|
|
* It first removes all child groups recursively
|
|
|
|
* in depth first order and then removes @group
|
|
|
|
* because the presence of the child groups
|
|
|
|
* prevents removing @group.
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupRemove(virCgroupPtr group)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
size_t i;
|
|
|
|
char *grppath = NULL;
|
|
|
|
|
|
|
|
VIR_DEBUG("Removing cgroup %s", group->path);
|
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
|
|
|
/* Skip over controllers not mounted */
|
|
|
|
if (!group->controllers[i].mountPoint)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* We must never rmdir() in systemd's hierarchy */
|
|
|
|
if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Don't delete the root group, if we accidentally
|
|
|
|
ended up in it for some reason */
|
|
|
|
if (STREQ(group->controllers[i].placement, "/"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virCgroupPathOfController(group,
|
|
|
|
i,
|
|
|
|
NULL,
|
|
|
|
&grppath) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
VIR_DEBUG("Removing cgroup %s and all child cgroups", grppath);
|
|
|
|
rc = virCgroupRemoveRecursively(grppath);
|
|
|
|
VIR_FREE(grppath);
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Done removing cgroup %s", group->path);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
/*
|
|
|
|
* Returns 1 if some PIDs are killed, 0 if none are killed, or -1 on error
|
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupKillInternal(virCgroupPtr group, int signum, virHashTablePtr pids)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
2013-07-19 15:43:04 +01:00
|
|
|
int ret = -1;
|
2013-05-24 12:14:02 +02:00
|
|
|
bool killedAny = false;
|
2011-02-22 17:33:59 +00:00
|
|
|
char *keypath = NULL;
|
|
|
|
bool done = false;
|
cgroup: avoid leaking a file
Clang detected a dead store to rc. It turns out that in fixing this,
I also found a FILE* leak.
This is a subtle change in behavior, although unlikely to hit. The
pidfile is a kernel file, so we've probably got more serious problems
under foot if we fail to parse one. However, the previous behavior
was that even if one pid file failed to parse, we tried others,
whereas now we give up on the first failure. Either way, though,
the function returns -1, so the caller will know that something is
going wrong, and that not all pids were necessarily reaped. Besides,
there were other instances already in the code where failure in the
inner loop aborted the outer loop.
* src/util/cgroup.c (virCgroupKillInternal): Abort rather than
resuming loop on fscanf failure, and cleanup file on error.
2011-05-03 15:46:06 -06:00
|
|
|
FILE *fp = NULL;
|
|
|
|
VIR_DEBUG("group=%p path=%s signum=%d pids=%p",
|
|
|
|
group, group->path, signum, pids);
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupPathOfController(group, -1, "tasks", &keypath) < 0)
|
2013-07-19 15:43:04 +01:00
|
|
|
return -1;
|
2011-02-22 17:33:59 +00:00
|
|
|
|
|
|
|
/* PIDs may be forking as we kill them, so loop
|
|
|
|
* until there are no new PIDs found
|
|
|
|
*/
|
|
|
|
while (!done) {
|
|
|
|
done = true;
|
|
|
|
if (!(fp = fopen(keypath, "r"))) {
|
2013-07-26 16:02:22 +01:00
|
|
|
if (errno == ENOENT) {
|
|
|
|
VIR_DEBUG("No file %s, assuming done", keypath);
|
|
|
|
killedAny = false;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Failed to read %s"),
|
|
|
|
keypath);
|
2011-02-22 17:33:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
while (!feof(fp)) {
|
build: use correct type for pid and similar types
No thanks to 64-bit windows, with 64-bit pid_t, we have to avoid
constructs like 'int pid'. Our API in libvirt-qemu cannot be
changed without breaking ABI; but then again, libvirt-qemu can
only be used on systems that support UNIX sockets, which rules
out Windows (even if qemu could be compiled there) - so for all
points on the call chain that interact with this API decision,
we require a different variable name to make it clear that we
audited the use for safety.
Adding a syntax-check rule only solves half the battle; anywhere
that uses printf on a pid_t still needs to be converted, but that
will be a separate patch.
* cfg.mk (sc_correct_id_types): New syntax check.
* src/libvirt-qemu.c (virDomainQemuAttach): Document why we didn't
use pid_t for pid, and validate for overflow.
* include/libvirt/libvirt-qemu.h (virDomainQemuAttach): Tweak name
for syntax check.
* src/vmware/vmware_conf.c (vmwareExtractPid): Likewise.
* src/driver.h (virDrvDomainQemuAttach): Likewise.
* tools/virsh.c (cmdQemuAttach): Likewise.
* src/remote/qemu_protocol.x (qemu_domain_attach_args): Likewise.
* src/qemu_protocol-structs (qemu_domain_attach_args): Likewise.
* src/util/cgroup.c (virCgroupPidCode, virCgroupKillInternal):
Likewise.
* src/qemu/qemu_command.c(qemuParseProcFileStrings): Likewise.
(qemuParseCommandLinePid): Use pid_t for pid.
* daemon/libvirtd.c (daemonForkIntoBackground): Likewise.
* src/conf/domain_conf.h (_virDomainObj): Likewise.
* src/probes.d (rpc_socket_new): Likewise.
* src/qemu/qemu_command.h (qemuParseCommandLinePid): Likewise.
* src/qemu/qemu_driver.c (qemudGetProcessInfo, qemuDomainAttach):
Likewise.
* src/qemu/qemu_process.c (qemuProcessAttach): Likewise.
* src/qemu/qemu_process.h (qemuProcessAttach): Likewise.
* src/uml/uml_driver.c (umlGetProcessInfo): Likewise.
* src/util/virnetdev.h (virNetDevSetNamespace): Likewise.
* src/util/virnetdev.c (virNetDevSetNamespace): Likewise.
* tests/testutils.c (virtTestCaptureProgramOutput): Likewise.
* src/conf/storage_conf.h (_virStoragePerms): Use mode_t, uid_t,
and gid_t rather than int.
* src/security/security_dac.c (virSecurityDACSetOwnership): Likewise.
* src/conf/storage_conf.c (virStorageDefParsePerms): Avoid
compiler warning.
2012-02-10 16:08:11 -07:00
|
|
|
unsigned long pid_value;
|
|
|
|
if (fscanf(fp, "%lu", &pid_value) != 1) {
|
2011-02-22 17:33:59 +00:00
|
|
|
if (feof(fp))
|
|
|
|
break;
|
2013-07-19 15:43:04 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Failed to read %s"),
|
|
|
|
keypath);
|
cgroup: avoid leaking a file
Clang detected a dead store to rc. It turns out that in fixing this,
I also found a FILE* leak.
This is a subtle change in behavior, although unlikely to hit. The
pidfile is a kernel file, so we've probably got more serious problems
under foot if we fail to parse one. However, the previous behavior
was that even if one pid file failed to parse, we tried others,
whereas now we give up on the first failure. Either way, though,
the function returns -1, so the caller will know that something is
going wrong, and that not all pids were necessarily reaped. Besides,
there were other instances already in the code where failure in the
inner loop aborted the outer loop.
* src/util/cgroup.c (virCgroupKillInternal): Abort rather than
resuming loop on fscanf failure, and cleanup file on error.
2011-05-03 15:46:06 -06:00
|
|
|
goto cleanup;
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
build: use correct type for pid and similar types
No thanks to 64-bit windows, with 64-bit pid_t, we have to avoid
constructs like 'int pid'. Our API in libvirt-qemu cannot be
changed without breaking ABI; but then again, libvirt-qemu can
only be used on systems that support UNIX sockets, which rules
out Windows (even if qemu could be compiled there) - so for all
points on the call chain that interact with this API decision,
we require a different variable name to make it clear that we
audited the use for safety.
Adding a syntax-check rule only solves half the battle; anywhere
that uses printf on a pid_t still needs to be converted, but that
will be a separate patch.
* cfg.mk (sc_correct_id_types): New syntax check.
* src/libvirt-qemu.c (virDomainQemuAttach): Document why we didn't
use pid_t for pid, and validate for overflow.
* include/libvirt/libvirt-qemu.h (virDomainQemuAttach): Tweak name
for syntax check.
* src/vmware/vmware_conf.c (vmwareExtractPid): Likewise.
* src/driver.h (virDrvDomainQemuAttach): Likewise.
* tools/virsh.c (cmdQemuAttach): Likewise.
* src/remote/qemu_protocol.x (qemu_domain_attach_args): Likewise.
* src/qemu_protocol-structs (qemu_domain_attach_args): Likewise.
* src/util/cgroup.c (virCgroupPidCode, virCgroupKillInternal):
Likewise.
* src/qemu/qemu_command.c(qemuParseProcFileStrings): Likewise.
(qemuParseCommandLinePid): Use pid_t for pid.
* daemon/libvirtd.c (daemonForkIntoBackground): Likewise.
* src/conf/domain_conf.h (_virDomainObj): Likewise.
* src/probes.d (rpc_socket_new): Likewise.
* src/qemu/qemu_command.h (qemuParseCommandLinePid): Likewise.
* src/qemu/qemu_driver.c (qemudGetProcessInfo, qemuDomainAttach):
Likewise.
* src/qemu/qemu_process.c (qemuProcessAttach): Likewise.
* src/qemu/qemu_process.h (qemuProcessAttach): Likewise.
* src/uml/uml_driver.c (umlGetProcessInfo): Likewise.
* src/util/virnetdev.h (virNetDevSetNamespace): Likewise.
* src/util/virnetdev.c (virNetDevSetNamespace): Likewise.
* tests/testutils.c (virtTestCaptureProgramOutput): Likewise.
* src/conf/storage_conf.h (_virStoragePerms): Use mode_t, uid_t,
and gid_t rather than int.
* src/security/security_dac.c (virSecurityDACSetOwnership): Likewise.
* src/conf/storage_conf.c (virStorageDefParsePerms): Avoid
compiler warning.
2012-02-10 16:08:11 -07:00
|
|
|
if (virHashLookup(pids, (void*)pid_value))
|
2011-02-22 17:33:59 +00:00
|
|
|
continue;
|
|
|
|
|
build: use correct type for pid and similar types
No thanks to 64-bit windows, with 64-bit pid_t, we have to avoid
constructs like 'int pid'. Our API in libvirt-qemu cannot be
changed without breaking ABI; but then again, libvirt-qemu can
only be used on systems that support UNIX sockets, which rules
out Windows (even if qemu could be compiled there) - so for all
points on the call chain that interact with this API decision,
we require a different variable name to make it clear that we
audited the use for safety.
Adding a syntax-check rule only solves half the battle; anywhere
that uses printf on a pid_t still needs to be converted, but that
will be a separate patch.
* cfg.mk (sc_correct_id_types): New syntax check.
* src/libvirt-qemu.c (virDomainQemuAttach): Document why we didn't
use pid_t for pid, and validate for overflow.
* include/libvirt/libvirt-qemu.h (virDomainQemuAttach): Tweak name
for syntax check.
* src/vmware/vmware_conf.c (vmwareExtractPid): Likewise.
* src/driver.h (virDrvDomainQemuAttach): Likewise.
* tools/virsh.c (cmdQemuAttach): Likewise.
* src/remote/qemu_protocol.x (qemu_domain_attach_args): Likewise.
* src/qemu_protocol-structs (qemu_domain_attach_args): Likewise.
* src/util/cgroup.c (virCgroupPidCode, virCgroupKillInternal):
Likewise.
* src/qemu/qemu_command.c(qemuParseProcFileStrings): Likewise.
(qemuParseCommandLinePid): Use pid_t for pid.
* daemon/libvirtd.c (daemonForkIntoBackground): Likewise.
* src/conf/domain_conf.h (_virDomainObj): Likewise.
* src/probes.d (rpc_socket_new): Likewise.
* src/qemu/qemu_command.h (qemuParseCommandLinePid): Likewise.
* src/qemu/qemu_driver.c (qemudGetProcessInfo, qemuDomainAttach):
Likewise.
* src/qemu/qemu_process.c (qemuProcessAttach): Likewise.
* src/qemu/qemu_process.h (qemuProcessAttach): Likewise.
* src/uml/uml_driver.c (umlGetProcessInfo): Likewise.
* src/util/virnetdev.h (virNetDevSetNamespace): Likewise.
* src/util/virnetdev.c (virNetDevSetNamespace): Likewise.
* tests/testutils.c (virtTestCaptureProgramOutput): Likewise.
* src/conf/storage_conf.h (_virStoragePerms): Use mode_t, uid_t,
and gid_t rather than int.
* src/security/security_dac.c (virSecurityDACSetOwnership): Likewise.
* src/conf/storage_conf.c (virStorageDefParsePerms): Avoid
compiler warning.
2012-02-10 16:08:11 -07:00
|
|
|
VIR_DEBUG("pid=%lu", pid_value);
|
|
|
|
/* Cgroups is a Linux concept, so this cast is safe. */
|
|
|
|
if (kill((pid_t)pid_value, signum) < 0) {
|
2011-02-22 17:33:59 +00:00
|
|
|
if (errno != ESRCH) {
|
2013-07-19 15:43:04 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Failed to kill process %lu"),
|
|
|
|
pid_value);
|
2011-02-22 17:33:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
/* Leave RC == 0 since we didn't kill one */
|
|
|
|
} else {
|
2013-05-24 12:14:02 +02:00
|
|
|
killedAny = true;
|
2011-02-22 17:33:59 +00:00
|
|
|
done = false;
|
|
|
|
}
|
|
|
|
|
build: use correct type for pid and similar types
No thanks to 64-bit windows, with 64-bit pid_t, we have to avoid
constructs like 'int pid'. Our API in libvirt-qemu cannot be
changed without breaking ABI; but then again, libvirt-qemu can
only be used on systems that support UNIX sockets, which rules
out Windows (even if qemu could be compiled there) - so for all
points on the call chain that interact with this API decision,
we require a different variable name to make it clear that we
audited the use for safety.
Adding a syntax-check rule only solves half the battle; anywhere
that uses printf on a pid_t still needs to be converted, but that
will be a separate patch.
* cfg.mk (sc_correct_id_types): New syntax check.
* src/libvirt-qemu.c (virDomainQemuAttach): Document why we didn't
use pid_t for pid, and validate for overflow.
* include/libvirt/libvirt-qemu.h (virDomainQemuAttach): Tweak name
for syntax check.
* src/vmware/vmware_conf.c (vmwareExtractPid): Likewise.
* src/driver.h (virDrvDomainQemuAttach): Likewise.
* tools/virsh.c (cmdQemuAttach): Likewise.
* src/remote/qemu_protocol.x (qemu_domain_attach_args): Likewise.
* src/qemu_protocol-structs (qemu_domain_attach_args): Likewise.
* src/util/cgroup.c (virCgroupPidCode, virCgroupKillInternal):
Likewise.
* src/qemu/qemu_command.c(qemuParseProcFileStrings): Likewise.
(qemuParseCommandLinePid): Use pid_t for pid.
* daemon/libvirtd.c (daemonForkIntoBackground): Likewise.
* src/conf/domain_conf.h (_virDomainObj): Likewise.
* src/probes.d (rpc_socket_new): Likewise.
* src/qemu/qemu_command.h (qemuParseCommandLinePid): Likewise.
* src/qemu/qemu_driver.c (qemudGetProcessInfo, qemuDomainAttach):
Likewise.
* src/qemu/qemu_process.c (qemuProcessAttach): Likewise.
* src/qemu/qemu_process.h (qemuProcessAttach): Likewise.
* src/uml/uml_driver.c (umlGetProcessInfo): Likewise.
* src/util/virnetdev.h (virNetDevSetNamespace): Likewise.
* src/util/virnetdev.c (virNetDevSetNamespace): Likewise.
* tests/testutils.c (virtTestCaptureProgramOutput): Likewise.
* src/conf/storage_conf.h (_virStoragePerms): Use mode_t, uid_t,
and gid_t rather than int.
* src/security/security_dac.c (virSecurityDACSetOwnership): Likewise.
* src/conf/storage_conf.c (virStorageDefParsePerms): Avoid
compiler warning.
2012-02-10 16:08:11 -07:00
|
|
|
ignore_value(virHashAddEntry(pids, (void*)pid_value, (void*)1));
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
|
|
|
VIR_FORCE_FCLOSE(fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-26 16:02:22 +01:00
|
|
|
done:
|
2013-07-19 15:43:04 +01:00
|
|
|
ret = killedAny ? 1 : 0;
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2011-02-22 17:33:59 +00:00
|
|
|
VIR_FREE(keypath);
|
cgroup: avoid leaking a file
Clang detected a dead store to rc. It turns out that in fixing this,
I also found a FILE* leak.
This is a subtle change in behavior, although unlikely to hit. The
pidfile is a kernel file, so we've probably got more serious problems
under foot if we fail to parse one. However, the previous behavior
was that even if one pid file failed to parse, we tried others,
whereas now we give up on the first failure. Either way, though,
the function returns -1, so the caller will know that something is
going wrong, and that not all pids were necessarily reaped. Besides,
there were other instances already in the code where failure in the
inner loop aborted the outer loop.
* src/util/cgroup.c (virCgroupKillInternal): Abort rather than
resuming loop on fscanf failure, and cleanup file on error.
2011-05-03 15:46:06 -06:00
|
|
|
VIR_FORCE_FCLOSE(fp);
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
return ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static uint32_t
|
|
|
|
virCgroupPidCode(const void *name, uint32_t seed)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
build: use correct type for pid and similar types
No thanks to 64-bit windows, with 64-bit pid_t, we have to avoid
constructs like 'int pid'. Our API in libvirt-qemu cannot be
changed without breaking ABI; but then again, libvirt-qemu can
only be used on systems that support UNIX sockets, which rules
out Windows (even if qemu could be compiled there) - so for all
points on the call chain that interact with this API decision,
we require a different variable name to make it clear that we
audited the use for safety.
Adding a syntax-check rule only solves half the battle; anywhere
that uses printf on a pid_t still needs to be converted, but that
will be a separate patch.
* cfg.mk (sc_correct_id_types): New syntax check.
* src/libvirt-qemu.c (virDomainQemuAttach): Document why we didn't
use pid_t for pid, and validate for overflow.
* include/libvirt/libvirt-qemu.h (virDomainQemuAttach): Tweak name
for syntax check.
* src/vmware/vmware_conf.c (vmwareExtractPid): Likewise.
* src/driver.h (virDrvDomainQemuAttach): Likewise.
* tools/virsh.c (cmdQemuAttach): Likewise.
* src/remote/qemu_protocol.x (qemu_domain_attach_args): Likewise.
* src/qemu_protocol-structs (qemu_domain_attach_args): Likewise.
* src/util/cgroup.c (virCgroupPidCode, virCgroupKillInternal):
Likewise.
* src/qemu/qemu_command.c(qemuParseProcFileStrings): Likewise.
(qemuParseCommandLinePid): Use pid_t for pid.
* daemon/libvirtd.c (daemonForkIntoBackground): Likewise.
* src/conf/domain_conf.h (_virDomainObj): Likewise.
* src/probes.d (rpc_socket_new): Likewise.
* src/qemu/qemu_command.h (qemuParseCommandLinePid): Likewise.
* src/qemu/qemu_driver.c (qemudGetProcessInfo, qemuDomainAttach):
Likewise.
* src/qemu/qemu_process.c (qemuProcessAttach): Likewise.
* src/qemu/qemu_process.h (qemuProcessAttach): Likewise.
* src/uml/uml_driver.c (umlGetProcessInfo): Likewise.
* src/util/virnetdev.h (virNetDevSetNamespace): Likewise.
* src/util/virnetdev.c (virNetDevSetNamespace): Likewise.
* tests/testutils.c (virtTestCaptureProgramOutput): Likewise.
* src/conf/storage_conf.h (_virStoragePerms): Use mode_t, uid_t,
and gid_t rather than int.
* src/security/security_dac.c (virSecurityDACSetOwnership): Likewise.
* src/conf/storage_conf.c (virStorageDefParsePerms): Avoid
compiler warning.
2012-02-10 16:08:11 -07:00
|
|
|
unsigned long pid_value = (unsigned long)(intptr_t)name;
|
|
|
|
return virHashCodeGen(&pid_value, sizeof(pid_value), seed);
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
virCgroupPidEqual(const void *namea, const void *nameb)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
|
|
|
return namea == nameb;
|
|
|
|
}
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
virCgroupPidCopy(const void *name)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
|
|
|
return (void*)name;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
2011-02-22 17:33:59 +00:00
|
|
|
/*
|
2013-07-19 15:43:04 +01:00
|
|
|
* Returns 1 if some PIDs are killed, 0 if none are killed, or -1 on error
|
2011-02-22 17:33:59 +00:00
|
|
|
*/
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupKill(virCgroupPtr group, int signum)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
|
|
|
VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
|
2013-07-19 15:43:04 +01:00
|
|
|
int ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
/* The 'tasks' file in cgroups can contain duplicated
|
|
|
|
* pids, so we use a hash to track which we've already
|
|
|
|
* killed.
|
|
|
|
*/
|
|
|
|
virHashTablePtr pids = virHashCreateFull(100,
|
|
|
|
NULL,
|
|
|
|
virCgroupPidCode,
|
|
|
|
virCgroupPidEqual,
|
|
|
|
virCgroupPidCopy,
|
|
|
|
NULL);
|
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
ret = virCgroupKillInternal(group, signum, pids);
|
2011-02-22 17:33:59 +00:00
|
|
|
|
|
|
|
virHashFree(pids);
|
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
return ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
static int
|
|
|
|
virCgroupKillRecursiveInternal(virCgroupPtr group,
|
|
|
|
int signum,
|
|
|
|
virHashTablePtr pids,
|
|
|
|
bool dormdir)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
2013-07-19 15:43:04 +01:00
|
|
|
int ret = -1;
|
2011-02-22 17:33:59 +00:00
|
|
|
int rc;
|
2013-07-19 15:43:04 +01:00
|
|
|
bool killedAny = false;
|
2011-02-22 17:33:59 +00:00
|
|
|
char *keypath = NULL;
|
2014-05-13 16:01:16 +08:00
|
|
|
DIR *dp = NULL;
|
2011-02-22 17:33:59 +00:00
|
|
|
virCgroupPtr subgroup = NULL;
|
|
|
|
struct dirent *ent;
|
2014-04-25 14:45:49 -06:00
|
|
|
int direrr;
|
2013-08-12 13:52:06 -06:00
|
|
|
VIR_DEBUG("group=%p path=%s signum=%d pids=%p",
|
|
|
|
group, group->path, signum, pids);
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (virCgroupPathOfController(group, -1, "", &keypath) < 0)
|
2013-07-19 15:43:04 +01:00
|
|
|
return -1;
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
if ((rc = virCgroupKillInternal(group, signum, pids)) < 0)
|
2014-05-13 16:01:16 +08:00
|
|
|
goto cleanup;
|
2013-07-19 15:43:04 +01:00
|
|
|
if (rc == 1)
|
|
|
|
killedAny = true;
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-26 16:02:22 +01:00
|
|
|
VIR_DEBUG("Iterate over children of %s (killedAny=%d)", keypath, killedAny);
|
2011-02-22 17:33:59 +00:00
|
|
|
if (!(dp = opendir(keypath))) {
|
2013-07-26 16:02:22 +01:00
|
|
|
if (errno == ENOENT) {
|
|
|
|
VIR_DEBUG("Path %s does not exist, assuming done", keypath);
|
|
|
|
killedAny = false;
|
|
|
|
goto done;
|
|
|
|
}
|
2013-07-19 15:43:04 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Cannot open %s"), keypath);
|
2014-05-13 16:01:16 +08:00
|
|
|
goto cleanup;
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
|
|
|
|
2014-04-25 14:45:49 -06:00
|
|
|
while ((direrr = virDirRead(dp, &ent, keypath)) > 0) {
|
2011-02-22 17:33:59 +00:00
|
|
|
if (STREQ(ent->d_name, "."))
|
|
|
|
continue;
|
|
|
|
if (STREQ(ent->d_name, ".."))
|
|
|
|
continue;
|
|
|
|
if (ent->d_type != DT_DIR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
VIR_DEBUG("Process subdir %s", ent->d_name);
|
|
|
|
|
2013-07-19 11:13:05 +01:00
|
|
|
if (virCgroupNew(-1, ent->d_name, group, -1, &subgroup) < 0)
|
2011-02-22 17:33:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
if ((rc = virCgroupKillRecursiveInternal(subgroup, signum, pids,
|
|
|
|
true)) < 0)
|
2011-02-22 17:33:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
if (rc == 1)
|
2013-07-19 15:43:04 +01:00
|
|
|
killedAny = true;
|
2011-02-22 17:33:59 +00:00
|
|
|
|
|
|
|
if (dormdir)
|
|
|
|
virCgroupRemove(subgroup);
|
|
|
|
|
|
|
|
virCgroupFree(&subgroup);
|
|
|
|
}
|
2014-04-25 14:45:49 -06:00
|
|
|
if (direrr < 0)
|
|
|
|
goto cleanup;
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-26 16:02:22 +01:00
|
|
|
done:
|
2013-07-19 15:43:04 +01:00
|
|
|
ret = killedAny ? 1 : 0;
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2011-02-22 17:33:59 +00:00
|
|
|
virCgroupFree(&subgroup);
|
2014-05-13 16:01:16 +08:00
|
|
|
VIR_FREE(keypath);
|
|
|
|
if (dp)
|
|
|
|
closedir(dp);
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
return ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupKillRecursive(virCgroupPtr group, int signum)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
2013-07-19 15:43:04 +01:00
|
|
|
int ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
|
|
|
|
virHashTablePtr pids = virHashCreateFull(100,
|
|
|
|
NULL,
|
|
|
|
virCgroupPidCode,
|
|
|
|
virCgroupPidEqual,
|
|
|
|
virCgroupPidCopy,
|
|
|
|
NULL);
|
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
ret = virCgroupKillRecursiveInternal(group, signum, pids, false);
|
2011-02-22 17:33:59 +00:00
|
|
|
|
|
|
|
virHashFree(pids);
|
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
return ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupKillPainfully(virCgroupPtr group)
|
2011-02-22 17:33:59 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2013-07-19 15:43:04 +01:00
|
|
|
int ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
VIR_DEBUG("cgroup=%p path=%s", group, group->path);
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < 15; i++) {
|
2011-02-22 17:33:59 +00:00
|
|
|
int signum;
|
|
|
|
if (i == 0)
|
|
|
|
signum = SIGTERM;
|
|
|
|
else if (i == 8)
|
|
|
|
signum = SIGKILL;
|
|
|
|
else
|
2012-10-11 18:31:20 +02:00
|
|
|
signum = 0; /* Just check for existence */
|
2011-02-22 17:33:59 +00:00
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
ret = virCgroupKillRecursive(group, signum);
|
|
|
|
VIR_DEBUG("Iteration %zu rc=%d", i, ret);
|
|
|
|
/* If ret == -1 we hit error, if 0 we ran out of PIDs */
|
|
|
|
if (ret <= 0)
|
2011-02-22 17:33:59 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
usleep(200 * 1000);
|
|
|
|
}
|
2013-07-19 15:43:04 +01:00
|
|
|
VIR_DEBUG("Complete %d", ret);
|
|
|
|
return ret;
|
2011-02-22 17:33:59 +00:00
|
|
|
}
|
2011-02-28 14:13:58 +00:00
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
|
|
|
|
static char *
|
|
|
|
virCgroupIdentifyRoot(virCgroupPtr group)
|
2013-04-05 12:48:47 +01:00
|
|
|
{
|
|
|
|
char *ret = NULL;
|
|
|
|
size_t i;
|
|
|
|
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2013-04-05 12:48:47 +01:00
|
|
|
char *tmp;
|
|
|
|
if (!group->controllers[i].mountPoint)
|
|
|
|
continue;
|
|
|
|
if (!(tmp = strrchr(group->controllers[i].mountPoint, '/'))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Could not find directory separator in %s"),
|
|
|
|
group->controllers[i].mountPoint);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-08 11:08:46 +01:00
|
|
|
if (VIR_STRNDUP(ret, group->controllers[i].mountPoint,
|
|
|
|
tmp - group->controllers[i].mountPoint) < 0)
|
|
|
|
return NULL;
|
2013-04-05 12:48:47 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Could not find any mounted controllers"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 11:47:13 -06:00
|
|
|
/**
|
|
|
|
* virCgroupGetCpuCfsQuota:
|
|
|
|
*
|
|
|
|
* @group: The cgroup to get cpu.cfs_quota_us for
|
|
|
|
* @cfs_quota: Pointer to the returned cpu bandwidth (in usecs) that this tg
|
|
|
|
* will be allowed to consume over period
|
|
|
|
*
|
|
|
|
* Returns: 0 on success
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virCgroupGetCpuCfsQuota(virCgroupPtr group, long long *cfs_quota)
|
|
|
|
{
|
|
|
|
return virCgroupGetValueI64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPU,
|
|
|
|
"cpu.cfs_quota_us", cfs_quota);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage)
|
|
|
|
{
|
|
|
|
return virCgroupGetValueU64(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_CPUACCT,
|
|
|
|
"cpuacct.usage", usage);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuacctStat(virCgroupPtr group, unsigned long long *user,
|
|
|
|
unsigned long long *sys)
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
char *p;
|
|
|
|
int ret = -1;
|
|
|
|
static double scale = -1.0;
|
|
|
|
|
|
|
|
if (virCgroupGetValueStr(group, VIR_CGROUP_CONTROLLER_CPUACCT,
|
|
|
|
"cpuacct.stat", &str) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(p = STRSKIP(str, "user ")) ||
|
|
|
|
virStrToLong_ull(p, &p, 10, user) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot parse user stat '%s'"),
|
|
|
|
p);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!(p = STRSKIP(p, "\nsystem ")) ||
|
|
|
|
virStrToLong_ull(p, NULL, 10, sys) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot parse sys stat '%s'"),
|
|
|
|
p);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
/* times reported are in system ticks (generally 100 Hz), but that
|
|
|
|
* rate can theoretically vary between machines. Scale things
|
|
|
|
* into approximate nanoseconds. */
|
|
|
|
if (scale < 0) {
|
|
|
|
long ticks_per_sec = sysconf(_SC_CLK_TCK);
|
|
|
|
if (ticks_per_sec == -1) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Cannot determine system clock HZ"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
scale = 1000000000.0 / ticks_per_sec;
|
|
|
|
}
|
|
|
|
*user *= scale;
|
|
|
|
*sys *= scale;
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-08-12 11:47:13 -06:00
|
|
|
VIR_FREE(str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetFreezerState(virCgroupPtr group, const char *state)
|
|
|
|
{
|
|
|
|
return virCgroupSetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_FREEZER,
|
|
|
|
"freezer.state", state);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetFreezerState(virCgroupPtr group, char **state)
|
|
|
|
{
|
|
|
|
return virCgroupGetValueStr(group,
|
|
|
|
VIR_CGROUP_CONTROLLER_FREEZER,
|
|
|
|
"freezer.state", state);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupIsolateMount(virCgroupPtr group, const char *oldroot,
|
|
|
|
const char *mountopts)
|
2013-04-05 12:48:47 +01:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
char *opts = NULL;
|
|
|
|
char *root = NULL;
|
|
|
|
|
|
|
|
if (!(root = virCgroupIdentifyRoot(group)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Mounting cgroups at '%s'", root);
|
|
|
|
|
|
|
|
if (virFileMakePath(root) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to create directory %s"),
|
|
|
|
root);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&opts,
|
2013-07-04 12:17:18 +02:00
|
|
|
"mode=755,size=65536%s", mountopts) < 0)
|
2013-04-05 12:48:47 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_NODEV|MS_NOEXEC, opts) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Failed to mount %s on %s type %s"),
|
|
|
|
"tmpfs", root, "tmpfs");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-21 15:58:16 +08:00
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
2013-04-05 12:48:47 +01:00
|
|
|
if (!group->controllers[i].mountPoint)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!virFileExists(group->controllers[i].mountPoint)) {
|
|
|
|
char *src;
|
|
|
|
if (virAsprintf(&src, "%s%s%s",
|
|
|
|
oldroot,
|
|
|
|
group->controllers[i].mountPoint,
|
2013-07-04 12:17:18 +02:00
|
|
|
group->controllers[i].placement) < 0)
|
2013-04-05 12:48:47 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
VIR_DEBUG("Create mount point '%s'",
|
|
|
|
group->controllers[i].mountPoint);
|
2013-04-05 12:48:47 +01:00
|
|
|
if (virFileMakePath(group->controllers[i].mountPoint) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to create directory %s"),
|
|
|
|
group->controllers[i].mountPoint);
|
|
|
|
VIR_FREE(src);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
if (mount(src, group->controllers[i].mountPoint, NULL, MS_BIND,
|
|
|
|
NULL) < 0) {
|
2013-04-05 12:48:47 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Failed to bind cgroup '%s' on '%s'"),
|
|
|
|
src, group->controllers[i].mountPoint);
|
2013-07-08 11:08:46 +01:00
|
|
|
VIR_FREE(src);
|
2013-04-05 12:48:47 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(src);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group->controllers[i].linkPoint) {
|
|
|
|
VIR_DEBUG("Link mount point '%s' to '%s'",
|
|
|
|
group->controllers[i].mountPoint,
|
|
|
|
group->controllers[i].linkPoint);
|
|
|
|
if (symlink(group->controllers[i].mountPoint,
|
|
|
|
group->controllers[i].linkPoint) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to symlink directory %s to %s"),
|
|
|
|
group->controllers[i].mountPoint,
|
|
|
|
group->controllers[i].linkPoint);
|
2014-09-01 20:08:07 +08:00
|
|
|
goto cleanup;
|
2013-04-05 12:48:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-04-05 12:48:47 +01:00
|
|
|
VIR_FREE(root);
|
|
|
|
VIR_FREE(opts);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-08-12 14:02:26 -06:00
|
|
|
|
|
|
|
|
2014-02-24 12:23:33 +00:00
|
|
|
int virCgroupSetOwner(virCgroupPtr cgroup,
|
|
|
|
uid_t uid,
|
|
|
|
gid_t gid,
|
|
|
|
int controllers)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
char *base = NULL, *entry = NULL;
|
|
|
|
DIR *dh = NULL;
|
2014-04-25 14:45:49 -06:00
|
|
|
int direrr;
|
2014-02-24 12:23:33 +00:00
|
|
|
|
|
|
|
for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
|
|
|
|
struct dirent *de;
|
|
|
|
|
|
|
|
if (!((1 << i) & controllers))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!cgroup->controllers[i].mountPoint)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virAsprintf(&base, "%s%s", cgroup->controllers[i].mountPoint,
|
|
|
|
cgroup->controllers[i].placement) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(dh = opendir(base))) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to open dir '%s'"), base);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-04-25 14:45:49 -06:00
|
|
|
while ((direrr = virDirRead(dh, &de, base)) > 0) {
|
2014-02-24 12:23:33 +00:00
|
|
|
if (STREQ(de->d_name, ".") ||
|
|
|
|
STREQ(de->d_name, ".."))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virAsprintf(&entry, "%s/%s", base, de->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (chown(entry, uid, gid) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot chown '%s' to (%u, %u)"),
|
|
|
|
entry, uid, gid);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(entry);
|
|
|
|
}
|
2014-04-25 14:45:49 -06:00
|
|
|
if (direrr < 0)
|
|
|
|
goto cleanup;
|
2014-02-24 12:23:33 +00:00
|
|
|
|
|
|
|
if (chown(base, uid, gid) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot chown '%s' to (%u, %u)"),
|
|
|
|
base, uid, gid);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(base);
|
|
|
|
closedir(dh);
|
|
|
|
dh = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (dh)
|
|
|
|
closedir(dh);
|
|
|
|
VIR_FREE(entry);
|
|
|
|
VIR_FREE(base);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-13 16:03:14 +02:00
|
|
|
/**
|
|
|
|
* virCgroupSupportsCpuBW():
|
|
|
|
* Check whether the host supports CFS bandwidth.
|
|
|
|
*
|
|
|
|
* Return true when CFS bandwidth is supported,
|
|
|
|
* false when CFS bandwidth is not supported.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virCgroupSupportsCpuBW(virCgroupPtr cgroup)
|
|
|
|
{
|
|
|
|
char *path = NULL;
|
|
|
|
int ret = false;
|
|
|
|
|
|
|
|
if (!cgroup)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (virCgroupPathOfController(cgroup, VIR_CGROUP_CONTROLLER_CPU,
|
|
|
|
"cpu.cfs_period_us", &path) < 0) {
|
|
|
|
virResetLastError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virFileExists(path);
|
|
|
|
|
2014-03-25 07:53:22 +01:00
|
|
|
cleanup:
|
2013-09-13 16:03:14 +02:00
|
|
|
VIR_FREE(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 14:02:26 -06:00
|
|
|
#else /* !VIR_CGROUP_SUPPORTED */
|
|
|
|
|
2013-08-11 16:04:27 +04:00
|
|
|
bool
|
|
|
|
virCgroupAvailable(void)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:28 +04:00
|
|
|
int
|
|
|
|
virCgroupNewPartition(const char *path ATTRIBUTE_UNUSED,
|
|
|
|
bool create ATTRIBUTE_UNUSED,
|
|
|
|
int controllers ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupNewSelf(virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupNewDomainPartition(virCgroupPtr partition ATTRIBUTE_UNUSED,
|
|
|
|
const char *driver ATTRIBUTE_UNUSED,
|
|
|
|
const char *name ATTRIBUTE_UNUSED,
|
|
|
|
bool create ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupNewVcpu(virCgroupPtr domain ATTRIBUTE_UNUSED,
|
|
|
|
int vcpuid ATTRIBUTE_UNUSED,
|
|
|
|
bool create ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupNewEmulator(virCgroupPtr domain ATTRIBUTE_UNUSED,
|
|
|
|
bool create ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-15 14:45:23 -04:00
|
|
|
int
|
|
|
|
virCgroupNewIOThread(virCgroupPtr domain ATTRIBUTE_UNUSED,
|
|
|
|
int iothreadid ATTRIBUTE_UNUSED,
|
|
|
|
bool create ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:28 +04:00
|
|
|
int
|
|
|
|
virCgroupNewDetect(pid_t pid ATTRIBUTE_UNUSED,
|
|
|
|
int controllers ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:27 +04:00
|
|
|
int
|
|
|
|
virCgroupNewDetectMachine(const char *name ATTRIBUTE_UNUSED,
|
|
|
|
const char *drivername ATTRIBUTE_UNUSED,
|
|
|
|
pid_t pid ATTRIBUTE_UNUSED,
|
|
|
|
const char *partition ATTRIBUTE_UNUSED,
|
|
|
|
int controllers ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-02 11:05:50 +02:00
|
|
|
|
|
|
|
int virCgroupTerminateMachine(const char *name ATTRIBUTE_UNUSED,
|
|
|
|
const char *drivername ATTRIBUTE_UNUSED,
|
|
|
|
bool privileged ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:29 +04:00
|
|
|
int
|
|
|
|
virCgroupNewMachine(const char *name ATTRIBUTE_UNUSED,
|
|
|
|
const char *drivername ATTRIBUTE_UNUSED,
|
|
|
|
bool privileged ATTRIBUTE_UNUSED,
|
|
|
|
const unsigned char *uuid ATTRIBUTE_UNUSED,
|
|
|
|
const char *rootdir ATTRIBUTE_UNUSED,
|
|
|
|
pid_t pidleader ATTRIBUTE_UNUSED,
|
|
|
|
bool isContainer ATTRIBUTE_UNUSED,
|
|
|
|
const char *partition ATTRIBUTE_UNUSED,
|
|
|
|
int controllers ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
virCgroupNewIgnoreError(void)
|
|
|
|
{
|
|
|
|
VIR_DEBUG("No cgroups present/configured/accessible, ignoring error");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-08-11 16:04:27 +04:00
|
|
|
|
|
|
|
void
|
|
|
|
virCgroupFree(virCgroupPtr *group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
virCgroupHasController(virCgroupPtr cgroup ATTRIBUTE_UNUSED,
|
|
|
|
int controller ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:28 +04:00
|
|
|
int
|
|
|
|
virCgroupPathOfController(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
int controller ATTRIBUTE_UNUSED,
|
|
|
|
const char *key ATTRIBUTE_UNUSED,
|
|
|
|
char **path ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:29 +04:00
|
|
|
int
|
|
|
|
virCgroupAddTask(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
pid_t pid ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupAddTaskController(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
pid_t pid ATTRIBUTE_UNUSED,
|
|
|
|
int controller ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupMoveTask(virCgroupPtr src_group ATTRIBUTE_UNUSED,
|
|
|
|
virCgroupPtr dest_group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-14 18:48:59 +01:00
|
|
|
int
|
|
|
|
virCgroupGetBlkioIoServiced(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
long long *bytes_read ATTRIBUTE_UNUSED,
|
|
|
|
long long *bytes_write ATTRIBUTE_UNUSED,
|
|
|
|
long long *requests_read ATTRIBUTE_UNUSED,
|
|
|
|
long long *requests_write ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetBlkioIoDeviceServiced(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
long long *bytes_read ATTRIBUTE_UNUSED,
|
|
|
|
long long *bytes_write ATTRIBUTE_UNUSED,
|
|
|
|
long long *requests_read ATTRIBUTE_UNUSED,
|
|
|
|
long long *requests_write ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:29 +04:00
|
|
|
int
|
|
|
|
virCgroupSetBlkioWeight(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int weight ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetBlkioWeight(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int *weight ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:28 +04:00
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceWeight(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int weight ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-12-11 16:29:50 +08:00
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceReadIops(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int riops ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceWriteIops(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int wiops ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceReadBps(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long rbps ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetBlkioDeviceWriteBps(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long wbps ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-08-11 16:04:28 +04:00
|
|
|
|
2013-08-11 16:04:29 +04:00
|
|
|
int
|
|
|
|
virCgroupSetMemory(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetMemoryUsage(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long *kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetMemoryHardLimit(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetMemoryHardLimit(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetMemorySoftLimit(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetMemorySoftLimit(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetMemSwapHardLimit(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetMemSwapHardLimit(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetMemSwapUsage(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *kb ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetCpusetMems(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *mems ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpusetMems(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char **mems ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetCpusetCpus(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *cpus ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpusetCpus(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char **cpus ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-07-18 10:02:29 +02:00
|
|
|
int
|
|
|
|
virCgroupAllowAllDevices(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
int perms ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
2013-08-11 16:04:29 +04:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupDenyAllDevices(virCgroupPtr group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupAllowDevice(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char type ATTRIBUTE_UNUSED,
|
|
|
|
int major ATTRIBUTE_UNUSED,
|
|
|
|
int minor ATTRIBUTE_UNUSED,
|
|
|
|
int perms ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupAllowDeviceMajor(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char type ATTRIBUTE_UNUSED,
|
|
|
|
int major ATTRIBUTE_UNUSED,
|
|
|
|
int perms ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:28 +04:00
|
|
|
int
|
|
|
|
virCgroupAllowDevicePath(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
int perms ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:29 +04:00
|
|
|
int
|
|
|
|
virCgroupDenyDevice(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char type ATTRIBUTE_UNUSED,
|
|
|
|
int major ATTRIBUTE_UNUSED,
|
|
|
|
int minor ATTRIBUTE_UNUSED,
|
|
|
|
int perms ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupDenyDeviceMajor(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char type ATTRIBUTE_UNUSED,
|
|
|
|
int major ATTRIBUTE_UNUSED,
|
|
|
|
int perms ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:28 +04:00
|
|
|
int
|
|
|
|
virCgroupDenyDevicePath(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
int perms ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:29 +04:00
|
|
|
int
|
|
|
|
virCgroupSetCpuShares(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long shares ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuShares(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *shares ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetCpuCfsPeriod(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long cfs_period ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuCfsPeriod(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *cfs_period ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetCpuCfsQuota(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
long long cfs_quota ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 14:02:26 -06:00
|
|
|
int
|
|
|
|
virCgroupRemoveRecursively(char *grppath ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-11 16:04:29 +04:00
|
|
|
int
|
|
|
|
virCgroupRemove(virCgroupPtr group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENXIO, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 14:02:26 -06:00
|
|
|
int
|
|
|
|
virCgroupKill(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
int signum ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupKillRecursive(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
int signum ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupKillPainfully(virCgroupPtr group ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 11:47:13 -06:00
|
|
|
int
|
|
|
|
virCgroupGetCpuCfsQuota(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
long long *cfs_quota ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuacctUsage(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *usage ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuacctPercpuUsage(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char **usage ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetCpuacctStat(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *user ATTRIBUTE_UNUSED,
|
|
|
|
unsigned long long *sys ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-21 09:10:48 +01:00
|
|
|
int
|
|
|
|
virCgroupGetDomainTotalCpuStats(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
virTypedParameterPtr params ATTRIBUTE_UNUSED,
|
|
|
|
int nparams ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 11:47:13 -06:00
|
|
|
int
|
|
|
|
virCgroupSetFreezerState(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *state ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetFreezerState(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
char **state ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-12 13:52:06 -06:00
|
|
|
int
|
|
|
|
virCgroupIsolateMount(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
const char *oldroot ATTRIBUTE_UNUSED,
|
|
|
|
const char *mountopts ATTRIBUTE_UNUSED)
|
2013-04-05 12:48:47 +01:00
|
|
|
{
|
2013-07-08 11:08:46 +01:00
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
2013-04-05 12:48:47 +01:00
|
|
|
}
|
2013-08-12 14:02:26 -06:00
|
|
|
|
2013-09-13 16:03:14 +02:00
|
|
|
|
|
|
|
bool
|
|
|
|
virCgroupSupportsCpuBW(virCgroupPtr cgroup ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
VIR_DEBUG("Control groups not supported on this platform");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-02-25 17:18:54 -07:00
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupGetPercpuStats(virCgroupPtr group ATTRIBUTE_UNUSED,
|
|
|
|
virTypedParameterPtr params ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int nparams ATTRIBUTE_UNUSED,
|
|
|
|
int start_cpu ATTRIBUTE_UNUSED,
|
2014-04-09 16:47:26 +02:00
|
|
|
unsigned int ncpus ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int nvcpupids ATTRIBUTE_UNUSED)
|
2014-02-25 17:18:54 -07:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virCgroupSetOwner(virCgroupPtr cgroup ATTRIBUTE_UNUSED,
|
|
|
|
uid_t uid ATTRIBUTE_UNUSED,
|
|
|
|
gid_t gid ATTRIBUTE_UNUSED,
|
|
|
|
int controllers ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("Control groups not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-08-12 14:02:26 -06:00
|
|
|
#endif /* !VIR_CGROUP_SUPPORTED */
|